miniblog 1.0.0.beta
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.autotest +5 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.simplecov +13 -0
- data/.travis.yml +17 -0
- data/CHANGELOG.md +7 -0
- data/Gemfile +20 -0
- data/Gemfile.4.1 +19 -0
- data/MIT-LICENSE +20 -0
- data/README.md +86 -0
- data/Rakefile +29 -0
- data/app/assets/images/crowdblog/.gitkeep +0 -0
- data/app/assets/javascripts/miniblog.js +7 -0
- data/app/assets/javascripts/miniblog/alerts.js +5 -0
- data/app/assets/javascripts/miniblog/form-errors.js +3 -0
- data/app/assets/stylesheets/application.css +3 -0
- data/app/assets/stylesheets/miniblog.css +5 -0
- data/app/assets/stylesheets/miniblog/bootstrap_and_overrides.css.scss +1 -0
- data/app/assets/stylesheets/miniblog/posts.css.scss +34 -0
- data/app/controllers/miniblog/admin/assets_controller.rb +20 -0
- data/app/controllers/miniblog/admin/base_controller.rb +7 -0
- data/app/controllers/miniblog/admin/posts_controller.rb +63 -0
- data/app/controllers/miniblog/admin/preview_controller.rb +8 -0
- data/app/controllers/miniblog/admin/states_controller.rb +11 -0
- data/app/controllers/miniblog/admin/transitions_controller.rb +22 -0
- data/app/controllers/miniblog/application_controller.rb +14 -0
- data/app/controllers/miniblog/posts_controller.rb +7 -0
- data/app/helpers/miniblog/admin/posts_helper.rb +18 -0
- data/app/helpers/miniblog/application_helper.rb +4 -0
- data/app/models/miniblog/asset.rb +7 -0
- data/app/models/miniblog/post.rb +135 -0
- data/app/models/miniblog/status_change_record.rb +6 -0
- data/app/models/miniblog/user.rb +32 -0
- data/app/presenters/miniblog/post_presenter.rb +13 -0
- data/app/uploaders/attachment_uploader.rb +48 -0
- data/app/views/layouts/miniblog/admin/base.html.erb +26 -0
- data/app/views/miniblog/admin/posts/_form.html.erb +10 -0
- data/app/views/miniblog/admin/posts/_form.html.slim +27 -0
- data/app/views/miniblog/admin/posts/_post.html.erb +14 -0
- data/app/views/miniblog/admin/posts/_post.html.slim +20 -0
- data/app/views/miniblog/admin/posts/edit.html.erb +19 -0
- data/app/views/miniblog/admin/posts/edit.html.slim +14 -0
- data/app/views/miniblog/admin/posts/index.html.erb +5 -0
- data/app/views/miniblog/admin/posts/new.html.erb +17 -0
- data/app/views/miniblog/admin/posts/new.html.slim +12 -0
- data/app/views/miniblog/application/_navbar.html.erb +19 -0
- data/app/views/miniblog/application/_notices.html.slim +6 -0
- data/app/views/miniblog/posts/_post.html.slim +4 -0
- data/app/views/miniblog/posts/index.html.slim +1 -0
- data/config/cucumber.yml +9 -0
- data/config/initializers/date_formats.rb +1 -0
- data/config/initializers/state_machine.rb +4 -0
- data/config/locales/devise.en.yml +57 -0
- data/config/routes.rb +26 -0
- data/db/migrate/20120217213920_create_miniblog_posts.rb +17 -0
- data/db/migrate/20120219071614_create_miniblog_assets.rb +10 -0
- data/lib/generators/miniblog/views_generator.rb +11 -0
- data/lib/miniblog.rb +18 -0
- data/lib/miniblog/devise/failure_app.rb +9 -0
- data/lib/miniblog/engine.rb +9 -0
- data/lib/miniblog/rspec.rb +1 -0
- data/lib/miniblog/rspec/miniblog_shared_examples.rb +77 -0
- data/lib/miniblog/version.rb +3 -0
- data/miniblog.gemspec +40 -0
- data/script/cucumber +10 -0
- data/script/rails +8 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +16 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +2 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.gitkeep +0 -0
- data/spec/dummy/app/models/.gitkeep +0 -0
- data/spec/dummy/app/models/user.rb +3 -0
- data/spec/dummy/app/views/home/show.html.slim +4 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +28 -0
- data/spec/dummy/config/boot.rb +4 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +82 -0
- data/spec/dummy/config/environments/test.rb +39 -0
- data/spec/dummy/config/initializers/assets.rb +8 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/carrierwave.rb +18 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/miniblog.rb +2 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +3 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/db/schema.rb +50 -0
- data/spec/dummy/db/seed.rb +6 -0
- data/spec/dummy/lib/assets/.gitkeep +0 -0
- data/spec/dummy/lib/tasks/cucumber.rake +65 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/features/miniblog_spec.rb +5 -0
- data/spec/generators/miniblog/views_generator_spec.rb +16 -0
- data/spec/models/post_spec.rb +163 -0
- data/spec/spec_helper.rb +58 -0
- data/vendor/assets/javascripts/markdown.js +1470 -0
- data/vendor/assets/javascripts/uploader/jquery.html5uploader.js +148 -0
- metadata +464 -0
@@ -0,0 +1,22 @@
|
|
1
|
+
module Miniblog
|
2
|
+
module Admin
|
3
|
+
class TransitionsController < Miniblog::Admin::BaseController
|
4
|
+
before_filter :load_post, only: [:create]
|
5
|
+
|
6
|
+
def create
|
7
|
+
namespace = '_as_publisher' if current_user.is_publisher?
|
8
|
+
@post.send "#{params[:transition]}#{namespace}"
|
9
|
+
status = @post.status_change_records.build(user: current_user, state: params[:transition])
|
10
|
+
status.save
|
11
|
+
redirect_to admin_posts_path
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def load_post
|
17
|
+
post = Post.scoped_for(current_user).find(params[:id])
|
18
|
+
@post = PostPresenter.new(post, current_user)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Miniblog
|
2
|
+
class ApplicationController < ::ApplicationController
|
3
|
+
def method_missing(method_name)
|
4
|
+
if method_name == :current_user
|
5
|
+
Rails.logger.warn("current_user in Miniblog::ApplicationController should be overriden")
|
6
|
+
::User.new
|
7
|
+
elsif method_name == :authenticate_user!
|
8
|
+
Rails.logger.warn("authenticate_user! in Miniblog::ApplicationController should be overriden")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
helper_method :current_user
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Miniblog::Admin::PostsHelper
|
2
|
+
def edit_post_button(post)
|
3
|
+
link_to 'Edit', miniblog.edit_admin_post_path(post),
|
4
|
+
class: "btn btn-default btn-block"
|
5
|
+
end
|
6
|
+
|
7
|
+
def delete_post_button(post)
|
8
|
+
link_to 'Delete', miniblog.admin_post_path(post),
|
9
|
+
class: "btn btn-default btn-block",
|
10
|
+
method: :delete, data: { confirm: "Are you sure?"}
|
11
|
+
end
|
12
|
+
|
13
|
+
def publish_post_button(post)
|
14
|
+
link_to 'Publish', miniblog.admin_post_state_path(post),
|
15
|
+
class: "btn btn-block #{post.published? ? "btn-success" : "btn-danger" }",
|
16
|
+
method: :put
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
module Miniblog
|
2
|
+
class Post < ActiveRecord::Base
|
3
|
+
belongs_to :author, :class_name => Miniblog.author_user_class_name
|
4
|
+
belongs_to :publisher, :class_name => Miniblog.publisher_user_class_name
|
5
|
+
has_many :assets
|
6
|
+
|
7
|
+
delegate :name, to: :author, prefix: true, allow_nil: true
|
8
|
+
delegate :email, to: :author, prefix: true, allow_nil: true
|
9
|
+
delegate :gravatar_url, to: :author
|
10
|
+
|
11
|
+
delegate :year, to: :published_at
|
12
|
+
|
13
|
+
validates_presence_of :title
|
14
|
+
|
15
|
+
attr_accessor :transition
|
16
|
+
|
17
|
+
LEGACY_TITLE_REGEXP = /(\d+-\d+-\d+)-(.*)/
|
18
|
+
|
19
|
+
state_machine :state, initial: :drafted do
|
20
|
+
state :drafted
|
21
|
+
state :published
|
22
|
+
|
23
|
+
before_transition on: :publish do |post, transition|
|
24
|
+
post.published_at ||= Time.now
|
25
|
+
end
|
26
|
+
|
27
|
+
before_transition on: :draft do |post, transition|
|
28
|
+
post.published_at = nil
|
29
|
+
end
|
30
|
+
|
31
|
+
event :draft do
|
32
|
+
transition all => :drafted
|
33
|
+
end
|
34
|
+
|
35
|
+
event :publish do
|
36
|
+
transition all => :published
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# CLASS METHODS
|
41
|
+
class << self
|
42
|
+
def all_posts_json
|
43
|
+
includes(:author).
|
44
|
+
order_by_publish_date.to_json only: [:id, :title, :state, :published_at, :ready_for_review],
|
45
|
+
methods: [:author_email, :published?]
|
46
|
+
end
|
47
|
+
|
48
|
+
def by_author(author_id)
|
49
|
+
published_and_ordered.where(author_id: author_id)
|
50
|
+
end
|
51
|
+
|
52
|
+
def last_published(number)
|
53
|
+
published_and_ordered.limit(number)
|
54
|
+
end
|
55
|
+
|
56
|
+
def order_by_publish_date
|
57
|
+
order('published_at DESC, created_at DESC, id DESC')
|
58
|
+
end
|
59
|
+
|
60
|
+
def published
|
61
|
+
where(state: 'published')
|
62
|
+
end
|
63
|
+
|
64
|
+
def published_and_ordered
|
65
|
+
published.order_by_publish_date
|
66
|
+
end
|
67
|
+
|
68
|
+
def scoped_for(user)
|
69
|
+
user.is_publisher? ? all : user.authored_posts
|
70
|
+
end
|
71
|
+
|
72
|
+
def for_admin_index
|
73
|
+
ordered_by_state.order_by_publish_date
|
74
|
+
end
|
75
|
+
|
76
|
+
def ordered_by_state
|
77
|
+
order(:state)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Must be after Class methods (otherwise a missing method error will raise)
|
82
|
+
scope :for_index, -> { last_published(3) }
|
83
|
+
scope :for_history, -> { last_published(13) }
|
84
|
+
scope :all_for_feed, -> { last_published(15) }
|
85
|
+
|
86
|
+
|
87
|
+
# INSTANCE METHODS
|
88
|
+
def allowed_to_update_permalink?
|
89
|
+
!self.published?
|
90
|
+
end
|
91
|
+
|
92
|
+
def day
|
93
|
+
"%02d" % published_at.day
|
94
|
+
end
|
95
|
+
|
96
|
+
def formatted_published_date
|
97
|
+
published_at.strftime("%b %d, %Y")
|
98
|
+
end
|
99
|
+
|
100
|
+
def html_body
|
101
|
+
@@renderer ||= Redcarpet::Markdown.new(Redcarpet::Render::HTML,
|
102
|
+
:autolink => true,
|
103
|
+
:fenced_code_blocks => true,
|
104
|
+
:space_after_headers => false)
|
105
|
+
@@renderer.render(self.body).html_safe
|
106
|
+
end
|
107
|
+
|
108
|
+
def month
|
109
|
+
"%02d" % published_at.month
|
110
|
+
end
|
111
|
+
|
112
|
+
def publish_if_allowed(transition, user)
|
113
|
+
if user.is_publisher?
|
114
|
+
self.publisher = user
|
115
|
+
self.send(transition)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def regenerate_permalink
|
120
|
+
self.permalink = title.parameterize
|
121
|
+
end
|
122
|
+
|
123
|
+
#
|
124
|
+
# Use this methods to generate the post url
|
125
|
+
# always use with the splat
|
126
|
+
# operator
|
127
|
+
#
|
128
|
+
# Example:
|
129
|
+
# post_url(*post.url_params)
|
130
|
+
#
|
131
|
+
def url_params
|
132
|
+
[self.year, self.month, self.day, self.permalink, 'html']
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Miniblog
|
2
|
+
module User
|
3
|
+
def self.included(base)
|
4
|
+
base.send(:has_many, :authored_posts, inverse_of: :author,
|
5
|
+
foreign_key: 'author_id', class_name: 'Miniblog::Post')
|
6
|
+
base.send(:has_many, :published_posts, -> {
|
7
|
+
where(state: 'published').
|
8
|
+
order('published_at DESC')
|
9
|
+
},
|
10
|
+
inverse_of: :author,
|
11
|
+
foreign_key: 'author_id', class_name: 'Miniblog::Post')
|
12
|
+
base.send(:has_one, :last_post, -> {
|
13
|
+
where(state: 'published').
|
14
|
+
order('published_at DESC, created_at DESC, id DESC')
|
15
|
+
},
|
16
|
+
class_name: 'Miniblog::Post',
|
17
|
+
foreign_key: :author_id)
|
18
|
+
end
|
19
|
+
|
20
|
+
def is_publisher?
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
def last_post_at
|
25
|
+
last_post.try(:published_at)
|
26
|
+
end
|
27
|
+
|
28
|
+
def last_published_at
|
29
|
+
published_posts.first ? published_posts.first.published_at : nil
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class AttachmentUploader < CarrierWave::Uploader::Base
|
4
|
+
|
5
|
+
# Include RMagick or MiniMagick support:
|
6
|
+
# include CarrierWave::RMagick
|
7
|
+
# include CarrierWave::MiniMagick
|
8
|
+
|
9
|
+
# Choose what kind of storage to use for this uploader:
|
10
|
+
# storage :file
|
11
|
+
#storage :fog
|
12
|
+
|
13
|
+
# Override the directory where uploaded files will be stored.
|
14
|
+
# This is a sensible default for uploaders that are meant to be mounted:
|
15
|
+
def store_dir
|
16
|
+
"#{model.class.to_s.underscore}/#{model.id}"
|
17
|
+
end
|
18
|
+
|
19
|
+
# Provide a default URL as a default if there hasn't been a file uploaded:
|
20
|
+
# def default_url
|
21
|
+
# "/images/fallback/" + [version_name, "default.png"].compact.join('_')
|
22
|
+
# end
|
23
|
+
|
24
|
+
# Process files as they are uploaded:
|
25
|
+
# process :scale => [200, 300]
|
26
|
+
#
|
27
|
+
# def scale(width, height)
|
28
|
+
# # do something
|
29
|
+
# end
|
30
|
+
|
31
|
+
# Create different versions of your uploaded files:
|
32
|
+
# version :thumb do
|
33
|
+
# process :scale => [50, 50]
|
34
|
+
# end
|
35
|
+
|
36
|
+
# Add a white list of extensions which are allowed to be uploaded.
|
37
|
+
# For images you might use something like this:
|
38
|
+
# def extension_white_list
|
39
|
+
# %w(jpg jpeg gif png)
|
40
|
+
# end
|
41
|
+
|
42
|
+
# Override the filename of the uploaded files:
|
43
|
+
# Avoid using model.id or version_name here, see uploader/store.rb for details.
|
44
|
+
# def filename
|
45
|
+
# "something.jpg" if original_filename
|
46
|
+
# end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Crowd Blog</title>
|
5
|
+
<%= stylesheet_link_tag :miniblog, media: 'all' %>
|
6
|
+
<%= javascript_include_tag :miniblog %>
|
7
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
8
|
+
<meta charset="utf-8">
|
9
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
10
|
+
<%= csrf_meta_tags %>
|
11
|
+
<%= yield :scripts %>
|
12
|
+
</head>
|
13
|
+
<body>
|
14
|
+
<div class="container">
|
15
|
+
<div class="row">
|
16
|
+
<%= render '/miniblog/application/navbar' %>
|
17
|
+
</div>
|
18
|
+
<div class="row">
|
19
|
+
<%= render '/miniblog/application/notices' %>
|
20
|
+
</div>
|
21
|
+
</div>
|
22
|
+
<div class="container">
|
23
|
+
<%= yield %>
|
24
|
+
</div>
|
25
|
+
</body>
|
26
|
+
</html>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<div class="col-xs-10 col-xs-offset-1">
|
2
|
+
<div class="form-group">
|
3
|
+
<%= f.label :title, class: 'control-label' %>
|
4
|
+
<%= f.text_field :title, class: "form-control input-lg" %>
|
5
|
+
</div>
|
6
|
+
<div class="form-group">
|
7
|
+
<%= f.label :body %>
|
8
|
+
<%= f.text_area :body, class: "form-control", rows: 20 %>
|
9
|
+
</div>
|
10
|
+
</div>
|
@@ -0,0 +1,27 @@
|
|
1
|
+
.row
|
2
|
+
.span12
|
3
|
+
.control-group.title
|
4
|
+
= f.label :title, :class => 'control-label'
|
5
|
+
.controls
|
6
|
+
= f.text_field :title, :class => "input-xlarge span5"
|
7
|
+
span.help-inline.title
|
8
|
+
|
9
|
+
.row
|
10
|
+
.control-group.body
|
11
|
+
.span12
|
12
|
+
= label_tag 'post_body', 'Body', class: 'control-label'
|
13
|
+
.controls.span5
|
14
|
+
= f.text_area :body, :class => "span5", :rows => 20
|
15
|
+
span.help-inline= link_to 'Markdown syntax', 'http://daringfireball.net/projects/markdown/syntax', target: '_BLANK'
|
16
|
+
|
17
|
+
#post-preview.span7
|
18
|
+
.inner
|
19
|
+
|
20
|
+
.row.form-box
|
21
|
+
.span5
|
22
|
+
#uploader
|
23
|
+
.inner Drop images...
|
24
|
+
|
25
|
+
.span7#assets
|
26
|
+
- f.object.assets.each do |asset|
|
27
|
+
.asset= "![image alt](#{asset.attachment_url})"
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<%= div_for post, class: "row" do %>
|
2
|
+
<div class="col-sm-4 col-sm-offset-1"><h4><%= post.title %></h4></div>
|
3
|
+
<div class="visible-xs col-xs-12">
|
4
|
+
<div class="col-xs-4"><%= edit_post_button(post) %></div>
|
5
|
+
<div class="col-xs-4"><%= publish_post_button(post) %></div>
|
6
|
+
<div class="col-xs-4"><%= delete_post_button(post) %></div>
|
7
|
+
</div>
|
8
|
+
<div class="hidden-xs">
|
9
|
+
<div class="col-sm-2"><%= edit_post_button(post) %></div>
|
10
|
+
<div class="col-sm-2"><%= publish_post_button(post) %></div>
|
11
|
+
<div class="col-sm-2"><%= delete_post_button(post) %></div>
|
12
|
+
</div>
|
13
|
+
<% end %>
|
14
|
+
<hr/>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
= cache [:admin, post] do
|
2
|
+
= content_tag_for :tr, post, :"data-post-id" => post.id, :"data-state" => post.state, :"data-ready-for-review" => post.ready_for_review do
|
3
|
+
td= post.title
|
4
|
+
td= post.author_email
|
5
|
+
td.span2.published-at = post.published_at.try(:to_s, :miniblog_short)
|
6
|
+
td.span1
|
7
|
+
- if current_user.is_publisher?
|
8
|
+
= link_to 'Publish', '#', :class => "btn btn-small publish-btn btn-danger #{(post.published? ? 'draft' : 'publish')}"
|
9
|
+
td.span1
|
10
|
+
.btn-group.states
|
11
|
+
button.btn.btn-small.draft Drafted
|
12
|
+
button.btn.btn-small.finish Finished
|
13
|
+
- if !current_user.is_publisher?
|
14
|
+
button.btn.btn-small.disabled.publish-status
|
15
|
+
- if current_user.is_publisher?
|
16
|
+
button.btn.btn-small.review Reviewed
|
17
|
+
td.span1
|
18
|
+
= link_to 'Delete', miniblog.admin_post_path(post), :method => :delete, :data => { :confirm => 'Are you sure?' }, :class => "btn btn-small"
|
19
|
+
td.span1
|
20
|
+
= link_to 'Edit', miniblog.edit_admin_post_path(post), :class => "btn btn-small"
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<div class="page-header">
|
2
|
+
<h1>Edit Post</h1>
|
3
|
+
</div>
|
4
|
+
<%= form_for [:admin, @post] do |f| %>
|
5
|
+
<div class="row">
|
6
|
+
<%= render 'form', f: f %>
|
7
|
+
</div>
|
8
|
+
<div class="row well">
|
9
|
+
<div class="col-xs-4 col-xs-offset-1 col-sm-2 col-sm-offset-3">
|
10
|
+
<%= f.submit "Update", class: "btn btn-primary btn-block" %>
|
11
|
+
</div>
|
12
|
+
<div class="col-xs-3 col-sm-2">
|
13
|
+
<%= link_to 'Preview', miniblog.admin_preview_path(@post), class: "btn btn-default btn-block" %>
|
14
|
+
</div>
|
15
|
+
<div class="col-xs-3 col-sm-2">
|
16
|
+
<%= link_to 'Cancel', miniblog.admin_posts_path, class: "btn btn-default btn-block" %>
|
17
|
+
</div>
|
18
|
+
</div>
|
19
|
+
<% end %>
|