effective_posts 0.4.6 → 0.4.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/admin/posts_controller.rb +18 -5
  3. data/app/controllers/effective/posts_controller.rb +87 -17
  4. data/app/helpers/effective_posts_helper.rb +43 -16
  5. data/app/mailers/effective/posts_mailer.rb +34 -0
  6. data/app/models/effective/datatables/posts.rb +14 -4
  7. data/app/models/effective/post.rb +63 -5
  8. data/app/views/admin/posts/_actions.html.haml +16 -9
  9. data/app/views/admin/posts/_form.html.haml +28 -21
  10. data/app/views/admin/posts/edit.html.haml +1 -1
  11. data/app/views/admin/posts/excerpts.html.haml +1 -1
  12. data/app/views/admin/posts/new.html.haml +1 -1
  13. data/app/views/effective/posts/_additional_fields.html.haml +2 -0
  14. data/app/views/effective/posts/_categories.html.haml +4 -0
  15. data/app/views/effective/posts/_form.html.haml +27 -0
  16. data/app/views/effective/posts/_recent_posts.html.haml +5 -0
  17. data/app/views/effective/posts/_spacer.html.haml +1 -0
  18. data/app/views/effective/posts/edit.html.haml +2 -0
  19. data/app/views/effective/posts/index.html.haml +7 -2
  20. data/app/views/effective/posts/new.html.haml +2 -0
  21. data/app/views/effective/posts/show.html.haml +3 -3
  22. data/app/views/effective/posts/submitted.html.haml +10 -0
  23. data/app/views/effective/posts_mailer/post_submitted_to_admin.html.haml +22 -0
  24. data/app/views/layouts/effective_posts_mailer_layout.html.haml +7 -0
  25. data/config/routes.rb +12 -7
  26. data/lib/effective_posts.rb +19 -1
  27. data/lib/effective_posts/version.rb +1 -1
  28. data/lib/generators/effective_posts/install_generator.rb +10 -0
  29. data/lib/generators/templates/effective_posts.rb +52 -9
  30. data/lib/generators/templates/effective_posts_mailer_preview.rb +22 -0
  31. metadata +36 -25
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 11c1c38bb1cb3976cf832a0e195990c8ec092ce3
4
- data.tar.gz: e7c14e2c912bb93409deb13bef22ec6128b89c8e
3
+ metadata.gz: b6c0cbc8c247c67715094aba5e52ea2f212cdbe3
4
+ data.tar.gz: a67ae4457e20eb7675c78dc1c769a1b1cc84595b
5
5
  SHA512:
6
- metadata.gz: 5793b3006b5dc1583d20a24bad119c9cc478a0fa53f281fcea25148c212ac74ea720da4ca8490a6c05481ea9412aea7f7892d87c0b7bde459e3277b6709ff49a
7
- data.tar.gz: 613e891c1f32be3c11e7bb4ccb23ecd59be1ed8b06795843dc5bbaab04ed41175a50a88cbc8b5eb86d20506808f0faffea4512a0ca33061358b7556a74e3ed3b
6
+ metadata.gz: 1999a9f99ca390aab1e0576f8a1d5df86b0d33ccc7b29ea66b3903dd69777ac0ba29341a91f18abd1dc9e8360c98f5409d93cdeb4fe5f088d861cff8ae6b27e0
7
+ data.tar.gz: b09a6b63101b33d938f70230485668f0c2c1dd1b34d1bd01650a04757c9aa01d1943e9a5226af362d4826bff0ba9f4931bcb2d04b3621a7946b65956872eba3c
@@ -12,7 +12,7 @@ module Admin
12
12
  end
13
13
 
14
14
  def new
15
- @post = Effective::Post.new(:published_at => Time.zone.now)
15
+ @post = Effective::Post.new(published_at: Time.zone.now)
16
16
  @page_title = 'New Post'
17
17
 
18
18
  EffectivePosts.authorized?(self, :new, @post)
@@ -30,6 +30,7 @@ module Admin
30
30
  if params[:commit] == 'Save and Edit Content' && defined?(EffectiveRegions)
31
31
  redirect_to effective_regions.edit_path(effective_posts.post_path(@post), :exit => effective_posts.edit_admin_post_path(@post))
32
32
  elsif params[:commit] == 'Save and Add New'
33
+ flash[:success] = 'Successfully created post'
33
34
  redirect_to effective_posts.new_admin_post_path
34
35
  else
35
36
  flash[:success] = 'Successfully created post'
@@ -58,6 +59,7 @@ module Admin
58
59
  if params[:commit] == 'Save and Edit Content' && defined?(EffectiveRegions)
59
60
  redirect_to effective_regions.edit_path(effective_posts.post_path(@post), :exit => effective_posts.edit_admin_post_path(@post))
60
61
  elsif params[:commit] == 'Save and Add New'
62
+ flash[:success] = 'Successfully updated post'
61
63
  redirect_to effective_posts.new_admin_post_path
62
64
  else
63
65
  flash[:success] = 'Successfully updated post'
@@ -83,6 +85,20 @@ module Admin
83
85
  redirect_to effective_posts.admin_posts_path
84
86
  end
85
87
 
88
+ def approve
89
+ @post = Effective::Post.find(params[:id])
90
+
91
+ EffectivePosts.authorized?(self, :approve, @post)
92
+
93
+ if @post.update_attributes(draft: false)
94
+ flash[:success] = 'Successfully approved post. It is now displayed on the website.'
95
+ else
96
+ flash[:danger] = "Unable to approve post: #{@post.errors.full_messages.join(', ')}"
97
+ end
98
+
99
+ redirect_to(:back) rescue redirect_to(effective_posts.admin_posts_path)
100
+ end
101
+
86
102
  def excerpts
87
103
  @page_title = 'Post Excerpts'
88
104
 
@@ -91,13 +107,10 @@ module Admin
91
107
  @posts = Effective::Post.includes(:regions)
92
108
  end
93
109
 
94
-
95
110
  private
96
111
 
97
112
  def post_params
98
- params.require(:effective_post).permit(
99
- :title, :draft, :category, :published_at, :roles => []
100
- )
113
+ params.require(:effective_post).permit(EffectivePosts.permitted_params)
101
114
  end
102
115
 
103
116
  end
@@ -2,34 +2,25 @@ module Effective
2
2
  class PostsController < ApplicationController
3
3
  layout (EffectivePosts.layout.kind_of?(Hash) ? EffectivePosts.layout[:posts] : EffectivePosts.layout)
4
4
 
5
- after_action :monkey_patch_for_kaminari, :only => [:index]
5
+ before_action :authenticate_user!, only: [:new, :create, :edit, :update],
6
+ if: -> { EffectivePosts.submissions_require_current_user }
6
7
 
7
- def index
8
- @posts = (Rails::VERSION::MAJOR > 3 ? Effective::Post.all : Effective::Post.scoped)
9
-
10
- @posts = @posts.for_role(current_user.try(:roles)) if defined?(EffectiveRoles)
11
- @posts = @posts.with_category(params[:category]) if params[:category]
12
- @posts = @posts.published
13
- @posts = @posts.includes(:regions)
8
+ after_action :monkey_patch_for_kaminari, only: [:index]
14
9
 
15
- @posts = @posts.order("#{EffectivePosts.posts_table_name}.published_at DESC")
10
+ def index
11
+ @posts ||= Effective::Post.posts(user: current_user, category: params[:category])
16
12
  @posts = @posts.page(params[:page]).per(EffectivePosts.per_page)
17
13
 
18
14
  EffectivePosts.authorized?(self, :index, Effective::Post)
19
15
 
20
- @page_title = (params[:category] || 'Posts').titleize
16
+ @page_title = (params[:page_title] || params[:category] || 'Posts').titleize
21
17
  end
22
18
 
23
19
  def show
24
- @posts = (Rails::VERSION::MAJOR > 3 ? Effective::Post.all : Effective::Post.scoped)
25
-
26
- @posts = @posts.with_category(params[:category]) if params[:category]
27
- @posts = @posts.published if params[:edit].to_s != 'true'
28
- @posts = @posts.includes(:regions)
29
-
20
+ @posts ||= Effective::Post.posts(user: current_user, category: params[:category], drafts: (params[:edit].to_s == 'true' || params[:preview].to_s == 'true'))
30
21
  @post = @posts.find(params[:id])
31
22
 
32
- if defined?(EffectiveRoles)
23
+ if @post.respond_to?(:roles_permit?)
33
24
  raise Effective::AccessDenied unless @post.roles_permit?(current_user)
34
25
  end
35
26
 
@@ -38,8 +29,87 @@ module Effective
38
29
  @page_title = @post.title
39
30
  end
40
31
 
32
+ # Public user submit a post functionality
33
+ def new
34
+ @post ||= Effective::Post.new(published_at: Time.zone.now)
35
+ @page_title = 'New Post'
36
+
37
+ EffectivePosts.authorized?(self, :new, @post)
38
+ end
39
+
40
+ def create
41
+ @post ||= Effective::Post.new(post_params)
42
+ @post.user = current_user if defined?(current_user)
43
+ @post.draft = (EffectivePosts.submissions_require_approval == true)
44
+
45
+ EffectivePosts.authorized?(self, :create, @post)
46
+
47
+ if @post.save
48
+ @page_title ||= 'Post Submitted'
49
+ flash.now[:success] = 'Successfully submitted post'
50
+
51
+ if EffectivePosts.submissions_require_approval
52
+ @post.send_post_submitted_to_admin!
53
+ end
54
+
55
+ render :submitted
56
+ else
57
+ @page_title ||= 'New Post'
58
+ flash.now[:danger] = 'Unable to submit post'
59
+ render action: :new
60
+ end
61
+ end
62
+
63
+ def edit
64
+ @post ||= Effective::Post.find(params[:id])
65
+ @page_title ||= 'Edit Post'
66
+
67
+ EffectivePosts.authorized?(self, :edit, @post)
68
+ end
69
+
70
+ def update
71
+ @post ||= Effective::Post.find(params[:id])
72
+ draft_was = @post.draft
73
+ @post.draft = (EffectivePosts.submissions_require_approval == true)
74
+
75
+ EffectivePosts.authorized?(self, :update, @post)
76
+
77
+ if @post.update_attributes(post_params)
78
+ @page_title ||= 'Post Submitted'
79
+ flash.now[:success] = 'Successfully re-submitted post'
80
+
81
+ if EffectivePosts.submissions_require_approval && draft_was != true
82
+ @post.send_post_submitted_to_admin!
83
+ end
84
+
85
+ render :submitted
86
+ else
87
+ @page_title ||= 'Edit Post'
88
+ flash.now[:danger] = 'Unable to update post'
89
+ render action: :edit
90
+ end
91
+ end
92
+
93
+ def destroy
94
+ @post ||= Effective::Post.find(params[:id])
95
+
96
+ EffectivePosts.authorized?(self, :destroy, @post)
97
+
98
+ if @post.destroy
99
+ flash[:success] = 'Successfully deleted post'
100
+ else
101
+ flash[:danger] = 'Unable to delete post'
102
+ end
103
+
104
+ redirect_to effective_posts.posts_path
105
+ end
106
+
41
107
  private
42
108
 
109
+ def post_params
110
+ params.require(:effective_post).permit(EffectivePosts.permitted_params)
111
+ end
112
+
43
113
  def monkey_patch_for_kaminari
44
114
  @template = @template.tap { |template| template.extend(EffectiveKaminariHelper) }
45
115
  end
@@ -1,6 +1,17 @@
1
1
  require 'cgi'
2
2
 
3
3
  module EffectivePostsHelper
4
+ def effective_post_path(post, opts = nil)
5
+ category = post.category.to_s.downcase
6
+ opts ||= {}
7
+
8
+ if EffectivePosts.use_category_routes
9
+ "/#{category}" + effective_posts.post_path(post, opts)
10
+ else
11
+ effective_posts.post_path(post, opts.merge(category: category))
12
+ end
13
+ end
14
+
4
15
  def render_post(post)
5
16
  render(partial: 'effective/posts/post', locals: { post: post })
6
17
  end
@@ -19,33 +30,37 @@ module EffectivePostsHelper
19
30
  # :omission => '...' passed to the final text node's truncate
20
31
  # :length => 200 to set the max inner_text length of the content
21
32
  # All other options are passed to the link_to 'Read more'
22
- def post_excerpt(post, opts = {})
23
- content = effective_region(post, :content, :editable => false) { '<p>Default content</p>'.html_safe }
24
-
25
- options = {
26
- :label => 'Read more',
27
- :omission => '...',
28
- :length => 200
29
- }.merge(opts)
33
+ def post_excerpt(post, read_more_link: true, label: 'Read more', omission: '...', length: 200)
34
+ content = effective_region(post, :body, :editable => false) { '<p>Default content</p>'.html_safe }
30
35
 
31
36
  divider = content.index(Effective::Snippets::ReadMoreDivider::TOKEN)
32
- length = options.delete(:length)
33
- omission = options.delete(:omission)
37
+ read_more = (read_more_link && label.present?) ? readmore_link(post, label: label) : ''
34
38
 
35
39
  CGI.unescapeHTML(if divider.present?
36
- truncate_html(content, Effective::Snippets::ReadMoreDivider::TOKEN, '') + readmore_link(post, options)
40
+ truncate_html(content, Effective::Snippets::ReadMoreDivider::TOKEN, '') + readmore
37
41
  elsif length.present?
38
- truncate_html(content, length, omission) + readmore_link(post, options)
42
+ truncate_html(content, length, omission) + read_more
39
43
  else
40
44
  content
41
45
  end).html_safe
42
46
  end
43
47
 
44
- def readmore_link(post, options)
48
+ def read_more_link(post, options)
45
49
  content_tag(:p, class: 'post-read-more') do
46
50
  link_to((options.delete(:label) || 'Read more'), effective_posts.post_path(post), options)
47
51
  end
48
52
  end
53
+ alias_method :readmore_link, :read_more_link
54
+
55
+ ### Post Categories
56
+
57
+ def post_categories
58
+ categories = EffectivePosts.categories
59
+ end
60
+
61
+ def render_post_categories
62
+ render(partial: '/effective/posts/categories', locals: { categories: post_categories })
63
+ end
49
64
 
50
65
  def link_to_post_category(category, options = {})
51
66
  category = category.to_s.downcase
@@ -54,9 +69,21 @@ module EffectivePostsHelper
54
69
  link_to(category.to_s.titleize, href, options)
55
70
  end
56
71
 
57
- def effective_post_path(post)
58
- category = post.category.to_s.downcase
59
- EffectivePosts.use_category_routes ? "/#{category}/#{post.to_param}" : effective_posts.post_path(post, category: category.to_s)
72
+ ### Recent Posts
73
+
74
+ def recent_posts(user: current_user, category: nil, limit: EffectivePosts.per_page)
75
+ @recent_posts ||= Effective::Post.posts(user: user, category: category).limit(limit)
76
+ end
77
+
78
+ def render_recent_posts(user: current_user, category: nil, limit: EffectivePosts.per_page)
79
+ posts = recent_posts(user: user, category: category, limit: limit)
80
+
81
+ render partial: '/effective/posts/recent_posts', locals: { posts: posts }
82
+ end
83
+
84
+ ### Submitting a Post
85
+ def link_to_submit_post(label = 'Submit a post', options = {})
86
+ link_to(label, effective_posts.new_post_path, options)
60
87
  end
61
88
 
62
89
  end
@@ -0,0 +1,34 @@
1
+ module Effective
2
+ class PostsMailer < ActionMailer::Base
3
+ helper EffectivePostsHelper
4
+
5
+ layout EffectivePosts.mailer[:layout].presence || 'effective_posts_mailer_layout'
6
+
7
+ def post_submitted_to_admin(post_param)
8
+ @post = (post_param.kind_of?(Effective::Post) ? post_param : Effective::Post.find(post_param))
9
+
10
+ mail(
11
+ to: EffectivePosts.mailer[:admin_email],
12
+ from: EffectivePosts.mailer[:default_from],
13
+ subject: subject_for_post_submitted_to_admin(@post)
14
+ )
15
+ end
16
+
17
+ private
18
+
19
+ def subject_for_post_submitted_to_admin(post)
20
+ string_or_callable = EffectivePosts.mailer[:subject_for_post_submitted_to_admin]
21
+
22
+ if string_or_callable.respond_to?(:call) # This is a Proc or a function, not a string
23
+ string_or_callable = self.instance_exec(post, &string_or_callable)
24
+ end
25
+
26
+ prefix_subject(string_or_callable.presence || "A new post has been submitted that needs approval")
27
+ end
28
+
29
+ def prefix_subject(text)
30
+ prefix = (EffectivePosts.mailer[:subject_prefix].to_s rescue '')
31
+ prefix.present? ? (prefix.chomp(' ') + ' ' + text) : text
32
+ end
33
+ end
34
+ end
@@ -6,14 +6,24 @@ if defined?(EffectiveDatatables)
6
6
  default_order :published_at, :desc
7
7
 
8
8
  table_column :published_at
9
- table_column :id, :visible => false
9
+ table_column :id, visible: false
10
10
 
11
11
  table_column :title
12
- table_column :category, :filter => {:type => :select, :values => EffectivePosts.categories }
12
+ table_column :category, filter: { type: :select, values: EffectivePosts.categories }
13
13
 
14
- table_column :draft
14
+ if EffectivePosts.submissions_enabled
15
+ table_column :approved, column: 'NOT(draft)', as: :boolean do |post|
16
+ post.draft ? 'No' : 'Yes'
17
+ end
15
18
 
16
- table_column :actions, :sortable => false, :filter => false, :partial => '/admin/posts/actions'
19
+ table_column :draft, visible: false
20
+ else
21
+ table_column :draft
22
+ end
23
+
24
+ table_column :created_at, label: 'Submitted at', visible: false
25
+
26
+ table_column :actions, sortable: false, filter: false, partial: '/admin/posts/actions'
17
27
  end
18
28
 
19
29
  def collection
@@ -17,12 +17,48 @@ module Effective
17
17
  # timestamps
18
18
  # end
19
19
 
20
- validates_presence_of :title, :category, :published_at
21
- validates_length_of :title, maximum: 255
20
+ validates :title, presence: true, length: { maximum: 255 }
21
+ validates :category, presence: true
22
+ validates :published_at, presence: true
22
23
 
23
- scope :drafts, -> { where(:draft => true) }
24
- scope :published, -> { where(:draft => false).where("#{EffectivePosts.posts_table_name}.published_at < ?", Time.zone.now) }
25
- scope :with_category, proc { |category| where(:category => category.to_s.downcase) }
24
+ scope :drafts, -> { where(draft: true) }
25
+ scope :published, -> { where(draft: false).where("#{EffectivePosts.posts_table_name}.published_at < ?", Time.zone.now) }
26
+ scope :with_category, -> (category) { where(category: category.to_s.downcase) }
27
+
28
+ scope :posts, -> (user: nil, category: nil, drafts: false) {
29
+ scope = (Rails::VERSION::MAJOR > 3 ? all : scoped)
30
+ scope = scope.includes(:regions).order(published_at: :desc)
31
+
32
+ if user.present? && user.respond_to?(:roles) && defined?(EffectiveRoles)
33
+ scope = scope.for_role(user.roles)
34
+ end
35
+
36
+ if category.present?
37
+ scope = scope.with_category(category)
38
+ end
39
+
40
+ if drafts.blank?
41
+ scope = scope.published
42
+ end
43
+
44
+ scope
45
+ }
46
+
47
+ def to_s
48
+ title.presence || 'New Post'
49
+ end
50
+
51
+ def approved?
52
+ draft == false
53
+ end
54
+
55
+ def body
56
+ region(:body).content
57
+ end
58
+
59
+ def body=(input)
60
+ region(:body).content = input
61
+ end
26
62
 
27
63
  def to_param
28
64
  "#{id}-#{title.parameterize}"
@@ -32,5 +68,27 @@ module Effective
32
68
  def time_to_read_in_seconds(reading_speed = 3.333)
33
69
  (regions.to_a.sum { |region| (region.content || '').scan(/\w+/).size } / reading_speed).seconds
34
70
  end
71
+
72
+ def send_post_submitted_to_admin!
73
+ send_email(:post_submitted_to_admin, to_param)
74
+ end
75
+
76
+ private
77
+
78
+ def send_email(email, *mailer_args)
79
+ begin
80
+ if EffectivePosts.mailer[:delayed_job_deliver] && EffectivePosts.mailer[:deliver_method] == :deliver_later
81
+ Effective::PostsMailer.delay.public_send(email, *mailer_args)
82
+ elsif EffectivePosts.mailer[:deliver_method].present?
83
+ Effective::PostsMailer.public_send(email, *mailer_args).public_send(EffectivePosts.mailer[:deliver_method])
84
+ else
85
+ Effective::PostsMailer.public_send(email, *mailer_args).deliver_now
86
+ end
87
+ rescue => e
88
+ raise e unless Rails.env.production?
89
+ return false
90
+ end
91
+ end
92
+
35
93
  end
36
94
  end
@@ -1,9 +1,16 @@
1
- %span.actions
2
- = link_to 'Visit', effective_post_path(post), :target => '_blank'
3
- = '-'
4
- - if defined?(EffectiveRegions)
5
- = link_to 'Edit Content', '/edit' + effective_posts.post_path(post), 'data-no-turbolink' => true
6
- = '-'
7
- = link_to 'Edit', effective_posts.edit_admin_post_path(post.id)
8
- = '-'
9
- = link_to 'Delete', effective_posts.admin_post_path(post.id), :data => {:method => :delete, :confirm => "Are you sure? This post will be made unavailable."}
1
+ - if EffectivePosts.submissions_enabled && EffectivePosts.submissions_require_approval && !post.approved?
2
+ = link_to effective_posts.admin_approve_post_path(post.id), title: 'Approve' do
3
+ %span.glyphicon.glyphicon-ok
4
+
5
+ = link_to effective_posts.edit_admin_post_path(post.id), title: 'Settings' do
6
+ %span.glyphicon.glyphicon-cog
7
+
8
+ = link_to effective_post_path(post, (post.draft? ? {preview: true} : nil)), title: 'View', target: '_blank' do
9
+ %span.glyphicon.glyphicon-eye-open
10
+
11
+ - if defined?(EffectiveRegions)
12
+ = link_to effective_post_path(post, edit: true), title: 'Edit Content', 'data-no-turbolink': true, target: '_blank' do
13
+ %span.glyphicon.glyphicon-edit
14
+
15
+ = link_to effective_posts.admin_post_path(post.id), title: 'Delete', data: { method: :delete, confirm: 'Are you sure you want to delete? This cannot be undone.' } do
16
+ %span.glyphicon.glyphicon-trash
@@ -1,28 +1,35 @@
1
- = simple_form_for(post, (EffectivePosts.simple_form_options || {}).merge(:url => (post.persisted? ? effective_posts.admin_post_path(post.id) : effective_posts.admin_posts_path))) do |f|
2
- = f.input :title, :hint => "Give this post a title"
1
+ = simple_form_for(post, (EffectivePosts.admin_simple_form_options || {}).merge(:url => (post.persisted? ? effective_posts.admin_post_path(post.id) : effective_posts.admin_posts_path))) do |f|
2
+ = f.input :title, hint: 'The title of your post.'
3
3
 
4
4
  - if Array(EffectivePosts.categories).length > 0
5
- = f.input :category, :collection => EffectivePosts.categories, :as => :select, :include_blank => false
5
+ = f.input :category,
6
+ as: (defined?(EffectiveFormInputs) ? :effective_select : :select),
7
+ collection: EffectivePosts.categories,
8
+ include_blank: false
6
9
  - else
7
- = f.input :category, :as => :hidden, :input_html => {:value => 'posts'}
10
+ = f.input :category, as: :hidden, input_html: { value: 'posts' }
8
11
 
9
- %h4 Publish
10
- = f.input :draft, :hint => "Save this post as a draft. It will not be accessible on the website."
12
+ = render partial: '/effective/posts/additional_fields', locals: { post: post, form: f, f: f }
11
13
 
12
- - if defined?(EffectiveFormInputs)
13
- = f.input :published_at, :as => :effective_date_time_picker, :hint => "Set this to a future date to schedule a post to appear in the future."
14
- - else
15
- = f.input :published_at, :hint => "Set this to a future date to schedule a post to appear in the future."
14
+ = f.input :draft, hint: 'Save this post as a draft. It will not be accessible on the website.'
15
+
16
+ = f.input :published_at,
17
+ as: (defined?(EffectiveFormInputs) ? :effective_date_time_picker : :datetime),
18
+ label: 'Publish date',
19
+ hint: 'When your post will be displayed on the website.'
20
+
21
+ = f.input :body,
22
+ as: (defined?(EffectiveFormInputs) ? :effective_ckeditor_text_area : :text),
23
+ hint: 'The body of your post.',
24
+ toolbar: 'simple',
25
+ required: true
16
26
 
17
- - if defined?(EffectiveRoles) and f.object.respond_to?(:roles)
18
- = f.input :roles, :collection => EffectiveRoles.roles_collection(f.object), :as => :check_boxes, :hint => '* leave blank for a regular public post that anyone can view'
27
+ - if defined?(EffectiveRoles) and f.object.respond_to?(:roles) && EffectivePosts.use_effective_roles
28
+ = f.input :roles, collection: EffectiveRoles.roles_collection(f.object), as: :check_boxes, hint: '* leave blank for a regular public post that anyone can view'
19
29
 
20
- .form-group
21
- .col-xs-12
22
- .form-group
23
- .pull-right
24
- = f.button :submit, 'Save', data: { disable_with: 'Saving...' }
25
- - if defined?(EffectiveRegions)
26
- = f.button :submit, 'Save and Edit Content', data: { disable_with: 'Saving...' }
27
- = f.button :submit, 'Save and Add New', data: { disable_with: 'Saving...' }
28
- = link_to 'Cancel', effective_posts.admin_posts_path
30
+ .effective-post-actions.pull-right
31
+ = f.button :submit, 'Save', data: { disable_with: 'Saving...' }
32
+ - if defined?(EffectiveRegions)
33
+ = f.button :submit, 'Save and Edit Content', data: { disable_with: 'Saving...' }
34
+ = f.button :submit, 'Save and Add New', data: { disable_with: 'Saving...' }
35
+ = link_to 'Cancel', effective_posts.admin_posts_path
@@ -1,2 +1,2 @@
1
1
  %h2= @page_title
2
- = render :partial => 'form', :as => :post, :object => @post
2
+ = render partial: 'form', as: :post, object: @post
@@ -9,7 +9,7 @@
9
9
  = post.title
10
10
  .row
11
11
  .col-sm-6
12
- %pre= effective_region(post, :content, :editable => false)
12
+ %pre= effective_region(post, :body, :editable => false)
13
13
 
14
14
  .col-sm-6
15
15
  %pre= post_excerpt(post)
@@ -1,2 +1,2 @@
1
1
  %h2= @page_title
2
- = render :partial => 'form', :as => :post, :object => @post
2
+ = render partial: 'form', as: :post, object: @post
@@ -0,0 +1,2 @@
1
+ -# intentionally left blank
2
+ -# = form.input :start_at
@@ -0,0 +1,4 @@
1
+ .effective-posts-categories
2
+ - categories.each do |category|
3
+ .effective-post-category
4
+ = link_to_post_category(category)
@@ -0,0 +1,27 @@
1
+ = simple_form_for(post, (EffectivePosts.simple_form_options || {}).merge(:url => (post.persisted? ? effective_posts.post_path(post.id) : effective_posts.posts_path))) do |f|
2
+ = f.input :title, hint: 'The title of your post.'
3
+
4
+ - if Array(EffectivePosts.categories).length > 0
5
+ = f.input :category,
6
+ as: (defined?(EffectiveFormInputs) ? :effective_select : :select),
7
+ collection: EffectivePosts.categories,
8
+ include_blank: false
9
+ - else
10
+ = f.input :category, as: :hidden, input_html: { value: 'posts' }
11
+
12
+ = render partial: '/effective/posts/additional_fields', locals: { post: post, form: f, f: f }
13
+
14
+ = f.input :published_at,
15
+ as: (defined?(EffectiveFormInputs) ? :effective_date_time_picker : :datetime),
16
+ label: 'Publish date',
17
+ hint: 'When your post will be displayed on the website.'
18
+
19
+ = f.input :body,
20
+ as: (defined?(EffectiveFormInputs) ? :effective_ckeditor_text_area : :text),
21
+ hint: 'The body of your post.',
22
+ toolbar: 'simple',
23
+ required: true
24
+
25
+ %p.effective-post-actions.pull-right
26
+ = f.button :submit, 'Save', data: { disable_with: 'Saving...' }
27
+ = link_to 'Cancel', effective_posts.posts_path
@@ -0,0 +1,5 @@
1
+ .effective-posts-recent-posts
2
+ - posts.each do |post|
3
+ .effective-post
4
+ %p= link_to post, effective_post_path(post)
5
+
@@ -0,0 +1 @@
1
+ - # intentionally left blank
@@ -0,0 +1,2 @@
1
+ %h2= @page_title
2
+ = render partial: 'form', as: :post, object: @post
@@ -1,4 +1,9 @@
1
+ - if EffectivePosts.submissions_enabled
2
+ %p.text-right= link_to 'Submit New Post', effective_posts.new_post_path, class: 'btn btn-primary'
3
+
4
+ %nav= paginate @posts
5
+
1
6
  .effective-posts
2
- = render :partial => @posts, :spacer_template => 'spacer'
7
+ = render partial: @posts, spacer_template: 'spacer'
3
8
 
4
- %nav= paginate @posts
9
+ %nav= paginate @posts
@@ -0,0 +1,2 @@
1
+ %h2= @page_title
2
+ = render partial: 'form', as: :post, object: @post
@@ -1,12 +1,12 @@
1
1
  .effective-post
2
2
  %h2.post-title
3
- %a{:href => effective_post_path(@post)}
3
+ %a{href: effective_post_path(@post)}
4
4
  = simple_effective_region @post, :title do
5
5
  = @post.title
6
6
 
7
7
  %p.post-meta= post_meta(@post)
8
8
 
9
- .post-content
10
- = effective_region @post, :content do
9
+ .post-body.post-content
10
+ = effective_region @post, :body do
11
11
  %p Default content
12
12
 
@@ -0,0 +1,10 @@
1
+ %h2= @page_title
2
+
3
+ %p= EffectivePosts.submissions_note
4
+
5
+ - if (EffectivePosts.authorized?(controller, :edit, @post) rescue false)
6
+ %p= link_to 'Edit Post', effective_posts.edit_post_path(@post), class: 'btn btn-primary btn-edit-post'
7
+
8
+ %hr
9
+
10
+ = render file: '/effective/posts/show'
@@ -0,0 +1,22 @@
1
+ %p A new post has been submitted and needs your approval before it may be displayed on the website:
2
+
3
+ %p= link_to 'Approve Post', effective_posts.admin_approve_post_path(@post)
4
+
5
+ %hr
6
+
7
+ %h3 Title
8
+ %p= @post.title
9
+
10
+ %h3 Category
11
+ %p= @post.category
12
+
13
+ - if @post.user.present?
14
+ %h3 Submitted by
15
+ %p= @post.user
16
+
17
+ %h3 Body
18
+ %p= @post.body
19
+
20
+ %hr
21
+
22
+ %p= link_to 'Approve Post', effective_posts.admin_approve_post_path(@post)
@@ -0,0 +1,7 @@
1
+ !!!
2
+ %html{style: 'background: #fff;'}
3
+ %head
4
+ %meta{:content => 'text/html; charset=UTF-8', 'http-equiv' => 'Content-Type'}
5
+
6
+ %body{style: 'background: #fff;'}
7
+ = yield
data/config/routes.rb CHANGED
@@ -1,18 +1,23 @@
1
1
  EffectivePosts::Engine.routes.draw do
2
2
  namespace :admin do
3
- resources :posts, :except => [:show]
4
- match 'posts/excerpts', :to => 'posts#excerpts', :via => [:get]
3
+ resources :posts, except: [:show]
4
+
5
+ if EffectivePosts.submissions_enabled && EffectivePosts.submissions_require_approval
6
+ match 'posts/:id/approve', to: 'posts#approve', via: :get, as: :approve_post
7
+ end
8
+
9
+ match 'posts/excerpts', to: 'posts#excerpts', via: :get
5
10
  end
6
11
 
7
12
  scope :module => 'effective' do
8
- resources :posts, :only => [:index, :show]
13
+ resources :posts, only: ([:index, :show] unless EffectivePosts.submissions_enabled)
9
14
 
10
15
  if EffectivePosts.use_category_routes
11
- EffectivePosts.categories.each do |category|
12
- next if category.to_s == 'posts'
16
+ Array(EffectivePosts.categories).map { |category| category.to_s }.each do |category|
17
+ next if category == 'posts'
13
18
 
14
- match "#{category}", :to => 'posts#index', :via => [:get], :defaults => {:category => category.to_s }
15
- match "#{category}/:id", :to => 'posts#show', :via => [:get], :defaults => {:category => category.to_s }
19
+ match category, to: 'posts#index', via: :get, defaults: {:category => category }
20
+ match "#{category}/:id", to: 'posts#show', via: :get, defaults: {:category => category }
16
21
  end
17
22
  end
18
23
  end
@@ -10,15 +10,28 @@ module EffectivePosts
10
10
  mattr_accessor :posts_table_name
11
11
 
12
12
  mattr_accessor :authorization_method
13
- mattr_accessor :simple_form_options
13
+ mattr_accessor :permitted_params
14
+
14
15
  mattr_accessor :layout
16
+ mattr_accessor :simple_form_options
17
+ mattr_accessor :admin_simple_form_options
15
18
 
16
19
  mattr_accessor :categories
17
20
  mattr_accessor :use_category_routes
18
21
 
22
+ mattr_accessor :use_effective_roles
23
+
19
24
  mattr_accessor :per_page
20
25
  mattr_accessor :post_meta_author
21
26
 
27
+ mattr_accessor :submissions_enabled
28
+ mattr_accessor :submissions_require_current_user
29
+ mattr_accessor :submissions_require_approval
30
+ mattr_accessor :submissions_note
31
+
32
+ # These are hashes of configs
33
+ mattr_accessor :mailer
34
+
22
35
  def self.setup
23
36
  yield self
24
37
  end
@@ -29,4 +42,9 @@ module EffectivePosts
29
42
  end
30
43
  true
31
44
  end
45
+
46
+ def self.permitted_params
47
+ @@permitted_params ||= [:title, :draft, :category, :published_at, :body, roles: []]
48
+ end
49
+
32
50
  end
@@ -1,3 +1,3 @@
1
1
  module EffectivePosts
2
- VERSION = '0.4.6'.freeze
2
+ VERSION = '0.4.7'.freeze
3
3
  end
@@ -25,6 +25,16 @@ module EffectivePosts
25
25
  migration_template '../../../db/migrate/01_create_effective_posts.rb.erb', 'db/migrate/create_effective_posts.rb'
26
26
  end
27
27
 
28
+ def copy_mailer_preview
29
+ mailer_preview_path = (Rails.application.config.action_mailer.preview_path rescue nil)
30
+
31
+ if mailer_preview_path.present?
32
+ template 'effective_posts_mailer_preview.rb', File.join(mailer_preview_path, 'effective_posts_mailer_preview.rb')
33
+ else
34
+ puts "couldn't find action_mailer.preview_path. Skipping effective_posts_mailer_preview."
35
+ end
36
+ end
37
+
28
38
  def show_readme
29
39
  readme "README" if behavior == :invoke
30
40
  end
@@ -31,22 +31,65 @@ EffectivePosts.setup do |config|
31
31
  # Layout Settings
32
32
  # Configure the Layout per controller, or all at once
33
33
  config.layout = {
34
- :pages => 'application',
35
- :admin => 'application'
34
+ posts: 'application',
35
+ admin: 'application'
36
36
  }
37
37
 
38
38
  # SimpleForm Options
39
- # This Hash of options will be passed into any simple_form_for() calls
39
+ # This Hash of options will be passed into any client facing simple_form_for() calls
40
40
  config.simple_form_options = {}
41
+ config.admin_simple_form_options = {} # For the /admin/posts/new form
41
42
 
42
43
  # config.simple_form_options = {
43
- # :html => {:class => 'form-horizontal'},
44
- # :wrapper => :horizontal_form,
45
- # :wrapper_mappings => {
46
- # :boolean => :horizontal_boolean,
47
- # :check_boxes => :horizontal_radio_and_checkboxes,
48
- # :radio_buttons => :horizontal_radio_and_checkboxes
44
+ # html: {class: 'form-horizontal'},
45
+ # wrapper: :horizontal_form,
46
+ # wrapper_mappings: {
47
+ # boolean: :horizontal_boolean,
48
+ # check_boxes: :horizontal_radio_and_checkboxes,
49
+ # radio_buttons: :horizontal_radio_and_checkboxes
49
50
  # }
50
51
  # }
51
52
 
53
+ # Display the effective roles 'choose roles' input when an admin creates a new post
54
+ config.use_effective_roles = true
55
+
56
+ # Submissions
57
+ # Allow users to submit posts (optionally for approval) to display on the website
58
+ config.submissions_enabled = true
59
+
60
+ # When true, a user might be signed in to submit a post. (calls devise's authenticate_user!)
61
+ config.submissions_require_current_user = false
62
+
63
+ # When true, an Admin must first approve any newly submitted posts before they'll be displayed
64
+ config.submissions_require_approval = true
65
+
66
+ # The Thank you message when they submit a post
67
+ config.submissions_note = "News & Event submitted! A confirmation email has been sent to the AALA office. When approved, your submission will appear on the website."
68
+
69
+ # Mailer Settings
70
+ # effective_posts will send the admin an email when a post is submitted
71
+ # For all the emails, the same :subject_prefix will be prefixed. Leave as nil / empty string if you don't want any prefix
72
+ #
73
+ # The subject_for_post_submitted_to_admin can be one of:
74
+ # - nil / empty string to use the built in defaults
75
+ # - A string with the full subject line for this email
76
+ # - A Proc to create the subject line based on the email
77
+ # In all three of these cases, the subject_prefix will still be used.
78
+
79
+ # The Procs are the same for admin & buyer receipt, the seller Proc is different
80
+ # subject_for_post_submitted_to_admin: Proc.new { |post| "Post needs approval"}
81
+
82
+ config.mailer = {
83
+ subject_prefix: '[example]',
84
+ subject_for_post_submitted_to_admin: '',
85
+
86
+ layout: 'effective_posts_mailer_layout',
87
+
88
+ default_from: 'info@example.com',
89
+ admin_email: 'admin@example.com',
90
+
91
+ deliver_method: nil, # :deliver (rails < 4.2), :deliver_now (rails >= 4.2) or :deliver_later
92
+ delayed_job_deliver: false
93
+ }
94
+
52
95
  end
@@ -0,0 +1,22 @@
1
+ # In Rails 4.1 and above, visit:
2
+ # http://localhost:3000/rails/mailers
3
+ # to see a preview of the following 3 emails:
4
+
5
+ class EffectivePostsMailerPreview < ActionMailer::Preview
6
+ def post_submitted_to_admin
7
+ Effective::PostsMailer.post_submitted_to_admin(build_preview_post)
8
+ end
9
+
10
+ protected
11
+
12
+ def build_preview_post
13
+ post = Effective::Post.new(
14
+ title: 'An example post',
15
+ category: EffectivePosts.categories.first.presence || 'posts',
16
+ published_at: Time.zone.now,
17
+ draft: true,
18
+ content: 'This is a new post that has been submitted by a public user.'
19
+ )
20
+ end
21
+
22
+ end
metadata CHANGED
@@ -1,141 +1,141 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: effective_posts
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.6
4
+ version: 0.4.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Code and Effect
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-01-21 00:00:00.000000000 Z
11
+ date: 2016-09-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: 3.2.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: 3.2.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: haml-rails
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '>='
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '>='
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: sass-rails
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - '>='
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - '>='
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: kaminari
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - '>='
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - '>='
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: nokogiri
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - '>='
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
75
  version: '0'
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - '>='
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: simple_form
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - '>='
87
+ - - ">="
88
88
  - !ruby/object:Gem::Version
89
89
  version: '0'
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - '>='
94
+ - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: effective_ckeditor
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - '>='
101
+ - - ">="
102
102
  - !ruby/object:Gem::Version
103
103
  version: 1.2.0
104
104
  type: :runtime
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - '>='
108
+ - - ">="
109
109
  - !ruby/object:Gem::Version
110
110
  version: 1.2.0
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: effective_datatables
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
- - - '>='
115
+ - - ">="
116
116
  - !ruby/object:Gem::Version
117
117
  version: 2.0.0
118
118
  type: :runtime
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
- - - '>='
122
+ - - ">="
123
123
  - !ruby/object:Gem::Version
124
124
  version: 2.0.0
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: effective_regions
127
127
  requirement: !ruby/object:Gem::Requirement
128
128
  requirements:
129
- - - '>='
129
+ - - ">="
130
130
  - !ruby/object:Gem::Version
131
- version: 1.2.2
131
+ version: 1.6.9
132
132
  type: :runtime
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
- - - '>='
136
+ - - ">="
137
137
  - !ruby/object:Gem::Version
138
- version: 1.2.2
138
+ version: 1.6.9
139
139
  description: A blog implementation with WYSIWYG content editing, post scheduling,
140
140
  pagination and optional top level routes for each post category.
141
141
  email:
@@ -155,6 +155,7 @@ files:
155
155
  - app/helpers/effective_kaminari_helper.rb
156
156
  - app/helpers/effective_posts_helper.rb
157
157
  - app/helpers/effective_truncate_html_helper.rb
158
+ - app/mailers/effective/posts_mailer.rb
158
159
  - app/models/effective/access_denied.rb
159
160
  - app/models/effective/datatables/posts.rb
160
161
  - app/models/effective/post.rb
@@ -165,10 +166,18 @@ files:
165
166
  - app/views/admin/posts/excerpts.html.haml
166
167
  - app/views/admin/posts/index.html.haml
167
168
  - app/views/admin/posts/new.html.haml
169
+ - app/views/effective/posts/_additional_fields.html.haml
170
+ - app/views/effective/posts/_categories.html.haml
171
+ - app/views/effective/posts/_form.html.haml
168
172
  - app/views/effective/posts/_post.html.haml
173
+ - app/views/effective/posts/_recent_posts.html.haml
169
174
  - app/views/effective/posts/_spacer.html.haml
175
+ - app/views/effective/posts/edit.html.haml
170
176
  - app/views/effective/posts/index.html.haml
177
+ - app/views/effective/posts/new.html.haml
171
178
  - app/views/effective/posts/show.html.haml
179
+ - app/views/effective/posts/submitted.html.haml
180
+ - app/views/effective/posts_mailer/post_submitted_to_admin.html.haml
172
181
  - app/views/effective/snippets/_read_more_divider.html.haml
173
182
  - app/views/kaminari/_first_page.html.haml
174
183
  - app/views/kaminari/_gap.html.haml
@@ -177,6 +186,7 @@ files:
177
186
  - app/views/kaminari/_page.html.haml
178
187
  - app/views/kaminari/_paginator.html.haml
179
188
  - app/views/kaminari/_prev_page.html.haml
189
+ - app/views/layouts/effective_posts_mailer_layout.html.haml
180
190
  - config/routes.rb
181
191
  - db/migrate/01_create_effective_posts.rb.erb
182
192
  - lib/effective_posts.rb
@@ -185,6 +195,7 @@ files:
185
195
  - lib/generators/effective_posts/install_generator.rb
186
196
  - lib/generators/templates/README
187
197
  - lib/generators/templates/effective_posts.rb
198
+ - lib/generators/templates/effective_posts_mailer_preview.rb
188
199
  - spec/effective_pages_spec.rb
189
200
  - spec/spec_helper.rb
190
201
  - spec/support/factories.rb
@@ -198,17 +209,17 @@ require_paths:
198
209
  - lib
199
210
  required_ruby_version: !ruby/object:Gem::Requirement
200
211
  requirements:
201
- - - '>='
212
+ - - ">="
202
213
  - !ruby/object:Gem::Version
203
214
  version: '0'
204
215
  required_rubygems_version: !ruby/object:Gem::Requirement
205
216
  requirements:
206
- - - '>='
217
+ - - ">="
207
218
  - !ruby/object:Gem::Version
208
219
  version: '0'
209
220
  requirements: []
210
221
  rubyforge_project:
211
- rubygems_version: 2.4.6
222
+ rubygems_version: 2.4.5.1
212
223
  signing_key:
213
224
  specification_version: 4
214
225
  summary: A blog implementation with WYSIWYG content editing, post scheduling, pagination