refinerycms-blog 1.1 → 1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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>