spree_essential_blog 0.1.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/.travis.yml +12 -0
- data/Gemfile +2 -0
- data/LICENSE +27 -0
- data/README.md +126 -0
- data/Rakefile +21 -0
- data/Versionfile +5 -0
- data/app/controllers/blog/admin/disqus_settings_controller.rb +22 -0
- data/app/controllers/blog/admin/post_categories_controller.rb +20 -0
- data/app/controllers/blog/admin/post_images_controller.rb +37 -0
- data/app/controllers/blog/admin/post_products_controller.rb +25 -0
- data/app/controllers/blog/admin/posts_controller.rb +32 -0
- data/app/controllers/blog/post_categories_controller.rb +23 -0
- data/app/controllers/blog/posts_controller.rb +60 -0
- data/app/controllers/pages_controller_decorator.rb +5 -0
- data/app/helpers/blog/posts_helper.rb +24 -0
- data/app/models/blog_configuration.rb +10 -0
- data/app/models/post.rb +87 -0
- data/app/models/post_category.rb +33 -0
- data/app/models/post_image.rb +45 -0
- data/app/models/post_product.rb +9 -0
- data/app/overrides/spree_essential_blog.rb +5 -0
- data/app/validators/datetime_validator.rb +6 -0
- data/app/views/blog/admin/configurations/_disqus_config.html.erb +4 -0
- data/app/views/blog/admin/disqus_settings/edit.html.erb +16 -0
- data/app/views/blog/admin/disqus_settings/show.html.erb +14 -0
- data/app/views/blog/admin/post_categories/_form.html.erb +8 -0
- data/app/views/blog/admin/post_categories/edit.html.erb +11 -0
- data/app/views/blog/admin/post_categories/index.html.erb +45 -0
- data/app/views/blog/admin/post_categories/new.html.erb +10 -0
- data/app/views/blog/admin/post_images/_form.html.erb +8 -0
- data/app/views/blog/admin/post_images/edit.html.erb +17 -0
- data/app/views/blog/admin/post_images/index.html.erb +40 -0
- data/app/views/blog/admin/post_images/new.html.erb +19 -0
- data/app/views/blog/admin/post_products/_form.html.erb +8 -0
- data/app/views/blog/admin/post_products/_related_products_table.html.erb +18 -0
- data/app/views/blog/admin/post_products/edit.html.erb +18 -0
- data/app/views/blog/admin/post_products/index.html.erb +50 -0
- data/app/views/blog/admin/post_products/new.html.erb +21 -0
- data/app/views/blog/admin/posts/_form.html.erb +40 -0
- data/app/views/blog/admin/posts/edit.html.erb +12 -0
- data/app/views/blog/admin/posts/index.html.erb +54 -0
- data/app/views/blog/admin/posts/new.html.erb +14 -0
- data/app/views/blog/admin/posts/show.html.erb +17 -0
- data/app/views/blog/admin/shared/_post_tabs.html.erb +26 -0
- data/app/views/blog/post_categories/show.html.erb +12 -0
- data/app/views/blog/posts/archive.html.erb +13 -0
- data/app/views/blog/posts/index.html.erb +16 -0
- data/app/views/blog/posts/index.rss.builder +17 -0
- data/app/views/blog/posts/show.html.erb +37 -0
- data/app/views/blog/shared/_archive.html.erb +31 -0
- data/app/views/blog/shared/_disqus_comments.html.erb +20 -0
- data/app/views/blog/shared/_preview.html.erb +18 -0
- data/app/views/blog/shared/_sidebar.html.erb +26 -0
- data/config/locales/en.yml +77 -0
- data/config/routes.rb +38 -0
- data/lib/generators/spree_essentials/blog_generator.rb +20 -0
- data/lib/generators/templates/db/migrate/acts_as_taggable_on_posts.rb +32 -0
- data/lib/generators/templates/db/migrate/create_post_categories.rb +13 -0
- data/lib/generators/templates/db/migrate/create_post_categories_posts.rb +12 -0
- data/lib/generators/templates/db/migrate/create_post_products.rb +13 -0
- data/lib/generators/templates/db/migrate/create_posts.rb +18 -0
- data/lib/spree_essential_blog/version.rb +3 -0
- data/lib/spree_essential_blog.rb +34 -0
- data/lib/tasks/sample/sailing.jpg +0 -0
- data/lib/tasks/sample/sailing2.jpg +0 -0
- data/lib/tasks/sample/sailing3.jpg +0 -0
- data/lib/tasks/sample.rake +41 -0
- data/public/stylesheets/posts.css +65 -0
- data/spree_essential_blog.gemspec +32 -0
- data/test/dummy_hooks/after_migrate.rb.sample +1 -0
- data/test/dummy_hooks/before_migrate.rb +13 -0
- data/test/dummy_hooks/templates/admin/all.css +3 -0
- data/test/dummy_hooks/templates/admin/all.js +1 -0
- data/test/dummy_hooks/templates/store/all.css +3 -0
- data/test/dummy_hooks/templates/store/all.js +1 -0
- data/test/integration/admin/disqus_integration_test.rb +25 -0
- data/test/integration/admin/post_category_integration_test.rb +131 -0
- data/test/integration/admin/post_integration_test.rb +80 -0
- data/test/integration/post_category_integration_test.rb +43 -0
- data/test/integration/post_integration_test.rb +240 -0
- data/test/support/factories.rb +15 -0
- data/test/test_helper.rb +7 -0
- data/test/unit/post_category_test.rb +24 -0
- data/test/unit/post_test.rb +33 -0
- metadata +236 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Copyright (c) 2011 Spencer Steffen and Citrus Media Group.
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without modification,
|
5
|
+
are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
* Redistributions of source code must retain the above copyright notice,
|
8
|
+
this list of conditions and the following disclaimer.
|
9
|
+
|
10
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
this list of conditions and the following disclaimer in the documentation
|
12
|
+
and/or other materials provided with the distribution.
|
13
|
+
|
14
|
+
* Neither the name of Citrus Media Group nor the names of its
|
15
|
+
contributors may be used to endorse or promote products derived from this
|
16
|
+
software without specific prior written permission.
|
17
|
+
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
19
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
20
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
21
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
22
|
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
23
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
24
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
25
|
+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
26
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
27
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
# Spree Essentials [![Build Status](https://secure.travis-ci.org/citrus/spree_essential_blog.png)](http://travis-ci.org/citrus/spree_essential_blog)
|
2
|
+
|
3
|
+
|
4
|
+
A complete blogging solution for [Spree Commerce](http://spreecommerce.com) with archives, categories, tags, disqus comments and related products.
|
5
|
+
|
6
|
+
This extension relies on [spree_essentials](https://github.com/citrus/spree_essentials) for it's editor, uploads admin and tab in the admin.
|
7
|
+
|
8
|
+
|
9
|
+
Installation
|
10
|
+
------------
|
11
|
+
|
12
|
+
If you don't already have an existing Spree site, [click here](https://gist.github.com/946719) then come back later... You can also read the Spree docs [here](http://spreecommerce.com/documentation/getting_started.html)...
|
13
|
+
|
14
|
+
Otherwise, follow these steps to get up and running with spree_essential_blog:
|
15
|
+
|
16
|
+
First, add spree_essential_blog to your Gemfile... it hasn't been released to Rubygems yet so we'll grab it from git.
|
17
|
+
|
18
|
+
gem 'spree_essential_blog', :git => 'git://github.com/citrus/spree_essential_blog.git'
|
19
|
+
|
20
|
+
Run the generators to create the migration files.
|
21
|
+
|
22
|
+
rails g spree_essentials:install
|
23
|
+
rails g spree_essentials:blog
|
24
|
+
|
25
|
+
Now migrate your database...
|
26
|
+
|
27
|
+
rake db:migrate
|
28
|
+
|
29
|
+
Boot your server and checkout the admin!
|
30
|
+
|
31
|
+
rails s
|
32
|
+
|
33
|
+
|
34
|
+
### Sample Posts
|
35
|
+
|
36
|
+
If you'd like some sample posts, just use the rake command from your project:
|
37
|
+
|
38
|
+
rake db:sample:blog
|
39
|
+
|
40
|
+
|
41
|
+
|
42
|
+
Testing
|
43
|
+
-------
|
44
|
+
|
45
|
+
Clone this repo to where you develop, bundle up, then run `dummier` to get the show started:
|
46
|
+
|
47
|
+
git clone git://github.com/citrus/spree_essential_blog.git
|
48
|
+
cd spree_essential_blog
|
49
|
+
bundle install
|
50
|
+
bundle exec dummier
|
51
|
+
|
52
|
+
This will generate a fresh rails app in test/dummy, install spree & spree_essential_blog, then migrate the test database. Sweet.
|
53
|
+
|
54
|
+
Now run the tests with:
|
55
|
+
|
56
|
+
rake
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
Demo
|
61
|
+
----
|
62
|
+
|
63
|
+
You can easily use the `test/dummy` app as a demo of spree_essential_blog. Just `cd` to where you develop and run:
|
64
|
+
|
65
|
+
git clone git://github.com/citrus/spree_essential_blog.git
|
66
|
+
cd spree_essential_blog
|
67
|
+
mv lib/dummy_hooks/after_migrate.rb.sample lib/dummy_hooks/after_migrate.rb
|
68
|
+
bundle install
|
69
|
+
bundle exec dummier
|
70
|
+
cd test/dummy
|
71
|
+
rails s
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
Change Log
|
77
|
+
----------
|
78
|
+
|
79
|
+
**2011/12/15**
|
80
|
+
|
81
|
+
* Remove spork dev dependency
|
82
|
+
|
83
|
+
**2011/12/14**
|
84
|
+
|
85
|
+
* Add Spree 0.70.x compatibility
|
86
|
+
|
87
|
+
|
88
|
+
**2011/8/9**
|
89
|
+
|
90
|
+
* Pulled [GH2](https://github.com/citrus/spree_essential_blog/pull/2) (Thanks [@dv](https://github.com/dv)!)
|
91
|
+
|
92
|
+
|
93
|
+
**2011/6/7**
|
94
|
+
|
95
|
+
* Added Disqus for comments
|
96
|
+
* Re-namespaced from Admin::Blog to Blog::Admin
|
97
|
+
* Added more tests for categories
|
98
|
+
|
99
|
+
|
100
|
+
**2011/6/6**
|
101
|
+
|
102
|
+
* Pulled [GH1](https://github.com/citrus/spree_essential_blog/pull/1) (Thanks [@detierno](https://github.com/detierno)!)
|
103
|
+
* Switched to [dummier](https://github.com/citrus/dummier) for demo & testing
|
104
|
+
* Improved testing
|
105
|
+
* Improved documentation
|
106
|
+
|
107
|
+
|
108
|
+
**2011/4/13 - 2011/5/27**
|
109
|
+
|
110
|
+
* Initial development
|
111
|
+
|
112
|
+
|
113
|
+
Contributors
|
114
|
+
------------
|
115
|
+
|
116
|
+
* Spencer Steffen ([@citrus](https://github.com/citrus))
|
117
|
+
* Denis Tierno ([@detierno](https://github.com/detierno))
|
118
|
+
* David Verhasselt ([@dv](https://github.com/dv))
|
119
|
+
|
120
|
+
If you'd like to help out feel free to fork and send me pull requests!
|
121
|
+
|
122
|
+
|
123
|
+
License
|
124
|
+
-------
|
125
|
+
|
126
|
+
Copyright (c) 2011 Spencer Steffen & Citrus, released under the New BSD License All rights reserved.
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'rubygems'
|
3
|
+
begin
|
4
|
+
require 'bundler/setup'
|
5
|
+
rescue LoadError
|
6
|
+
puts 'You must run `gem install bundler` and `bundle install` to run rake tasks'
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'rake'
|
10
|
+
require 'rake/testtask'
|
11
|
+
|
12
|
+
Bundler::GemHelper.install_tasks
|
13
|
+
|
14
|
+
Rake::TestTask.new(:test) do |t|
|
15
|
+
t.libs << 'lib'
|
16
|
+
t.libs << 'test'
|
17
|
+
t.pattern = 'test/**/*_test.rb'
|
18
|
+
t.verbose = false
|
19
|
+
end
|
20
|
+
|
21
|
+
task :default => :test
|
data/Versionfile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
class Blog::Admin::DisqusSettingsController < Admin::BaseController
|
2
|
+
|
3
|
+
def show
|
4
|
+
@config = BlogConfiguration.current
|
5
|
+
@preferences = ['disqus_shortname']
|
6
|
+
end
|
7
|
+
|
8
|
+
def edit
|
9
|
+
@config = BlogConfiguration.current
|
10
|
+
@preferences = ['disqus_shortname']
|
11
|
+
end
|
12
|
+
|
13
|
+
def update
|
14
|
+
BlogConfiguration.current.update_attributes(params[:blog_configuration])
|
15
|
+
respond_to do |format|
|
16
|
+
format.html {
|
17
|
+
redirect_to admin_disqus_settings_path
|
18
|
+
}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Blog::Admin::PostCategoriesController < Admin::ResourceController
|
2
|
+
|
3
|
+
before_filter :load_data
|
4
|
+
|
5
|
+
def location_after_save
|
6
|
+
admin_post_categories_url(@post)
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def translated_object_name
|
12
|
+
I18n.t('post_category.model_name')
|
13
|
+
end
|
14
|
+
|
15
|
+
def load_data
|
16
|
+
@post = Post.find_by_path(params[:post_id])
|
17
|
+
@post_categories = PostCategory.all
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class Blog::Admin::PostImagesController < Admin::ResourceController
|
2
|
+
|
3
|
+
before_filter :load_data
|
4
|
+
|
5
|
+
create.before :set_viewable
|
6
|
+
update.before :set_viewable
|
7
|
+
destroy.before :destroy_before
|
8
|
+
|
9
|
+
def update_positions
|
10
|
+
params[:positions].each do |id, index|
|
11
|
+
PostImage.update_all(['position=?', index], ['id=?', id])
|
12
|
+
end
|
13
|
+
|
14
|
+
respond_to do |format|
|
15
|
+
format.js { render :text => 'Ok' }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def location_after_save
|
22
|
+
admin_post_images_url(@post)
|
23
|
+
end
|
24
|
+
|
25
|
+
def load_data
|
26
|
+
@post = Post.find_by_path(params[:post_id])
|
27
|
+
end
|
28
|
+
|
29
|
+
def set_viewable
|
30
|
+
@post_image.viewable = @post
|
31
|
+
end
|
32
|
+
|
33
|
+
def destroy_before
|
34
|
+
@viewable = @post_image.viewable
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class Blog::Admin::PostProductsController < Admin::BaseController
|
2
|
+
|
3
|
+
before_filter :load_data
|
4
|
+
|
5
|
+
def create
|
6
|
+
position = @post.products.count
|
7
|
+
@product = Variant.find(params[:variant_id]).product
|
8
|
+
PostProduct.create(:post_id => @post.id, :product_id => @product.id, :position => position)
|
9
|
+
render :partial => "admin/blog/post_products/related_products_table", :locals => { :post => @post }, :layout => false
|
10
|
+
end
|
11
|
+
|
12
|
+
def destroy
|
13
|
+
@related = PostProduct.find(params[:id])
|
14
|
+
if @related.destroy
|
15
|
+
render_js_for_destroy
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def load_data
|
22
|
+
@post = Post.find_by_path(params[:post_id])
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class Blog::Admin::PostsController < Admin::ResourceController
|
2
|
+
|
3
|
+
private
|
4
|
+
|
5
|
+
update.before :set_category_ids
|
6
|
+
|
7
|
+
def set_category_ids
|
8
|
+
if params[:post] && params[:post][:post_category_ids].is_a?(Array)
|
9
|
+
params[:post][:post_category_ids].reject!{|i| i.to_i == 0 }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def translated_object_name
|
14
|
+
I18n.t('post.model_name')
|
15
|
+
end
|
16
|
+
|
17
|
+
def location_after_save
|
18
|
+
params[:redirect_to] || object_url
|
19
|
+
end
|
20
|
+
|
21
|
+
def find_resource
|
22
|
+
@object ||= Post.find_by_path(params[:id])
|
23
|
+
end
|
24
|
+
|
25
|
+
def collection
|
26
|
+
params[:search] ||= {}
|
27
|
+
params[:search][:meta_sort] ||= "posted_at.desc"
|
28
|
+
@search = Post.metasearch(params[:search])
|
29
|
+
@collection = @search.page(params[:page]).per(Post.per_page)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Blog::PostCategoriesController < Spree::BaseController
|
2
|
+
helper 'blog/posts'
|
3
|
+
|
4
|
+
before_filter :get_sidebar, :only => [:index, :search, :show]
|
5
|
+
|
6
|
+
|
7
|
+
def show
|
8
|
+
@category = PostCategory.find_by_permalink(params[:id])
|
9
|
+
@posts = @category.posts.live
|
10
|
+
@posts = @posts.page(params[:page]).per(Post.per_page)
|
11
|
+
end
|
12
|
+
|
13
|
+
def get_sidebar
|
14
|
+
@archive_posts = Post.live.limit(10)
|
15
|
+
@post_categories = PostCategory.all
|
16
|
+
get_tags
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_tags
|
20
|
+
@tags = Post.live.tag_counts.order('count DESC').limit(25)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
class Blog::PostsController < Spree::BaseController
|
2
|
+
|
3
|
+
helper :products
|
4
|
+
|
5
|
+
before_filter :get_sidebar, :only => [:index, :search, :show]
|
6
|
+
|
7
|
+
def index
|
8
|
+
@posts_by_month = Post.live.limit(50).group_by { |post| post.posted_at.strftime("%B %Y") }
|
9
|
+
scope = Post.live
|
10
|
+
if params[:year].present?
|
11
|
+
year = params[:year].to_i
|
12
|
+
month = 1
|
13
|
+
day = 1
|
14
|
+
if has_month = params[:month].present?
|
15
|
+
if has_day = params[:day].present?
|
16
|
+
day = params[:day].to_i
|
17
|
+
end
|
18
|
+
month = params[:month].to_i
|
19
|
+
end
|
20
|
+
start = Date.new(year, month, day)
|
21
|
+
stop = start + 1.year
|
22
|
+
if has_month
|
23
|
+
stop = start + 1.month
|
24
|
+
if has_day
|
25
|
+
stop = start + 1.day
|
26
|
+
end
|
27
|
+
end
|
28
|
+
scope = scope.where("posted_at >= ? AND posted_at <= ?", start, stop)
|
29
|
+
end
|
30
|
+
@posts = scope.page(params[:page]).per(Post.per_page)
|
31
|
+
end
|
32
|
+
|
33
|
+
def search
|
34
|
+
query = params[:query].gsub(/%46/, '.')
|
35
|
+
@posts = Post.live.tagged_with(query).page(params[:page]).per(Post.per_page)
|
36
|
+
get_tags
|
37
|
+
render :template => 'blog/posts/index'
|
38
|
+
end
|
39
|
+
|
40
|
+
def show
|
41
|
+
@blog_config = BlogConfiguration.current
|
42
|
+
@post = Post.live.includes(:tags, :images, :products).find_by_path(params[:id]) rescue nil
|
43
|
+
return redirect_to archive_posts_path unless @post
|
44
|
+
end
|
45
|
+
|
46
|
+
def archive
|
47
|
+
@posts = Post.live.all
|
48
|
+
end
|
49
|
+
|
50
|
+
def get_sidebar
|
51
|
+
@archive_posts = Post.live.limit(10)
|
52
|
+
@post_categories = PostCategory.all
|
53
|
+
get_tags
|
54
|
+
end
|
55
|
+
|
56
|
+
def get_tags
|
57
|
+
@tags = Post.live.tag_counts.order('count DESC').limit(25)
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Blog::PostsHelper
|
2
|
+
|
3
|
+
def post_seo_path(post)
|
4
|
+
full_post_path(post.year, post.month, post.day, post.to_param)
|
5
|
+
end
|
6
|
+
|
7
|
+
def post_seo_url(post)
|
8
|
+
full_post_url(post.year, post.month, post.day, post.to_param)
|
9
|
+
end
|
10
|
+
|
11
|
+
def post_rss(post)
|
12
|
+
output = []
|
13
|
+
post.images.each do |image|
|
14
|
+
output << image_tag(image.attachment.url, :alt => image.alt)
|
15
|
+
end
|
16
|
+
output << post.rendered_body
|
17
|
+
output.join("\n").html_safe
|
18
|
+
end
|
19
|
+
|
20
|
+
def date_full(date)
|
21
|
+
date.strftime('%A %B %d, %Y').gsub(/\s0/, ' ')
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
data/app/models/post.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
class Post < ActiveRecord::Base
|
2
|
+
|
3
|
+
acts_as_taggable
|
4
|
+
|
5
|
+
# for flash messages
|
6
|
+
alias_attribute :name, :title
|
7
|
+
|
8
|
+
has_and_belongs_to_many :post_categories
|
9
|
+
alias_attribute :categories, :post_categories
|
10
|
+
|
11
|
+
has_many :post_products, :dependent => :destroy
|
12
|
+
has_many :products, :through => :post_products
|
13
|
+
has_many :images, :as => :viewable, :class_name => 'PostImage', :order => :position, :dependent => :destroy
|
14
|
+
|
15
|
+
validates :title, :presence => true
|
16
|
+
validates :path, :presence => true, :uniqueness => true, :if => proc{ |record| !record.title.blank? }
|
17
|
+
validates :body, :presence => true
|
18
|
+
validates :posted_at, :datetime => true
|
19
|
+
|
20
|
+
cattr_reader :per_page
|
21
|
+
@@per_page = 10
|
22
|
+
|
23
|
+
scope :live, where(:live => true ).order('posted_at DESC')
|
24
|
+
|
25
|
+
before_validation :create_path, :if => proc{ |record| record.title_changed? }
|
26
|
+
|
27
|
+
|
28
|
+
# Creates date-part accessors for the posted_at timestamp for grouping purposes.
|
29
|
+
%w(day month year).each do |method|
|
30
|
+
define_method method do
|
31
|
+
self.posted_at.send(method)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def rendered_preview
|
36
|
+
preview = body.split("<!-- more -->")[0]
|
37
|
+
render(preview)
|
38
|
+
end
|
39
|
+
|
40
|
+
def rendered_body
|
41
|
+
render(body.gsub("<!-- more -->", ""))
|
42
|
+
end
|
43
|
+
|
44
|
+
def preview_image
|
45
|
+
images.first if has_images?
|
46
|
+
end
|
47
|
+
|
48
|
+
def has_images?
|
49
|
+
images && !images.empty?
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
def live?
|
54
|
+
live && live == true
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_param
|
58
|
+
path
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def render(val)
|
65
|
+
val = val.is_a?(Symbol) ? send(val) : val
|
66
|
+
RDiscount.new(val).to_html.html_safe
|
67
|
+
end
|
68
|
+
|
69
|
+
def create_path
|
70
|
+
#downcase.gsub(/\s/, '-').gsub(/[^a-z0-9\-\_]/, '').gsub(/[\-]+/, '-')
|
71
|
+
count = 2
|
72
|
+
new_path = title.to_s.parameterize
|
73
|
+
exists = path_exists?(new_path)
|
74
|
+
while exists do
|
75
|
+
dupe_path = "#{new_path}_#{count}"
|
76
|
+
exists = path_exists?(dupe_path)
|
77
|
+
count += 1
|
78
|
+
end
|
79
|
+
self.path = dupe_path || new_path
|
80
|
+
end
|
81
|
+
|
82
|
+
def path_exists?(new_path)
|
83
|
+
post = ::Post.find_by_path(new_path)
|
84
|
+
post != nil && !(post == self)
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class PostCategory < ActiveRecord::Base
|
2
|
+
|
3
|
+
validates :name, :presence => true
|
4
|
+
validates :permalink, :presence => true, :uniqueness => true, :if => proc{ |record| !record.name.blank? }
|
5
|
+
|
6
|
+
has_and_belongs_to_many :posts
|
7
|
+
|
8
|
+
before_validation :create_permalink
|
9
|
+
|
10
|
+
def to_param
|
11
|
+
permalink
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def create_permalink
|
17
|
+
count = 2
|
18
|
+
new_permalink = name.to_s.parameterize
|
19
|
+
exists = permalink_exists?(new_permalink)
|
20
|
+
while exists do
|
21
|
+
dupe_permalink = "#{new_permalink}_#{count}"
|
22
|
+
exists = permalink_exists?(dupe_permalink)
|
23
|
+
count += 1
|
24
|
+
end
|
25
|
+
self.permalink = dupe_permalink || new_permalink
|
26
|
+
end
|
27
|
+
|
28
|
+
def permalink_exists?(new_permalink)
|
29
|
+
post_category = PostCategory.find_by_permalink(new_permalink)
|
30
|
+
post_category != nil && !(post_category == self)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
class PostImage < Image
|
2
|
+
|
3
|
+
validate :no_attachement_errors
|
4
|
+
|
5
|
+
if defined?(SpreeHeroku)
|
6
|
+
has_attached_file :attachment,
|
7
|
+
:styles => Proc.new{ |clip| clip.instance.attachment_sizes },
|
8
|
+
:default_style => :medium,
|
9
|
+
:path => "assets/posts/:id/:style/:basename.:extension",
|
10
|
+
:storage => "s3",
|
11
|
+
:s3_credentials => "#{Rails.root}/config/s3.yml"
|
12
|
+
else
|
13
|
+
has_attached_file :attachment,
|
14
|
+
:styles => Proc.new{ |clip| clip.instance.attachment_sizes },
|
15
|
+
:default_style => :medium,
|
16
|
+
:url => "/assets/posts/:id/:style/:basename.:extension",
|
17
|
+
:path => ":rails_root/public/assets/posts/:id/:style/:basename.:extension"
|
18
|
+
end
|
19
|
+
|
20
|
+
def image_content?
|
21
|
+
attachment_content_type.match(/\/(jpeg|png|gif|tiff|x-photoshop)/)
|
22
|
+
end
|
23
|
+
|
24
|
+
def has_alt?
|
25
|
+
!self.alt.blank?
|
26
|
+
end
|
27
|
+
|
28
|
+
def attachment_sizes
|
29
|
+
if image_content?
|
30
|
+
{ :mini => '48x48>', :small => '150x150>', :medium => '600x600>', :large => '950x700>' }
|
31
|
+
else
|
32
|
+
{}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def no_attachement_errors
|
37
|
+
unless attachment.errors.empty?
|
38
|
+
# uncomment this to get rid of the less-than-useful interrim messages
|
39
|
+
# errors.clear
|
40
|
+
errors.add :attachment, "Paperclip returned errors for file '#{attachment_file_name}' - check ImageMagick installation or image source file."
|
41
|
+
false
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,5 @@
|
|
1
|
+
Deface::Override.new(:virtual_path => "admin/configurations/index",
|
2
|
+
:name => "blog_disqus_admin_configurations_menu",
|
3
|
+
:insert_bottom => "[data-hook='admin_configurations_menu'], #admin_configurations_menu[data-hook]",
|
4
|
+
:partial => "blog/admin/configurations/disqus_config",
|
5
|
+
:disabled => false)
|
@@ -0,0 +1,6 @@
|
|
1
|
+
class DatetimeValidator < ActiveModel::EachValidator
|
2
|
+
def validate_each(record, attribute, value)
|
3
|
+
date = DateTime.parse(value.to_s) rescue ArgumentError
|
4
|
+
record.errors.add(attribute, I18n.t(:invalid_date_time, :scope => [:activerecord, :errors, :messages])) if date == ArgumentError
|
5
|
+
end
|
6
|
+
end
|