refinerycms-blog 1.1 → 1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/app/controllers/admin/blog/posts_controller.rb +53 -0
  2. data/app/controllers/admin/blog/settings_controller.rb +14 -2
  3. data/app/controllers/blog/posts_controller.rb +18 -7
  4. data/app/helpers/blog_posts_helper.rb +23 -7
  5. data/app/models/blog_category.rb +4 -11
  6. data/app/models/blog_comment.rb +11 -3
  7. data/app/models/blog_post.rb +35 -14
  8. data/app/models/categorization.rb +5 -0
  9. data/app/views/admin/blog/_submenu.html.erb +37 -27
  10. data/app/views/admin/blog/categories/_category.html.erb +3 -3
  11. data/app/views/admin/blog/categories/_form.html.erb +1 -1
  12. data/app/views/admin/blog/categories/index.html.erb +2 -6
  13. data/app/views/admin/blog/comments/_comment.html.erb +2 -2
  14. data/app/views/admin/blog/posts/_form.html.erb +1 -1
  15. data/app/views/admin/blog/posts/_post.html.erb +5 -5
  16. data/app/views/admin/blog/posts/index.html.erb +2 -6
  17. data/app/views/admin/blog/posts/uncategorized.html.erb +26 -0
  18. data/app/views/admin/blog/settings/notification_recipients.html.erb +1 -1
  19. data/app/views/blog/posts/_nav.html.erb +9 -11
  20. data/app/views/blog/posts/_post.html.erb +5 -7
  21. data/app/views/blog/posts/archive.html.erb +8 -6
  22. data/app/views/blog/posts/index.html.erb +10 -7
  23. data/app/views/blog/posts/show.html.erb +5 -4
  24. data/app/views/blog/shared/_categories.html.erb +10 -8
  25. data/app/views/blog/shared/_post.html.erb +9 -7
  26. data/app/views/blog/shared/_posts.html.erb +10 -8
  27. data/app/views/blog/shared/_rss_feed.html.erb +1 -1
  28. data/changelog.md +24 -0
  29. data/config/locales/de.yml +128 -0
  30. data/config/locales/en.yml +17 -3
  31. data/config/locales/es.yml +122 -0
  32. data/config/locales/fr.yml +125 -0
  33. data/config/locales/it.yml +1 -0
  34. data/config/locales/pl.yml +134 -0
  35. data/config/locales/pt-BR.yml +125 -0
  36. data/config/locales/ru.yml +125 -0
  37. data/config/routes.rb +5 -2
  38. data/features/authors.feature +15 -0
  39. data/features/support/factories/blog_categories.rb +1 -1
  40. data/features/support/factories/blog_posts.rb +3 -1
  41. data/features/support/step_definitions/authors_steps.rb +7 -0
  42. data/lib/gemspec.rb +1 -0
  43. data/lib/generators/refinerycms_blog/templates/db/migrate/migration_number_add_user_id_to_blog_posts.rb +11 -0
  44. data/lib/generators/refinerycms_blog_generator.rb +14 -7
  45. data/lib/refinerycms-blog.rb +2 -6
  46. data/public/javascripts/refinery/refinerycms-blog.js +3 -3
  47. data/spec/models/blog_categories_spec.rb +28 -8
  48. data/spec/models/blog_posts_spec.rb +167 -7
  49. metadata +16 -20
  50. data/Gemfile +0 -2
  51. data/Gemfile.lock +0 -11
@@ -3,6 +3,59 @@ class Admin::Blog::PostsController < Admin::BaseController
3
3
  crudify :blog_post,
4
4
  :title_attribute => :title,
5
5
  :order => 'published_at DESC'
6
+
7
+ def uncategorized
8
+ @blog_posts = BlogPost.uncategorized.paginate({
9
+ :page => params[:page],
10
+ :per_page => BlogPost.per_page
11
+ })
12
+ end
13
+
14
+ def create
15
+ # if the position field exists, set this object as last object, given the conditions of this class.
16
+ if BlogPost.column_names.include?("position")
17
+ params[:blog_post].merge!({
18
+ :position => ((BlogPost.maximum(:position, :conditions => "")||-1) + 1)
19
+ })
20
+ end
21
+
22
+ if BlogPost.column_names.include?("user_id")
23
+ params[:blog_post].merge!({
24
+ :user_id => current_user.id
25
+ })
26
+ end
27
+
28
+ if (@blog_post = BlogPost.create(params[:blog_post])).valid?
29
+ (request.xhr? ? flash.now : flash).notice = t(
30
+ 'refinery.crudify.created',
31
+ :what => "'#{@blog_post.title}'"
32
+ )
33
+
34
+ unless from_dialog?
35
+ unless params[:continue_editing] =~ /true|on|1/
36
+ redirect_back_or_default(admin_blog_posts_url)
37
+ else
38
+ unless request.xhr?
39
+ redirect_to :back
40
+ else
41
+ render :partial => "/shared/message"
42
+ end
43
+ end
44
+ else
45
+ render :text => "<script>parent.window.location = '#{admin_blog_posts_url}';</script>"
46
+ end
47
+ else
48
+ unless request.xhr?
49
+ render :action => 'new'
50
+ else
51
+ render :partial => "/shared/admin/error_messages",
52
+ :locals => {
53
+ :object => @blog_post,
54
+ :include_object_name => true
55
+ }
56
+ end
57
+ end
58
+ end
6
59
 
7
60
  before_filter :find_all_categories,
8
61
  :only => [:new, :edit, :create, :update]
@@ -10,7 +10,8 @@ class Admin::Blog::SettingsController < Admin::BaseController
10
10
  unless request.xhr? or from_dialog?
11
11
  redirect_back_or_default(admin_blog_posts_path)
12
12
  else
13
- render :text => "<script type='text/javascript'>parent.window.location = '#{admin_blog_posts_path}';</script>"
13
+ render :text => "<script type='text/javascript'>parent.window.location = '#{admin_blog_posts_path}';</script>",
14
+ :layout => false
14
15
  end
15
16
  end
16
17
  end
@@ -20,7 +21,18 @@ class Admin::Blog::SettingsController < Admin::BaseController
20
21
  unless request.xhr?
21
22
  redirect_back_or_default(admin_blog_posts_path)
22
23
  else
23
- render :json => {:enabled => enabled}
24
+ render :json => {:enabled => enabled},
25
+ :layout => false
26
+ end
27
+ end
28
+
29
+ def comments
30
+ enabled = BlogComment.toggle!
31
+ unless request.xhr?
32
+ redirect_back_or_default(admin_blog_posts_path)
33
+ else
34
+ render :json => {:enabled => enabled},
35
+ :layout => false
24
36
  end
25
37
  end
26
38
 
@@ -45,12 +45,23 @@ class Blog::PostsController < BlogController
45
45
  end
46
46
 
47
47
  def archive
48
- date = "#{params[:month]}/#{params[:year]}"
49
- @archive_date = Time.parse(date)
50
- @blog_posts = BlogPost.live.by_archive(@archive_date).paginate({
51
- :page => params[:page],
52
- :per_page => RefinerySetting.find_or_set(:blog_posts_per_page, 10)
53
- })
48
+ if params[:month].present?
49
+ date = "#{params[:month]}/#{params[:year]}"
50
+ @archive_date = Time.parse(date)
51
+ @date_title = @archive_date.strftime('%B %Y')
52
+ @blog_posts = BlogPost.live.by_archive(@archive_date).paginate({
53
+ :page => params[:page],
54
+ :per_page => RefinerySetting.find_or_set(:blog_posts_per_page, 10)
55
+ })
56
+ else
57
+ date = "01/#{params[:year]}"
58
+ @archive_date = Time.parse(date)
59
+ @date_title = @archive_date.strftime('%Y')
60
+ @blog_posts = BlogPost.live.by_year(@archive_date).paginate({
61
+ :page => params[:page],
62
+ :per_page => RefinerySetting.find_or_set(:blog_posts_per_page, 10)
63
+ })
64
+ end
54
65
  respond_with (@blog_posts)
55
66
  end
56
67
 
@@ -67,7 +78,7 @@ protected
67
78
  end
68
79
 
69
80
  def find_all_blog_posts
70
- @blog_posts = BlogPost.live.paginate({
81
+ @blog_posts = BlogPost.live.includes(:comments, :categories).paginate({
71
82
  :page => params[:page],
72
83
  :per_page => RefinerySetting.find_or_set(:blog_posts_per_page, 10)
73
84
  })
@@ -2,23 +2,39 @@ module BlogPostsHelper
2
2
  def blog_archive_list
3
3
  posts = BlogPost.select('published_at').all_previous
4
4
  return nil if posts.blank?
5
- html = '<section id="blog_archive_list"><h2>Archives</h2><nav><ul>'
5
+ html = '<section id="blog_archive_list"><h2>'
6
+ html << t('blog.shared.archives')
7
+ html << '</h2><nav><ul>'
6
8
  links = []
9
+ super_old_links = []
7
10
 
8
11
  posts.each do |e|
9
- links << e.published_at.strftime('%m/%Y')
12
+ if e.published_at >= Time.now.end_of_year.advance(:years => -3)
13
+ links << e.published_at.strftime('%m/%Y')
14
+ else
15
+ super_old_links << e.published_at.strftime('01/%Y')
16
+ end
10
17
  end
11
18
  links.uniq!
19
+ super_old_links.uniq!
12
20
  links.each do |l|
13
21
  year = l.split('/')[1]
14
22
  month = l.split('/')[0]
15
23
  count = BlogPost.by_archive(Time.parse(l)).size
16
- text = "#{Date::MONTHNAMES[month.to_i]} #{year} (#{count})"
17
- html += "<li>"
18
- html += link_to(text, archive_blog_posts_path(:year => year, :month => month))
19
- html += "</li>"
24
+ text = t("date.month_names")[month.to_i] + " #{year} (#{count})"
25
+ html << "<li>"
26
+ html << link_to(text, archive_blog_posts_path(:year => year, :month => month))
27
+ html << "</li>"
20
28
  end
21
- html += '</ul></nav></section>'
29
+ super_old_links.each do |l|
30
+ year = l.split('/')[1]
31
+ count = BlogPost.by_year(Time.parse(l)).size
32
+ text = "#{year} (#{count})"
33
+ html << "<li>"
34
+ html << link_to(text, archive_blog_posts_path(:year => year))
35
+ html << "</li>"
36
+ end
37
+ html << '</ul></nav></section>'
22
38
  html.html_safe
23
39
  end
24
40
 
@@ -1,23 +1,16 @@
1
1
  class BlogCategory < ActiveRecord::Base
2
2
 
3
- has_and_belongs_to_many :posts, :class_name => 'BlogPost'
3
+ has_many :categorizations
4
+ has_many :posts, :through => :categorizations, :source => :blog_post
4
5
 
5
6
  acts_as_indexed :fields => [:title]
6
7
 
7
- validates_presence_of :title
8
- validates_uniqueness_of :title
8
+ validates :title, :presence => true, :uniqueness => true
9
9
 
10
10
  has_friendly_id :title, :use_slug => true
11
11
 
12
- # this might be able to be optimised a little more
13
12
  def post_count
14
- count = 0
15
-
16
- self.posts.each do |p|
17
- count += 1 if p.live?
18
- end
19
-
20
- count
13
+ posts.select(&:live?).count
21
14
  end
22
15
 
23
16
  end
@@ -1,5 +1,7 @@
1
1
  class BlogComment < ActiveRecord::Base
2
2
 
3
+ attr_accessible :name, :email, :message
4
+
3
5
  filters_spam :author_field => :name,
4
6
  :email_field => :email,
5
7
  :message_field => :body
@@ -10,9 +12,8 @@ class BlogComment < ActiveRecord::Base
10
12
 
11
13
  alias_attribute :message, :body
12
14
 
13
- validates_presence_of :name, :message
14
- validates_format_of :email,
15
- :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
15
+ validates :name, :message, :presence => true
16
+ validates :email, :format => { :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i }
16
17
 
17
18
  scope :unmoderated, :conditions => {:state => nil}
18
19
  scope :approved, :conditions => {:state => 'approved'}
@@ -38,6 +39,13 @@ class BlogComment < ActiveRecord::Base
38
39
  self.state.nil?
39
40
  end
40
41
 
42
+ def self.toggle!
43
+ currently = RefinerySetting.find_or_set(:comments_allowed, true, {
44
+ :scoping => 'blog'
45
+ })
46
+ RefinerySetting.set(:comments_allowed, {:value => !currently, :scoping => 'blog'})
47
+ end
48
+
41
49
  before_create do |comment|
42
50
  unless BlogComment::Moderation.enabled?
43
51
  comment.state = comment.ham? ? 'approved' : 'rejected'
@@ -1,32 +1,43 @@
1
1
  class BlogPost < ActiveRecord::Base
2
-
3
- has_many :comments, :class_name => 'BlogComment'
4
- has_and_belongs_to_many :categories, :class_name => 'BlogCategory'
2
+
3
+ default_scope :order => 'published_at DESC'
4
+ #.first & .last will be reversed -- consider a with_exclusive_scope on these?
5
+
6
+ belongs_to :author, :class_name => 'User', :foreign_key => :user_id
7
+
8
+ has_many :comments, :class_name => 'BlogComment', :dependent => :destroy
9
+
10
+ has_many :categorizations
11
+ has_many :categories, :through => :categorizations, :source => :blog_category
5
12
 
6
13
  acts_as_indexed :fields => [:title, :body]
7
14
 
8
- validates_presence_of :title
9
- validates_uniqueness_of :title
15
+ validates :title, :presence => true, :uniqueness => true
16
+ validates :body, :presence => true
10
17
 
11
18
  has_friendly_id :title, :use_slug => true
12
19
 
13
20
  scope :by_archive, lambda { |archive_date|
14
- where(['published_at between ? and ?', archive_date.beginning_of_month, archive_date.end_of_month]).order("published_at DESC")
21
+ where(['published_at between ? and ?', archive_date.beginning_of_month, archive_date.end_of_month])
22
+ }
23
+
24
+ scope :by_year, lambda { |archive_year|
25
+ where(['published_at between ? and ?', archive_year.beginning_of_year, archive_year.end_of_year])
15
26
  }
16
27
 
17
- scope :all_previous, where(['published_at <= ?', Time.now.beginning_of_month]).order("published_at DESC")
18
-
19
- scope :live, lambda { where( "published_at < ? and draft = ?", Time.now, false).order("published_at DESC") }
28
+ scope :all_previous, lambda { where(['published_at <= ?', Time.now.beginning_of_month]) }
20
29
 
21
- scope :previous, lambda { |i| where(["published_at < ?", i.published_at]).order("published_at DESC").limit(1) }
22
- scope :next, lambda { |i| where(["published_at > ?", i.published_at]).order("published_at ASC").limit(1) }
30
+ scope :live, lambda { where( "published_at <= ? and draft = ?", Time.now, false) }
23
31
 
32
+ scope :previous, lambda { |i| where(["published_at < ? and draft = ?", i.published_at, false]).limit(1) }
33
+ # next is now in << self
34
+
24
35
  def next
25
- self.class.next(self).first
36
+ BlogPost.next(self).first
26
37
  end
27
38
 
28
39
  def prev
29
- self.class.previous(self).first
40
+ BlogPost.previous(self).first
30
41
  end
31
42
 
32
43
  def live?
@@ -40,17 +51,27 @@ class BlogPost < ActiveRecord::Base
40
51
  end
41
52
 
42
53
  class << self
54
+ def next current_record
55
+ self.send(:with_exclusive_scope) do
56
+ where(["published_at > ? and draft = ?", current_record.published_at, false]).order("published_at ASC")
57
+ end
58
+ end
59
+
43
60
  def comments_allowed?
44
61
  RefinerySetting.find_or_set(:comments_allowed, true, {
45
62
  :scoping => 'blog'
46
63
  })
47
64
  end
65
+
66
+ def uncategorized
67
+ BlogPost.live.reject { |p| p.categories.any? }
68
+ end
48
69
  end
49
70
 
50
71
  module ShareThis
51
72
  DEFAULT_KEY = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
52
73
 
53
- class << self
74
+ class << self
54
75
  def key
55
76
  RefinerySetting.find_or_set(:share_this_key, BlogPost::ShareThis::DEFAULT_KEY, {
56
77
  :scoping => 'blog'
@@ -0,0 +1,5 @@
1
+ class Categorization < ActiveRecord::Base
2
+ set_table_name 'blog_categories_blog_posts'
3
+ belongs_to :blog_post
4
+ belongs_to :blog_category
5
+ end
@@ -1,6 +1,6 @@
1
1
  <nav id='actions' class='multilist'>
2
2
  <ul class='search_list'>
3
- <li>
3
+ <li class='not_a_link'>
4
4
  <%= render :partial => "/shared/admin/search",
5
5
  :locals => {
6
6
  :url => admin_blog_posts_url
@@ -9,45 +9,51 @@
9
9
  </ul>
10
10
 
11
11
  <ul class='collapsible_menu'>
12
- <li>
12
+ <li class='not_a_link'>
13
13
  <%= link_to t('.posts.title'), '#',
14
14
  :class => 'page_copy_icon' %>
15
15
  </li>
16
16
  <li>
17
17
  <%= link_to t('.posts.manage'), admin_blog_posts_path,
18
18
  :class => 'page_icon' %>
19
- <li>
20
- <%= link_to t('.posts.new'), new_admin_blog_post_url,
21
- :class => 'page_add_icon' %>
22
- </li>
23
- </ul>
24
-
25
- <ul class='collapsible_menu'>
26
- <li>
27
- <% if BlogComment.unmoderated.any? %>
28
- <% title = t('.comments.title_with_count', :new_count => BlogComment.unmoderated.size) %>
29
- <% else %>
30
- <% title = t('.comments.title') %>
31
- <% end %>
32
- <%= link_to title, '#',
33
- :class => 'comments_icon' %>
34
19
  </li>
35
20
  <li>
36
- <%= link_to t('.comments.new'), admin_blog_comments_path,
37
- :class => 'comment_icon' %>
21
+ <%= link_to t('.posts.uncategorized'), uncategorized_admin_blog_posts_url,
22
+ :class => 'page_icon' %>
38
23
  </li>
39
24
  <li>
40
- <%= link_to t('.comments.approved'), approved_admin_blog_comments_path,
41
- :class => 'comment_tick_icon' %>
42
- </li>
43
- <li>
44
- <%= link_to t('.comments.rejected'), rejected_admin_blog_comments_path,
45
- :class => 'comment_cross_icon' %>
25
+ <%= link_to t('.posts.new'), new_admin_blog_post_url,
26
+ :class => 'page_add_icon' %>
46
27
  </li>
47
28
  </ul>
29
+ <% if BlogPost.comments_allowed? %>
30
+ <ul class='collapsible_menu'>
31
+ <li class='not_a_link'>
32
+ <% if BlogComment.unmoderated.any? %>
33
+ <% title = t('.comments.title_with_count', :new_count => BlogComment.unmoderated.size) %>
34
+ <% else %>
35
+ <% title = t('.comments.title') %>
36
+ <% end %>
37
+ <%= link_to title, '#',
38
+ :class => 'comments_icon' %>
39
+ </li>
40
+ <li>
41
+ <%= link_to t('.comments.new'), admin_blog_comments_path,
42
+ :class => 'comment_icon' %>
43
+ </li>
44
+ <li>
45
+ <%= link_to t('.comments.approved'), approved_admin_blog_comments_path,
46
+ :class => 'comment_tick_icon' %>
47
+ </li>
48
+ <li>
49
+ <%= link_to t('.comments.rejected'), rejected_admin_blog_comments_path,
50
+ :class => 'comment_cross_icon' %>
51
+ </li>
52
+ </ul>
53
+ <% end %>
48
54
 
49
55
  <ul class='collapsible_menu'>
50
- <li>
56
+ <li class='not_a_link'>
51
57
  <%= link_to t('.categories.title'), '#',
52
58
  :class => 'folder_icon' %>
53
59
  </li>
@@ -62,10 +68,14 @@
62
68
  </ul>
63
69
 
64
70
  <ul class='collapsible_menu'>
65
- <li>
71
+ <li class='not_a_link'>
66
72
  <%= link_to t('.settings.title'), admin_blog_settings_path,
67
73
  :class => 'settings_icon' %>
68
74
  </li>
75
+ <li>
76
+ <%= link_to t('.settings.comments'), comments_admin_blog_settings_url,
77
+ :class => "#{BlogPost.comments_allowed? ? 'success' : 'failure'}_icon" %>
78
+ </li>
69
79
  <li>
70
80
  <%= link_to t('.settings.moderation'), moderation_admin_blog_settings_url,
71
81
  :class => "#{BlogComment::Moderation.enabled? ? 'success' : 'failure'}_icon" %>
@@ -1,6 +1,6 @@
1
1
  <li class='clearfix record <%= cycle("on", "on-hover") %>' id="<%= dom_id(category) -%>">
2
2
  <span class='title'>
3
- <%=h category.title %>
3
+ <%= category.title %>
4
4
  <span class="preview">&nbsp;</span>
5
5
  </span>
6
6
  <span class='actions'>
@@ -10,7 +10,7 @@
10
10
  <%= link_to refinery_icon_tag("delete.png"), admin_blog_category_path(category),
11
11
  :class => "cancel confirm-delete",
12
12
  :title => t('.delete'),
13
- :'data-method' => 'delete',
14
- :'data-confirm' => t('shared.admin.delete.message', :title => category.title) %>
13
+ :method => :delete,
14
+ :confirm => t('shared.admin.delete.message', :title => category.title) %>
15
15
  </span>
16
16
  </li>