ecrire 0.22.1 → 0.23.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/.travis.yml +5 -0
- data/Gemfile +19 -0
- data/README.md +2 -0
- data/Rakefile +1 -1
- data/lib/ecrire/app/assets/javascripts/admin/editor/content.coffee +46 -76
- data/lib/ecrire/app/assets/javascripts/admin/editor/ext.coffee +3 -0
- data/lib/ecrire/app/assets/javascripts/admin/editor/extensions/clipboard.coffee +2 -1
- data/lib/ecrire/app/assets/javascripts/admin/editor/extensions/code.coffee +1 -2
- data/lib/ecrire/app/assets/javascripts/admin/editor/extensions/image.coffee +73 -1
- data/lib/ecrire/app/assets/javascripts/admin/editor/parsers/image.coffee +6 -115
- data/lib/ecrire/app/assets/javascripts/admin/editor.coffee +1 -0
- data/lib/ecrire/app/assets/javascripts/admin/{editor/navigation → navigation}/delete.js.coffee +1 -2
- data/lib/ecrire/app/assets/javascripts/admin/{editor/navigation → navigation}/draft.js.coffee +1 -3
- data/lib/ecrire/app/assets/javascripts/admin/{editor/navigation → navigation}/help.js.coffee +1 -1
- data/lib/ecrire/app/assets/javascripts/admin/{editor/navigation → navigation}/save.js.coffee +2 -2
- data/lib/ecrire/app/assets/javascripts/admin/{editor/navigation → navigation}/sticky.js.coffee +1 -2
- data/lib/ecrire/app/assets/javascripts/admin/{editor/navigation → navigation}/toggle.js.coffee +1 -2
- data/lib/ecrire/app/assets/javascripts/admin/posts/header.coffee +96 -40
- data/lib/ecrire/app/assets/javascripts/admin/posts/new.js.coffee +1 -2
- data/lib/ecrire/app/assets/javascripts/admin/posts/tag.js.coffee +7 -0
- data/lib/ecrire/app/assets/javascripts/admin/posts/tags.js.coffee +15 -0
- data/lib/ecrire/app/assets/javascripts/admin/posts/title.coffee +38 -9
- data/lib/ecrire/app/assets/javascripts/admin/tags/list.js.coffee +34 -0
- data/lib/ecrire/app/assets/javascripts/admin.js +1 -1
- data/lib/ecrire/app/assets/javascripts/application.js +2 -3
- data/lib/ecrire/app/assets/javascripts/shared/overlay.js.coffee +1 -2
- data/lib/ecrire/app/assets/javascripts/shared/popup.js.coffee +1 -2
- data/lib/ecrire/app/assets/stylesheets/admin/base.css.scss +1 -2
- data/lib/ecrire/app/assets/stylesheets/admin/editor/base.css.scss +0 -7
- data/lib/ecrire/app/assets/stylesheets/admin/editor/content.css.scss +30 -12
- data/lib/ecrire/app/assets/stylesheets/admin/editor/header.css.scss +154 -32
- data/lib/ecrire/app/assets/stylesheets/admin/posts.css.scss +8 -1
- data/lib/ecrire/app/assets/stylesheets/admin/tags.css.scss +78 -0
- data/lib/ecrire/app/assets/stylesheets/admin/title.css.scss +51 -0
- data/lib/ecrire/app/assets/stylesheets/admin/variables.css.scss +3 -0
- data/lib/ecrire/app/assets/stylesheets/shared/popup.css.scss +2 -0
- data/lib/ecrire/app/controllers/admin/application_controller.rb +6 -0
- data/lib/ecrire/app/controllers/admin/images_controller.rb +5 -6
- data/lib/ecrire/app/controllers/admin/posts/tags_controller.rb +35 -0
- data/lib/ecrire/app/controllers/admin/posts_controller.rb +13 -12
- data/lib/ecrire/app/controllers/admin/tags_controller.rb +4 -0
- data/lib/ecrire/app/controllers/admin/titles_controller.rb +39 -0
- data/lib/ecrire/app/controllers/application_controller.rb +13 -3
- data/lib/ecrire/app/controllers/ecrire/posts_controller.rb +6 -5
- data/lib/ecrire/app/helpers/posts_helper.rb +0 -12
- data/lib/ecrire/app/models/admin/image.rb +4 -7
- data/lib/ecrire/app/models/admin/post.rb +2 -7
- data/lib/ecrire/app/models/admin/tag.rb +4 -0
- data/lib/ecrire/app/models/admin/title.rb +4 -0
- data/lib/ecrire/app/models/post.rb +30 -22
- data/lib/ecrire/app/models/tag.rb +5 -0
- data/lib/ecrire/app/models/title.rb +62 -0
- data/lib/ecrire/app/views/admin/images/errors.json.jbuilder +1 -0
- data/lib/ecrire/app/views/admin/posts/edit.html.erb +61 -21
- data/lib/ecrire/app/views/admin/posts/header/_dropping.html.erb +12 -0
- data/lib/ecrire/app/views/admin/posts/header/_error.html.erb +18 -0
- data/lib/ecrire/app/views/admin/posts/header/_uploading.html.erb +21 -0
- data/lib/ecrire/app/views/admin/posts/new.html.erb +23 -0
- data/lib/ecrire/app/views/admin/posts/tags/_tag.html.erb +10 -0
- data/lib/ecrire/app/views/admin/posts/tags/create.js.erb +1 -0
- data/lib/ecrire/app/views/admin/posts/tags/dialog/_tag.html.erb +3 -0
- data/lib/ecrire/app/views/admin/posts/tags/dialog/_tags.html.erb +12 -0
- data/lib/ecrire/app/views/admin/posts/tags/index.js.erb +1 -0
- data/lib/ecrire/app/views/admin/posts/tags/update.js.erb +2 -0
- data/lib/ecrire/app/views/admin/titles/_title.html.erb +11 -0
- data/lib/ecrire/app/views/admin/titles/_titles.html.erb +22 -0
- data/lib/ecrire/app/views/admin/titles/create.js.erb +1 -0
- data/lib/ecrire/app/views/admin/titles/edit.js.erb +1 -0
- data/lib/ecrire/app/views/admin/titles/errors.js.erb +5 -0
- data/lib/ecrire/app/views/admin/titles/index.js.erb +1 -0
- data/lib/ecrire/app/views/admin/titles/update.js.erb +1 -0
- data/lib/ecrire/app/views/layouts/admin/application.html.erb +1 -1
- data/lib/ecrire/app/views/sessions/_navigation.html.erb +1 -1
- data/lib/ecrire/application.rb +1 -1
- data/lib/ecrire/config/routes.rb +2 -2
- data/lib/ecrire/db/migrate/20150305123321_move_labels_to_titles.rb +27 -0
- data/lib/ecrire/db/migrate/20150318549479_create_tags.rb +13 -0
- data/lib/ecrire/db/schema.rb +25 -22
- data/lib/ecrire/onboarding/assets/javascripts/base.js +1 -1
- data/lib/ecrire/onboarding/assets/javascripts/databases/information.js.coffee +1 -1
- data/lib/ecrire/onboarding/assets/javascripts/message.js.coffee +1 -1
- data/lib/ecrire/template/Gemfile +4 -3
- data/lib/ecrire/template/assets/javascripts/base.js.coffee +1 -0
- data/lib/ecrire/version.rb +1 -1
- data/test/editor/controllers/base_controller_test.rb +1 -0
- data/test/editor/models/post_test.rb +26 -56
- data/test/editor/models/title_test.rb +31 -0
- data/test/editor/test_helper.rb +2 -0
- data/test/fixtures/posts.yml +0 -4
- data/test/fixtures/tags.yml +2 -0
- data/test/fixtures/titles.yml +11 -0
- data/test/initializations/railtie_test.rb +2 -2
- data/test/themes/template/secrets.yml +6 -0
- metadata +49 -24
- data/lib/ecrire/app/assets/stylesheets/admin/editor/image.css.scss +0 -58
- data/lib/ecrire/app/controllers/admin/labels_controller.rb +0 -18
- data/lib/ecrire/app/models/label.rb +0 -3
- data/lib/ecrire/app/views/admin/images/errors.js.erb +0 -1
- data/lib/ecrire/app/views/admin/posts/title.html.erb +0 -10
- data/test/editor/controllers/admin/images_controller_test.rb +0 -6
- data/test/editor/controllers/admin/posts_controller_test.rb +0 -90
- data/test/editor/controllers/images_controller_test.rb +0 -2
- data/test/editor/controllers/partials_controller_test.rb +0 -2
- data/test/editor/controllers/posts_controller_test.rb +0 -21
- data/test/editor/helpers/posts_helper_test.rb +0 -3
- data/test/editor/models/partial_test.rb +0 -4
- data/test/fixtures/partials.yml +0 -9
- /data/lib/ecrire/app/views/admin/images/{update.js.erb → create.js.erb} +0 -0
- /data/{lib/ecrire/template/assets/javascripts/post.js.coffee → test/themes/onboarding/.keep} +0 -0
@@ -0,0 +1,78 @@
|
|
1
|
+
#TagsList {
|
2
|
+
@include display(flex);
|
3
|
+
@include align-items(center);
|
4
|
+
margin: 1em 0;
|
5
|
+
min-height: 5em;
|
6
|
+
|
7
|
+
svg {
|
8
|
+
height: 20px;
|
9
|
+
fill: #AAA;
|
10
|
+
}
|
11
|
+
|
12
|
+
ul.tags {
|
13
|
+
@include flex(1 auto);
|
14
|
+
@include display(flex);
|
15
|
+
@include flex-wrap(wrap);
|
16
|
+
@include align-items(center);
|
17
|
+
margin: 0 1em;
|
18
|
+
outline: none;
|
19
|
+
|
20
|
+
[as='Post.Tag'] {
|
21
|
+
@include display(flex);
|
22
|
+
@include flex(0 auto);
|
23
|
+
@include align-items(center);
|
24
|
+
@include transition(color 0.3s, border-color 0.3s, box-shadow 0.3s, opacity 0.2s);
|
25
|
+
@include user-select(none);
|
26
|
+
|
27
|
+
margin: 1em;
|
28
|
+
padding: 4px 8px;
|
29
|
+
border-radius: 2px;
|
30
|
+
border: 1px solid #C7D4DB;
|
31
|
+
box-shadow: inset 0 2px 4px 0 #F5F8FA;
|
32
|
+
cursor: pointer;
|
33
|
+
|
34
|
+
svg {
|
35
|
+
@include transition(fill 0.3s);
|
36
|
+
margin-left: 6px;
|
37
|
+
fill: rgb(219, 196, 196);
|
38
|
+
}
|
39
|
+
|
40
|
+
&:hover svg {
|
41
|
+
fill: #AE4D4D;
|
42
|
+
}
|
43
|
+
|
44
|
+
&:active {
|
45
|
+
border-color: #B37878;
|
46
|
+
box-shadow: inset 0 0px 4px 0 #DBC5C5;
|
47
|
+
color: #B37878;
|
48
|
+
}
|
49
|
+
|
50
|
+
&.remove {
|
51
|
+
opacity: 0;
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
[as='Tags.List'] {
|
59
|
+
h2, h3 {
|
60
|
+
text-align: center;
|
61
|
+
}
|
62
|
+
|
63
|
+
input {
|
64
|
+
font-size: 1.5em;
|
65
|
+
}
|
66
|
+
li {
|
67
|
+
cursor: pointer;
|
68
|
+
margin: 0;
|
69
|
+
padding: 1em;
|
70
|
+
font-size: 1.3em;
|
71
|
+
&:not(:last-child) {
|
72
|
+
border-bottom: 1px solid #C8D9E2;
|
73
|
+
}
|
74
|
+
}
|
75
|
+
li.tagged {
|
76
|
+
border-left: 4px solid #AAA;
|
77
|
+
}
|
78
|
+
}
|
@@ -0,0 +1,51 @@
|
|
1
|
+
[as='Post.Title'] {
|
2
|
+
h3 {
|
3
|
+
padding: 0.2em;
|
4
|
+
text-align: center;
|
5
|
+
}
|
6
|
+
|
7
|
+
div.input {
|
8
|
+
outline: none;
|
9
|
+
background: #FFF;
|
10
|
+
font-size: 1.6em;
|
11
|
+
line-height: 1em;
|
12
|
+
padding: 0.3em 0.2em;
|
13
|
+
text-align: center;
|
14
|
+
box-shadow: inset 0 0 2px #375874;
|
15
|
+
}
|
16
|
+
& > p {
|
17
|
+
text-align: center;
|
18
|
+
font-size: 0.8em;
|
19
|
+
text-shadow: 0 1px 0 #E7EFF5;
|
20
|
+
}
|
21
|
+
|
22
|
+
ul.titles {
|
23
|
+
border-top: 1px solid #B2BCC3;
|
24
|
+
|
25
|
+
h4 {
|
26
|
+
text-align: center;
|
27
|
+
margin: 0.5em 0 0 0;
|
28
|
+
}
|
29
|
+
|
30
|
+
li {
|
31
|
+
margin: 0;
|
32
|
+
padding: 1em;
|
33
|
+
p.title {
|
34
|
+
font-size: 1.2em;
|
35
|
+
margin: 0;
|
36
|
+
}
|
37
|
+
|
38
|
+
p.url {
|
39
|
+
font-size: 0.8em;
|
40
|
+
}
|
41
|
+
}
|
42
|
+
li:nth-child(odd) {
|
43
|
+
background: #D1DAE0;
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
ul.errors {
|
48
|
+
text-align: center;
|
49
|
+
color: $red-color;
|
50
|
+
}
|
51
|
+
}
|
@@ -26,6 +26,7 @@ div.popup {
|
|
26
26
|
box-shadow: 0 0 3px #1a3642;
|
27
27
|
color: #15191B;
|
28
28
|
z-index: 101;
|
29
|
+
overflow: hidden;
|
29
30
|
|
30
31
|
h1, h2, h3, h4, h5, h6 {
|
31
32
|
font-family: initial;
|
@@ -67,6 +68,7 @@ div.popup.errors {
|
|
67
68
|
|
68
69
|
#PostsListPopup {
|
69
70
|
ol {
|
71
|
+
list-style-type: none;
|
70
72
|
font-size: 1.2em;
|
71
73
|
padding: 0;
|
72
74
|
li {
|
@@ -7,12 +7,11 @@ module Admin
|
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
10
|
-
def
|
11
|
-
@image = post.header
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
render 'errors'
|
10
|
+
def create
|
11
|
+
@image = post.header || post.build_header
|
12
|
+
|
13
|
+
unless @image.update(image_params)
|
14
|
+
render 'errors', status: 500
|
16
15
|
end
|
17
16
|
end
|
18
17
|
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Admin
|
2
|
+
module Posts
|
3
|
+
class TagsController < Admin::ApplicationController
|
4
|
+
def index
|
5
|
+
@post = Admin::Post.find(params[:post_id])
|
6
|
+
@tags = Admin::Tag.where.not(id: @post.tags.map(&:id))
|
7
|
+
end
|
8
|
+
|
9
|
+
def update
|
10
|
+
@post = Admin::Post.find(params[:post_id])
|
11
|
+
@tag = Admin::Tag.find(params[:id])
|
12
|
+
if @post.tags.include? @tag
|
13
|
+
@post.tags = @post.tags.where.not(id: @tag.id)
|
14
|
+
else
|
15
|
+
@post.tags << @tag
|
16
|
+
end
|
17
|
+
|
18
|
+
@post.save!
|
19
|
+
end
|
20
|
+
|
21
|
+
def create
|
22
|
+
@post = Admin::Post.find(params[:post_id])
|
23
|
+
@tag = Admin::Tag.find_or_create_by!(tag_params)
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def tag_params
|
29
|
+
params.require(:tag).permit('name')
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -3,9 +3,7 @@ module Admin
|
|
3
3
|
before_action :fetch_post, only: [:show, :edit, :update]
|
4
4
|
|
5
5
|
def new
|
6
|
-
|
7
|
-
render 'title' and return
|
8
|
-
end
|
6
|
+
@post = Admin::Post.new
|
9
7
|
end
|
10
8
|
|
11
9
|
def index
|
@@ -18,15 +16,13 @@ module Admin
|
|
18
16
|
end
|
19
17
|
|
20
18
|
def create
|
21
|
-
@post = Admin::Post.create
|
19
|
+
@post = Admin::Post.create(title: title_params[:title])
|
22
20
|
|
23
|
-
|
24
|
-
|
25
|
-
if @post.errors.blank?
|
26
|
-
redirect_to edit_admin_post_path(@post.id)
|
27
|
-
end
|
28
|
-
end
|
21
|
+
if @post.errors.any?
|
22
|
+
render 'new' and return
|
29
23
|
end
|
24
|
+
|
25
|
+
redirect_to edit_admin_post_path(@post)
|
30
26
|
end
|
31
27
|
|
32
28
|
def destroy
|
@@ -51,7 +47,7 @@ module Admin
|
|
51
47
|
when 'publish'
|
52
48
|
@post.publish!
|
53
49
|
flash[:notice] = t(".successful", title: @post.title)
|
54
|
-
redirect_to post_path(@post
|
50
|
+
redirect_to post_path(@post) and return
|
55
51
|
when 'unpublish'
|
56
52
|
@post.unpublish!
|
57
53
|
end
|
@@ -71,9 +67,14 @@ module Admin
|
|
71
67
|
protected
|
72
68
|
|
73
69
|
def post_params
|
74
|
-
params.require(:post).permit(:
|
70
|
+
params.require(:post).permit(:content, :status, :stylesheet, :javascript, :slug)
|
71
|
+
end
|
72
|
+
|
73
|
+
def title_params
|
74
|
+
params.require(:post).permit(:title)
|
75
75
|
end
|
76
76
|
|
77
|
+
|
77
78
|
def fetch_post
|
78
79
|
@post = Admin::Post.find(params[:id])
|
79
80
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Admin
|
2
|
+
class TitlesController < Admin::ApplicationController
|
3
|
+
helper_method :post
|
4
|
+
|
5
|
+
def index
|
6
|
+
if post.draft?
|
7
|
+
render 'edit' and return
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def create
|
12
|
+
@title = Admin::Title.new(title_params) do |title|
|
13
|
+
title.post = post
|
14
|
+
end
|
15
|
+
|
16
|
+
unless @title.save
|
17
|
+
render 'errors'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def update
|
22
|
+
@title = Admin::Title.find(params[:id])
|
23
|
+
unless @title.update(title_params)
|
24
|
+
render 'errors'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
def title_params
|
31
|
+
params.require(:title).permit(:name)
|
32
|
+
end
|
33
|
+
|
34
|
+
def post
|
35
|
+
@post ||= Post.find(params[:post_id])
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -3,7 +3,7 @@ class ApplicationController < ::ActionController::Base
|
|
3
3
|
protect_from_forgery with: :exception
|
4
4
|
helper_method :current_user, :signed_in?
|
5
5
|
|
6
|
-
helper_method :warden, :
|
6
|
+
helper_method :warden, :post_path
|
7
7
|
|
8
8
|
def current_user
|
9
9
|
warden.user
|
@@ -13,10 +13,20 @@ class ApplicationController < ::ActionController::Base
|
|
13
13
|
!warden.user.nil?
|
14
14
|
end
|
15
15
|
|
16
|
-
|
17
|
-
|
16
|
+
# This is the less ugly hack I could find. The reason why this is here
|
17
|
+
# is because helpers are hard to debug due to their anonymous Module use.
|
18
|
+
def post_path(post, options = {})
|
19
|
+
options[:controller] = :posts
|
20
|
+
options[:action] = :show
|
21
|
+
options[:year] = post.published_at.year
|
22
|
+
options[:month] = post.published_at.month
|
23
|
+
|
24
|
+
options[:id] = (options.delete(:title) || post.titles.first).slug
|
25
|
+
|
26
|
+
url_for options
|
18
27
|
end
|
19
28
|
|
29
|
+
|
20
30
|
protected
|
21
31
|
|
22
32
|
def authenticate!
|
@@ -1,13 +1,10 @@
|
|
1
1
|
module Ecrire
|
2
2
|
class PostsController < ::ApplicationController
|
3
|
-
# TODO: This should be included. I have no idea why this isn't.
|
4
|
-
# It actually is loaded in dev as soon as the reloader reloads the files
|
5
|
-
include Rails.application.routes.url_helpers
|
6
3
|
|
7
4
|
before_action :pagination, only: :index
|
8
5
|
protect_from_forgery except: :index
|
9
6
|
|
10
|
-
helper_method :post
|
7
|
+
helper_method :post, :posts
|
11
8
|
|
12
9
|
def index
|
13
10
|
respond_to do |format|
|
@@ -22,6 +19,9 @@ module Ecrire
|
|
22
19
|
def show
|
23
20
|
redirect_to :root and return if post.nil?
|
24
21
|
redirect_to :root and return unless post.published?
|
22
|
+
if post.titles.first != @title
|
23
|
+
redirect_to post_path(post), status: :moved_permanently
|
24
|
+
end
|
25
25
|
end
|
26
26
|
|
27
27
|
protected
|
@@ -31,7 +31,8 @@ module Ecrire
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def post
|
34
|
-
@
|
34
|
+
@title ||= Title.find_by_slug(params[:id])
|
35
|
+
@post ||= @title.post
|
35
36
|
end
|
36
37
|
|
37
38
|
def pagination
|
@@ -1,16 +1,4 @@
|
|
1
1
|
module PostsHelper
|
2
|
-
def post_path(post, options = {})
|
3
|
-
if post.published?
|
4
|
-
options[:year] = post.published_at.year
|
5
|
-
options[:month] = post.published_at.month
|
6
|
-
else
|
7
|
-
options[:year] = post.created_at.year
|
8
|
-
options[:month] = post.created_at.month
|
9
|
-
end
|
10
|
-
|
11
|
-
super(post.becomes(::Post), options)
|
12
|
-
end
|
13
|
-
|
14
2
|
def edit_post_link(options = {})
|
15
3
|
return unless signed_in?
|
16
4
|
|
@@ -34,18 +34,15 @@ module Admin
|
|
34
34
|
self.url = @file.url
|
35
35
|
self.key = @file.key
|
36
36
|
rescue StandardError => e
|
37
|
-
errors.add
|
37
|
+
errors.add 's3', "Couldn't upload file to S3"
|
38
38
|
return false
|
39
39
|
end
|
40
40
|
return true
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
-
def remove_file
|
45
|
-
end
|
46
|
-
|
47
44
|
def path(file)
|
48
|
-
items = [post.id, file.original_filename]
|
45
|
+
items = [self.post.id, file.original_filename]
|
49
46
|
|
50
47
|
items.prepend(s3.path) unless s3.path.blank?
|
51
48
|
|
@@ -56,7 +53,7 @@ module Admin
|
|
56
53
|
include ::S3
|
57
54
|
|
58
55
|
attr_reader :bucket, :access_key, :secret_key, :path, :errors
|
59
|
-
|
56
|
+
|
60
57
|
def initialize(options={})
|
61
58
|
@errors = ActiveModel::Errors.new(self)
|
62
59
|
@access_key = options.fetch('access_key', "")
|
@@ -85,7 +82,7 @@ module Admin
|
|
85
82
|
@bucket.retrieve
|
86
83
|
@connected = true
|
87
84
|
rescue Error::ResponseError, ArgumentError, SocketError => e
|
88
|
-
errors.add :remote,
|
85
|
+
errors.add :remote, "Couldn't connect to S3."
|
89
86
|
end
|
90
87
|
end
|
91
88
|
|
@@ -3,16 +3,11 @@ require 'kramdown'
|
|
3
3
|
module Admin
|
4
4
|
class Post < ::Post
|
5
5
|
|
6
|
-
class Callbacks
|
7
|
-
def after_create(record)
|
8
|
-
Admin::Image.create(post: record)
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
6
|
has_one :header, class_name: Admin::Image
|
7
|
+
has_many :titles, -> { order "titles.created_at DESC" }, class_name: Admin::Title
|
8
|
+
|
13
9
|
before_save :compile!, prepend: true
|
14
10
|
before_save :excerptize!
|
15
|
-
after_create Callbacks.new
|
16
11
|
|
17
12
|
def publish!(params = {})
|
18
13
|
self.assign_attributes(params)
|
@@ -2,6 +2,7 @@ require 'nokogiri'
|
|
2
2
|
|
3
3
|
class Post < ActiveRecord::Base
|
4
4
|
has_one :header, class_name: Image
|
5
|
+
has_many :titles, -> { order "titles.created_at DESC" }
|
5
6
|
|
6
7
|
store_accessor :properties, :labels
|
7
8
|
|
@@ -15,13 +16,31 @@ class Post < ActiveRecord::Base
|
|
15
16
|
|
16
17
|
scope :published, lambda { status("published") }
|
17
18
|
scope :drafted, lambda { status("drafted") }
|
18
|
-
scope :slug, lambda { |slug| where("posts.slug is ?", slug) }
|
19
19
|
scope :without, lambda { |post| where("posts.id != ?", post.id) }
|
20
20
|
|
21
|
-
validates :
|
22
|
-
validates :slug, presence: true, uniqueness: true
|
21
|
+
validates :titles, length: {minimum: 1}
|
23
22
|
|
24
|
-
|
23
|
+
before_save :update_tags
|
24
|
+
|
25
|
+
attr_writer :tags
|
26
|
+
|
27
|
+
def title=(new_title)
|
28
|
+
if self.published?
|
29
|
+
self.titles.new(name: new_title)
|
30
|
+
else
|
31
|
+
title = self.titles.first || self.titles.new
|
32
|
+
title.post = self
|
33
|
+
title.name = new_title
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def title
|
38
|
+
(self.titles.first || self.titles.new).name
|
39
|
+
end
|
40
|
+
|
41
|
+
def slug
|
42
|
+
self.titles.first.slug
|
43
|
+
end
|
25
44
|
|
26
45
|
def status=(new_status)
|
27
46
|
if new_status.eql? "publish"
|
@@ -59,32 +78,21 @@ class Post < ActiveRecord::Base
|
|
59
78
|
(self.compiled_excerpt || "").html_safe
|
60
79
|
end
|
61
80
|
|
62
|
-
|
63
|
-
def title=(new_title)
|
64
|
-
super
|
65
|
-
if self.draft?
|
66
|
-
self.slug = nil
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
81
|
def header?
|
71
82
|
!self.header.nil? && !self.header.url.blank?
|
72
83
|
end
|
73
84
|
|
74
|
-
def
|
75
|
-
|
76
|
-
Label.where(id: ids.split(',')).to_a
|
77
|
-
end
|
78
|
-
|
79
|
-
def labels=(labels)
|
80
|
-
super(labels.map(&:id).join(','))
|
85
|
+
def tags
|
86
|
+
@tags ||= Tag.where("tags.id in (?)", super || [])
|
81
87
|
end
|
82
88
|
|
83
89
|
protected
|
84
90
|
|
85
|
-
def
|
86
|
-
|
87
|
-
|
91
|
+
def update_tags
|
92
|
+
ids = tags.map(&:id)
|
93
|
+
unless ids == read_attribute(:tags)
|
94
|
+
write_attribute(:tags, ids)
|
95
|
+
end
|
88
96
|
end
|
89
97
|
|
90
98
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
class Title < ActiveRecord::Base
|
2
|
+
class Validator < ActiveModel::Validator
|
3
|
+
def validate(record)
|
4
|
+
validate_length! record
|
5
|
+
validate_uniqueness! record
|
6
|
+
validate_draft! record
|
7
|
+
end
|
8
|
+
|
9
|
+
def validate_length!(record)
|
10
|
+
if record.name.blank?
|
11
|
+
msg = "Your title can't be blank."
|
12
|
+
record.errors['name'] << msg
|
13
|
+
record.post.errors['title'] << msg
|
14
|
+
elsif record.name.length < 1
|
15
|
+
msg = "Your title needs to be at least 1 character long."
|
16
|
+
record.errors['name'] << msg
|
17
|
+
record.post.errors['title'] << msg
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def validate_uniqueness!(record)
|
22
|
+
title = Title.where('titles.slug = ? OR titles.name = ?', record.slug, record.name).first
|
23
|
+
unless title.nil?
|
24
|
+
msg = "You already have a post with this title: #{title.name}"
|
25
|
+
record.errors['uniqueness'] << msg
|
26
|
+
record.post.errors['title'] << msg
|
27
|
+
return
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def validate_draft!(record)
|
32
|
+
if record.post.published? && !record.new_record?
|
33
|
+
msg = "You cannot modify an existing title when a post is published"
|
34
|
+
record.errors['draft'] << msg
|
35
|
+
record.post.errors['title'] << msg
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
include ActiveModel::Validations
|
42
|
+
validates_with Title::Validator
|
43
|
+
|
44
|
+
before_validation :generate_slug
|
45
|
+
|
46
|
+
belongs_to :post, required: true
|
47
|
+
|
48
|
+
scope :slug, lambda { |slug|
|
49
|
+
where('titles.slug = ?', slug)
|
50
|
+
}
|
51
|
+
|
52
|
+
def name=(new_name)
|
53
|
+
super new_name.strip
|
54
|
+
end
|
55
|
+
|
56
|
+
protected
|
57
|
+
|
58
|
+
def generate_slug
|
59
|
+
self.slug = self.name.parameterize.downcase
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
json.array! @image.errors.values
|