spree_essential_blog 0.1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. data/.gitignore +7 -0
  2. data/.travis.yml +12 -0
  3. data/Gemfile +2 -0
  4. data/LICENSE +27 -0
  5. data/README.md +126 -0
  6. data/Rakefile +21 -0
  7. data/Versionfile +5 -0
  8. data/app/controllers/blog/admin/disqus_settings_controller.rb +22 -0
  9. data/app/controllers/blog/admin/post_categories_controller.rb +20 -0
  10. data/app/controllers/blog/admin/post_images_controller.rb +37 -0
  11. data/app/controllers/blog/admin/post_products_controller.rb +25 -0
  12. data/app/controllers/blog/admin/posts_controller.rb +32 -0
  13. data/app/controllers/blog/post_categories_controller.rb +23 -0
  14. data/app/controllers/blog/posts_controller.rb +60 -0
  15. data/app/controllers/pages_controller_decorator.rb +5 -0
  16. data/app/helpers/blog/posts_helper.rb +24 -0
  17. data/app/models/blog_configuration.rb +10 -0
  18. data/app/models/post.rb +87 -0
  19. data/app/models/post_category.rb +33 -0
  20. data/app/models/post_image.rb +45 -0
  21. data/app/models/post_product.rb +9 -0
  22. data/app/overrides/spree_essential_blog.rb +5 -0
  23. data/app/validators/datetime_validator.rb +6 -0
  24. data/app/views/blog/admin/configurations/_disqus_config.html.erb +4 -0
  25. data/app/views/blog/admin/disqus_settings/edit.html.erb +16 -0
  26. data/app/views/blog/admin/disqus_settings/show.html.erb +14 -0
  27. data/app/views/blog/admin/post_categories/_form.html.erb +8 -0
  28. data/app/views/blog/admin/post_categories/edit.html.erb +11 -0
  29. data/app/views/blog/admin/post_categories/index.html.erb +45 -0
  30. data/app/views/blog/admin/post_categories/new.html.erb +10 -0
  31. data/app/views/blog/admin/post_images/_form.html.erb +8 -0
  32. data/app/views/blog/admin/post_images/edit.html.erb +17 -0
  33. data/app/views/blog/admin/post_images/index.html.erb +40 -0
  34. data/app/views/blog/admin/post_images/new.html.erb +19 -0
  35. data/app/views/blog/admin/post_products/_form.html.erb +8 -0
  36. data/app/views/blog/admin/post_products/_related_products_table.html.erb +18 -0
  37. data/app/views/blog/admin/post_products/edit.html.erb +18 -0
  38. data/app/views/blog/admin/post_products/index.html.erb +50 -0
  39. data/app/views/blog/admin/post_products/new.html.erb +21 -0
  40. data/app/views/blog/admin/posts/_form.html.erb +40 -0
  41. data/app/views/blog/admin/posts/edit.html.erb +12 -0
  42. data/app/views/blog/admin/posts/index.html.erb +54 -0
  43. data/app/views/blog/admin/posts/new.html.erb +14 -0
  44. data/app/views/blog/admin/posts/show.html.erb +17 -0
  45. data/app/views/blog/admin/shared/_post_tabs.html.erb +26 -0
  46. data/app/views/blog/post_categories/show.html.erb +12 -0
  47. data/app/views/blog/posts/archive.html.erb +13 -0
  48. data/app/views/blog/posts/index.html.erb +16 -0
  49. data/app/views/blog/posts/index.rss.builder +17 -0
  50. data/app/views/blog/posts/show.html.erb +37 -0
  51. data/app/views/blog/shared/_archive.html.erb +31 -0
  52. data/app/views/blog/shared/_disqus_comments.html.erb +20 -0
  53. data/app/views/blog/shared/_preview.html.erb +18 -0
  54. data/app/views/blog/shared/_sidebar.html.erb +26 -0
  55. data/config/locales/en.yml +77 -0
  56. data/config/routes.rb +38 -0
  57. data/lib/generators/spree_essentials/blog_generator.rb +20 -0
  58. data/lib/generators/templates/db/migrate/acts_as_taggable_on_posts.rb +32 -0
  59. data/lib/generators/templates/db/migrate/create_post_categories.rb +13 -0
  60. data/lib/generators/templates/db/migrate/create_post_categories_posts.rb +12 -0
  61. data/lib/generators/templates/db/migrate/create_post_products.rb +13 -0
  62. data/lib/generators/templates/db/migrate/create_posts.rb +18 -0
  63. data/lib/spree_essential_blog/version.rb +3 -0
  64. data/lib/spree_essential_blog.rb +34 -0
  65. data/lib/tasks/sample/sailing.jpg +0 -0
  66. data/lib/tasks/sample/sailing2.jpg +0 -0
  67. data/lib/tasks/sample/sailing3.jpg +0 -0
  68. data/lib/tasks/sample.rake +41 -0
  69. data/public/stylesheets/posts.css +65 -0
  70. data/spree_essential_blog.gemspec +32 -0
  71. data/test/dummy_hooks/after_migrate.rb.sample +1 -0
  72. data/test/dummy_hooks/before_migrate.rb +13 -0
  73. data/test/dummy_hooks/templates/admin/all.css +3 -0
  74. data/test/dummy_hooks/templates/admin/all.js +1 -0
  75. data/test/dummy_hooks/templates/store/all.css +3 -0
  76. data/test/dummy_hooks/templates/store/all.js +1 -0
  77. data/test/integration/admin/disqus_integration_test.rb +25 -0
  78. data/test/integration/admin/post_category_integration_test.rb +131 -0
  79. data/test/integration/admin/post_integration_test.rb +80 -0
  80. data/test/integration/post_category_integration_test.rb +43 -0
  81. data/test/integration/post_integration_test.rb +240 -0
  82. data/test/support/factories.rb +15 -0
  83. data/test/test_helper.rb +7 -0
  84. data/test/unit/post_category_test.rb +24 -0
  85. data/test/unit/post_test.rb +33 -0
  86. metadata +236 -0
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ *.gem
2
+ .bundle
3
+ .DS_Store
4
+ Gemfile.lock
5
+ pkg/*
6
+ test/dummy_hooks/after_migrate.rb
7
+ test/dummy
data/.travis.yml ADDED
@@ -0,0 +1,12 @@
1
+ rvm:
2
+ - 1.8.7
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - rbx
6
+ - ree
7
+
8
+ before_script:
9
+ - "sh -e /etc/init.d/xvfb start"
10
+ - "bundle exec dummier"
11
+
12
+ script: "DISPLAY=:99.0 bundle exec rake"
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "http://rubygems.org"
2
+ gemspec
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,5 @@
1
+ "0.60.x" => { :version => "0.1.0" }
2
+ "0.50.x" => { :version => "0.1.0" }
3
+ "0.40.x" => {}
4
+ "0.30.x" => {}
5
+ "0.10.x" => {}
@@ -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,5 @@
1
+ if defined?(PagesController)
2
+ PagesController.instance_eval do
3
+ helper 'blog/posts'
4
+ end
5
+ 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
@@ -0,0 +1,10 @@
1
+ class BlogConfiguration < Configuration
2
+
3
+ def self.current
4
+ self.create if self.count == 0
5
+ self.first
6
+ end
7
+
8
+ preference :disqus_shortname, :string, :default => ''
9
+
10
+ end
@@ -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,9 @@
1
+ class PostProduct < ActiveRecord::Base
2
+
3
+ belongs_to :post
4
+ belongs_to :product
5
+
6
+ validates_associated :post
7
+ validates_associated :product
8
+
9
+ 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
@@ -0,0 +1,4 @@
1
+ <tr>
2
+ <td><%= link_to t("blog.settings.disqus"), edit_admin_disqus_settings_path %></td>
3
+ <td><%= t("blog.settings.explain_disqus") %></td>
4
+ </tr>