radiant-forum-extension 2.0.9 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -21,7 +21,7 @@ As far as I know this is the best forum software available for rails. I look for
21
21
 
22
22
  ## Status
23
23
 
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, please.
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.
25
25
 
26
26
  ## Latest
27
27
 
@@ -91,6 +91,12 @@ You probably also want to include this somewhere, as on all reader-service pages
91
91
 
92
92
  Have a look at the included sample layout for a starting point.
93
93
 
94
+ ## Speed and caching
95
+
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.
99
+
94
100
  ## Private discussion
95
101
 
96
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.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.0.9
1
+ 2.1.1
@@ -32,12 +32,16 @@ class PostsController < ForumBaseController
32
32
  format.html {
33
33
  expires_now
34
34
  }
35
- format.js {
36
- render :partial => 'posts/form', :layout => false
35
+ format.js {
36
+ if @post.page
37
+ render :partial => 'pages/add_comment', :layout => false
38
+ else
39
+ render :partial => 'topics/reply', :layout => false
40
+ end
37
41
  }
38
42
  end
39
43
  end
40
-
44
+
41
45
  def create
42
46
  @post.save!
43
47
  Radiant::Cache.clear if @post.page
@@ -49,16 +53,14 @@ class PostsController < ForumBaseController
49
53
  flash[:error] = t("validation_failure")
50
54
  respond_to do |format|
51
55
  format.html { render :action => 'new' }
52
- format.js { render :partial => 'posts/form' }
56
+ format.js { render :template => 'posts/new', :layout => false }
53
57
  end
54
58
  end
55
59
 
56
60
  def edit
57
61
  respond_to do |format|
58
- format.html {
59
- expires_now
60
- }
61
- format.js { render :partial => 'posts/form' }
62
+ format.html { expires_now }
63
+ format.js { render :template => 'posts/edit', :layout => false }
62
64
  end
63
65
  end
64
66
 
@@ -74,7 +76,7 @@ class PostsController < ForumBaseController
74
76
  flash[:error] = t("validation_failure")
75
77
  respond_to do |format|
76
78
  format.html { render :action => 'edit' }
77
- format.js { render :partial => 'posts/form' }
79
+ format.js { render :template => 'posts/edit', :layout => false }
78
80
  end
79
81
  end
80
82
 
@@ -1,23 +1,33 @@
1
- - add_reader_css '/stylesheets/forum.css'
2
- - if current_reader
1
+ - content_for :forum_css do
2
+ %link{:rel => 'stylesheet', :type => 'text/css', :href => '/stylesheets/forum.css', :media => 'all'}
3
+
4
+ - content_for :gallery_js do
5
+ %script{:src => '/javascripts/gallery.js', :type => 'text/javascript'}
6
+
7
+ - content_for :forum_js do
8
+ %script{:src => '/javascripts/forum.js', :type => 'text/javascript'}
9
+
10
+ - content_for :toolbar_js do
3
11
  - if Radiant::Config['forum.toolbar?']
4
- - add_reader_js '/punymce/puny_mce_src.js'
5
- - add_reader_js '/punymce/plugins/link/link.js'
6
- - add_reader_js '/punymce/plugins/image/image.js'
7
- - add_reader_js '/punymce/plugins/emoticons/emoticons.js'
8
- - add_reader_js '/punymce/plugins/editsource/editsource.js'
9
- - add_reader_js '/punymce/plugins/paste.js'
10
- - add_reader_js '/javascripts/forum.js'
11
- - add_reader_js '/javascripts/gallery.js'
12
+ %script{:src => '/punymce/puny_mce_src.js', :type => 'text/javascript'}
13
+ %script{:src => '/punymce/plugins/link/link.js', :type => 'text/javascript'}
14
+ %script{:src => '/punymce/plugins/image/image.js', :type => 'text/javascript'}
15
+ %script{:src => '/punymce/plugins/emoticons/emoticons.js', :type => 'text/javascript'}
16
+ %script{:src => '/punymce/plugins/editsource/editsource.js', :type => 'text/javascript'}
17
+ %script{:src => '/punymce/plugins/paste.js', :type => 'text/javascript'}
18
+
19
+ - content_for :all_forum_js do
20
+ = yield :forum_js
21
+ = yield :gallery_js
22
+ = yield :toolbar_js
12
23
 
13
24
  - content_for :section_navigation do
14
25
  = link_to t('navigation.forum'), topics_url, :class => 'section'
15
26
  = link_to t('navigation.topics'), topics_url
16
27
  = link_to t('navigation.forums'), forums_url
17
- = link_to t('navigation.readers'), readers_url
18
- = link_to t('navigation.new_topic'), new_post_url
19
- = link_to t('navigation.your_account'), reader_account_url
20
28
  = link_to t('navigation.search'), posts_url
29
+ = link_to t('navigation.new_topic'), new_post_url
30
+ = link_to t('navigation.loading'), reader_session_url, :class => "remotecontent"
21
31
  - if Radiant::Config['forum.help_url']
22
32
  = link_to t('navigation.forum_help'), Radiant::Config['forum.help_url']
23
33
 
@@ -0,0 +1,23 @@
1
+ - page ||= @page
2
+
3
+ - if page.commentable?
4
+ - if current_reader
5
+ - if current_reader.activated?
6
+ .post.replyform
7
+ .wrapper
8
+ .post_header
9
+ .speaker
10
+ = standard_gravatar_for(current_reader)
11
+ %h2.person
12
+ = t("add_comment")
13
+ %p.context
14
+ = t('if_you_not', :name => current_reader.name)
15
+ = link_to(t('log_out'), reader_logout_url) + '.'
16
+ = render :partial => 'posts/form', :locals => {:post => page.posts.build, :omit_label => true}
17
+ - else
18
+ = render :partial => 'reader_activations/activation_required', :locals => {:purpose => t('to_reply')}
19
+ - else
20
+ = render :partial => 'reader_sessions/login_form', :locals => {:purpose => t('to_reply')}
21
+ - else
22
+ %p.administrative
23
+ = t("page_locked")
@@ -1,15 +1,13 @@
1
+ - posts ||= @posts
2
+ - page ||= @page
3
+
1
4
  #forum
2
5
  %h2
3
- Comments on
6
+ = t('comments_on')
4
7
  = page.title
5
8
 
6
- - if posts.nil?
7
- %p None yet
8
- %pre
9
- = posts.inspect
10
-
9
+ - if posts.empty?
10
+ %p
11
+ = t('none_yet')
11
12
  - else
12
- %pre
13
- = posts.inspect
14
- %p
15
- = current_reader.inspect
13
+ = render :partial => 'posts/post', :collection => posts
@@ -3,7 +3,7 @@
3
3
  - if post.editable_by?(current_reader)
4
4
  - if post.editable_interval
5
5
  = t('time_remaining_to_edit', :time => distance_of_time_in_words(post.still_editable_for))
6
- = link_to t('edit_your_post'), edit_topic_post_url(post.topic, post), :class => 'edit_post remote', :id => "revise_post_#{post.id}"
6
+ = link_to t('edit_your_post'), edit_topic_post_url(post.topic, post), :class => 'remote', :id => "revise_post_#{post.id}"
7
7
  - elsif admin?
8
8
  = link_to t("edit_minimal"), edit_topic_post_url(post.topic, post), :class => 'edit_post remote', :id => "revise_post_#{post.id}", :title => "edit post"
9
9
  = link_to t("remove_minimal"), remove_post_url(post), :class => 'delete_post remote', :id => "delete_post_#{post.id}", :title => "remove post"
@@ -9,7 +9,7 @@
9
9
  - cssclasses << 'deleted' if post.frozen?
10
10
 
11
11
  .post{:id => post.dom_id, :class => cssclasses.join(' ')}
12
- .post_wrapper
12
+ .wrapper
13
13
  - unless headless
14
14
  .speaker
15
15
  = standard_gravatar_for(post.reader)
@@ -1,7 +1,7 @@
1
1
  = render :partial => 'forums/standard_parts'
2
2
 
3
3
  - content_for :form do
4
- .post_wrapper
4
+ .wrapper
5
5
  .post_header
6
6
  .speaker
7
7
  = standard_gravatar_for(@post.reader)
@@ -2,7 +2,7 @@
2
2
 
3
3
  - content_for :form do
4
4
  .post
5
- .post_wrapper
5
+ .wrapper
6
6
  .post_header
7
7
  .speaker
8
8
  = standard_gravatar_for(current_reader)
@@ -7,7 +7,7 @@
7
7
  - if current_reader
8
8
  - if current_reader.activated?
9
9
  .post.replyform
10
- .post_wrapper
10
+ .wrapper
11
11
  .post_header
12
12
  .speaker
13
13
  = standard_gravatar_for(current_reader)
@@ -2,7 +2,7 @@
2
2
 
3
3
  %div{:id => topic.dom_id}
4
4
  .post
5
- .post_wrapper
5
+ .wrapper
6
6
  .speaker
7
7
  = standard_gravatar_for(post.reader, forum_topic_url(topic.forum, topic))
8
8
  .post_header
@@ -44,8 +44,10 @@
44
44
  = yield :pagination if @posts.next_page
45
45
 
46
46
  - content_for :reply_form do
47
- = render :partial => 'topics/reply', :locals => {:topic => @topic}
48
-
47
+ .new_post
48
+ .wrapper
49
+ %p.add_reply
50
+ = link_to t('add_reply'), new_topic_post_url(@topic), :class => 'remote autoload'
49
51
 
50
52
 
51
53
  #forum
@@ -1,6 +1,7 @@
1
1
  en:
2
2
  a_reply_to: "A reply to"
3
3
  add_post: "Add a %{message_type}"
4
+ add_comment: "Add a comment"
4
5
  add_reply: "Add a reply"
5
6
  ago: "ago"
6
7
  all: "all"
@@ -15,7 +16,13 @@ en:
15
16
  by: "by"
16
17
  by_content_and_author: "by content, author or category."
17
18
  comment: "comment"
19
+ comment_count:
20
+ zero: "No comments yet"
21
+ one: "One comment"
22
+ other: "%{count} comments."
18
23
  comment_on_page: "a comment added to the page"
24
+ comments_closed: "Comments closed."
25
+ comments_on: "Comments on"
19
26
  comments: "comments"
20
27
  config:
21
28
  forum:
@@ -106,6 +113,7 @@ en:
106
113
  now_has: "now has"
107
114
  of: "of"
108
115
  on_date: "on %{date}"
116
+ page_locked: "Sorry: comments are not allowed on this page"
109
117
  post: "comment"
110
118
  post_body: "Post body"
111
119
  post_count_from:
@@ -168,7 +176,7 @@ en:
168
176
  yesterday: "yesterday at %l:%M%p"
169
177
  time_remaining_to_edit: "You have %{time} left to "
170
178
  to_comment: "To comment"
171
- to_post: "To reply"
179
+ to_post: "To post messages"
172
180
  to_start_topic: "To start a topic"
173
181
  topic_count:
174
182
  zero: "No discussion."
data/config/routes.rb CHANGED
@@ -13,4 +13,6 @@ ActionController::Routing::Routes.draw do |map|
13
13
  end
14
14
 
15
15
  map.forum_home "/forum.:format", :controller => 'topics', :action => 'index'
16
+ map.add_comment "/pages/:page_id/posts/new.:format", :controller => 'posts', :action => 'new'
17
+
16
18
  end
data/forum_extension.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require_dependency 'application_controller'
2
2
 
3
3
  class ForumExtension < Radiant::Extension
4
- version "2.0.9"
4
+ version "2.1.1"
5
5
  description "Nice clean forums and page comments for inclusion in your radiant site. Requires the reader extension and share_layouts."
6
6
  url "http://spanner.org/radiant/forum"
7
7
 
@@ -57,7 +57,7 @@ module CommentableModel # for inclusion into ActiveRecord::Base
57
57
  end
58
58
 
59
59
  def voice_count
60
- posts.distinct_readers.count #actually counting posts, but the total is the same
60
+ posts.distinct_readers.count #nb. actually counting posts from distinct readers
61
61
  end
62
62
 
63
63
  def other_voices
data/lib/forum_page.rb CHANGED
@@ -8,18 +8,27 @@ module ForumPage
8
8
  end
9
9
 
10
10
  module InstanceMethods
11
-
12
- def still_commentable?
13
- commentable? && !comments_closed? && (!commentable_period || Time.now - self.created_at < commentable_period)
11
+
12
+ def show_comments?
13
+ commentable?
14
14
  end
15
15
 
16
- def commentable_period
17
- Radiant::Config['forum.commentable_period'].to_i.days if Radiant::Config['forum.commentable_period']
16
+ def still_commentable?
17
+ return false unless Radiant::Config['forum.allow_page_comments?'] && commentable?
18
+ return false if comments_closed?
19
+ return true unless commentable_period && commentable_period > 0
20
+ return Time.now - self.created_at < commentable_period
18
21
  end
19
22
 
20
23
  def locked?
21
24
  !still_commentable?
22
25
  end
26
+
27
+ private
28
+
29
+ def commentable_period
30
+ Radiant::Config['forum.commentable_period'].to_i.days if Radiant::Config['forum.commentable_period']
31
+ end
23
32
 
24
33
  end
25
34
  end
data/lib/forum_tags.rb CHANGED
@@ -277,23 +277,25 @@ module ForumTags
277
277
  The address for add-a-comment links
278
278
  }
279
279
  tag 'comment_url' do |tag|
280
- new_page_post_path(tag.locals.page)
280
+ add_comment_path(tag.locals.page)
281
281
  end
282
282
 
283
283
  tag 'comment_link' do |tag|
284
284
  options = tag.attr.dup
285
- attributes = options.inject('') { |s, (k, v)| s << %{#{k.to_s.downcase}="#{v}" } }.strip
286
- attributes = " #{attributes}" unless attributes.empty?
287
- text = tag.double? ? tag.expand : I18n.t("Add a comment")
288
- %{<a href="#{tag.render('comment_url')}"#{attributes}>#{text}</a>}
285
+ if tag.locals.page.still_commentable?
286
+ attributes = options.inject('') { |s, (k, v)| s << %{#{k.to_s.downcase}="#{v}" } }.strip
287
+ text = tag.double? ? tag.expand : I18n.t("add_comment")
288
+ %{<a href="#{tag.render('comment_url')}" #{attributes}>#{text}</a>}
289
+ else
290
+ I18n.t("comments_closed")
291
+ end
289
292
  end
290
293
 
291
294
  desc %{
292
295
  Anything between if_comments tags is displayed only - dramatic pause - if there are comments.
293
296
  }
294
297
  tag 'if_comments' do |tag|
295
- raise TagError, "can't have if_comments without a page" unless page = tag.locals.page
296
- tag.expand if page.posts.any?
298
+ tag.expand if tag.locals.page.posts.any?
297
299
  end
298
300
 
299
301
  desc %{
@@ -303,38 +305,41 @@ module ForumTags
303
305
  <pre><code><r:unless_comments>...</r:unless_comments></code></pre>
304
306
  }
305
307
  tag 'unless_comments' do |tag|
306
- raise TagError, "can't have unless_comments without a page" unless page = tag.locals.page
307
- tag.expand unless page.posts.any?
308
+ tag.expand unless tag.locals.page.posts.any?
308
309
  end
309
310
 
310
311
  tag 'comments' do |tag|
311
- raise TagError, "can't have comments without a page" unless page = tag.locals.page
312
- if page.commentable?
313
- output = []
314
- tag.locals.posts = tag.locals.paginated_list = page.posts.paginate
315
- tag.expand
316
- end
312
+ tag.expand if tag.locals.page.commentable?
317
313
  end
318
314
 
319
315
  desc %{
320
316
  Renders string (internationalised) like "1 comment", "27 comments" or "no comments yet".
321
317
  }
322
318
  tag 'comments:summary' do |tag|
323
- I18n.t("comment_count", :count => tag.locals.posts.total_entries)
319
+ if tag.locals.posts.respond_to? :total_entries
320
+ I18n.t("comment_count", :count => tag.locals.posts.total_entries)
321
+ else
322
+ I18n.t("comment_count", :count => tag.locals.posts.length)
323
+ end
324
324
  end
325
325
 
326
326
  desc %{
327
- Loops over the (paginated) comment set in ascending order of date. Within the loop you can
327
+ Loops over the (paginated) comment set in ascending order of date. Within the loop you can
328
328
  use the r:comment shorthand or any r:forum:post:* tags. Note that r:forum:topic tags won't
329
329
  work: there is no topic to show.
330
330
  }
331
331
  tag 'comments:each' do |tag|
332
332
  results = []
333
+ if paging = pagination_find_options
334
+ tag.locals.posts = tag.locals.paginated_list = page.posts.paginate(paging)
335
+ else
336
+ tag.locals.posts = page.posts
337
+ end
333
338
  tag.locals.posts.each do |post|
334
339
  tag.locals.post = post
335
340
  results << tag.expand
336
341
  end
337
- results << tag.render('pagination', tag.attr.dup)
342
+ results << tag.render('pagination', tag.attr.dup) if paging
338
343
  results
339
344
  end
340
345
 
@@ -347,15 +352,19 @@ module ForumTags
347
352
  can hook into using the supplied forum javascript or your own equivalent.
348
353
  }
349
354
  tag 'comments:all' do |tag|
350
- posts = tag.locals.posts
355
+ if tag.attr['paginated'] == 'true'
356
+ tag.locals.posts = tag.locals.paginated_list = tag.locals.page.posts.paginate(pagination_parameters)
357
+ else
358
+ tag.locals.posts = tag.locals.page.posts
359
+ end
351
360
  results = ""
352
361
  results << %{<div class="page_comments">}
353
362
  results << %{<p class="context">#{tag.render('comments:summary')}</p>}
354
- posts.each do |post|
363
+ tag.locals.posts.each do |post|
355
364
  tag.locals.post = post
356
365
  results << tag.render('comment')
357
366
  end
358
- results << %{<p class="add_comment">#{tag.render('comment_link', 'class' => 'remote')}</p>}
367
+ results << %{<div class="new_post"><div class="wrapper"><p class="add_comment">#{tag.render('comment_link', 'class' => 'remote')}</p></div></div>}
359
368
  results << tag.render('pagination', tag.attr.dup)
360
369
  results << "</div>"
361
370
  results
@@ -371,7 +380,7 @@ module ForumTags
371
380
  tag.locals.reader = post.reader
372
381
  tag.expand
373
382
  else
374
- output = %{<div class="post"><div class="post_wrapper">}
383
+ output = %{<div class="post"><div class="wrapper">}
375
384
  output << %{<div class="post_header">}
376
385
  output << %{<h2>#{tag.render("forum:post:reader")}</h2>}
377
386
  output << %{<p class="context">#{tag.render("forum:post:context")}</p>}
@@ -1,5 +1,5 @@
1
1
  /*
2
- Sample jquery-based forum scripts.
2
+ Sample jquery-based forum and gallery scripts.
3
3
 
4
4
  * inline editing of posts by authors and administrators.
5
5
  * attachment and upload of files.
@@ -10,21 +10,6 @@
10
10
 
11
11
  (function($) {
12
12
 
13
- $.ajaxSetup({
14
- 'beforeSend': function(xhr) {
15
- xhr.setRequestHeader("Accept", "text/javascript");
16
- }
17
- });
18
-
19
- // general-purpose event blocker
20
- function squash(e) {
21
- if(e) {
22
- e.preventDefault();
23
- e.stopPropagation();
24
- if (e.target) e.target.blur();
25
- }
26
- }
27
-
28
13
  function RemoteAction (url, holder) {
29
14
  var self = this;
30
15
  $.extend(self, {
@@ -33,36 +18,41 @@
33
18
  container: null,
34
19
  form: null,
35
20
 
36
- getForm: function () {
21
+ fetch: function () {
37
22
  if (self.showing()) self.hide();
38
23
  else if (self.form) self.show();
39
24
  else {
40
25
  self.wait();
41
- self.container.load(self.url, self.captureForm);
26
+ $.get(self.url, self.step);
42
27
  }
43
28
  },
44
- captureForm: function () {
45
- self.form = self.container.find('form');
46
- self.form.submit(self.submitForm);
47
- self.form.find('div.upload_stack').upload_stack();
48
- self.form.find('a.cancel').click(self.cancel);
49
- self.form.find("textarea.toolbarred").add_editor({});
50
-
51
- self.unwait();
52
- self.show();
53
- },
54
- submitForm: function (event) {
29
+ submit: function (e) {
55
30
  var ajaxable = true;
56
31
  self.container.find('input:file').each(function () {
57
32
  var file = $(this).val();
58
33
  if (file && file != "") ajaxable = false;
59
34
  });
60
35
  if (ajaxable) {
61
- squash(event);
36
+ e.preventDefault();
62
37
  self.form.find('textarea.toolbarred').read_editor();
63
- $.post(self.form.attr('action'), self.form.serialize(), self.finish);
38
+ $.post(self.form.attr('action'), self.form.serialize(), self.step);
64
39
  } else {
65
40
  return true; // allow event through so that uploads are sent by normal HTTP POST
41
+ // toolbar is read in onSubmit
42
+ }
43
+ },
44
+ step: function (results) {
45
+ self.unwait();
46
+ if (results) self.container.html(results);
47
+ self.form = self.container.find('form');
48
+ if (self.form.length > 0) {
49
+ self.form.submit(self.submit);
50
+ self.form.find('div.upload_stack').upload_stack();
51
+ self.form.find('a.cancel').click(self.cancel);
52
+ self.form.find("textarea.toolbarred").add_editor({});
53
+ self.show();
54
+ } else {
55
+ holder.replaceWith(results);
66
56
  }
67
57
  },
68
58
  cancel: function (event) {
@@ -70,11 +60,6 @@
70
60
  self.unwait();
71
61
  self.hide();
72
62
  },
73
- finish: function (results) {
74
- self.unwait();
75
- var newpost = holder.replaceWith(results);
76
- newpost.blush();
77
- },
78
63
  show: function () {
79
64
  self.unwait();
80
65
  self.holder.hide();
@@ -94,70 +79,73 @@
94
79
  holder.unwait();
95
80
  }
96
81
  });
97
-
98
- self.container = $('<div class="post_form" />').hide();
82
+ self.container = $('<div class="remote_form" />').hide();
99
83
  self.holder.append(self.container);
100
84
  }
101
85
 
102
-
103
- function EditablePost(container, conf) {
86
+ function ActionHolder(container, conf) {
104
87
  var self = this;
105
88
  $.extend(self, {
106
89
  container: container,
107
- wrapper: container.find('.post_wrapper'),
108
- head: container.find('.post_header'),
109
- body: container.find('.post_body'),
90
+ wrapper: container.find('.wrapper'),
110
91
  actions: {},
92
+ initActions: function () {
93
+ self.actions = {};
94
+ self.container.find('a.remote').each(function () {
95
+ var a = $(this);
96
+ var href = a.attr('href');
97
+ self.addAction(href);
98
+ a.click(function (event) {
99
+ squash(event);
100
+ a.addClass('waiting');
101
+ self.showAction(href);
102
+ });
103
+ if (a.is('.autoload')) self.showAction(href);
104
+ });
105
+ },
111
106
  addAction: function (url) {
112
107
  if (!self.actions[url]) self.actions[url] = new RemoteAction(url, self);
113
108
  return self.actions[url];
114
109
  },
115
110
  showAction: function (url) {
116
111
  $.each(self.actions, function (key, action) { action.hide(); });
117
- self.actions[url].getForm();
112
+ self.actions[url].fetch();
118
113
  },
119
114
  append: function (el) {
120
- return self.wrapper.append(el);
115
+ return self.container.append(el);
121
116
  },
122
- replaceWith: function (post) {
123
- self.container.replaceWith(post);
124
- return $(post).editable_post();
117
+ replaceWith: function (html) {
118
+ self.container.html(html);
119
+ self.wrapper = self.container.find('.wrapper');
120
+ self.initActions();
121
+ return self.container;
125
122
  },
126
123
  show: function () {
127
- self.body.show();
124
+ self.wrapper.show();
128
125
  },
129
126
  hide: function () {
130
- self.body.hide();
127
+ self.wrapper.hide();
131
128
  },
132
129
  toggle: function (event) {
133
130
  squash(event);
134
- if (self.body.is(":visible")) self.hide();
131
+ if (self.wrapper.is(":visible")) self.hide();
135
132
  else self.show();
136
133
  },
137
134
  wait: function () {
138
135
  self.container.addClass('waiting');
136
+ self.container.find('a.remote').addClass('waiting');
139
137
  },
140
138
  unwait: function () {
141
139
  self.container.removeClass('waiting');
142
- self.container.find('a').removeClass('waiting');
140
+ self.container.find('a.remote').removeClass('waiting');
143
141
  }
144
142
  });
145
-
146
- container.find('a.remote').each(function () {
147
- var a = $(this);
148
- var href = a.attr('href');
149
- self.addAction(href);
150
- a.click(function (event) {
151
- squash(event);
152
- a.addClass('waiting');
153
- self.showAction(href);
154
- });
155
- });
143
+ self.initActions();
156
144
  }
157
145
 
158
- $.fn.editable_post = function(conf) {
146
+ $.fn.enable_remote_actions = function(conf) {
159
147
  this.each(function() {
160
- new EditablePost($(this), conf);
148
+ new ActionHolder($(this), conf);
161
149
  });
162
150
  return this;
163
151
  };
@@ -182,10 +170,11 @@
182
170
  else self.show();
183
171
  }
184
172
  });
185
-
186
- self.shower = $('<a href="#" class="shower">Hide</a>').appendTo(self.head.find('p.context'));
187
- self.shower.click(self.toggle);
188
- if ($('a.prev_page').length > 0) self.hide();
173
+ if ($('a.prev_page').length > 0) {
174
+ self.shower = $('<a href="#" class="shower">Hide</a>').appendTo(self.head.find('p.context'));
175
+ self.shower.click(self.toggle);
176
+ self.hide();
177
+ }
189
178
  }
190
179
 
191
180
  $.fn.hideable_post = function() {
@@ -195,9 +184,6 @@
195
184
  return this;
196
185
  };
197
186
 
198
-
199
-
200
-
201
187
  function UploadStack(container) {
202
188
  var self = this;
203
189
  $.extend(self, {
@@ -289,147 +275,12 @@
289
275
  });
290
276
  return this;
291
277
  };
292
-
293
- /*
294
- * jQuery Color Animations
295
- * Copyright 2007 John Resig
296
- * Released under the MIT and GPL licenses.
297
- * syntax corrected but otherwise untouched.
298
- */
299
-
300
- // We override the animation for all of these color styles
301
- $.each(['backgroundColor', 'borderBottomColor', 'borderLeftColor', 'borderRightColor', 'borderTopColor', 'color', 'outlineColor'], function(i, attr) {
302
- $.fx.step[attr] = function(fx) {
303
- if (!fx.colorInit) {
304
- fx.start = getColor(fx.elem, attr);
305
- fx.end = getRGB(fx.end);
306
- fx.colorInit = true;
307
- }
308
-
309
- fx.elem.style[attr] = "rgb(" + [
310
- Math.max(Math.min(parseInt((fx.pos * (fx.end[0] - fx.start[0])) + fx.start[0], 10), 255), 0),
311
- Math.max(Math.min(parseInt((fx.pos * (fx.end[1] - fx.start[1])) + fx.start[1], 10), 255), 0),
312
- Math.max(Math.min(parseInt((fx.pos * (fx.end[2] - fx.start[2])) + fx.start[2], 10), 255), 0)
313
- ].join(",") + ")";
314
- }
315
- });
316
-
317
- // Color Conversion functions from highlightFade
318
- // By Blair Mitchelmore
319
- // http://jquery.offput.ca/highlightFade/
320
-
321
- // Parse strings looking for color tuples [255,255,255]
322
- function getRGB(color) {
323
- var result;
324
-
325
- // Check if we're already dealing with an array of colors
326
- if ( color && color.constructor == Array && color.length == 3 )
327
- return color;
328
-
329
- // Look for rgb(num,num,num)
330
- if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color))
331
- return [parseInt(result[1], 10), parseInt(result[2], 10), parseInt(result[3], 10)];
332
-
333
- // Look for rgb(num%,num%,num%)
334
- if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color))
335
- return [parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55];
336
-
337
- // Look for #a0b1c2
338
- if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color))
339
- return [parseInt(result[1],16), parseInt(result[2],16), parseInt(result[3],16)];
340
-
341
- // Look for #fff
342
- if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color))
343
- return [parseInt(result[1]+result[1],16), parseInt(result[2]+result[2],16), parseInt(result[3]+result[3],16)];
344
-
345
- // Look for rgba(0, 0, 0, 0) == transparent in Safari 3
346
- if (result = /rgba\(0, 0, 0, 0\)/.exec(color))
347
- return colors['transparent'];
348
-
349
- // Otherwise, we're most likely dealing with a named color
350
- return colors[jQuery.trim(color).toLowerCase()];
351
- }
352
-
353
- function getColor(elem, attr) {
354
- var color;
355
-
356
- do {
357
- color = $.curCSS(elem, attr);
358
-
359
- // Keep going until we find an element that has color, or we hit the body
360
- if ( color != '' && color != 'transparent' || jQuery.nodeName(elem, "body") )
361
- break;
362
-
363
- attr = "backgroundColor";
364
- } while ( elem = elem.parentNode );
365
-
366
- return getRGB(color);
367
- };
368
-
369
- // Some named colors to work with
370
- // From Interface by Stefan Petre
371
- // http://interface.eyecon.ro/
372
-
373
- var colors = {
374
- aqua:[0,255,255],
375
- azure:[240,255,255],
376
- beige:[245,245,220],
377
- black:[0,0,0],
378
- blue:[0,0,255],
379
- brown:[165,42,42],
380
- cyan:[0,255,255],
381
- darkblue:[0,0,139],
382
- darkcyan:[0,139,139],
383
- darkgrey:[169,169,169],
384
- darkgreen:[0,100,0],
385
- darkkhaki:[189,183,107],
386
- darkmagenta:[139,0,139],
387
- darkolivegreen:[85,107,47],
388
- darkorange:[255,140,0],
389
- darkorchid:[153,50,204],
390
- darkred:[139,0,0],
391
- darksalmon:[233,150,122],
392
- darkviolet:[148,0,211],
393
- fuchsia:[255,0,255],
394
- gold:[255,215,0],
395
- green:[0,128,0],
396
- indigo:[75,0,130],
397
- khaki:[240,230,140],
398
- lightblue:[173,216,230],
399
- lightcyan:[224,255,255],
400
- lightgreen:[144,238,144],
401
- lightgrey:[211,211,211],
402
- lightpink:[255,182,193],
403
- lightyellow:[255,255,224],
404
- lime:[0,255,0],
405
- magenta:[255,0,255],
406
- maroon:[128,0,0],
407
- navy:[0,0,128],
408
- olive:[128,128,0],
409
- orange:[255,165,0],
410
- pink:[255,192,203],
411
- purple:[128,0,128],
412
- violet:[128,0,128],
413
- red:[255,0,0],
414
- silver:[192,192,192],
415
- white:[255,255,255],
416
- yellow:[255,255,0],
417
- transparent: [255,255,255]
418
- };
419
-
420
- $.fn.blush = function(color, duration) {
421
- color = color || "#FFFF9C";
422
- duration = duration || 1500;
423
- var backto = this.css("background-color");
424
- if (backto == "" || backto == 'transparent') backto = '#ffffff';
425
- this.css("background-color", color).animate({"background-color": backto}, duration);
426
- };
427
-
428
278
 
429
279
  })(jQuery);
430
280
 
431
281
  $(function() {
432
- $(".post").editable_post({});
282
+ $(".post").enable_remote_actions({});
283
+ $(".new_post").enable_remote_actions({});
433
284
  $(".post.first").hideable_post({});
434
285
  $(".upload_stack").upload_stack({});
435
286
  $(".toolbarred").add_editor({});
@@ -26,6 +26,8 @@
26
26
  closer: container.find('div.closer'),
27
27
  stack: [],
28
28
  item: null,
29
+ click_outside: null,
30
+ escape_key: null,
29
31
  add: function (a) {
30
32
  self.stack.push(new GalleryItem(a));
31
33
  },
@@ -46,9 +48,11 @@
46
48
  self.hide();
47
49
  self.mimic();
48
50
  self.zoomer.zoomUp(self.item, self.show);
51
+ self.setClosers();
49
52
  },
50
53
  zoomDown: function () {
51
54
  self.zoomer.zoomDown(self.item);
55
+ self.unsetClosers();
52
56
  self.hide();
53
57
  },
54
58
  crossfade: function () {
@@ -69,21 +73,38 @@
69
73
  return self.stack.indexOf(self.item);
70
74
  },
71
75
  next: function (e) {
72
- e.preventDefault();
76
+ if (e) e.preventDefault();
73
77
  var at = self.current();
74
78
  var next = (at == self.stack.length-1) ? 0 : at + 1;
75
79
  self.display(self.stack[next]);
76
80
  },
77
81
  previous: function (e) {
78
- e.preventDefault();
82
+ if (e) e.preventDefault();
79
83
  var at = self.current();
80
84
  var previous = (at == 0) ? self.stack.length-1 : at - 1;
81
85
  self.display(self.stack[previous]);
82
86
  },
83
87
  close: function (e) {
84
- e.preventDefault();
88
+ if (e) e.preventDefault();
85
89
  self.zoomDown();
86
90
  },
91
+ setClosers: function (argument) {
92
+ self.click_outside = $('#pagecontent').click(self.close);
93
+ self.escape_key = $(document).keyup(function(e) {
94
+ if (e.keyCode == 27) self.close(e);
95
+ if (!e.metaKey) {
96
+ if (e.keyCode == 39) self.next(e);
97
+ if (e.keyCode == 37) self.previous(e);
98
+ }
99
+ return false;
100
+ });
101
+ },
102
+ unsetClosers: function (argument) {
103
+ if (self.click_outside) $('#pagecontent').unbind('click', self.click_outside);
104
+ if (self.escape_key) $(document).unbind('keyup', self.escape_key);
105
+ },
106
+
107
+
87
108
  show: function () {
88
109
  self.zoomer.hide();
89
110
  self.container.show();
@@ -112,7 +133,7 @@
112
133
  left: p.left + (self.image.innerWidth() - d.width)/2,
113
134
  top: p.top + (self.image.innerHeight() - d.height)/2
114
135
  };
115
- if (resized.top <= 10) resized.top = 10;
136
+ if (r.top <= 10) r.top = 10;
116
137
  self.image.animate(d, 'fast');
117
138
  self.container.animate(r, 'fast');
118
139
  self.controls.css({left: (d.width - 96)/2});
@@ -172,10 +193,14 @@
172
193
  },
173
194
  zoomDown: function (item, onZoom) {
174
195
  if (!onZoom) onZoom = self.hide;
196
+ self.interrupt();
175
197
  self.show();
176
198
  self.sprite.css($.extend(self.defaultUpState, $.gallery.currentPosition()));
177
199
  self.sprite.animate($.extend({}, self.defaultDownState, item.position()), self.zoomDuration, onZoom);
178
200
  },
201
+ interrupt: function () {
202
+ self.sprite.stop(true, false);
203
+ },
179
204
  show: function () {
180
205
  self.sprite.show();
181
206
  },
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{radiant-forum-extension}
8
- s.version = "2.0.9"
8
+ s.version = "2.1.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["spanner"]
12
- s.date = %q{2011-02-07}
12
+ s.date = %q{2011-02-09}
13
13
  s.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.}
14
14
  s.email = %q{will@spanner.org}
15
15
  s.extra_rdoc_files = [
@@ -57,7 +57,7 @@ Gem::Specification.new do |s|
57
57
  "app/views/forums/show.html.haml",
58
58
  "app/views/forums/show.rss.builder",
59
59
  "app/views/layouts/feed.rss.builder",
60
- "app/views/pages/_comment.html.haml",
60
+ "app/views/pages/_add_comment.html.haml",
61
61
  "app/views/pages/_comments.html.haml",
62
62
  "app/views/posts/_attachment.html.haml",
63
63
  "app/views/posts/_attachments.html.haml",
@@ -5,6 +5,7 @@ describe ForumsController do
5
5
 
6
6
  before do
7
7
  controller.stub!(:request).and_return(request)
8
+ Radiant::Config['forum.public?'] = true
8
9
  end
9
10
 
10
11
  describe "on get to index" do
@@ -137,9 +137,9 @@ describe PostsController do
137
137
  xhr :get, :new, :topic_id => @topic.id, :forum_id => forum_id(:public)
138
138
  end
139
139
 
140
- it "should render a bare post form" do
140
+ it "should render a bare reply form" do
141
141
  response.should be_success
142
- response.should render_template('posts/_form')
142
+ response.should render_template('topics/_reply')
143
143
  response.layout.should be_nil
144
144
  end
145
145
  end
@@ -221,7 +221,7 @@ describe PostsController do
221
221
 
222
222
  it "should re-render the bare post form" do
223
223
  response.should be_success
224
- response.should render_template('posts/_form')
224
+ response.should render_template('posts/new')
225
225
  response.layout.should be_nil
226
226
  end
227
227
 
@@ -6,6 +6,7 @@ describe TopicsController do
6
6
  before do
7
7
  Page.current_site = sites(:test) if defined? Site
8
8
  controller.stub!(:request).and_return(request)
9
+ Radiant::Config['forum.public?'] = true
9
10
  end
10
11
 
11
12
  describe "on get to index" do
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: radiant-forum-extension
3
3
  version: !ruby/object:Gem::Version
4
- hash: 29
4
+ hash: 9
5
5
  prerelease: false
6
6
  segments:
7
7
  - 2
8
- - 0
9
- - 9
10
- version: 2.0.9
8
+ - 1
9
+ - 1
10
+ version: 2.1.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - spanner
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-02-07 00:00:00 +00:00
18
+ date: 2011-02-09 00:00:00 +00:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -100,7 +100,7 @@ files:
100
100
  - app/views/forums/show.html.haml
101
101
  - app/views/forums/show.rss.builder
102
102
  - app/views/layouts/feed.rss.builder
103
- - app/views/pages/_comment.html.haml
103
+ - app/views/pages/_add_comment.html.haml
104
104
  - app/views/pages/_comments.html.haml
105
105
  - app/views/posts/_attachment.html.haml
106
106
  - app/views/posts/_attachments.html.haml
@@ -1,10 +0,0 @@
1
- %div.post{:id => post.dom_id}
2
- %div.post_header
3
- %h2
4
- %a{:href => "/readers/#{post.reader.id}", :class => 'main'}
5
- = gravatar_for(post.reader, {:size => 40}, {:class => 'gravatar'}}
6
- = post.reader.name
7
- %p.context
8
- = post.created_at
9
- %div.post_body
10
- = post.body