canyon 0.0.1
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.
- checksums.yaml +7 -0
- data/LICENSE.txt +20 -0
- data/README.md +29 -0
- data/app/assets/javascripts/canyon/application.js +3 -0
- data/app/assets/javascripts/canyon/vendor/jquery.autosize.js +211 -0
- data/app/assets/stylesheets/canyon/application.scss +11 -0
- data/app/controllers/canyon/application_controller.rb +11 -0
- data/app/controllers/canyon/posts_controller.rb +74 -0
- data/app/helpers/canyon/application_helper.rb +8 -0
- data/app/models/canyon/author.rb +5 -0
- data/app/models/canyon/post.rb +49 -0
- data/app/views/canyon/posts/_form.html.erb +140 -0
- data/app/views/canyon/posts/edit.html.erb +11 -0
- data/app/views/canyon/posts/index.html.erb +35 -0
- data/app/views/canyon/posts/new.html.erb +11 -0
- data/app/views/layouts/canyon/application.html.erb +53 -0
- data/bin/rails +12 -0
- data/config/locales/en.yml +22 -0
- data/config/routes.rb +5 -0
- data/db/migrate/20150416053508_create_authors.rb +9 -0
- data/db/migrate/20150416053518_create_posts.rb +17 -0
- data/db/seeds.rb +15 -0
- data/lib/canyon.rb +4 -0
- data/lib/canyon/engine.rb +17 -0
- data/lib/canyon/version.rb +3 -0
- data/lib/tasks/canyon_tasks.rake +7 -0
- metadata +139 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 36a8f1d8684684e4323ac062af0294d15e24153a
|
4
|
+
data.tar.gz: 675bdcb88f2151e34d525e5f0913a9978128ed11
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c5b2473b776ae3965fa0ffb8f4f975971c331ba861d186da32aa7f51c3b1beedc186d64928f2239f00786cc7091c1b38ba367a057a341cb32e1c300fd4e4bdfe
|
7
|
+
data.tar.gz: 9ab5818e3dfe53e2aaf40c2d64e4a6f52d3f7da321d2f8db9d3c382432973ec689ef258cf280d6e44c1630a01c6ae356eeec0410378e67265b16724c77dfcb3e
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2015 Martin Jagusch
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Canyon
|
2
|
+
|
3
|
+
Canyon is a Rails blogging engine, including only the backend. How you present the blog posts is up to you.
|
4
|
+
|
5
|
+
### Installation
|
6
|
+
|
7
|
+
Include the engine to your projects Gemfile. `Gemfile`
|
8
|
+
|
9
|
+
```
|
10
|
+
gem 'canyon'
|
11
|
+
```
|
12
|
+
|
13
|
+
Include the routes from the engine to your applications routing file. `config/routes.rb`
|
14
|
+
|
15
|
+
``` ruby
|
16
|
+
Rails.application.routes.draw do
|
17
|
+
# All your application routes.
|
18
|
+
|
19
|
+
mount Canyon::Engine => '/cy-admin'
|
20
|
+
end
|
21
|
+
```
|
22
|
+
|
23
|
+
Load the engine sample data. `db/seeds.rb` (optional)
|
24
|
+
|
25
|
+
```
|
26
|
+
Canyon::Engine.load_seed
|
27
|
+
```
|
28
|
+
|
29
|
+
Running `rake db:seed` will now include a default user and blog post.
|
@@ -0,0 +1,211 @@
|
|
1
|
+
/*!
|
2
|
+
Autosize 3.0.7
|
3
|
+
license: MIT
|
4
|
+
http://www.jacklmoore.com/autosize
|
5
|
+
*/
|
6
|
+
(function (global, factory) {
|
7
|
+
if (typeof define === 'function' && define.amd) {
|
8
|
+
define(['exports', 'module'], factory);
|
9
|
+
} else if (typeof exports !== 'undefined' && typeof module !== 'undefined') {
|
10
|
+
factory(exports, module);
|
11
|
+
} else {
|
12
|
+
var mod = {
|
13
|
+
exports: {}
|
14
|
+
};
|
15
|
+
factory(mod.exports, mod);
|
16
|
+
global.autosize = mod.exports;
|
17
|
+
}
|
18
|
+
})(this, function (exports, module) {
|
19
|
+
'use strict';
|
20
|
+
|
21
|
+
function assign(ta) {
|
22
|
+
var _ref = arguments[1] === undefined ? {} : arguments[1];
|
23
|
+
|
24
|
+
var _ref$setOverflowX = _ref.setOverflowX;
|
25
|
+
var setOverflowX = _ref$setOverflowX === undefined ? true : _ref$setOverflowX;
|
26
|
+
var _ref$setOverflowY = _ref.setOverflowY;
|
27
|
+
var setOverflowY = _ref$setOverflowY === undefined ? true : _ref$setOverflowY;
|
28
|
+
|
29
|
+
if (!ta || !ta.nodeName || ta.nodeName !== 'TEXTAREA' || ta.hasAttribute('data-autosize-on')) return;
|
30
|
+
|
31
|
+
var heightOffset = null;
|
32
|
+
var overflowY = 'hidden';
|
33
|
+
|
34
|
+
function init() {
|
35
|
+
var style = window.getComputedStyle(ta, null);
|
36
|
+
|
37
|
+
if (style.resize === 'vertical') {
|
38
|
+
ta.style.resize = 'none';
|
39
|
+
} else if (style.resize === 'both') {
|
40
|
+
ta.style.resize = 'horizontal';
|
41
|
+
}
|
42
|
+
|
43
|
+
if (style.boxSizing === 'content-box') {
|
44
|
+
heightOffset = -(parseFloat(style.paddingTop) + parseFloat(style.paddingBottom));
|
45
|
+
} else {
|
46
|
+
heightOffset = parseFloat(style.borderTopWidth) + parseFloat(style.borderBottomWidth);
|
47
|
+
}
|
48
|
+
|
49
|
+
update();
|
50
|
+
}
|
51
|
+
|
52
|
+
function changeOverflow(value) {
|
53
|
+
{
|
54
|
+
// Chrome/Safari-specific fix:
|
55
|
+
// When the textarea y-overflow is hidden, Chrome/Safari do not reflow the text to account for the space
|
56
|
+
// made available by removing the scrollbar. The following forces the necessary text reflow.
|
57
|
+
var width = ta.style.width;
|
58
|
+
ta.style.width = '0px';
|
59
|
+
// Force reflow:
|
60
|
+
/* jshint ignore:start */
|
61
|
+
ta.offsetWidth;
|
62
|
+
/* jshint ignore:end */
|
63
|
+
ta.style.width = width;
|
64
|
+
}
|
65
|
+
|
66
|
+
overflowY = value;
|
67
|
+
|
68
|
+
if (setOverflowY) {
|
69
|
+
ta.style.overflowY = value;
|
70
|
+
}
|
71
|
+
|
72
|
+
update();
|
73
|
+
}
|
74
|
+
|
75
|
+
function update() {
|
76
|
+
var startHeight = ta.style.height;
|
77
|
+
var htmlTop = window.pageYOffset;
|
78
|
+
var bodyTop = document.body.scrollTop;
|
79
|
+
var originalHeight = ta.style.height;
|
80
|
+
|
81
|
+
ta.style.height = 'auto';
|
82
|
+
|
83
|
+
var endHeight = ta.scrollHeight + heightOffset;
|
84
|
+
|
85
|
+
if (ta.scrollHeight === 0) {
|
86
|
+
// If the scrollHeight is 0, then the element probably has display:none or is detached from the DOM.
|
87
|
+
ta.style.height = originalHeight;
|
88
|
+
return;
|
89
|
+
}
|
90
|
+
|
91
|
+
ta.style.height = endHeight + 'px';
|
92
|
+
|
93
|
+
// prevents scroll-position jumping
|
94
|
+
document.documentElement.scrollTop = htmlTop;
|
95
|
+
document.body.scrollTop = bodyTop;
|
96
|
+
|
97
|
+
var style = window.getComputedStyle(ta, null);
|
98
|
+
|
99
|
+
if (style.height !== ta.style.height) {
|
100
|
+
if (overflowY !== 'visible') {
|
101
|
+
changeOverflow('visible');
|
102
|
+
return;
|
103
|
+
}
|
104
|
+
} else {
|
105
|
+
if (overflowY !== 'hidden') {
|
106
|
+
changeOverflow('hidden');
|
107
|
+
return;
|
108
|
+
}
|
109
|
+
}
|
110
|
+
|
111
|
+
if (startHeight !== ta.style.height) {
|
112
|
+
var evt = document.createEvent('Event');
|
113
|
+
evt.initEvent('autosize:resized', true, false);
|
114
|
+
ta.dispatchEvent(evt);
|
115
|
+
}
|
116
|
+
}
|
117
|
+
|
118
|
+
var destroy = (function (style) {
|
119
|
+
window.removeEventListener('resize', update);
|
120
|
+
ta.removeEventListener('input', update);
|
121
|
+
ta.removeEventListener('keyup', update);
|
122
|
+
ta.removeAttribute('data-autosize-on');
|
123
|
+
ta.removeEventListener('autosize:destroy', destroy);
|
124
|
+
|
125
|
+
Object.keys(style).forEach(function (key) {
|
126
|
+
ta.style[key] = style[key];
|
127
|
+
});
|
128
|
+
}).bind(ta, {
|
129
|
+
height: ta.style.height,
|
130
|
+
resize: ta.style.resize,
|
131
|
+
overflowY: ta.style.overflowY,
|
132
|
+
overflowX: ta.style.overflowX,
|
133
|
+
wordWrap: ta.style.wordWrap });
|
134
|
+
|
135
|
+
ta.addEventListener('autosize:destroy', destroy);
|
136
|
+
|
137
|
+
// IE9 does not fire onpropertychange or oninput for deletions,
|
138
|
+
// so binding to onkeyup to catch most of those events.
|
139
|
+
// There is no way that I know of to detect something like 'cut' in IE9.
|
140
|
+
if ('onpropertychange' in ta && 'oninput' in ta) {
|
141
|
+
ta.addEventListener('keyup', update);
|
142
|
+
}
|
143
|
+
|
144
|
+
window.addEventListener('resize', update);
|
145
|
+
ta.addEventListener('input', update);
|
146
|
+
ta.addEventListener('autosize:update', update);
|
147
|
+
ta.setAttribute('data-autosize-on', true);
|
148
|
+
|
149
|
+
if (setOverflowY) {
|
150
|
+
ta.style.overflowY = 'hidden';
|
151
|
+
}
|
152
|
+
if (setOverflowX) {
|
153
|
+
ta.style.overflowX = 'hidden';
|
154
|
+
ta.style.wordWrap = 'break-word';
|
155
|
+
}
|
156
|
+
|
157
|
+
init();
|
158
|
+
}
|
159
|
+
|
160
|
+
function destroy(ta) {
|
161
|
+
if (!(ta && ta.nodeName && ta.nodeName === 'TEXTAREA')) return;
|
162
|
+
var evt = document.createEvent('Event');
|
163
|
+
evt.initEvent('autosize:destroy', true, false);
|
164
|
+
ta.dispatchEvent(evt);
|
165
|
+
}
|
166
|
+
|
167
|
+
function update(ta) {
|
168
|
+
if (!(ta && ta.nodeName && ta.nodeName === 'TEXTAREA')) return;
|
169
|
+
var evt = document.createEvent('Event');
|
170
|
+
evt.initEvent('autosize:update', true, false);
|
171
|
+
ta.dispatchEvent(evt);
|
172
|
+
}
|
173
|
+
|
174
|
+
var autosize = null;
|
175
|
+
|
176
|
+
// Do nothing in Node.js environment and IE8 (or lower)
|
177
|
+
if (typeof window === 'undefined' || typeof window.getComputedStyle !== 'function') {
|
178
|
+
autosize = function (el) {
|
179
|
+
return el;
|
180
|
+
};
|
181
|
+
autosize.destroy = function (el) {
|
182
|
+
return el;
|
183
|
+
};
|
184
|
+
autosize.update = function (el) {
|
185
|
+
return el;
|
186
|
+
};
|
187
|
+
} else {
|
188
|
+
autosize = function (el, options) {
|
189
|
+
if (el) {
|
190
|
+
Array.prototype.forEach.call(el.length ? el : [el], function (x) {
|
191
|
+
return assign(x, options);
|
192
|
+
});
|
193
|
+
}
|
194
|
+
return el;
|
195
|
+
};
|
196
|
+
autosize.destroy = function (el) {
|
197
|
+
if (el) {
|
198
|
+
Array.prototype.forEach.call(el.length ? el : [el], destroy);
|
199
|
+
}
|
200
|
+
return el;
|
201
|
+
};
|
202
|
+
autosize.update = function (el) {
|
203
|
+
if (el) {
|
204
|
+
Array.prototype.forEach.call(el.length ? el : [el], update);
|
205
|
+
}
|
206
|
+
return el;
|
207
|
+
};
|
208
|
+
}
|
209
|
+
|
210
|
+
module.exports = autosize;
|
211
|
+
});
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Canyon
|
2
|
+
class PostsController < ApplicationController
|
3
|
+
def index
|
4
|
+
@posts = Post.published_or_drafted
|
5
|
+
end
|
6
|
+
|
7
|
+
def new
|
8
|
+
@post = Post.new(published_at: Time.now)
|
9
|
+
end
|
10
|
+
|
11
|
+
def edit
|
12
|
+
@post = Post.find(params[:id])
|
13
|
+
end
|
14
|
+
|
15
|
+
def destroy
|
16
|
+
post = Post.find(params[:id])
|
17
|
+
raise post.inspect
|
18
|
+
end
|
19
|
+
|
20
|
+
def create
|
21
|
+
@post = Post.new(post_params)
|
22
|
+
|
23
|
+
if @post.save
|
24
|
+
if params[:preview].present?
|
25
|
+
@post.update_attributes(published: false)
|
26
|
+
# TODO Link to preview page.
|
27
|
+
flash[:warning] = %Q(A preview can be seen here: Coming Soon.)
|
28
|
+
render :edit
|
29
|
+
else
|
30
|
+
flash[:success] = %Q(Post "#{@post.title}" has been successfully created.)
|
31
|
+
redirect_to posts_path
|
32
|
+
end
|
33
|
+
else
|
34
|
+
render :new
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def update
|
39
|
+
origin = Post.find(params[:id])
|
40
|
+
|
41
|
+
if params[:preview].present?
|
42
|
+
@post = origin.dup
|
43
|
+
@post.assign_attributes(parent_id: origin.parent_id || origin.id, published: false)
|
44
|
+
|
45
|
+
if @post.update_attributes(post_params)
|
46
|
+
# TODO Link to preview page.
|
47
|
+
flash[:warning] = %Q(A preview can be seen here: Coming Soon.)
|
48
|
+
end
|
49
|
+
|
50
|
+
render :edit
|
51
|
+
else
|
52
|
+
@post = origin.preview? ? origin.parent : origin
|
53
|
+
|
54
|
+
if params[:delete].present?
|
55
|
+
@post.update_attributes(deleted: true)
|
56
|
+
flash[:success] = %Q(Post "#{@post.title}" has been successfully deleted.)
|
57
|
+
redirect_to posts_path
|
58
|
+
elsif @post.update_attributes(post_params)
|
59
|
+
flash[:success] = %Q(Post "#{@post.title}" has been successfully updated.)
|
60
|
+
redirect_to posts_path
|
61
|
+
else
|
62
|
+
render :edit
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def post_params
|
70
|
+
params[:post].permit(:title, :slug, :language, :author_id, :published, :published_at,
|
71
|
+
:summary, :content)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Canyon
|
2
|
+
class Post < ActiveRecord::Base
|
3
|
+
LANGUAGES = [:en, :de]
|
4
|
+
|
5
|
+
belongs_to :author
|
6
|
+
|
7
|
+
delegate :name, to: 'author', prefix: true
|
8
|
+
|
9
|
+
scope :published, -> do
|
10
|
+
where(parent_id: nil).
|
11
|
+
where(published: true).
|
12
|
+
where(deleted: false).
|
13
|
+
where('published_at <= ?', Time.now).
|
14
|
+
order(published_at: :desc)
|
15
|
+
end
|
16
|
+
|
17
|
+
scope :published_or_drafted, -> do
|
18
|
+
where(parent_id: nil).where(deleted: false).order(published_at: :desc)
|
19
|
+
end
|
20
|
+
|
21
|
+
validates :title, :slug, :language, :content, :published_at, presence: true
|
22
|
+
|
23
|
+
before_validation :parameterize_slug
|
24
|
+
|
25
|
+
def parameterize_slug
|
26
|
+
self.slug = title.parameterize if slug.blank? && title.present?
|
27
|
+
end
|
28
|
+
|
29
|
+
def draft?
|
30
|
+
!published?
|
31
|
+
end
|
32
|
+
|
33
|
+
def preview?
|
34
|
+
!parent_id.nil?
|
35
|
+
end
|
36
|
+
|
37
|
+
def parent
|
38
|
+
self.class.find(parent_id)
|
39
|
+
end
|
40
|
+
|
41
|
+
def last_preview
|
42
|
+
Canyon::Post.where(parent_id: id).order(updated_at: :desc).first
|
43
|
+
end
|
44
|
+
|
45
|
+
def has_newer_preview?
|
46
|
+
!new_record? && !preview? && !last_preview.nil? && last_preview.updated_at > updated_at
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
<% if post.errors.any? %>
|
2
|
+
<div class="alert alert-danger alert-dismissible" role="alert">
|
3
|
+
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
4
|
+
<span aria-hidden="true">×</span>
|
5
|
+
</button>
|
6
|
+
|
7
|
+
<h4>Houston. There has been an error. Imagine what would have been happen if we would be in space. Scary!</h4>
|
8
|
+
|
9
|
+
<ul>
|
10
|
+
<% post.errors.full_messages.each do |message| %>
|
11
|
+
<li><%= message %></li>
|
12
|
+
<% end %>
|
13
|
+
</ul>
|
14
|
+
</div>
|
15
|
+
<% end %>
|
16
|
+
|
17
|
+
<% if @post.has_newer_preview? %>
|
18
|
+
<div class="alert alert-warning alert-dismissible" role="alert">
|
19
|
+
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
20
|
+
<span aria-hidden="true">×</span>
|
21
|
+
</button>
|
22
|
+
|
23
|
+
<p>
|
24
|
+
An preview that is newer as this post has been found.
|
25
|
+
<%= link_to %Q("#{@post.last_preview.title}"), edit_post_path(@post.last_preview.id) %>
|
26
|
+
</p>
|
27
|
+
</div>
|
28
|
+
<% end %>
|
29
|
+
|
30
|
+
<%= form_for post do |f| %>
|
31
|
+
<div class="row">
|
32
|
+
<div class="col-md-6">
|
33
|
+
<div class="form-group">
|
34
|
+
<%= f.label :title %>
|
35
|
+
<%= f.text_field :title, class: 'form-control' %>
|
36
|
+
</div>
|
37
|
+
</div>
|
38
|
+
|
39
|
+
<div class="col-md-6">
|
40
|
+
<div class="form-group">
|
41
|
+
<%= f.label :slug %>
|
42
|
+
<%= f.text_field :slug, class: 'form-control', placeholder: 'e.g., separated-by-dashes' %>
|
43
|
+
</div>
|
44
|
+
</div>
|
45
|
+
</div>
|
46
|
+
|
47
|
+
<div class="row">
|
48
|
+
<div class="col-md-6">
|
49
|
+
<div class="form-group">
|
50
|
+
<%= f.label :language %>
|
51
|
+
<%= f.select :language, Canyon::Post::LANGUAGES, {}, class: 'form-control' %>
|
52
|
+
</div>
|
53
|
+
</div>
|
54
|
+
|
55
|
+
<div class="col-md-6">
|
56
|
+
<div class="form-group">
|
57
|
+
<%= f.label :author_id %>
|
58
|
+
<%= f.select :author_id, Canyon::Author.all.map { |author| [author.name, author.id] }, {}, class: 'form-control' %>
|
59
|
+
</div>
|
60
|
+
</div>
|
61
|
+
</div>
|
62
|
+
|
63
|
+
<div class="row">
|
64
|
+
<div class="col-md-6">
|
65
|
+
<div class="form-group">
|
66
|
+
<%= f.label :published_at %>
|
67
|
+
<div>
|
68
|
+
<%= f.datetime_select :published_at, :minute_step => 5 %>
|
69
|
+
</div>
|
70
|
+
</div>
|
71
|
+
</div>
|
72
|
+
|
73
|
+
<div class="col-md-6">
|
74
|
+
<div class="form-group">
|
75
|
+
<%= f.label :published %>
|
76
|
+
<div>
|
77
|
+
<%= f.label :published do %>
|
78
|
+
<%= f.check_box :published %>
|
79
|
+
<% end %>
|
80
|
+
</div>
|
81
|
+
</div>
|
82
|
+
</div>
|
83
|
+
</div>
|
84
|
+
|
85
|
+
<div class="form-group">
|
86
|
+
<%= f.label :summary %>
|
87
|
+
<%= f.text_area :summary, rows: 3, class: 'form-control post-summary', placeholder: 'A short summary for the homepage.' %>
|
88
|
+
</div>
|
89
|
+
|
90
|
+
<div class="form-group">
|
91
|
+
<%= f.label :content %>
|
92
|
+
<%= f.text_area :content, rows: 5, class: 'form-control post-content', placeholder: 'The main content.' %>
|
93
|
+
</div>
|
94
|
+
|
95
|
+
<%= link_to '#', class: 'btn btn-warning preview-act', role: 'button' do %>
|
96
|
+
<span class="glyphicon glyphicon-eye-open" aria-hidden="true"></span>
|
97
|
+
Preview Post
|
98
|
+
<% end %>
|
99
|
+
|
100
|
+
<% if @post.new_record? && !@post.preview? %>
|
101
|
+
<%= link_to '#', class: 'btn btn-success submit-act', role: 'button' do %>
|
102
|
+
<span class="glyphicon glyphicon-ok-circle" aria-hidden="true"></span>
|
103
|
+
Create Post
|
104
|
+
<% end %>
|
105
|
+
<% else %>
|
106
|
+
<%= link_to '#', class: 'btn btn-success submit-act', role: 'button' do %>
|
107
|
+
<span class="glyphicon glyphicon-ok-circle" aria-hidden="true"></span>
|
108
|
+
Update Post
|
109
|
+
<% end %>
|
110
|
+
|
111
|
+
<%= link_to '#', class: 'btn btn-danger delete-act', role: 'button' do %>
|
112
|
+
<span class="glyphicon glyphicon-remove-circle" aria-hidden="true"></span>
|
113
|
+
Delete Post
|
114
|
+
<% end %>
|
115
|
+
<% end %>
|
116
|
+
<% end %>
|
117
|
+
|
118
|
+
<script>
|
119
|
+
var form = $('form');
|
120
|
+
|
121
|
+
$('.preview-act').click(function() {
|
122
|
+
form.attr('action', form.attr('action') + '?preview=1');
|
123
|
+
form.submit();
|
124
|
+
return false;
|
125
|
+
});
|
126
|
+
|
127
|
+
$('.delete-act').click(function() {
|
128
|
+
form.attr('action', form.attr('action') + '?delete=1');
|
129
|
+
form.submit();
|
130
|
+
return false;
|
131
|
+
});
|
132
|
+
|
133
|
+
$('.submit-act').click(function() {
|
134
|
+
form.submit();
|
135
|
+
return false;
|
136
|
+
});
|
137
|
+
|
138
|
+
autosize($('.post-summary'));
|
139
|
+
autosize($('.post-content'));
|
140
|
+
</script>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<% page_title(%Q(Edit Post ##{@post.id})) %>
|
2
|
+
|
3
|
+
<h3>
|
4
|
+
Edit Post
|
5
|
+
<%= link_to posts_path, class: 'btn btn-sm btn-primary', role: 'button' do %>
|
6
|
+
<span class="glyphicon glyphicon-th-list" aria-hidden="true"></span>
|
7
|
+
All Posts
|
8
|
+
<% end %>
|
9
|
+
</h3>
|
10
|
+
|
11
|
+
<%= render partial: 'form', locals: {post: @post} %>
|
@@ -0,0 +1,35 @@
|
|
1
|
+
<% page_title('Posts') %>
|
2
|
+
|
3
|
+
<h3>
|
4
|
+
Posts
|
5
|
+
<%= link_to new_post_path, class: 'btn btn-sm btn-primary', role: 'button' do %>
|
6
|
+
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
7
|
+
New Posts
|
8
|
+
<% end %>
|
9
|
+
</h3>
|
10
|
+
|
11
|
+
<table class="table table-bordered">
|
12
|
+
<tr>
|
13
|
+
<th>Title</th>
|
14
|
+
<th>Language</th>
|
15
|
+
<th>Author</th>
|
16
|
+
<th>Date</th>
|
17
|
+
</tr>
|
18
|
+
<% @posts.each do |post| %>
|
19
|
+
<tr>
|
20
|
+
<td>
|
21
|
+
<%= link_to post.title, edit_post_path(post) %>
|
22
|
+
<% if post.draft? %>
|
23
|
+
- Draft
|
24
|
+
<% end %>
|
25
|
+
</td>
|
26
|
+
<td><%= post.language %></td>
|
27
|
+
<td><%= post.author_name %></td>
|
28
|
+
<td>
|
29
|
+
<abbr title="<%= l(post.published_at, format: :detailed) if post.published_at.present? %>">
|
30
|
+
<%= l(post.published_at, format: :simple) if post.published_at.present? %>
|
31
|
+
</abbr><br />
|
32
|
+
</td>
|
33
|
+
</tr>
|
34
|
+
<% end %>
|
35
|
+
</table>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<% page_title('New Post') %>
|
2
|
+
|
3
|
+
<h3>
|
4
|
+
New Post
|
5
|
+
<%= link_to posts_path, class: 'btn btn-sm btn-primary', role: 'button' do %>
|
6
|
+
<span class="glyphicon glyphicon-th-list" aria-hidden="true"></span>
|
7
|
+
All Posts
|
8
|
+
<% end %>
|
9
|
+
</h3>
|
10
|
+
|
11
|
+
<%= render partial: 'form', locals: {post: @post} %>
|
@@ -0,0 +1,53 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7
|
+
<%= stylesheet_link_tag 'canyon/application', media: 'all' %>
|
8
|
+
<%= javascript_include_tag 'canyon/application' %>
|
9
|
+
<%= csrf_meta_tags %>
|
10
|
+
<title><%= content_for(:page_title) %></title>
|
11
|
+
</head>
|
12
|
+
<body>
|
13
|
+
<nav class="navbar navbar-inverse navbar-fixed-top">
|
14
|
+
<div class="container">
|
15
|
+
<div class="navbar-header">
|
16
|
+
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar">
|
17
|
+
<span class="sr-only">Toggle navigation</span>
|
18
|
+
<span class="icon-bar"></span>
|
19
|
+
<span class="icon-bar"></span>
|
20
|
+
<span class="icon-bar"></span>
|
21
|
+
</button>
|
22
|
+
<%= link_to 'Canyon', posts_path, class: 'navbar-brand' %>
|
23
|
+
</div>
|
24
|
+
<div id="navbar" class="collapse navbar-collapse">
|
25
|
+
<ul class="nav navbar-nav">
|
26
|
+
<li class="active"><%= link_to 'Posts', posts_path %></li>
|
27
|
+
<li><%= link_to 'Categories', '#' %></li>
|
28
|
+
<li><%= link_to 'Authors', '#' %></li>
|
29
|
+
<li>
|
30
|
+
<%= link_to request.base_url, target: '_blank' do %>
|
31
|
+
<%= request.base_url %>
|
32
|
+
<span class="glyphicon glyphicon-new-window" aria-hidden="true"></span>
|
33
|
+
<% end %>
|
34
|
+
</li>
|
35
|
+
</ul>
|
36
|
+
</div>
|
37
|
+
</div>
|
38
|
+
</nav>
|
39
|
+
|
40
|
+
<div class="container">
|
41
|
+
<% flash.each do |name, flash_message| %>
|
42
|
+
<div class="alert alert-<%= name %> alert-dismissible" role="alert">
|
43
|
+
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
44
|
+
<span aria-hidden="true">×</span>
|
45
|
+
</button>
|
46
|
+
<p><%= raw flash_message %></p>
|
47
|
+
</div>
|
48
|
+
<% end %>
|
49
|
+
|
50
|
+
<%= yield %>
|
51
|
+
</div>
|
52
|
+
</body>
|
53
|
+
</html>
|
data/bin/rails
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# This command will automatically be run when you run "rails" with Rails 4 gems installed from the root of your application.
|
3
|
+
|
4
|
+
ENGINE_ROOT = File.expand_path('../..', __FILE__)
|
5
|
+
ENGINE_PATH = File.expand_path('../../lib/canyon/engine', __FILE__)
|
6
|
+
|
7
|
+
# Set up gems listed in the Gemfile.
|
8
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
|
9
|
+
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
|
10
|
+
|
11
|
+
require 'rails/all'
|
12
|
+
require 'rails/engine/commands'
|
@@ -0,0 +1,22 @@
|
|
1
|
+
en:
|
2
|
+
date:
|
3
|
+
month_names:
|
4
|
+
-
|
5
|
+
- January
|
6
|
+
- February
|
7
|
+
- March
|
8
|
+
- April
|
9
|
+
- May
|
10
|
+
- June
|
11
|
+
- July
|
12
|
+
- August
|
13
|
+
- September
|
14
|
+
- October
|
15
|
+
- November
|
16
|
+
- December
|
17
|
+
time:
|
18
|
+
am: am
|
19
|
+
pm: pm
|
20
|
+
formats:
|
21
|
+
simple: "%B %d, %Y"
|
22
|
+
detailed: "%Y/%m/%d %I:%M %p"
|
data/config/routes.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
class CreatePosts < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :canyon_posts do |t|
|
4
|
+
t.string :title, null: false
|
5
|
+
t.string :slug, null: false
|
6
|
+
t.string :language, null: false
|
7
|
+
t.text :summary
|
8
|
+
t.text :content, null: false
|
9
|
+
t.integer :parent_id
|
10
|
+
t.integer :author_id, null: false
|
11
|
+
t.boolean :deleted, null: false, default: false
|
12
|
+
t.boolean :published, null: false, default: false
|
13
|
+
t.datetime :published_at, null: false
|
14
|
+
t.timestamps null: false
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/db/seeds.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
Canyon::Author.delete_all
|
2
|
+
Canyon::Post.delete_all
|
3
|
+
|
4
|
+
author = Canyon::Author.create(name: 'Guybrush Threepwood', slug: 'guybrush')
|
5
|
+
Canyon::Author.create(name: 'Elaine Marley', slug: 'elaine')
|
6
|
+
|
7
|
+
Canyon::Post.create!(
|
8
|
+
author: author,
|
9
|
+
language: 'en',
|
10
|
+
title: 'Markdown Syntax Example',
|
11
|
+
slug: 'markdown-syntax-example',
|
12
|
+
summary: 'This is our fist summary.',
|
13
|
+
content: 'This is our **fist posts**.',
|
14
|
+
published_at: Time.now,
|
15
|
+
)
|
data/lib/canyon.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'bootstrap-sass'
|
2
|
+
require 'sass-rails'
|
3
|
+
require 'jquery-rails'
|
4
|
+
|
5
|
+
module Canyon
|
6
|
+
class Engine < ::Rails::Engine
|
7
|
+
isolate_namespace Canyon
|
8
|
+
|
9
|
+
initializer :append_migrations do |app|
|
10
|
+
unless app.root.to_s.match root.to_s
|
11
|
+
config.paths['db/migrate'].expanded.each do |expanded_path|
|
12
|
+
app.config.paths['db/migrate'] << expanded_path
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: canyon
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Martin Jagusch
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-07-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bootstrap-sass
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: sass-rails
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: jquery-rails
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: sqlite3
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description:
|
84
|
+
email:
|
85
|
+
- _@mj.io
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- LICENSE.txt
|
91
|
+
- README.md
|
92
|
+
- app/assets/javascripts/canyon/application.js
|
93
|
+
- app/assets/javascripts/canyon/vendor/jquery.autosize.js
|
94
|
+
- app/assets/stylesheets/canyon/application.scss
|
95
|
+
- app/controllers/canyon/application_controller.rb
|
96
|
+
- app/controllers/canyon/posts_controller.rb
|
97
|
+
- app/helpers/canyon/application_helper.rb
|
98
|
+
- app/models/canyon/author.rb
|
99
|
+
- app/models/canyon/post.rb
|
100
|
+
- app/views/canyon/posts/_form.html.erb
|
101
|
+
- app/views/canyon/posts/edit.html.erb
|
102
|
+
- app/views/canyon/posts/index.html.erb
|
103
|
+
- app/views/canyon/posts/new.html.erb
|
104
|
+
- app/views/layouts/canyon/application.html.erb
|
105
|
+
- bin/rails
|
106
|
+
- config/locales/en.yml
|
107
|
+
- config/routes.rb
|
108
|
+
- db/migrate/20150416053508_create_authors.rb
|
109
|
+
- db/migrate/20150416053518_create_posts.rb
|
110
|
+
- db/seeds.rb
|
111
|
+
- lib/canyon.rb
|
112
|
+
- lib/canyon/engine.rb
|
113
|
+
- lib/canyon/version.rb
|
114
|
+
- lib/tasks/canyon_tasks.rake
|
115
|
+
homepage: https://github.com/mjio/canyon
|
116
|
+
licenses:
|
117
|
+
- MIT
|
118
|
+
metadata: {}
|
119
|
+
post_install_message:
|
120
|
+
rdoc_options: []
|
121
|
+
require_paths:
|
122
|
+
- lib
|
123
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
124
|
+
requirements:
|
125
|
+
- - ">="
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
version: '0'
|
128
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - ">="
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: '0'
|
133
|
+
requirements: []
|
134
|
+
rubyforge_project:
|
135
|
+
rubygems_version: 2.4.5
|
136
|
+
signing_key:
|
137
|
+
specification_version: 4
|
138
|
+
summary: Rails blogging engine
|
139
|
+
test_files: []
|