enki-engine 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +13 -0
- data/.rspec +1 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/LICENSE +284 -0
- data/README.textile +112 -0
- data/Rakefile +13 -0
- data/TODO.textile +8 -0
- data/app/assets/images/admin/flash_bg.gif +0 -0
- data/app/assets/images/admin/future_bg.png +0 -0
- data/app/assets/images/admin/gray_bg.gif +0 -0
- data/app/assets/images/admin/new_button.png +0 -0
- data/app/assets/images/admin/silver_bg.gif +0 -0
- data/app/assets/images/admin/submit_bg.gif +0 -0
- data/app/assets/images/admin/subnav_bg.gif +0 -0
- data/app/assets/images/openid_icon.png +0 -0
- data/app/assets/images/rails.png +0 -0
- data/app/assets/images/silk/arrow_undo.png +0 -0
- data/app/assets/images/silk/delete.png +0 -0
- data/app/assets/images/silk/pencil.png +0 -0
- data/app/assets/javascripts/admin/actions.js +18 -0
- data/app/assets/javascripts/admin/comments.js +3 -0
- data/app/assets/javascripts/admin/common.js +109 -0
- data/app/assets/javascripts/admin/dashboard.js +33 -0
- data/app/assets/javascripts/admin/edit-preview.js +40 -0
- data/app/assets/javascripts/admin/pages.js +1 -0
- data/app/assets/javascripts/admin/posts.js +1 -0
- data/app/assets/javascripts/admin/shortcut.js +223 -0
- data/app/assets/javascripts/admin.js +17 -0
- data/app/assets/javascripts/application.js +15 -0
- data/app/assets/javascripts/common.js +22 -0
- data/app/assets/javascripts/enki.js +13 -0
- data/app/assets/javascripts/live-comment-preview.js +36 -0
- data/app/assets/stylesheets/admin.css +304 -0
- data/app/assets/stylesheets/application.css.scss +486 -0
- data/app/assets/stylesheets/humanmsg.css +112 -0
- data/app/assets/stylesheets/login.css +65 -0
- data/app/controllers/enki/admin/base_controller.rb +15 -0
- data/app/controllers/enki/admin/comments_controller.rb +52 -0
- data/app/controllers/enki/admin/dashboard_controller.rb +12 -0
- data/app/controllers/enki/admin/health_controller.rb +20 -0
- data/app/controllers/enki/admin/pages_controller.rb +97 -0
- data/app/controllers/enki/admin/posts_controller.rb +104 -0
- data/app/controllers/enki/admin/undo_items_controller.rb +43 -0
- data/app/controllers/enki/application_controller.rb +21 -0
- data/app/controllers/enki/archives_controller.rb +7 -0
- data/app/controllers/enki/base_controller.rb +5 -0
- data/app/controllers/enki/comments_controller.rb +43 -0
- data/app/controllers/enki/pages_controller.rb +7 -0
- data/app/controllers/enki/posts_controller.rb +27 -0
- data/app/helpers/enki/admin/navigation_helper.rb +10 -0
- data/app/helpers/enki/application_helper.rb +35 -0
- data/app/helpers/enki/date_helper.rb +15 -0
- data/app/helpers/enki/form_helper.rb +7 -0
- data/app/helpers/enki/host_helper.rb +19 -0
- data/app/helpers/enki/navigation_helper.rb +23 -0
- data/app/helpers/enki/page_title_helper.rb +33 -0
- data/app/helpers/enki/posts_helper.rb +9 -0
- data/app/helpers/enki/tag_helper.rb +7 -0
- data/app/helpers/enki/url_helper.rb +25 -0
- data/app/models/enki/base/post.rb +153 -0
- data/app/models/enki/comment.rb +75 -0
- data/app/models/enki/comment_activity.rb +33 -0
- data/app/models/enki/delete_comment_undo.rb +33 -0
- data/app/models/enki/delete_page_undo.rb +32 -0
- data/app/models/enki/delete_post_undo.rb +36 -0
- data/app/models/enki/page.rb +42 -0
- data/app/models/enki/post.rb +4 -0
- data/app/models/enki/stats.rb +19 -0
- data/app/models/enki/tag.rb +19 -0
- data/app/models/enki/tagging.rb +7 -0
- data/app/models/enki/undo_item.rb +10 -0
- data/app/views/enki/admin/comments/_comment.html.erb +12 -0
- data/app/views/enki/admin/comments/index.html.erb +30 -0
- data/app/views/enki/admin/comments/show.html.erb +9 -0
- data/app/views/enki/admin/dashboard/show.html.erb +61 -0
- data/app/views/enki/admin/health/index.html.erb +3 -0
- data/app/views/enki/admin/pages/_form.html.erb +3 -0
- data/app/views/enki/admin/pages/_page.html.erb +12 -0
- data/app/views/enki/admin/pages/index.html.erb +25 -0
- data/app/views/enki/admin/pages/new.html.erb +8 -0
- data/app/views/enki/admin/pages/show.html.erb +8 -0
- data/app/views/enki/admin/posts/_form.html.erb +11 -0
- data/app/views/enki/admin/posts/_post.html.erb +12 -0
- data/app/views/enki/admin/posts/_taggings_form.html.erb +4 -0
- data/app/views/enki/admin/posts/_upload_form.html.erb +0 -0
- data/app/views/enki/admin/posts/index.html.erb +25 -0
- data/app/views/enki/admin/posts/new.html.erb +8 -0
- data/app/views/enki/admin/posts/show.html.erb +8 -0
- data/app/views/enki/admin/undo_items/index.html.erb +24 -0
- data/app/views/enki/archives/index.html.erb +17 -0
- data/app/views/enki/comments/_comment.html.erb +4 -0
- data/app/views/enki/pages/_page.html.erb +3 -0
- data/app/views/enki/pages/show.html.erb +5 -0
- data/app/views/enki/posts/_post.html.erb +13 -0
- data/app/views/enki/posts/index.atom.builder +27 -0
- data/app/views/enki/posts/index.html.erb +15 -0
- data/app/views/enki/posts/show.html.erb +37 -0
- data/autotest/discover.rb +2 -0
- data/config/cucumber.yml +8 -0
- data/config/enki.yml.sample +16 -0
- data/config/initializers/enki_ext.rb +3 -0
- data/config/initializers/set_chronic_timezone.rb +1 -0
- data/config/initializers/verification.rb +135 -0
- data/config/routes.rb +34 -0
- data/db/migrate/20110709024316_initialize_db.rb +97 -0
- data/db/seeds.rb +8 -0
- data/enki-engine.gemspec +47 -0
- data/features/admin_dashboard.feature +10 -0
- data/features/admin_health.feature +10 -0
- data/features/admin_undo.feature +20 -0
- data/features/browsing.feature +16 -0
- data/features/step_definitions/admin.rb +27 -0
- data/features/step_definitions/browsing.rb +3 -0
- data/features/step_definitions/posts.rb +11 -0
- data/features/step_definitions/web_steps.rb +1 -0
- data/features/support/env.rb +59 -0
- data/features/support/paths.rb +35 -0
- data/features/support/selectors.rb +39 -0
- data/lib/core_extensions/object.rb +9 -0
- data/lib/core_extensions/string.rb +22 -0
- data/lib/enki/config.rb +44 -0
- data/lib/enki/engine.rb +19 -0
- data/lib/enki/html5_tags.rb +8 -0
- data/lib/enki/pagination_shim.rb +25 -0
- data/lib/enki/version.rb +3 -0
- data/lib/enki.rb +14 -0
- data/lib/enki_formatter.rb +11 -0
- data/lib/tag_list.rb +2 -0
- data/lib/tags_helper.rb +13 -0
- data/lib/tasks/cucumber.rake +65 -0
- data/lib/tasks/enki.rake +29 -0
- data/lib/undo_failed.rb +2 -0
- data/script/cucumber +10 -0
- data/spec/controllers/admin/comments_controller_spec.rb +140 -0
- data/spec/controllers/admin/dashboard_controller_spec.rb +47 -0
- data/spec/controllers/admin/health_controller_spec.rb +49 -0
- data/spec/controllers/admin/pages_controller_spec.rb +136 -0
- data/spec/controllers/admin/posts_controller_spec.rb +183 -0
- data/spec/controllers/admin/undo_items_controller_spec.rb +93 -0
- data/spec/controllers/archives_controller_spec.rb +37 -0
- data/spec/controllers/comments_controller_spec.rb +126 -0
- data/spec/controllers/pages_controller_spec.rb +46 -0
- data/spec/controllers/posts_controller_spec.rb +168 -0
- data/spec/dummy/Gemfile +5 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/config/application.rb +34 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +6 -0
- data/spec/dummy/config/enki.yml +20 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +34 -0
- data/spec/dummy/config/environments/test.rb +32 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +14 -0
- data/spec/dummy/config.ru +6 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/factories/factories.rb +36 -0
- data/spec/helpers/page_title_helper_spec.rb +54 -0
- data/spec/helpers/url_helper_spec.rb +23 -0
- data/spec/lib/slugorize_spec.rb +44 -0
- data/spec/models/comment_activity_spec.rb +60 -0
- data/spec/models/comment_spec.rb +125 -0
- data/spec/models/delete_comment_undo_spec.rb +52 -0
- data/spec/models/delete_post_undo_spec.rb +18 -0
- data/spec/models/page_spec.rb +75 -0
- data/spec/models/post_spec.rb +257 -0
- data/spec/models/stats_spec.rb +28 -0
- data/spec/models/tag_spec.rb +13 -0
- data/spec/models/tagging_spec.rb +30 -0
- data/spec/rcov.opts +2 -0
- data/spec/routing/admin/pages_routing_spec.rb +29 -0
- data/spec/routing/archives_routing_spec.rb +9 -0
- data/spec/routing/comments_routing_spec.rb +17 -0
- data/spec/routing/pages_routing_spec.rb +9 -0
- data/spec/routing/posts_routing_spec.rb +26 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +60 -0
- data/spec/support/be_valid_html5.rb +150 -0
- data/spec/support/be_valid_xhtml.rb +148 -0
- data/spec/support/routes_override_helper.rb +12 -0
- data/spec/views/admin/comments/index.html_spec.rb +26 -0
- data/spec/views/admin/comments/show.html_spec.rb +28 -0
- data/spec/views/admin/dashboard/show.html_spec.rb +37 -0
- data/spec/views/admin/pages/index.html_spec.rb +23 -0
- data/spec/views/admin/pages/new.html_spec.rb +16 -0
- data/spec/views/admin/pages/show.html_spec.rb +16 -0
- data/spec/views/admin/posts/index.html_spec.rb +24 -0
- data/spec/views/admin/posts/new.html_spec.rb +16 -0
- data/spec/views/admin/posts/show.html_spec.rb +16 -0
- data/spec/views/admin/undo_items/index.html_spec.rb +19 -0
- data/spec/views/archives/index.html_spec.rb +34 -0
- data/spec/views/pages/show.html_spec.rb +23 -0
- data/spec/views/posts/index.atom.builder_spec.rb +36 -0
- data/spec/views/posts/index.html_spec.rb +39 -0
- data/spec/views/posts/show.html_spec.rb +49 -0
- data/vendor/assets/javascripts/humanmsg.js +86 -0
- data/vendor/assets/javascripts/jquery.easing.1.3.js +205 -0
- data/vendor/assets/javascripts/jquery.form.js +869 -0
- data/vendor/assets/javascripts/jquery.jfeed.js +143 -0
- data/vendor/assets/javascripts/jquery.livequery.js +250 -0
- metadata +464 -0
@@ -0,0 +1,12 @@
|
|
1
|
+
<tr class="<%= cycle('', 'alt') %>">
|
2
|
+
<td><%= post.published_at.strftime('%d %b, %Y') %></td>
|
3
|
+
<td><%= link_to (post.title == '' ? '[Untitled]' : truncate(post.title, :length => 30)), enki.admin_post_path(post) %></td>
|
4
|
+
<td><% tmpc = truncate(post.body, :length => 55) %><%= (tmpc != '' ? tmpc : ' ') %></td>
|
5
|
+
<% if comments? -%><td><%= post.approved_comments.size -%></td><% end -%>
|
6
|
+
<td>
|
7
|
+
<%= link_to(image_tag('silk/pencil.png', :alt => 'edit'), enki.admin_post_path(post)) %>
|
8
|
+
<%= form_for(post, :as => :post, :url => enki.admin_post_path(post), :html => {:class => 'delete-item', :method => :delete}) do |form| -%>
|
9
|
+
<%= image_submit_tag("silk/delete.png", :alt => 'Delete Comment') %>
|
10
|
+
<% end -%>
|
11
|
+
</td>
|
12
|
+
</tr>
|
File without changes
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<h1>Your posts</h1>
|
2
|
+
<table>
|
3
|
+
<thead>
|
4
|
+
<tr>
|
5
|
+
<th>Published At</th>
|
6
|
+
<th>Title</th>
|
7
|
+
<th>Excerpt</th>
|
8
|
+
<% if comments? -%><th>Comments</th><% end -%>
|
9
|
+
<th>Actions</th>
|
10
|
+
</tr>
|
11
|
+
</thead>
|
12
|
+
|
13
|
+
<tbody>
|
14
|
+
<% if @posts.empty? -%>
|
15
|
+
<tr><td colspan="5">There are no posts at this time.</td></tr>
|
16
|
+
<% else -%>
|
17
|
+
<%= render :partial => 'enki/admin/posts/post', :collection => @posts %>
|
18
|
+
<% end -%>
|
19
|
+
</tbody>
|
20
|
+
|
21
|
+
</table>
|
22
|
+
|
23
|
+
<div class="pagination">
|
24
|
+
<%= paginated @posts %>
|
25
|
+
</div>
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<h1>New post</h1>
|
2
|
+
|
3
|
+
<%= simple_form_for(@post, :url => enki.admin_posts_path) do |form| %>
|
4
|
+
<%= render :partial => 'enki/admin/posts/form', :locals => {:form => form} -%>
|
5
|
+
<div class="actions">
|
6
|
+
<button type="submit" class="button">Save</button>
|
7
|
+
</div>
|
8
|
+
<% end -%>
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<h1>Editing post - <%= link_to(@post.title, post_path(@post)) %></h1>
|
2
|
+
|
3
|
+
<%= simple_form_for @post, :url => enki.admin_post_path(@post) do |form| %>
|
4
|
+
<%= render :partial => 'enki/admin/posts/form', :locals => {:form => form} %>
|
5
|
+
<div class="actions">
|
6
|
+
<button type="submit" class="button">Save</button>
|
7
|
+
</div>
|
8
|
+
<% end -%>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<h1>Recent actions</h1>
|
2
|
+
<table>
|
3
|
+
<thead>
|
4
|
+
<tr>
|
5
|
+
<th>Date</th>
|
6
|
+
<th>Description</th>
|
7
|
+
<th>Action</th>
|
8
|
+
</tr>
|
9
|
+
</thead>
|
10
|
+
|
11
|
+
<tbody>
|
12
|
+
<% @undo_items.each do |undo_item| -%>
|
13
|
+
<tr class="<%= cycle('', 'alt') %>">
|
14
|
+
<td><%= undo_item.created_at.strftime('%d %b, %Y') %></td>
|
15
|
+
<td><%= undo_item.description %></td>
|
16
|
+
<td>
|
17
|
+
<%= form_for(undo_item, :as => :undo_item, :url => enki.admin_undo_item_path(undo_item), :html => {:class => 'undo-item', :method => :post}) do |form| -%>
|
18
|
+
<%= image_submit_tag("silk/arrow_undo.png", :alt => 'Undo') %>
|
19
|
+
<% end -%>
|
20
|
+
</td>
|
21
|
+
</tr>
|
22
|
+
<% end -%>
|
23
|
+
</tbody>
|
24
|
+
</table>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<% content_for(:page_title) do %><%= archives_title %><% end -%>
|
2
|
+
<h2>Archives</h2>
|
3
|
+
|
4
|
+
<% if @months.empty? -%>
|
5
|
+
<p>Once posts are added they will start to appear in this archive.</p>
|
6
|
+
<% else -%>
|
7
|
+
|
8
|
+
<% @months.each do |month| -%>
|
9
|
+
<h3><%= format_month(month.date) %></h3>
|
10
|
+
<ul class='archive-month'>
|
11
|
+
<% month.posts.each do |post| -%>
|
12
|
+
<li><%= link_to(post.title, post_path(post)) %><% unless post.tags.empty? %> <span class='tags'>(<%= linked_tag_list(post.tags) %>)</span><% end -%></li>
|
13
|
+
<% end -%>
|
14
|
+
</ul>
|
15
|
+
<% end -%>
|
16
|
+
|
17
|
+
<% end -%>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<h2><%= link_to(post.title, post_path(post)) %></h2>
|
2
|
+
<div class="entrybody">
|
3
|
+
<%=raw post.body_html %>
|
4
|
+
</div>
|
5
|
+
<div class="meta">
|
6
|
+
<ul>
|
7
|
+
<li class="date">Posted on <%= format_post_date(post.published_at) %></li>
|
8
|
+
<% if comments? -%><li class="comments"><%= link_to(pluralize(post.approved_comments.size, "comment"), post_path(post, :anchor => 'comments')) %></li><% end -%>
|
9
|
+
<% unless post.tags.empty? -%>
|
10
|
+
<li class="tags">Tagged <%= linked_tag_list(post.tags) %></li>
|
11
|
+
<% end -%>
|
12
|
+
</ul>
|
13
|
+
</div>
|
@@ -0,0 +1,27 @@
|
|
1
|
+
url = if @tag.nil?
|
2
|
+
enki.formatted_posts_path(:format => 'atom', :only_path => false)
|
3
|
+
else
|
4
|
+
enki.posts_path(:tag => @tag, :format => 'atom', :only_path => false)
|
5
|
+
end
|
6
|
+
|
7
|
+
atom_feed(
|
8
|
+
:url => url,
|
9
|
+
:root_url => enki.posts_path(:tag => @tag, :only_path => false),
|
10
|
+
:schema_date => '2008'
|
11
|
+
) do |feed|
|
12
|
+
feed.title posts_title(@tag)
|
13
|
+
feed.updated @posts.empty? ? Time.now.utc : @posts.collect(&:edited_at).max
|
14
|
+
feed.generator "Enki", "uri" => "http://enkiblog.com"
|
15
|
+
|
16
|
+
feed.author do |xml|
|
17
|
+
xml.name author.name
|
18
|
+
xml.email author.email unless author.email.nil?
|
19
|
+
end
|
20
|
+
|
21
|
+
@posts.each do |post|
|
22
|
+
feed.entry(post, :url => post_path(post, :only_path => false), :published => post.published_at, :updated => post.edited_at) do |entry|
|
23
|
+
entry.title post.title
|
24
|
+
entry.content post.body_html, :type => 'html'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<% content_for(:page_title) do %><%= posts_title(@tag) %><% end -%>
|
2
|
+
<% content_for(:head) do %><%= auto_discovery_link_tag(:atom, @tag.nil? ? enki.formatted_posts_path(:format => 'atom') : enki.posts_path(:tag => @tag, :format => 'atom')) %><% end -%>
|
3
|
+
|
4
|
+
<% if @posts.empty? -%>
|
5
|
+
<p>There are no posts yet.</p>
|
6
|
+
<% else -%>
|
7
|
+
<% @posts.each do |post| -%>
|
8
|
+
<div class="post post-<%= post.id %>">
|
9
|
+
<%= render :partial => 'enki/posts/post', :locals => {:post => post} %>
|
10
|
+
</div>
|
11
|
+
<% end -%>
|
12
|
+
<% if more_content? -%>
|
13
|
+
<div class="related">Looking for more? Head on over to the <%= link_to("archives", enki.archives_path) %>.</div>
|
14
|
+
<% end -%>
|
15
|
+
<% end -%>
|
@@ -0,0 +1,37 @@
|
|
1
|
+
<% content_for(:page_title) do -%>
|
2
|
+
<%= post_title(@post) -%>
|
3
|
+
<% end -%>
|
4
|
+
<%= render :partial => 'enki/posts/post', :locals => {:post => @post} %>
|
5
|
+
|
6
|
+
<% if comments? -%>
|
7
|
+
<ol class="commentlist">
|
8
|
+
<% @post.approved_comments.each do |comment| -%>
|
9
|
+
<li<%=raw cycle(' class="alt"', '') %> id="comment-<%= comment.id %>">
|
10
|
+
<%= render :partial => 'enki/comments/comment', :locals => {:comment => comment} %>
|
11
|
+
</li>
|
12
|
+
<% end -%>
|
13
|
+
</ol>
|
14
|
+
<% end -%>
|
15
|
+
|
16
|
+
<div class="related">Looking for more? Head on over to the <%= link_to("archives", enki.archives_path) %>.</div>
|
17
|
+
|
18
|
+
<% if comments? -%>
|
19
|
+
<h2>Post a comment</h2>
|
20
|
+
<% unless @comment.errors.empty? -%>
|
21
|
+
<div class="errors">
|
22
|
+
<h3>Comment not added!</h3>
|
23
|
+
<ul>
|
24
|
+
<% @comment.errors.sort_by(&:first).each do |error| -%>
|
25
|
+
<li><%= format_comment_error(error) %></li>
|
26
|
+
<% end -%>
|
27
|
+
</ul>
|
28
|
+
</div>
|
29
|
+
<% end -%>
|
30
|
+
<%= form_for @comment, :url => post_comments_path(@post, @comment) do |form| -%>
|
31
|
+
<div>
|
32
|
+
<p><%= form.text_field 'author' %><label for="comment_author"><small>Name or <a href="http://openidexplained.com/">OpenID</a> (required)</small></label></p>
|
33
|
+
<p><%= form.text_area 'body' %><br><small>(<a href="http://lesstile.rubyforge.org">lesstile enabled</a> - surround code blocks with ---)</small></p>
|
34
|
+
<p><%= submit_tag "Add Comment" %></p>
|
35
|
+
</div>
|
36
|
+
<% end -%>
|
37
|
+
<% end -%>
|
data/config/cucumber.yml
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
<%
|
2
|
+
rerun = File.file?('rerun.txt') ? IO.read('rerun.txt') : ""
|
3
|
+
rerun_opts = rerun.to_s.strip.empty? ? "--format #{ENV['CUCUMBER_FORMAT'] || 'progress'} features" : "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} #{rerun}"
|
4
|
+
std_opts = "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} --strict --tags ~@wip"
|
5
|
+
%>
|
6
|
+
default: <%= std_opts %> features
|
7
|
+
wip: --tags @wip:3 --wip features
|
8
|
+
rerun: <%= rerun_opts %> --format rerun --out rerun.txt --strict --tags ~@wip
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# Configuration options for your blog - customise to taste and copy to the host app's config folder.
|
2
|
+
# This file contains no secret information, so can be stored in source control (unlike database.yml)
|
3
|
+
title: My Enki Blog
|
4
|
+
url: http://enkiblog.com
|
5
|
+
author:
|
6
|
+
name: Don Alias # For copyright notice and ATOM feeds
|
7
|
+
email: don@enkiblog.com # Exception emails will go here, and it is used in ATOM feeds
|
8
|
+
open_id: # These are used to login to the admin area
|
9
|
+
- http://enkiblog.com
|
10
|
+
- http://secondaryopenid.com
|
11
|
+
|
12
|
+
# Delete the following section if your site will not be acting as an OpenID delegate (http://wiki.openid.net/Delegation)
|
13
|
+
# If you're deploying with mongrel, make sure you read http://rhnh.net/2008/04/13/nginx-openid-delegation-and-yadis
|
14
|
+
open_id_delegation:
|
15
|
+
server: http://www.myopenid.com/server
|
16
|
+
delegate: http://username.myopenid.com
|
@@ -0,0 +1 @@
|
|
1
|
+
Chronic.time_class = Time.zone
|
@@ -0,0 +1,135 @@
|
|
1
|
+
# From https://github.com/sikachu/verification
|
2
|
+
# This used to be an official part of Rails but was deprecated.
|
3
|
+
#
|
4
|
+
module ActionController #:nodoc:
|
5
|
+
module Verification #:nodoc:
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
include AbstractController::Callbacks, Flash, Rendering
|
9
|
+
|
10
|
+
# This module provides a class-level method for specifying that certain
|
11
|
+
# actions are guarded against being called without certain prerequisites
|
12
|
+
# being met. This is essentially a special kind of before_filter.
|
13
|
+
#
|
14
|
+
# An action may be guarded against being invoked without certain request
|
15
|
+
# parameters being set, or without certain session values existing.
|
16
|
+
#
|
17
|
+
# When a verification is violated, values may be inserted into the flash, and
|
18
|
+
# a specified redirection is triggered. If no specific action is configured,
|
19
|
+
# verification failures will by default result in a 400 Bad Request response.
|
20
|
+
#
|
21
|
+
# Usage:
|
22
|
+
#
|
23
|
+
# class GlobalController < ActionController::Base
|
24
|
+
# # Prevent the #update_settings action from being invoked unless
|
25
|
+
# # the 'admin_privileges' request parameter exists. The
|
26
|
+
# # settings action will be redirected to in current controller
|
27
|
+
# # if verification fails.
|
28
|
+
# verify :params => "admin_privileges", :only => :update_post,
|
29
|
+
# :redirect_to => { :action => "settings" }
|
30
|
+
#
|
31
|
+
# # Disallow a post from being updated if there was no information
|
32
|
+
# # submitted with the post, and if there is no active post in the
|
33
|
+
# # session, and if there is no "note" key in the flash. The route
|
34
|
+
# # named category_url will be redirected to if verification fails.
|
35
|
+
#
|
36
|
+
# verify :params => "post", :session => "post", "flash" => "note",
|
37
|
+
# :only => :update_post,
|
38
|
+
# :add_flash => { "alert" => "Failed to create your message" },
|
39
|
+
# :redirect_to => :category_url
|
40
|
+
#
|
41
|
+
# Note that these prerequisites are not business rules. They do not examine
|
42
|
+
# the content of the session or the parameters. That level of validation should
|
43
|
+
# be encapsulated by your domain model or helper methods in the controller.
|
44
|
+
module ClassMethods
|
45
|
+
# Verify the given actions so that if certain prerequisites are not met,
|
46
|
+
# the user is redirected to a different action. The +options+ parameter
|
47
|
+
# is a hash consisting of the following key/value pairs:
|
48
|
+
#
|
49
|
+
# <tt>:params</tt>::
|
50
|
+
# a single key or an array of keys that must be in the <tt>params</tt>
|
51
|
+
# hash in order for the action(s) to be safely called.
|
52
|
+
# <tt>:session</tt>::
|
53
|
+
# a single key or an array of keys that must be in the <tt>session</tt>
|
54
|
+
# in order for the action(s) to be safely called.
|
55
|
+
# <tt>:flash</tt>::
|
56
|
+
# a single key or an array of keys that must be in the flash in order
|
57
|
+
# for the action(s) to be safely called.
|
58
|
+
# <tt>:method</tt>::
|
59
|
+
# a single key or an array of keys--any one of which must match the
|
60
|
+
# current request method in order for the action(s) to be safely called.
|
61
|
+
# (The key should be a symbol: <tt>:get</tt> or <tt>:post</tt>, for
|
62
|
+
# example.)
|
63
|
+
# <tt>:xhr</tt>::
|
64
|
+
# true/false option to ensure that the request is coming from an Ajax
|
65
|
+
# call or not.
|
66
|
+
# <tt>:add_flash</tt>::
|
67
|
+
# a hash of name/value pairs that should be merged into the session's
|
68
|
+
# flash if the prerequisites cannot be satisfied.
|
69
|
+
# <tt>:add_headers</tt>::
|
70
|
+
# a hash of name/value pairs that should be merged into the response's
|
71
|
+
# headers hash if the prerequisites cannot be satisfied.
|
72
|
+
# <tt>:redirect_to</tt>::
|
73
|
+
# the redirection parameters to be used when redirecting if the
|
74
|
+
# prerequisites cannot be satisfied. You can redirect either to named
|
75
|
+
# route or to the action in some controller.
|
76
|
+
# <tt>:render</tt>::
|
77
|
+
# the render parameters to be used when the prerequisites cannot be satisfied.
|
78
|
+
# <tt>:only</tt>::
|
79
|
+
# only apply this verification to the actions specified in the associated
|
80
|
+
# array (may also be a single value).
|
81
|
+
# <tt>:except</tt>::
|
82
|
+
# do not apply this verification to the actions specified in the associated
|
83
|
+
# array (may also be a single value).
|
84
|
+
def verify(options={})
|
85
|
+
before_filter :only => options[:only], :except => options[:except] do
|
86
|
+
verify_action options
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def verify_action(options) #:nodoc:
|
94
|
+
if prereqs_invalid?(options)
|
95
|
+
flash.update(options[:add_flash]) if options[:add_flash]
|
96
|
+
response.headers.merge!(options[:add_headers]) if options[:add_headers]
|
97
|
+
apply_remaining_actions(options) unless performed?
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def prereqs_invalid?(options) # :nodoc:
|
102
|
+
verify_presence_of_keys_in_hash_flash_or_params(options) ||
|
103
|
+
verify_method(options) ||
|
104
|
+
verify_request_xhr_status(options)
|
105
|
+
end
|
106
|
+
|
107
|
+
def verify_presence_of_keys_in_hash_flash_or_params(options) # :nodoc:
|
108
|
+
[*options[:params] ].find { |v| v && params[v.to_sym].nil? } ||
|
109
|
+
[*options[:session]].find { |v| session[v].nil? } ||
|
110
|
+
[*options[:flash] ].find { |v| flash[v].nil? }
|
111
|
+
end
|
112
|
+
|
113
|
+
def verify_method(options) # :nodoc:
|
114
|
+
[*options[:method]].all? { |v| request.method_symbol != v.to_sym } if options[:method]
|
115
|
+
end
|
116
|
+
|
117
|
+
def verify_request_xhr_status(options) # :nodoc:
|
118
|
+
request.xhr? != options[:xhr] unless options[:xhr].nil?
|
119
|
+
end
|
120
|
+
|
121
|
+
def apply_redirect_to(redirect_to_option) # :nodoc:
|
122
|
+
(redirect_to_option.is_a?(Symbol) && redirect_to_option != :back) ? self.__send__(redirect_to_option) : redirect_to_option
|
123
|
+
end
|
124
|
+
|
125
|
+
def apply_remaining_actions(options) # :nodoc:
|
126
|
+
case
|
127
|
+
when options[:render] ; render(options[:render])
|
128
|
+
when options[:redirect_to] ; redirect_to(apply_redirect_to(options[:redirect_to]))
|
129
|
+
else head(:bad_request)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
ActionController::Base.send :include, ActionController::Verification
|
data/config/routes.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
Enki::Engine.routes.draw do
|
2
|
+
scope :module => 'enki' do
|
3
|
+
namespace :admin do
|
4
|
+
|
5
|
+
resources :posts, :pages do
|
6
|
+
post 'preview', :on => :collection
|
7
|
+
end
|
8
|
+
resources :comments
|
9
|
+
resources :undo_items do
|
10
|
+
post 'undo', :on => :member
|
11
|
+
end
|
12
|
+
|
13
|
+
match 'health(/:action)' => 'health', :action => 'index', :as => :health
|
14
|
+
|
15
|
+
root :to => 'dashboard#show'
|
16
|
+
end
|
17
|
+
resources :archives, :only => [:index]
|
18
|
+
resources :pages, :only => [:show]
|
19
|
+
end
|
20
|
+
|
21
|
+
constraints :year => /\d{4}/, :month => /\d{2}/, :day => /\d{2}/ do
|
22
|
+
get ':year/:month/:day/:slug/comments' => 'enki/comments#index'
|
23
|
+
post ':year/:month/:day/:slug/comments' => 'enki/comments#create'
|
24
|
+
get ':year/:month/:day/:slug/comments/new' => 'enki/comments#new'
|
25
|
+
get ':year/:month/:day/:slug' => 'enki/posts#show'
|
26
|
+
end
|
27
|
+
|
28
|
+
scope :to => 'enki/posts#index' do
|
29
|
+
get 'posts.:format', :as => :formatted_posts
|
30
|
+
get '(:tag)', :as => :posts
|
31
|
+
end
|
32
|
+
|
33
|
+
root :to => 'enki/posts#index'
|
34
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
class InitializeDb < ActiveRecord::Migration
|
2
|
+
|
3
|
+
def change
|
4
|
+
|
5
|
+
create_table "pages" do |t|
|
6
|
+
t.string "title", :null => false
|
7
|
+
t.string "slug", :null => false
|
8
|
+
t.text "body", :null => false
|
9
|
+
t.text "body_html", :null => false
|
10
|
+
|
11
|
+
t.timestamps
|
12
|
+
end
|
13
|
+
|
14
|
+
add_index "pages", 'slug'
|
15
|
+
add_index "pages", "title"
|
16
|
+
add_index "pages", "created_at"
|
17
|
+
|
18
|
+
create_table "posts" do |t|
|
19
|
+
t.string "title", :null => false
|
20
|
+
t.string "slug", :null => false
|
21
|
+
t.text "body", :null => false
|
22
|
+
t.text "body_html", :null => false
|
23
|
+
t.boolean "active", :default => true, :null => false
|
24
|
+
t.string "cached_tag_list"
|
25
|
+
t.datetime "published_at"
|
26
|
+
t.datetime "edited_at", :null => false
|
27
|
+
|
28
|
+
t.timestamps
|
29
|
+
end
|
30
|
+
|
31
|
+
add_index "posts", 'slug'
|
32
|
+
add_index "posts", "published_at"
|
33
|
+
|
34
|
+
create_table "undo_items" do |t|
|
35
|
+
t.string "type", :null => false
|
36
|
+
t.datetime "created_at", :null => false
|
37
|
+
t.text "data"
|
38
|
+
end
|
39
|
+
|
40
|
+
add_index "undo_items", "created_at"
|
41
|
+
|
42
|
+
if comments?
|
43
|
+
|
44
|
+
create_table "comments" do |t|
|
45
|
+
t.integer "post_id", :null => false
|
46
|
+
t.string "author"
|
47
|
+
t.string "author_url"
|
48
|
+
t.string "author_email"
|
49
|
+
t.text "body", :null => false
|
50
|
+
t.text "body_html", :null => false
|
51
|
+
|
52
|
+
t.timestamps
|
53
|
+
end
|
54
|
+
|
55
|
+
add_index "comments", "post_id"
|
56
|
+
add_index "comments", "created_at"
|
57
|
+
|
58
|
+
add_column "posts", "approved_comments_count", :integer, :default => 0, :null => false
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
if tags?
|
63
|
+
|
64
|
+
create_table 'tags' do |t|
|
65
|
+
t.string 'name'
|
66
|
+
t.integer "taggings_count", :default => 0, :null => false
|
67
|
+
end
|
68
|
+
|
69
|
+
create_table 'taggings' do |t|
|
70
|
+
t.references 'tag'
|
71
|
+
|
72
|
+
t.references 'taggable', :polymorphic => true
|
73
|
+
t.references 'tagger', :polymorphic => true
|
74
|
+
|
75
|
+
t.string 'context', :limit => 128
|
76
|
+
|
77
|
+
t.timestamps
|
78
|
+
end
|
79
|
+
|
80
|
+
add_index 'taggings', 'tag_id'
|
81
|
+
add_index 'taggings', ['taggable_id', 'taggable_type', 'context']
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def comments?
|
90
|
+
Enki::Config.default[:features, :comments]
|
91
|
+
end
|
92
|
+
|
93
|
+
def tags?
|
94
|
+
Enki::Config.default[:features, :tags]
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
data/db/seeds.rb
ADDED
data/enki-engine.gemspec
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "enki/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "enki-engine"
|
7
|
+
s.version = Enki::VERSION
|
8
|
+
s.date = "2012-02-22"
|
9
|
+
s.authors = ["James McCarthy", "Andy Triggs", "Xavier Shay"]
|
10
|
+
s.email = ["james2mccarthy@gmail.com"]
|
11
|
+
s.homepage = "http://github.com/ThisIsHatch/enki_engine"
|
12
|
+
s.summary = %Q{A Rails3 engine adapted from Xavier Shay's Enki blogging app}
|
13
|
+
s.description = %Q{An adaptation of the Enki blogging application as a Rails::Engine, for mounting in a host application}
|
14
|
+
|
15
|
+
s.rubyforge_project = "enki-engine"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.extra_rdoc_files = [
|
21
|
+
"LICENSE",
|
22
|
+
"README.textile"
|
23
|
+
]
|
24
|
+
s.require_paths = ["lib"]
|
25
|
+
|
26
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2.0") if s.respond_to? :required_rubygems_version=
|
27
|
+
|
28
|
+
s.add_runtime_dependency 'RedCloth', "~> 4.2.9"
|
29
|
+
s.add_runtime_dependency 'aaronh-chronic'
|
30
|
+
s.add_runtime_dependency 'coderay'
|
31
|
+
s.add_runtime_dependency 'lesstile'
|
32
|
+
s.add_runtime_dependency 'simple_form'
|
33
|
+
|
34
|
+
s.add_development_dependency 'acts-as-taggable-on'
|
35
|
+
s.add_development_dependency 'rails', "~> 3.2"
|
36
|
+
s.add_development_dependency 'rspec-rails', ">= 2.9"
|
37
|
+
s.add_development_dependency 'factory_girl_rails'
|
38
|
+
s.add_development_dependency 'nokogiri', '~> 1.5.0'
|
39
|
+
s.add_development_dependency 'webrat'
|
40
|
+
s.add_development_dependency 'sqlite3'
|
41
|
+
|
42
|
+
# s.add_development_dependency 'database_cleaner'
|
43
|
+
# s.add_development_dependency 'cucumber-rails', :require => false
|
44
|
+
# s.add_development_dependency 'cucumber-websteps', :require => false
|
45
|
+
end
|
46
|
+
|
47
|
+
|
@@ -0,0 +1,10 @@
|
|
1
|
+
Feature: Dashboard
|
2
|
+
Because I want to see what the go is with my blog
|
3
|
+
An admin
|
4
|
+
Should be able to see interesting things on the dashboard
|
5
|
+
|
6
|
+
Scenario: viewing dash board
|
7
|
+
Given I am logged in
|
8
|
+
And a post with comments exists
|
9
|
+
When I go to /admin
|
10
|
+
Then I should see "Latest Comments"
|
@@ -0,0 +1,10 @@
|
|
1
|
+
Feature: Health Monitor
|
2
|
+
Because I want to have confidence that my site works
|
3
|
+
An admin
|
4
|
+
Should be able generate an exception to verify the error reporting stack is working
|
5
|
+
|
6
|
+
Scenario: generating an exception
|
7
|
+
Given I am logged in
|
8
|
+
When I go to /admin
|
9
|
+
And I follow "Health"
|
10
|
+
Then a RuntimeError is thrown when I press "Throw exception"
|
@@ -0,0 +1,20 @@
|
|
1
|
+
Feature: Undo
|
2
|
+
Because I am human and make mistakes
|
3
|
+
An admin
|
4
|
+
Should be able to undo actions they make
|
5
|
+
|
6
|
+
Scenario: delete a comment, then undo it
|
7
|
+
Given I am logged in
|
8
|
+
And the following comment exists:
|
9
|
+
| body |
|
10
|
+
| Accidental Delete |
|
11
|
+
When I go to /admin
|
12
|
+
And I follow "Comments"
|
13
|
+
And I press "Delete Comment"
|
14
|
+
# Not sure why this doesn't redirect automatically
|
15
|
+
# And I follow "redirected"
|
16
|
+
And I follow "Actions"
|
17
|
+
And I press "Undo"
|
18
|
+
Then a comment exists with attributes:
|
19
|
+
| body |
|
20
|
+
| Accidental Delete |
|
@@ -0,0 +1,16 @@
|
|
1
|
+
Feature: Browsing
|
2
|
+
Because I write totally awesome posts
|
3
|
+
An everyday Joe
|
4
|
+
Should be able to read and comment on my posts
|
5
|
+
|
6
|
+
Scenario: browsing the home page
|
7
|
+
Given there is at least one post tagged "awesome"
|
8
|
+
When I go to the home page
|
9
|
+
Then I should see "This is a post"
|
10
|
+
And I should see a link to all posts tagged "awesome"
|
11
|
+
|
12
|
+
Scenario: browsing the archive, to find more content to read
|
13
|
+
Given there is at least one post titled "My Post"
|
14
|
+
When I go to the home page
|
15
|
+
And I follow "Archives"
|
16
|
+
Then I should see "My Post"
|