├── Gemfile
├── lib
├── spree_essential_blog
│ ├── version.rb
│ └── custom_hooks.rb
├── tasks
│ ├── sample
│ │ ├── sailing.jpg
│ │ ├── sailing2.jpg
│ │ └── sailing3.jpg
│ └── sample.rake
├── generators
│ ├── templates
│ │ └── db
│ │ │ └── migrate
│ │ │ ├── create_post_products.rb
│ │ │ ├── create_post_categories.rb
│ │ │ ├── create_post_categories_posts.rb
│ │ │ ├── create_posts.rb
│ │ │ └── acts_as_taggable_on_posts.rb
│ └── spree_essentials
│ │ └── blog_generator.rb
└── spree_essential_blog.rb
├── test
├── dummy_hooks
│ ├── after_migrate.rb.sample
│ └── before_migrate.rb
├── support
│ ├── helper_methods.rb
│ └── factories.rb
├── test_helper.rb
├── unit
│ ├── post_category_test.rb
│ └── post_test.rb
└── integration
│ ├── admin
│ ├── disqus_integration_test.rb
│ ├── post_integration_test.rb
│ └── post_category_integration_test.rb
│ ├── post_category_integration_test.rb
│ └── post_integration_test.rb
├── .gitignore
├── Versionfile
├── app
├── controllers
│ ├── pages_controller_decorator.rb
│ └── blog
│ │ ├── admin
│ │ ├── post_categories_controller.rb
│ │ ├── disqus_settings_controller.rb
│ │ ├── post_products_controller.rb
│ │ ├── post_images_controller.rb
│ │ └── posts_controller.rb
│ │ ├── post_categories_controller.rb
│ │ └── posts_controller.rb
├── models
│ ├── post_product.rb
│ ├── blog_configuration.rb
│ ├── post_category.rb
│ ├── post_image.rb
│ └── post.rb
├── views
│ └── blog
│ │ ├── admin
│ │ ├── configurations
│ │ │ └── _disqus_config.html.erb
│ │ ├── post_products
│ │ │ ├── _form.html.erb
│ │ │ ├── _related_products_table.html.erb
│ │ │ ├── new.html.erb
│ │ │ ├── edit.html.erb
│ │ │ └── index.html.erb
│ │ ├── post_images
│ │ │ ├── _form.html.erb
│ │ │ ├── new.html.erb
│ │ │ ├── edit.html.erb
│ │ │ └── index.html.erb
│ │ ├── post_categories
│ │ │ ├── _form.html.erb
│ │ │ ├── new.html.erb
│ │ │ ├── edit.html.erb
│ │ │ └── index.html.erb
│ │ ├── posts
│ │ │ ├── edit.html.erb
│ │ │ ├── new.html.erb
│ │ │ ├── show.html.erb
│ │ │ ├── _form.html.erb
│ │ │ └── index.html.erb
│ │ ├── disqus_settings
│ │ │ ├── show.html.erb
│ │ │ └── edit.html.erb
│ │ └── shared
│ │ │ └── _post_tabs.html.erb
│ │ ├── post_categories
│ │ └── show.html.erb
│ │ ├── posts
│ │ ├── archive.html.erb
│ │ ├── index.rss.builder
│ │ ├── index.html.erb
│ │ └── show.html.erb
│ │ └── shared
│ │ ├── _preview.html.erb
│ │ ├── _sidebar.html.erb
│ │ ├── _disqus_comments.html.erb
│ │ └── _archive.html.erb
├── validators
│ └── datetime_validator.rb
└── helpers
│ └── blog
│ └── posts_helper.rb
├── Rakefile
├── config
├── routes.rb
└── locales
│ └── en.yml
├── public
└── stylesheets
│ └── posts.css
├── spree_essential_blog.gemspec
├── LICENSE
└── README.md
/Gemfile:
--------------------------------------------------------------------------------
1 | source "http://rubygems.org"
2 | gemspec
3 |
--------------------------------------------------------------------------------
/lib/spree_essential_blog/version.rb:
--------------------------------------------------------------------------------
1 | module SpreeEssentialBlog
2 | VERSION = "0.1.0.rc1"
3 | end
4 |
--------------------------------------------------------------------------------
/test/dummy_hooks/after_migrate.rb.sample:
--------------------------------------------------------------------------------
1 | rake "db:migrate db:seed db:sample:blog", :env => "development"
2 |
--------------------------------------------------------------------------------
/lib/tasks/sample/sailing.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DV/spree_essential_blog/master/lib/tasks/sample/sailing.jpg
--------------------------------------------------------------------------------
/lib/tasks/sample/sailing2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DV/spree_essential_blog/master/lib/tasks/sample/sailing2.jpg
--------------------------------------------------------------------------------
/lib/tasks/sample/sailing3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DV/spree_essential_blog/master/lib/tasks/sample/sailing3.jpg
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.gem
2 | .bundle
3 | .DS_Store
4 | Gemfile.lock
5 | pkg/*
6 | test/dummy_hooks/after_migrate.rb
7 | test/dummy
8 |
--------------------------------------------------------------------------------
/Versionfile:
--------------------------------------------------------------------------------
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" => {}
6 |
--------------------------------------------------------------------------------
/test/dummy_hooks/before_migrate.rb:
--------------------------------------------------------------------------------
1 | rake "spree_core:install"
2 | run "rails g spree_essentials:install"
3 | run "rails g spree_essentials:blog"
4 |
--------------------------------------------------------------------------------
/app/controllers/pages_controller_decorator.rb:
--------------------------------------------------------------------------------
1 | if defined?(PagesController)
2 | PagesController.instance_eval do
3 | helper 'blog/posts'
4 | end
5 | end
--------------------------------------------------------------------------------
/app/models/post_product.rb:
--------------------------------------------------------------------------------
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
--------------------------------------------------------------------------------
/app/views/blog/admin/configurations/_disqus_config.html.erb:
--------------------------------------------------------------------------------
1 |
2 | | <%= link_to t("blog.settings.disqus"), edit_admin_disqus_settings_path %> |
3 | <%= t("blog.settings.explain_disqus") %> |
4 |
--------------------------------------------------------------------------------
/app/views/blog/admin/post_products/_form.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= form.label :attachment %>:
3 | <%= form.file_field :attachment %>
4 |
5 |
6 | <%= form.label :alt %>:
7 | <%= form.text_field :alt %>
8 |
9 |
--------------------------------------------------------------------------------
/app/views/blog/admin/post_images/_form.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= form.label :attachment %>:
3 | <%= form.file_field :attachment %>
4 |
5 |
6 | <%= form.label :alt %>:
7 | <%= form.text_field :alt, :class => 'text' %>
8 |
9 |
--------------------------------------------------------------------------------
/app/models/blog_configuration.rb:
--------------------------------------------------------------------------------
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
11 |
--------------------------------------------------------------------------------
/app/views/blog/admin/post_categories/_form.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= form.label :name %>:
3 | <%= form.text_field :name, :class => 'text' %>
4 |
5 |
6 | <%= form.label :permalink %>:
7 | <%= form.text_field :permalink, :class => 'text' %>
8 |
9 |
--------------------------------------------------------------------------------
/lib/spree_essential_blog/custom_hooks.rb:
--------------------------------------------------------------------------------
1 | module SpreeEssentialBlog
2 |
3 | class CustomHooks < Spree::ThemeSupport::HookListener
4 |
5 | insert_after :admin_configurations_menu, 'blog/admin/configurations/disqus_config'
6 |
7 | end
8 |
9 | end
10 |
--------------------------------------------------------------------------------
/test/support/helper_methods.rb:
--------------------------------------------------------------------------------
1 | module HelperMethods
2 |
3 | def random_email
4 | random_token.split("").insert(random_token.length / 2, "@").push(".com").join("")
5 | end
6 |
7 | def random_token
8 | Digest::SHA1.hexdigest((Time.now.to_i * rand).to_s)
9 | end
10 |
11 | end
12 |
--------------------------------------------------------------------------------
/app/validators/datetime_validator.rb:
--------------------------------------------------------------------------------
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
--------------------------------------------------------------------------------
/app/views/blog/post_categories/show.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= render :partial => 'blog/shared/preview', :collection => @posts, :as => :post %>
3 |
4 |
5 |
6 |
7 | <%= will_paginate @posts %>
8 |
9 |
10 |
11 | <% content_for :sidebar do %>
12 | <%= render :partial => 'blog/shared/sidebar' %>
13 | <% end %>
14 |
--------------------------------------------------------------------------------
/lib/generators/templates/db/migrate/create_post_products.rb:
--------------------------------------------------------------------------------
1 | class CreatePostProducts < ActiveRecord::Migration
2 | def self.up
3 | create_table :post_products do |t|
4 | t.references :post
5 | t.references :product
6 | t.integer :position
7 | end
8 | end
9 |
10 | def self.down
11 | drop_table :post_products
12 | end
13 | end
--------------------------------------------------------------------------------
/lib/generators/templates/db/migrate/create_post_categories.rb:
--------------------------------------------------------------------------------
1 | class CreatePostCategories < ActiveRecord::Migration
2 | def self.up
3 | create_table :post_categories, :force => true do |t|
4 | t.string :name
5 | t.string :permalink
6 | t.timestamps
7 | end
8 | end
9 |
10 | def self.down
11 | drop_table :post_categories
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/lib/generators/templates/db/migrate/create_post_categories_posts.rb:
--------------------------------------------------------------------------------
1 | class CreatePostCategoriesPosts < ActiveRecord::Migration
2 | def self.up
3 | create_table :post_categories_posts, :id => false, :force => true do |t|
4 | t.integer :post_id
5 | t.integer :post_category_id
6 | end
7 | end
8 |
9 | def self.down
10 | drop_table :post_categories_posts
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/app/views/blog/posts/archive.html.erb:
--------------------------------------------------------------------------------
1 | Archive
2 |
3 |
4 |
5 | <%= render 'blog/shared/archive', :posts => @posts %>
6 |
7 |
<%= t('.where_to_next') %>
8 |
9 | <%= link_to t('.go_to_store'), products_path %> <%= t('or') %>
10 | <%= link_to t('.back_to_posts'), posts_path %>
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/views/blog/admin/post_categories/new.html.erb:
--------------------------------------------------------------------------------
1 | <%= render :partial => 'blog/admin/shared/post_tabs', :locals => {:current => "Categories"} unless request.xhr? %>
2 |
3 | <%= t("new_category") %>
4 |
5 | <%= form_for(@post_category, :url => admin_post_categories_path(@post)) do |form| %>
6 | <%= render "form", :form => form %>
7 |
8 | <%= button t("create") %>
9 |
10 | <% end %>
11 |
--------------------------------------------------------------------------------
/test/test_helper.rb:
--------------------------------------------------------------------------------
1 | # Configure Rails Envinronment
2 | ENV["RAILS_ENV"] = "test"
3 |
4 | require 'rubygems'
5 | require 'spork'
6 |
7 | Spork.prefork do
8 | require File.expand_path("../dummy/config/environment.rb", __FILE__)
9 | require 'spree_essentials/test_helper'
10 | end
11 |
12 | Spork.each_run do
13 | Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
14 | include HelperMethods
15 | end
16 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
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
--------------------------------------------------------------------------------
/app/views/blog/admin/posts/edit.html.erb:
--------------------------------------------------------------------------------
1 | <%= render :partial => 'blog/admin/shared/post_tabs', :locals => {:current => "Post Details"} %>
2 |
3 | <% if @post.try(:errors).present? %>
4 | <%= render 'shared/error_messages', :target => @post %>
5 | <% end %>
6 |
7 | <%= form_for([:admin, @post]) do |f| %>
8 | <%= render "form", :form => f %>
9 |
10 | <%= button t("update") %> <%= t('or') %> <%= link_to t('cancel'), collection_url %>
11 |
12 | <% end %>
13 |
--------------------------------------------------------------------------------
/app/views/blog/admin/posts/new.html.erb:
--------------------------------------------------------------------------------
1 | <%= render :partial => 'admin/shared/contents_sub_menu' %>
2 |
3 | <%= t('.new_post') %>
4 |
5 | <% if @post.try(:errors).present? %>
6 | <%= render 'shared/error_messages', :target => @post %>
7 | <% end %>
8 |
9 | <%= form_for([:admin, @post]) do |f| %>
10 | <%= render "form", :form => f %>
11 |
12 | <%= button t("create") %> <%= t('or') %> <%= link_to t('cancel'), collection_url %>
13 |
14 | <% end %>
15 |
--------------------------------------------------------------------------------
/app/views/blog/admin/post_categories/edit.html.erb:
--------------------------------------------------------------------------------
1 | <%= render :partial => 'blog/admin/shared/post_tabs', :locals => {:current => "Categories"} %>
2 |
3 | <%= t(".edit_category") %>
4 |
5 | <%= form_for([:admin, @post, @post_category], :url => admin_post_category_path(@post, @post_category.id)) do |form| %>
6 | <%= render "form", :form => form %>
7 |
8 | <%= hidden_field_tag :post_id, @post.path, :class => 'hidden' %>
9 | <%= button t("update") %>
10 |
11 | <% end %>
12 |
--------------------------------------------------------------------------------
/app/views/blog/admin/disqus_settings/show.html.erb:
--------------------------------------------------------------------------------
1 | <%= render :partial => 'admin/shared/configuration_menu' %>
2 |
3 | <%= t("blog.settings.disqus") %>
4 |
5 |
6 | <% @preferences.each do |key| %>
7 | -
8 | <%= t("blog.settings.#{key}") %>:
9 |
<%= @config.preferences[key].inspect %>
10 |
11 | <% end %>
12 |
13 |
14 | <%= link_to_with_icon 'edit', t("edit"), edit_admin_disqus_settings_path, :id => 'admin_disqus_settings_link' %>
15 |
--------------------------------------------------------------------------------
/app/views/blog/posts/index.rss.builder:
--------------------------------------------------------------------------------
1 | xml.instruct! :xml, :version => "1.0"
2 | xml.rss :version => "2.0" do
3 | xml.channel do
4 | xml.title "#{Spree::Config[:site_name]} - Blog"
5 | xml.description "#{Spree::Config[:site_url]} - Blog"
6 | xml.link posts_url
7 |
8 | for post in @posts
9 | xml.item do
10 | xml.title post.title
11 | xml.description post_rss(post)
12 | xml.pubDate post.posted_at.to_s(:rfc822)
13 | xml.link post_seo_path(post)
14 | end
15 | end
16 | end
17 | end
--------------------------------------------------------------------------------
/app/controllers/blog/admin/post_categories_controller.rb:
--------------------------------------------------------------------------------
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
21 |
--------------------------------------------------------------------------------
/lib/generators/templates/db/migrate/create_posts.rb:
--------------------------------------------------------------------------------
1 | class CreatePosts < ActiveRecord::Migration
2 | def self.up
3 | create_table :posts do |t|
4 | t.string :title, :required => true
5 | t.string :path, :required => true
6 | t.string :teaser
7 | t.datetime :posted_at
8 | t.text :body
9 | t.string :author
10 | t.boolean :live, :default => true
11 | t.timestamps
12 | end
13 | end
14 |
15 | def self.down
16 | drop_table :posts
17 | end
18 | end
--------------------------------------------------------------------------------
/app/views/blog/posts/index.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <% if @posts.blank? %>
3 |
<%= t('blog.no_posts') %>
4 | <% else %>
5 | <%= render :partial => 'blog/shared/preview', :collection => @posts, :as => :post %>
6 | <% end %>
7 |
8 |
9 | <%= link_to(image_tag('blog/rss.png', :alt => t('.rss')) + t('.rss'), posts_path(:format => 'rss'), :id => "posts-rss") %>
10 | <%= will_paginate @posts %>
11 |
12 |
13 |
14 | <% content_for :sidebar do %>
15 | <%= render :partial => 'blog/shared/sidebar' %>
16 | <% end %>
17 |
--------------------------------------------------------------------------------
/app/views/blog/admin/posts/show.html.erb:
--------------------------------------------------------------------------------
1 | <%= render :partial => 'blog/admin/shared/post_tabs', :locals => {:current => "Post Details"} %>
2 |
3 | <%= h @post.posted_at.strftime('%A %B %d, %Y at %I:%M%p').gsub(/\s0/, ' ') %>
4 |
5 |
6 | <% for image in @post.images do %>
7 | <%= image_tag image.attachment.url(:small), :alt => @post.title %>
8 | <% end %>
9 |
10 | <%= RDiscount.new(@post.body).to_html.html_safe %>
11 |
12 |
13 |
14 |
15 | <%= link_to_edit @user %>
16 | <%= link_to t('back'), collection_url %>
17 |
18 |
--------------------------------------------------------------------------------
/app/views/blog/admin/post_products/_related_products_table.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | | <%= t('name') %> |
5 | |
6 |
7 |
8 |
9 | <% post.post_products.each do |post_product| %>
10 |
11 | | <%= post_product.product.name %> |
12 |
13 | <%= link_to_delete post_product, {:url => admin_post_product_url(post, post_product)} %>
14 | |
15 |
16 | <% end %>
17 |
18 |
--------------------------------------------------------------------------------
/app/views/blog/shared/_preview.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
<%= link_to h(post.title), post_seo_path(post) %>
5 | <%= date_full(post.posted_at) %>
6 |
7 |
8 | <% if post.has_images? %>
9 |
<%= link_to image_tag(post.images.first.attachment.url, :alt => post.title), post_seo_path(post) %>
10 | <% end %>
11 |
12 | <%= post.rendered_preview %>
13 |
14 |
15 |
16 |
<%= link_to t('.read_more'), post_seo_path(post) %>
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/views/blog/admin/disqus_settings/edit.html.erb:
--------------------------------------------------------------------------------
1 | <%= render :partial => 'admin/shared/configuration_menu' %>
2 |
3 | <%= t('blog.settings.disqus') %>
4 |
5 | <%= form_for(@config, :url => admin_disqus_settings_path) do |form| %>
6 |
7 |
8 | <%= form.label :preferred_disqus_shortname, t('blog.settings.disqus_shortname') %>:
9 | <%= form.text_field :preferred_disqus_shortname, :class => 'text' %>
10 |
11 |
12 |
13 | <%= button t('update') %>
14 | <%= t("or") %> <%= link_to t("cancel"), admin_disqus_settings_url %>
15 |
16 | <% end %>
17 |
--------------------------------------------------------------------------------
/app/views/blog/admin/post_images/new.html.erb:
--------------------------------------------------------------------------------
1 | <%= t("new_image") %>
2 |
3 | <%= form_for(@post_image, :url => admin_post_images_path(@post), :html => { :multipart => true }) do |form| %>
4 | <%= render "form", :form => form %>
5 |
6 | <%= button t("create") %>
7 | or <%= link_to t("cancel"), "#", :id => "cancel_link" %>
8 |
9 | <% end %>
10 |
11 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/controllers/blog/admin/disqus_settings_controller.rb:
--------------------------------------------------------------------------------
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
23 |
--------------------------------------------------------------------------------
/app/helpers/blog/posts_helper.rb:
--------------------------------------------------------------------------------
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
--------------------------------------------------------------------------------
/app/views/blog/admin/post_products/new.html.erb:
--------------------------------------------------------------------------------
1 | <%= t("new_product") %>
2 |
3 | <% form_for(@post_product, :url => admin_post_products_path(@post), :html => { :multipart => true }) do |form| %>
4 |
5 | <%= render "form", :form => form %>
6 |
7 |
8 | <%= button t("create") %>
9 | or <%= link_to t("cancel"), "#", :id => "cancel_link" %>
10 |
11 | <% end %>
12 |
13 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/controllers/blog/post_categories_controller.rb:
--------------------------------------------------------------------------------
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.paginate(:page => params[:page], :per_page => 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
--------------------------------------------------------------------------------
/test/unit/post_category_test.rb:
--------------------------------------------------------------------------------
1 | require_relative '../test_helper'
2 |
3 | class PostCategoryTest < Test::Unit::TestCase
4 |
5 | def setup
6 | PostCategory.destroy_all
7 | end
8 |
9 | subject { PostCategory.new }
10 |
11 | should validate_presence_of(:name)
12 |
13 | should "automatically set path" do
14 | @category = Factory.create(:post_category, :name => "This should parameterize")
15 | assert_equal "this-should-parameterize", @category.permalink
16 | end
17 |
18 | should "not duplicate path" do
19 | @category1 = Factory.create(:post_category)
20 | @category2 = Factory.create(:post_category)
21 | assert @category1.permalink != @category2.permalink
22 | end
23 |
24 | end
25 |
--------------------------------------------------------------------------------
/app/views/blog/admin/post_images/edit.html.erb:
--------------------------------------------------------------------------------
1 | <%= render :partial => 'blog/admin/shared/post_tabs', :locals => {:current => "Images"} %>
2 |
3 | <% if @post_image.try(:errors).present? %>
4 | <%= render 'shared/error_messages', :target => @post_image %>
5 | <% end %>
6 |
7 | <%= form_for([:admin, @post.id, @post_image], :url => admin_post_image_url(@post, @post_image), :html => { :multipart => true }) do |f| %>
8 |
9 | <%= label_tag ("thumbnail") %>:
10 | <%= link_to(image_tag(@post_image.attachment.url(:mini)), @post_image.attachment.url(:product)) %>
11 |
12 | <%= render "form", :form => f %>
13 |
14 | <%= button t("update") %>
15 | or <%= link_to t("cancel"), admin_post_images_url(@post), :id => "cancel_link" %>
16 |
17 | <% end %>
--------------------------------------------------------------------------------
/app/controllers/blog/admin/post_products_controller.rb:
--------------------------------------------------------------------------------
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
--------------------------------------------------------------------------------
/app/views/blog/admin/post_products/edit.html.erb:
--------------------------------------------------------------------------------
1 | <%= render :partial => 'blog/admin/shared/post_tabs', :locals => {:current => "Images"} %>
2 |
3 | <% if @post_product.try(:errors).present? %>
4 | <%= render 'shared/error_messages', :target => @post_product %>
5 | <% end %>
6 |
7 | <% form_for([:admin, @post.id, @post_product], :url => admin_post_product_url(@post, @post_product), :html => { :multipart => true }) do |f| %>
8 |
9 | <%= label_tag ("thumbnail") %>:
10 | <%= link_to(product_tag(@post_product.attachment.url(:mini)), @post_product.attachment.url(:product)) %>
11 |
12 | <%= render "form", :form => f %>
13 |
14 |
15 | <%= button t("update") %>
16 | or <%= link_to t("cancel"), admin_post_products_url(@post), :id => "cancel_link" %>
17 |
18 | <% end %>
--------------------------------------------------------------------------------
/test/integration/admin/disqus_integration_test.rb:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env ruby
2 | # encoding: UTF-8
3 |
4 | require_relative '../../test_helper'
5 |
6 | class Admin::DisqusIntegrationTest < ActiveSupport::IntegrationCase
7 |
8 | should "have a link to disqus config" do
9 | visit admin_configurations_path
10 | within "#content table.index" do
11 | assert has_link?("Disqus Settings", :href => edit_admin_disqus_settings_path)
12 | end
13 | end
14 |
15 | should "edit disqus config" do
16 | visit edit_admin_disqus_settings_path
17 | within "form.edit_blog_configuration" do
18 | fill_in "Disqus Shortname", :with => "its-just-a-test"
19 | click_button "Update"
20 | end
21 | assert_equal current_path, admin_disqus_settings_path
22 | assert_seen "its-just-a-test", :within => "#content .member-list h3"
23 | end
24 |
25 | end
26 |
--------------------------------------------------------------------------------
/app/views/blog/shared/_sidebar.html.erb:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/controllers/blog/admin/post_images_controller.rb:
--------------------------------------------------------------------------------
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
--------------------------------------------------------------------------------
/lib/generators/templates/db/migrate/acts_as_taggable_on_posts.rb:
--------------------------------------------------------------------------------
1 | class ActsAsTaggableOnPosts < ActiveRecord::Migration
2 | def self.up
3 | return if table_exists? :tags
4 |
5 | create_table :tags do |t|
6 | t.string :name
7 | end
8 |
9 | create_table :taggings do |t|
10 | t.references :tag
11 |
12 | # You should make sure that the column created is
13 | # long enough to store the required class names.
14 | t.references :taggable, :polymorphic => true
15 | t.references :tagger, :polymorphic => true
16 |
17 | t.string :context
18 |
19 | t.datetime :created_at
20 | end
21 |
22 | add_index :taggings, :tag_id
23 | add_index :taggings, [:taggable_id, :taggable_type, :context]
24 | end
25 |
26 | def self.down
27 | return unless table_exists? :tags
28 | drop_table :tags
29 | drop_table :taggings
30 | end
31 |
32 | end
33 |
--------------------------------------------------------------------------------
/test/unit/post_test.rb:
--------------------------------------------------------------------------------
1 | require_relative '../test_helper'
2 |
3 | class PostTest < Test::Unit::TestCase
4 |
5 | def setup
6 | Post.destroy_all
7 | end
8 |
9 | subject { Post.new }
10 |
11 | should validate_presence_of(:title)
12 | should validate_presence_of(:body)
13 |
14 | should "automatically set path" do
15 | @post = Factory.create(:post, :title => "This should parameterize")
16 | assert_equal "this-should-parameterize", @post.path
17 | end
18 |
19 | should "validate date time" do
20 | @post = Factory.build(:post)
21 | @post.posted_at = "testing"
22 | assert !@post.valid?
23 | end
24 |
25 | should "parse date time" do
26 | date = DateTime.parse("2011/4/1 16:15")
27 | @post = Factory.build(:post)
28 | @post.posted_at = "april 1 2011 - 4:15 pm"
29 | assert @post.valid?
30 | assert_equal date, @post.posted_at
31 | end
32 |
33 | end
34 |
--------------------------------------------------------------------------------
/lib/generators/spree_essentials/blog_generator.rb:
--------------------------------------------------------------------------------
1 | require 'generators/essentials_base'
2 |
3 | module SpreeEssentials
4 | module Generators
5 | class BlogGenerator < SpreeEssentials::Generators::EssentialsBase
6 |
7 | desc "Installs required migrations for spree_essentials_blog"
8 | source_root File.expand_path("../../templates/db/migrate", __FILE__)
9 |
10 | def copy_migrations
11 | migration_template "create_posts.rb", "db/migrate/create_posts.rb"
12 | migration_template "create_post_products.rb", "db/migrate/create_post_products.rb"
13 | migration_template "acts_as_taggable_on_posts.rb", "db/migrate/acts_as_taggable_on_posts.rb"
14 | migration_template "create_post_categories.rb", "db/migrate/create_post_categories.rb"
15 | migration_template "create_post_categories_posts.rb", "db/migrate/create_post_categories_posts.rb"
16 | end
17 |
18 | end
19 | end
20 | end
--------------------------------------------------------------------------------
/app/controllers/blog/admin/posts_controller.rb:
--------------------------------------------------------------------------------
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.paginate(:per_page => Spree::Config[:orders_per_page], :page => params[:page])
30 | end
31 |
32 | end
33 |
--------------------------------------------------------------------------------
/app/models/post_category.rb:
--------------------------------------------------------------------------------
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
--------------------------------------------------------------------------------
/lib/spree_essential_blog.rb:
--------------------------------------------------------------------------------
1 | require 'spree_essentials'
2 | require 'acts-as-taggable-on'
3 | require 'spree_essential_blog/custom_hooks'
4 |
5 | module SpreeEssentialBlog
6 |
7 | def self.tab
8 | [:posts, :post_images, :post_products ]
9 | end
10 |
11 | def self.sub_tab
12 | [:posts, { :label => 'admin.subnav.posts', :match_path => '/posts' }]
13 | end
14 |
15 | class Engine < Rails::Engine
16 |
17 | config.autoload_paths += %W(#{config.root}/lib)
18 |
19 | initializer "static assets" do |app|
20 | app.middleware.insert_before ::Rack::Lock, ::ActionDispatch::Static, "#{config.root}/public"
21 | end
22 |
23 | def self.activate
24 | Dir.glob(File.join(File.dirname(__FILE__), "../app/**/*_decorator.rb")) do |c|
25 | Rails.env.production? ? require(c) : load(c)
26 | end
27 | end
28 |
29 | config.to_prepare &method(:activate).to_proc
30 |
31 | end
32 |
33 | end
34 |
35 | SpreeEssentials.register :blog, SpreeEssentialBlog
36 |
--------------------------------------------------------------------------------
/test/support/factories.rb:
--------------------------------------------------------------------------------
1 | FactoryGirl.define do
2 |
3 | factory :post do
4 | title "Peanut Butter Jelly Time"
5 | posted_at { Time.now + rand(10000) }
6 | body "Vivamus rutrum nunc non neque consectetur quis placerat neque lobortis. Nam vestibulum, arcu sodales feugiat consectetur, nisl orci bibendum elit, eu euismod magna sapien ut nibh. Donec semper quam scelerisque tortor dictum gravida. In hac habitasse platea dictumst. Nam pulvinar, odio sed rhoncus suscipit, sem diam ultrices mauris, eu consequat purus metus eu velit. Proin metus odio, aliquam eget molestie nec, gravida ut sapien. Phasellus quis est sed turpis sollicitudin venenatis sed eu odio. Praesent eget neque eu eros interdum malesuada non vel leo. Sed fringilla porta ligula egestas tincidunt. Nullam risus magna, ornare vitae varius eget, scelerisque a libero."
7 | tag_list "peanut butter, jelly, sandwich, lunch"
8 | live true
9 | end
10 |
11 | factory :post_category do
12 | name "Jellies"
13 | end
14 |
15 | end
16 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | Rails.application.routes.draw do
2 |
3 | scope(:module => "Blog") do
4 |
5 | constraints(
6 | :year => /\d{4}/,
7 | :month => /\d{1,2}/,
8 | :day => /\d{1,2}/
9 | ) do
10 | get '/blog/:year(/:month(/:day))' => 'posts#index', :as => :post_date
11 | get '/blog/:year/:month/:day/:id' => 'posts#show', :as => :full_post
12 | get '/blog/category/:id' => 'post_categories#show', :as => :post_category
13 | end
14 |
15 | get '/blog/search/:query', :to => 'posts#search', :as => :search_posts, :query => /.*/
16 |
17 | resources :posts, :path => 'blog' do
18 | get :archive, :on => :collection
19 | end
20 |
21 | namespace :admin do
22 |
23 | resources :posts do
24 | resources :images, :controller => "post_images" do
25 | collection do
26 | post :update_positions
27 | end
28 | end
29 | resources :products, :controller => "post_products"
30 | resources :categories, :controller => "post_categories"
31 | end
32 |
33 | resource :disqus_settings
34 |
35 | end
36 |
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/app/views/blog/shared/_disqus_comments.html.erb:
--------------------------------------------------------------------------------
1 | <% disqus_short_name = @blog_config.preferred_disqus_shortname %>
2 | <% if !disqus_short_name.blank? %>
3 |
20 | <% end %>
21 |
--------------------------------------------------------------------------------
/app/views/blog/admin/shared/_post_tabs.html.erb:
--------------------------------------------------------------------------------
1 | <%= render :partial => 'admin/shared/contents_sub_menu' %>
2 |
3 | <%== t(".editing_post") + " “#{@post.title}”".html_safe %>
4 |
5 | <% content_for :sidebar do %>
6 |
7 | <%= @post.title %><%= (@post.posted_at || Time.now).strftime('%x') %>
8 |
9 |
10 |
26 |
27 |
28 | <% end %>
29 |
--------------------------------------------------------------------------------
/app/views/blog/posts/show.html.erb:
--------------------------------------------------------------------------------
1 | <% content_for :sidebar do %>
2 | <%= render :partial => 'blog/shared/sidebar' %>
3 | <% end %>
4 |
5 |
6 |
7 |
8 |
<%= h @post.posted_at.strftime('%A %B %d, %Y').gsub(/\s0/, ' ') %>
9 | <%=h @post.title %>
10 |
11 |
12 | <% if @post.has_images? %>
13 |
14 | <% @post.images.each_with_index do |image, index| %>
15 |
<%= link_to image_tag(image.attachment.url(:medium), :alt => image.has_alt? ? image.alt : "#{@post.title} - Photo #{index + 1}"), image.attachment.url(:large), :id => "photo_#{index}" %>
16 | <% end %>
17 |
18 | <% end %>
19 |
20 | <%= @post.rendered_body %>
21 |
22 |
23 |
24 |
25 |
Tagged:
26 |
<%= @post.tags.collect{|tag| link_to(tag.name, search_posts_path(tag.name)) }.join(", ").html_safe %>
27 |
28 |
29 | <% unless @post.products.empty? %>
30 |
31 | <%= render 'shared/products', :products => @post.products %>
32 |
33 | <% end %>
34 |
35 | <%= render "blog/shared/disqus_comments" if @blog_config %>
36 |
37 |
38 |
--------------------------------------------------------------------------------
/app/views/blog/shared/_archive.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <% posts.group_by(&:year).each do |year, posts_by_year| %>
3 |
<%= link_to year, post_date_path(year) %>
4 | <% index = 0 %>
5 | <% posts_by_year.group_by(&:month).each do |month, posts_by_month| %>
6 |
7 |
<%= link_to "#{Date::MONTHNAMES[month]} #{year}", post_date_path(year, month) %>
8 |
9 | <% posts_by_month.group_by(&:day).each do |day, posts_by_day| %>
10 | -
11 |
12 | <%= link_to day, post_date_path(year, month, day) %>
13 |
14 |
15 | <% posts_by_day.each_with_index do |post, index| %>
16 | - <%= link_to h(post.title), post_seo_path(post) %>
17 | <% end %>
18 |
19 |
20 |
21 | <% end %>
22 |
23 |
24 | <% end %>
25 |
26 | <% end %>
27 |
28 |
29 | <% content_for :head do %>
30 | <%= stylesheet_link_tag 'posts' %>
31 | <% end %>
--------------------------------------------------------------------------------
/lib/tasks/sample.rake:
--------------------------------------------------------------------------------
1 | namespace :db do
2 | namespace :sample do
3 | desc "creates sample blog posts"
4 | task :blog do
5 |
6 | require 'faker'
7 | require Rails.root.join('config/environment.rb')
8 |
9 | image_dir = File.expand_path("../sample", __FILE__)
10 | images = Dir[image_dir + "/*.jpg"]
11 |
12 | product_ids = Product.select('id').all.collect(&:id) rescue []
13 |
14 | 25.times { |i|
15 |
16 | post = Post.create(
17 | :title => Faker::Lorem.sentence,
18 | :posted_at => Time.now - i * rand(10000000),
19 | :body => Faker::Lorem.paragraph,
20 | :tag_list => Faker::Lorem.words(rand(10)).join(", ")
21 | )
22 |
23 | rand(5).times { |i|
24 | image = post.images.create(:attachment => File.open(images.sort_by{rand}.first), :alt => Faker::Lorem.sentence)
25 | }
26 |
27 | unless product_ids.empty?
28 | rand(5).times { |i|
29 | post.post_products.create(:product => Product.find(product_ids.sort_by{rand}.first), :position => i)
30 | }
31 | end
32 |
33 | print "*"
34 |
35 | }
36 |
37 | puts "\ndone."
38 |
39 | end
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/test/integration/post_category_integration_test.rb:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env ruby
2 | # encoding: UTF-8
3 |
4 | require_relative '../test_helper'
5 |
6 | class Blog::PostCategoryIntegrationTest < ActiveSupport::IntegrationCase
7 |
8 | include Blog::PostsHelper
9 |
10 | def setup
11 | Post.destroy_all
12 | PostCategory.destroy_all
13 | @categories = %w(Jellies Peanuts Butters).map{ |i| Factory.create(:post_category, :name => i) }
14 | 3.times{|i|
15 | post = Factory.create(:post, :title => "Capy post #{i}", :posted_at => Time.now - i.days)
16 | post.categories = [@categories.slice(i)]
17 | post.save
18 | }
19 | @posts = Post.order(:id).all
20 | end
21 |
22 | should "get the blog page" do
23 | visit posts_path
24 | within ".post-sidebar .post-categories" do
25 | assert_seen "Categories"
26 | @categories.each do |i|
27 | assert has_link?(i.name, :href => post_category_path(i))
28 | end
29 | end
30 | end
31 |
32 | should "only have the first post in the first category" do
33 | @post = @posts.shift
34 | visit post_category_path(@categories.first)
35 | assert_seen @post.title, :within => ".post-title h2"
36 | within "#content .posts" do
37 | @posts.each do |i|
38 | assert !has_content?(i.title)
39 | end
40 | end
41 | end
42 |
43 | end
44 |
--------------------------------------------------------------------------------
/public/stylesheets/posts.css:
--------------------------------------------------------------------------------
1 | /* ==========================================
2 | Post Archive
3 | */
4 |
5 | .posts {
6 | position: relative;
7 | }
8 |
9 | #posts-rss {
10 | display: block;
11 | position: absolute;
12 | left: 0;
13 | bottom: 4px;
14 | line-height: 16px;
15 | text-decoration: none;
16 | img {
17 | display: block;
18 | float: left;
19 | margin-right: 5px;
20 | }
21 | }
22 |
23 | .post-archive ul.archive {
24 | list-style-type: none;
25 | text-decoration: none;
26 | margin: 0;
27 | }
28 | .post-archive h4.year,
29 | .post-archive h5.month,
30 | .post-archive ul.archive li span.day {
31 | display: block;
32 | position: relative;
33 | border-bottom: 1px solid #ccc;
34 | font-size: 17px;
35 | font-weight: normal;
36 | margin: 0 0 0.5em 0;
37 | }
38 | .post-archive ul.archive li ul.posts {
39 | list-style-type: none;
40 | display: block;
41 | float: left;
42 | width: 90%;
43 | margin: 0 0 1em 0;
44 | padding: 0;
45 | }
46 | .post-archive ul.archive li ul.posts li {
47 | margin: 0 0 0.5em 0;
48 | }
49 |
50 |
51 | /* ==========================================
52 | Post Preview
53 | */
54 |
55 | .post-preview {
56 | clear: both;
57 | padding-bottom: 1em;
58 | border-bottom: 1px solid #ccc;
59 | }
60 | .post-preview h2 {
61 | margin-bottom: 0;
62 | }
63 | .post-comments {
64 | clear: both;
65 | }
--------------------------------------------------------------------------------
/app/views/blog/admin/post_images/index.html.erb:
--------------------------------------------------------------------------------
1 | <%= render :partial => 'blog/admin/shared/post_tabs', :locals => {:current => "Images"} %>
2 |
3 |
4 |
5 | | <%= t("thumbnail") %> |
6 | <%= t("action") %> |
7 |
8 |
9 | <% @post.images.each do |image| %>
10 |
11 | | <%= link_to(image_tag(image.attachment.url(:mini)), image.attachment.url(:large)) %> |
12 |
13 | <%= link_to_with_icon('edit', t("edit"), edit_admin_post_image_url(@post, image)) %>
14 |
15 | <%= link_to_delete image, {:url => admin_post_image_url(@post, image) }%>
16 | |
17 |
18 | <% end %>
19 |
20 |
21 |
22 |
23 |
24 |
25 | <%= link_to icon('add') + ' ' + t("new_image"), new_admin_post_image_url(@post), :id => "new_image_link" %>
26 |
27 |
28 | <% content_for :head do %>
29 |
40 | <% end %>
--------------------------------------------------------------------------------
/app/models/post_image.rb:
--------------------------------------------------------------------------------
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 attachment_sizes
25 | if image_content?
26 | { :mini => '48x48>', :small => '150x150>', :medium => '600x600>', :large => '950x700>' }
27 | else
28 | {}
29 | end
30 | end
31 |
32 | def no_attachement_errors
33 | unless attachment.errors.empty?
34 | # uncomment this to get rid of the less-than-useful interrim messages
35 | # errors.clear
36 | errors.add :attachment, "Paperclip returned errors for file '#{attachment_file_name}' - check ImageMagick installation or image source file."
37 | false
38 | end
39 | end
40 |
41 | end
42 |
--------------------------------------------------------------------------------
/app/views/blog/admin/posts/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%= form.field_container :title do %>
2 | <%= form.label :title, t("post.title") %>
3 | <%= form.text_field :title, :class => 'title' %>
4 | <%= error_message_on :post, :title%>
5 | <% end %>
6 |
7 | <%= form.field_container :posted_at do %>
8 | <%= form.label :posted_at, t("post.posted_at") %>
9 | <%= form.text_field :posted_at, :class => 'text', :value => (@post.posted_at.to_s(:long) unless @post.new_record?) %>
10 | <%= error_message_on :post, :posted_at %>
11 | <% end %>
12 |
13 | <%= form.field_container :body do %>
14 | <%= form.label :body, t("post.body") %><%= markdown_helper %>
15 | <%= form.text_area :body %>
16 | <%= error_message_on :post, :body %>
17 | <% end %>
18 |
19 | <%= form.field_container :tag_list do %>
20 | <%= form.label :tag_list, t("post.tags") %>
21 | <%= form.text_field :tag_list, :class => 'text' %>
22 | <%= error_message_on :post, :tag_list %>
23 | <% end %>
24 |
25 | <%= form.field_container :live do %>
26 | <%= form.check_box :live %> <%= form.label :live, t("post.live") %>
27 | <% end %>
28 |
29 | <% content_for :head do %>
30 | <%= stylesheet_link_tag "markitup.css" %>
31 | <%= javascript_include_tag 'date.js', 'jquery.autodate.js', 'jquery.markitup.js', 'markdown.set.js' %>
32 |
40 | <% end %>
--------------------------------------------------------------------------------
/spree_essential_blog.gemspec:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | $:.push File.expand_path("../lib", __FILE__)
3 | require "spree_essential_blog/version"
4 |
5 | Gem::Specification.new do |s|
6 | s.name = "spree_essential_blog"
7 | s.version = SpreeEssentialBlog::VERSION
8 | s.platform = Gem::Platform::RUBY
9 | s.authors = ["Spencer Steffen"]
10 | s.email = ["spencer@citrusme.com"]
11 | s.homepage = "https://github.com/citrus/spree_essential_blog"
12 | s.summary = %q{Spree Essential Blog is a blog plugin for Spree sites equipped with spree_essentials.}
13 | s.description = %q{Spree Essential Blog is a blog plugin for Spree sites equipped with spree_essentials.}
14 |
15 | s.files = `git ls-files`.split("\n")
16 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17 |
18 | s.require_paths = ["lib"]
19 |
20 | # Runtime
21 | s.add_dependency('spree_essentials', '>= 0.1.3')
22 | s.add_dependency('acts-as-taggable-on', '>= 2.0.6')
23 |
24 | # Development
25 | s.add_development_dependency('shoulda', '>= 2.11.3')
26 | s.add_development_dependency('dummier', '>= 0.2.0')
27 | s.add_development_dependency('factory_girl', '= 2.0.0.rc1')
28 | s.add_development_dependency('capybara', '>= 1.0.0')
29 | s.add_development_dependency('selenium-webdriver', '>= 0.2.2')
30 | s.add_development_dependency('sqlite3', '>= 1.3.3')
31 | s.add_development_dependency('spork', '>= 0.9.0.rc9')
32 | s.add_development_dependency('spork-testunit', '>= 0.0.5')
33 |
34 | end
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
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.
28 |
--------------------------------------------------------------------------------
/app/controllers/blog/posts_controller.rb:
--------------------------------------------------------------------------------
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.paginate(:page => params[:page], :per_page => Post.per_page)
31 | end
32 |
33 | def search
34 | query = params[:query].gsub(/%46/, '.')
35 | @posts = Post.live.tagged_with(query).paginate(:page => params[:page], :per_page => 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
--------------------------------------------------------------------------------
/app/views/blog/admin/post_products/index.html.erb:
--------------------------------------------------------------------------------
1 | <%= render :partial => 'blog/admin/shared/post_tabs', :locals => {:current => "Related Products"} %>
2 |
3 |
4 |
17 |
18 |
19 |
20 | <%= render :partial => "related_products_table", :locals => { :post => @post } %>
21 |
22 |
23 | <% content_for :head do %>
24 |
25 | <%= csrf_meta_tag %>
26 | <%= javascript_tag "var expand_variants = false;" %>
27 |
28 |
50 | <% end %>
--------------------------------------------------------------------------------
/app/views/blog/admin/post_categories/index.html.erb:
--------------------------------------------------------------------------------
1 | <%= render :partial => 'blog/admin/shared/post_tabs', :locals => {:current => "Categories"} %>
2 |
3 |
4 | <%= form_for([:admin, @post]) do |f| %>
5 |
25 | <% end %>
26 |
27 |
28 |
29 |
30 | <%= link_to icon('add') + ' ' + t("new_category"), new_admin_post_category_url(@post), :id => "btn_new_category" %>
31 |
32 |
33 | <% content_for :head do %>
34 |
45 | <% end %>
--------------------------------------------------------------------------------
/app/views/blog/admin/posts/index.html.erb:
--------------------------------------------------------------------------------
1 | <%= render :partial => 'admin/shared/contents_sub_menu' %>
2 |
3 |
11 |
12 |
13 | Listing Posts
14 |
15 |
16 |
17 |
18 |
19 | | <%= sort_link @search, :title, t("post.title") %> |
20 | <%= sort_link @search, :posted_at, t("post.posted_at") %> |
21 | <%= sort_link @search, :live, t("post.live") %> |
22 | <%= t("action") %> |
23 |
24 |
25 |
26 | <%- @collection.each do |post|%>
27 |
28 | | <%=link_to post.title, object_url(post) %> |
29 | <%=link_to post.posted_at.strftime('%x %X'), object_url(post) %> |
30 | <%= t(post.live ? 'yes' : 'no')%> |
31 |
32 | <%= link_to_edit post %>
33 | <%= link_to_delete post %>
34 | |
35 |
36 | <% end %>
37 |
38 |
39 |
40 | <%= will_paginate(:prev => "« #{t('previous')}", :next => "#{t('next')} »") %>
41 |
42 | <% content_for :sidebar do %>
43 |
44 |
<%= t(:search) %>
45 |
46 | <% @post = Post.metasearch %>
47 | <%= form_for [:admin, @post] do |f| %>
48 | <%- locals = {:f => f} %>
49 | <%= hook :admin_posts_index_search, locals do %>
50 |
51 |
52 | <%= f.text_field :title_like, :size => 25 %>
53 |
54 |
55 |
56 |
57 | <%= f.spree_date_picker :posted_at_greater_than %>
58 |
59 |
60 |
61 | <%= f.spree_date_picker :posted_at_less_than %>
62 |
63 |
64 |
65 |
66 | <% end %>
67 | <%= hook :admin_posts_index_search_buttons, locals do %>
68 |
<%= button t("search") %>
69 | <% end %>
70 | <% end %>
71 |
72 | <% end %>
--------------------------------------------------------------------------------
/app/models/post.rb:
--------------------------------------------------------------------------------
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("")[0]
37 | render(preview)
38 | end
39 |
40 | def rendered_body
41 | render(body.gsub("", ""))
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
--------------------------------------------------------------------------------
/test/integration/admin/post_integration_test.rb:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env ruby
2 | # encoding: UTF-8
3 |
4 | require_relative '../../test_helper'
5 |
6 | class Admin::PostIntegrationTest < ActiveSupport::IntegrationCase
7 |
8 | setup do
9 | Post.destroy_all
10 | @labels = %(Title, Posted At, Body, Tags).split(', ')
11 | @values = %(Just a post, #{Time.now}, #{Faker::Lorem.paragraphs(1 + rand(4)).join("\n\n")}, one tag).split(', ')
12 | end
13 |
14 | should "have a link to new post" do
15 | visit admin_posts_path
16 | btn = find(".actions a.button").native
17 | assert_match /#{new_admin_post_path}$/, btn.attribute('href')
18 | assert_equal "New Post", btn.text
19 | end
20 |
21 | should "get new post" do
22 | visit new_admin_post_path
23 | assert has_content?("New Post")
24 | within "#new_post" do
25 | @labels.each do |f|
26 | assert has_field?(f)
27 | end
28 | end
29 | end
30 |
31 | should "validate post" do
32 | visit new_admin_post_path
33 | click_button "Create"
34 | within "#errorExplanation" do
35 | assert_seen "3 errors prohibited this record from being saved:"
36 | assert_seen "Title can't be blank"
37 | assert_seen "Body can't be blank"
38 | assert_seen "Posted at is an invalid date."
39 | end
40 | end
41 |
42 | should "create a post" do
43 | visit new_admin_post_path
44 | within "#new_post" do
45 | @labels.each_with_index do |label, index|
46 | fill_in label, :with => @values[index]
47 | end
48 | end
49 | click_button "Create"
50 | assert_flash :notice, %(Post "Just a post" has been successfully created!)
51 | end
52 |
53 | context "an existing post" do
54 | setup do
55 | @post = Factory.create(:post)
56 | end
57 |
58 | should "edit and update" do
59 | visit edit_admin_post_path(@post)
60 |
61 | within "#edit_post_#{@post.id}" do
62 | @labels.each_with_index do |label, index|
63 | next if label == 'Posted At'
64 | fill_in label, :with => @values[index].reverse
65 | end
66 | end
67 | click_button "Update"
68 | assert_equal admin_post_path(@post.reload), current_path
69 | assert_flash :notice, %(Post "tsop a tsuJ" has been successfully updated!)
70 | end
71 |
72 | should "get destroyed" do
73 | visit admin_posts_path
74 | find("a[href='#']").click
75 | assert find_by_id("popup_ok").click
76 | end
77 |
78 | end
79 |
80 | end
81 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 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 a tab to live under in the admin.
7 |
8 | [todo] write more...
9 |
10 |
11 | Installation
12 | ------------
13 |
14 | 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)...
15 |
16 | Otherwise, follow these steps to get up and running with spree_essential_blog:
17 |
18 | First, add spree_essential_blog to your Gemfile... it hasn't been released to Rubygems yet so we'll grab them it git.
19 |
20 | gem 'spree_essential_blog', :git => 'git://github.com/citrus/spree_essential_blog.git'
21 |
22 | Run the generators to create the migration files.
23 |
24 | rails g spree_essentials:install
25 | rails g spree_essentials:blog
26 |
27 | Now migrate your database...
28 |
29 | rake db:migrate
30 |
31 | Boot your server and checkout the admin!
32 |
33 | rails s
34 |
35 |
36 | ### Sample Posts
37 |
38 | If you'd like some sample posts, just use the rake command from your project:
39 |
40 | rake db:sample:blog
41 |
42 |
43 |
44 | Testing
45 | -------
46 |
47 | Clone this repo to where you develop, bundle up, then run `dummier` to get the show started:
48 |
49 | git clone git://github.com/citrus/spree_essential_blog.git
50 | cd spree_essential_blog
51 | bundle install
52 | bundle exec dummier
53 |
54 | This will generate a fresh rails app in test/dummy, install spree & spree_essential_blog, then migrate the test database. Sweet.
55 |
56 |
57 | ### Spork
58 |
59 | If you want to spork, run:
60 |
61 | bundle exec spork
62 |
63 | In another window, run all tests:
64 |
65 | testdrb test/**/*_test.rb
66 |
67 | Or just a specific test:
68 |
69 | testdrb test/unit/page_test.rb
70 |
71 |
72 | ### No Spork
73 |
74 | If you don't want to spork, just use rake:
75 |
76 | rake
77 |
78 |
79 |
80 | Demo
81 | ----
82 |
83 | You can easily use the `test/dummy` app as a demo of spree_essential_blog. Just `cd` to where you develop and run:
84 |
85 | git clone git://github.com/citrus/spree_essential_blog.git
86 | cd spree_essential_blog
87 | mv lib/dummy_hooks/after_migrate.rb.sample lib/dummy_hooks/after_migrate.rb
88 | bundle install
89 | bundle exec dummier
90 | cd test/dummy
91 | rails s
92 |
93 |
94 |
95 |
96 | Change Log
97 | ----------
98 |
99 | **2011/6/7**
100 |
101 | * Added Disqus for comments
102 | * Re-namespaced from Admin::Blog to Blog::Admin
103 | * Added more tests for categories
104 |
105 |
106 | **2011/6/6**
107 |
108 | * pulled GH1 (Thanks [detierno](https://github.com/detierno)!)
109 | * Switched to [dummier](https://github.com/citrus/dummier) for demo & testing
110 | * Improved testing
111 | * Improved documentation
112 |
113 |
114 | **2011/4/13 - 2011/5/27**
115 |
116 | * Initial development
117 |
118 |
119 | Contributors
120 | ------------
121 |
122 | * Spencer Steffen ([citrus](https://github.com/citrus))
123 | * Denis Tierno ([detierno](https://github.com/detierno))
124 |
125 | If you'd like to help out feel free to fork and send me pull requests!
126 |
127 |
128 | License
129 | -------
130 |
131 | Copyright (c) 2011 Spencer Steffen, released under the New BSD License All rights reserved.
132 |
--------------------------------------------------------------------------------
/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | en:
2 | post:
3 | model_name: Post
4 | title: Title
5 | posted_at: Posted At
6 | body: Body
7 | live: Published
8 | tags: Tags
9 | post_category:
10 | model_name: Category
11 | name: Name
12 | permalink: Permalink
13 |
14 | admin:
15 | subnav:
16 | posts: Posts
17 |
18 | blog:
19 | admin:
20 | posts:
21 | new:
22 | new_post: New Post
23 | post_products:
24 | index:
25 | add_related_product: Add Related Products
26 | post_categories:
27 | index:
28 | manage_categories: Manage Categories
29 | new:
30 | new_category: New Category
31 | edit:
32 | edit_category: Edit Category
33 |
34 | shared:
35 | post_tabs:
36 | post_details: Post Details
37 | editing_post: Editing Post
38 | related_products: Related Products
39 | post_categories: Categories
40 | comments: Comments
41 | home:
42 | title: Recent Posts
43 | archive: Blog Archive
44 | no_posts: No posts found!
45 | posts:
46 | index:
47 | blog: Blog
48 | rss: Subscribe via RSS
49 | archive:
50 | where_to_next: Where to next?
51 | go_to_store: "Shop the Store"
52 | back_to_posts: "Back to the blog"
53 | settings:
54 | disqus: Disqus Settings
55 | explain_disqus: Configure Disqus Comments for Blog
56 | disqus_shortname: Disqus Shortname
57 | shared:
58 | sidebar:
59 | recent_posts: Recent Posts
60 | categories: Categories
61 | archives: Archive
62 | view_archive: View Full Archive
63 | tags: Tags
64 | preview:
65 | read_more: Read More
66 | post_tabs:
67 | editing_post: Editing Post
68 | post_details: Post Details
69 | related_products: Related Products
70 | post_categories: Categories
71 | subnav:
72 | posts: Blog Posts
73 |
74 |
75 | faker:
76 | lorem:
77 | words: [alias, consequatur, perferendis, voluptatem, accusantium, doloremque, aperiam, eaque, ipsa, quae, illo, inventore, veritatis, quasi, architecto, beatae, vitae, dicta, sunt, explicabo, aspernatur, aut, odit, aut, fugit, sed, quia, consequuntur, magni, dolores, eos, qui, ratione, voluptatem, sequi, nesciunt, neque, dolorem, ipsum, quia, dolor, sit, amet, consectetur, adipisci, velit, sed, quia, non, numquam, eius, modi, tempora, incidunt, ut, labore, et, dolore, magnam, aliquam, quaerat, voluptatem, ut, enim, ad, minima, veniam, quis, nostrum, exercitationem, ullam, corporis, nemo, enim, ipsam, voluptatem, quia, voluptas, sit, suscipit, laboriosam, nisi, ut, aliquid, ex, ea, commodi, consequatur, quis, autem, vel, eum, iure, reprehenderit, qui, in, ea, voluptate, velit, esse, quam, nihil, molestiae, et, iusto, odio, dignissimos, ducimus, qui, blanditiis, praesentium, laudantium, totam, rem, voluptatum, deleniti, atque, corrupti, quos, dolores, et, quas, molestias, excepturi, sint, occaecati, cupiditate, non, provident, sed, ut, perspiciatis, unde, omnis, iste, natus, error, similique, sunt, in, culpa, qui, officia, deserunt, mollitia, animi, id, est, laborum, et, dolorum, fuga, et, harum, quidem, rerum, facilis, est, et, expedita, distinctio, nam, libero, tempore, cum, soluta, nobis, est, eligendi, optio, cumque, nihil, impedit, quo, porro, quisquam, est, qui, minus, id, quod, maxime, placeat, facere, possimus, omnis, voluptas, assumenda, est, omnis, dolor, repellendus, temporibus, autem, quibusdam, et, aut, consequatur, vel, illum, qui, dolorem, eum, fugiat, quo, voluptas, nulla, pariatur, accusamus, officiis, debitis, aut, rerum, necessitatibus, saepe, eveniet, ut, et, voluptates, repudiandae, sint, molestiae, non, recusandae, itaque, earum, rerum, tenetur, sapiente, delectus, reiciendis, voluptatibus, maiores, doloribus, asperiores, repellat]
78 |
--------------------------------------------------------------------------------
/test/integration/admin/post_category_integration_test.rb:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env ruby
2 | # encoding: UTF-8
3 |
4 | require_relative '../../test_helper'
5 |
6 | class Admin::PostCategoryIntegrationTest < ActiveSupport::IntegrationCase
7 |
8 | #include Blog::PostsHelper
9 |
10 | def setup
11 | Post.destroy_all
12 | PostCategory.destroy_all
13 | @post = Factory.create(:post)
14 | @category = Factory.create(:post_category)
15 | end
16 |
17 | should "have a link to post categories" do
18 | visit admin_post_path(@post)
19 | within ".sidebar.post-menu" do
20 | assert has_link?("Categories")
21 | end
22 | end
23 |
24 | should "get the post categories index" do
25 | visit admin_post_categories_path(@post)
26 | assert_seen "Categories", :within => ".sidebar.post-menu li.active"
27 | assert_seen "Manage Categories", :within => ".edit_post legend"
28 | assert_seen @category.name, :within => "tr#post_category_#{@category.id} td label"
29 | assert has_selector?("button[type=submit]")
30 | assert has_selector?("a#btn_new_category")
31 | end
32 |
33 | should "add a new post category" do
34 | visit admin_post_categories_path(@post)
35 | click_link "btn_new_category"
36 | fill_in "Name", :with => "Just a Category"
37 | fill_in "Permalink", :with => "just-a-category"
38 | click_button "Create"
39 | @category = PostCategory.last
40 | assert_equal admin_post_categories_path(@post), current_path
41 | assert_flash :notice, %(Post category "#{@category.name}" has been successfully created!)
42 | assert_seen @category.name, :within => "tr#post_category_#{@category.id} td label"
43 | end
44 |
45 | should "edit existing post category" do
46 | visit edit_admin_post_category_path(@post, @category.id)
47 | assert_equal @category.name, find_field("Name").value
48 | assert_equal @category.permalink, find_field("Permalink").value
49 | fill_in "Name", :with => "Not just a Category"
50 | fill_in "Permalink", :with => "not-just-a-category"
51 | click_button "Update"
52 | assert_equal admin_post_categories_path(@post), current_path
53 | assert_flash :notice, %(Post category "Not just a Category" has been successfully updated!)
54 | assert_seen "Not just a Category", :within => "tr#post_category_#{@category.id} td label"
55 | end
56 |
57 | should "destroy the post category" do
58 | visit admin_post_categories_path(@post)
59 | find("tr#post_category_#{@category.id} td.options a[href='#']").click
60 | assert find_by_id("popup_ok").click
61 | end
62 |
63 | should "link a post category" do
64 | visit admin_post_categories_path(@post)
65 | assert !field_labeled(@category.name).checked?
66 | check @category.name
67 | click_button "Update"
68 | assert field_labeled(@category.name).checked?
69 | assert_equal admin_post_categories_path(@post), current_path
70 | assert_flash :notice, %(Post "#{@post.title}" has been successfully updated!)
71 | end
72 |
73 | should "unlink a post category" do
74 | @post.categories << @category
75 | @post.save
76 | visit admin_post_categories_path(@post.reload)
77 | assert field_labeled(@category.name).checked?
78 | uncheck @category.name
79 | click_button "Update"
80 | assert_equal admin_post_categories_path(@post), current_path
81 | assert !field_labeled(@category.name).checked?
82 | assert_flash :notice, %(Post "#{@post.title}" has been successfully updated!)
83 | end
84 |
85 | context "with multiple categories" do
86 |
87 | setup do
88 | PostCategory.destroy_all
89 | @categories = %w(one two three four five).map{|i| Factory.create(:post_category, :name => i) }
90 | end
91 |
92 | should "link a multiple post categories" do
93 | visit admin_post_categories_path(@post)
94 | @categories.each do |category|
95 | assert !field_labeled(category.name).checked?
96 | end
97 | @categories.take(3).each do |category|
98 | check category.name
99 | end
100 | click_button "Update"
101 | @categories.take(3).each do |category|
102 | assert field_labeled(category.name).checked?
103 | end
104 | @categories.slice(3, 2).each do |category|
105 | assert !field_labeled(category.name).checked?
106 | end
107 | assert_equal admin_post_categories_path(@post), current_path
108 | assert_flash :notice, %(Post "#{@post.title}" has been successfully updated!)
109 | end
110 |
111 | should "unlink multiple post category" do
112 | @post.categories = @categories.take(3)
113 | @post.save
114 | visit admin_post_categories_path(@post.reload)
115 | @categories.take(3).each do |category|
116 | assert field_labeled(category.name).checked?
117 | end
118 | @categories.take(3).each do |category|
119 | uncheck category.name
120 | end
121 | click_button "Update"
122 | @categories.each do |category|
123 | assert !field_labeled(category.name).checked?
124 | end
125 | assert_equal admin_post_categories_path(@post), current_path
126 | assert_flash :notice, %(Post "#{@post.title}" has been successfully updated!)
127 | end
128 |
129 | end
130 |
131 | end
132 |
--------------------------------------------------------------------------------
/test/integration/post_integration_test.rb:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env ruby
2 | # encoding: UTF-8
3 |
4 | require_relative '../test_helper'
5 |
6 | class Blog::PostIntegrationTest < ActiveSupport::IntegrationCase
7 |
8 |
9 | include Blog::PostsHelper
10 |
11 | def setup
12 | Post.destroy_all
13 | end
14 |
15 |
16 | context "with some posts" do
17 |
18 | setup do
19 | 11.times{ |i| Factory.create(:post, :title => "Capy post #{i}", :posted_at => Time.now - i.days) }
20 | assert_equal 11, Post.count
21 | end
22 |
23 | should "get the blog page" do
24 | visit posts_path
25 | # first post
26 | assert has_link?("Capy post 1")
27 | # last post
28 | assert has_link?("Capy post 9")
29 | # archive link
30 | assert has_link?("View Full Archive")
31 | # tag link
32 | assert has_link?("peanut butter")
33 | # page two
34 | assert has_link?("2")
35 | assert has_link?("Next →")
36 |
37 |
38 | #assert_equal nil, page.html.match(%( DateTime.parse("2011/2/17"), :tag_list => "gruyere, emmentaler, fondue")
63 | assert_equal 1, Post.count
64 | end
65 |
66 | should "find by seo path" do
67 | visit post_seo_path(@post)
68 | assert_seen "Peanut Butter Jelly Time", :within => ".post-title h1"
69 | assert_seen "Thursday February 17, 2011", :within => ".post-title h5"
70 | within ".post-tags" do
71 | assert has_link?("gruyere")
72 | assert has_link?("emmentaler")
73 | assert has_link?("fondue")
74 | end
75 | end
76 |
77 | should "not find by tags" do
78 | visit search_posts_path(:query => "some crazy random query")
79 | assert_seen "No posts found!", :within => ".posts h1"
80 | end
81 |
82 | should "find by tags" do
83 | visit search_posts_path(:query => "emmentaler")
84 | assert has_link?("Peanut Butter Jelly Time", :href => post_seo_path(@post))
85 | assert has_link?("Read More", :href => post_seo_path(@post))
86 | end
87 |
88 | end
89 |
90 |
91 | context "unpublished posts" do
92 |
93 | def assert_no_post(post)
94 | within "#sidebar .post-archive" do
95 | assert !has_link?(post.title)
96 | end
97 | within "#content .posts" do
98 | assert !has_link?(post.title)
99 | end
100 | within ".tag-cloud ul.tags" do
101 | post.tags.each do |tag|
102 | assert !has_link?(tag.name)
103 | end
104 | end
105 | end
106 |
107 | setup do
108 | @tags = %(totally, not published).split(", ")
109 | @post = Factory.create(:post, :title => "Unpublished Post", :tag_list => @tags.join(", "), :live => false)
110 | assert_equal 1, Post.count
111 | end
112 |
113 | should "not include post in index" do
114 | visit posts_path
115 | assert_no_post(@post)
116 | end
117 |
118 | should "not include post in day specific index" do
119 | visit post_date_path(:year => @post.year, :month => @post.month, :day => @post.day)
120 | assert_no_post(@post)
121 | end
122 |
123 | should "not include post in month specific index" do
124 | visit post_date_path(:year => @post.year, :month => @post.month)
125 | assert_no_post(@post)
126 | end
127 |
128 | should "not include post in year specific index" do
129 | visit post_date_path(:year => @post.year)
130 | assert_no_post(@post)
131 | end
132 |
133 | should "not include post in search results" do
134 | @tags.each do |tag|
135 | visit search_posts_path(tag)
136 | assert_no_post(@post)
137 | end
138 | end
139 |
140 | end
141 |
142 |
143 | context "published posts" do
144 |
145 | def assert_has_post(post)
146 | within "#sidebar .post-archive" do
147 | assert has_link?(post.title)
148 | end
149 | within "#content .posts" do
150 | assert has_link?(post.title)
151 | end
152 | within ".tag-cloud ul.tags" do
153 | post.tags.each do |tag|
154 | assert has_link?(tag.name)
155 | end
156 | end
157 | end
158 |
159 | setup do
160 | @tags = %(totally, published).split(", ")
161 | @post = Factory.create(:post, :title => "Published Post", :tag_list => @tags.join(", "), :live => true)
162 | assert_equal 1, Post.count
163 | end
164 |
165 | should "not include post in index" do
166 | visit posts_path
167 | assert_has_post(@post)
168 | end
169 |
170 | should "not include post in day specific index" do
171 | visit post_date_path(:year => @post.year, :month => @post.month, :day => @post.day)
172 | assert_has_post(@post)
173 | end
174 |
175 | should "not include post in month specific index" do
176 | visit post_date_path(:year => @post.year, :month => @post.month)
177 | assert_has_post(@post)
178 | end
179 |
180 | should "not include post in year specific index" do
181 | visit post_date_path(:year => @post.year)
182 | assert_has_post(@post)
183 | end
184 |
185 | should "not include post in search results" do
186 | @tags.each do |tag|
187 | visit search_posts_path(tag)
188 | assert_has_post(@post)
189 | end
190 | end
191 |
192 | end
193 |
194 | end
195 |
196 |
197 |
198 |
199 | # [todo] make these capy tests
200 | #
201 | #context "published, dated posts" do
202 | #
203 | # setup do
204 | # @date = DateTime.parse("2011/3/20 16:00")
205 | # @post = Factory.create(:post, :posted_at => @date)
206 | # 10.times {|i| Factory.create(:post, :title => "Today's Sample Post #{i}", :posted_at => @date) }
207 | # 10.times {|i| Factory.create(:post, :title => "Last Weeks's Sample Post #{i}", :posted_at => @date - 1.week) }
208 | # 10.times {|i| Factory.create(:post, :title => "Last Month's Sample Post #{i}", :posted_at => @date - 1.month) }
209 | # 10.times {|i| Factory.create(:post, :title => "Last Years's Sample Post #{i}", :posted_at => @date - 1.year) }
210 | # end
211 | #
212 | # should "assert proper post count" do
213 | # assert_equal 41, Post.count
214 | # end
215 | #
216 | # should "paginate posts by day" do
217 | # get :index, :year => @post.year, :month => @post.month, :day => @post.day
218 | # assert_equal 10, assigns(:posts).length
219 | # assert_equal 11, assigns(:posts).total_entries
220 | # assert_equal 2, assigns(:posts).total_pages
221 | # assert_response :success
222 | # end
223 | #
224 | # should "get posts by month" do
225 | # get :index, :year => @post.year, :month => @post.month
226 | # assert_equal 10, assigns(:posts).length
227 | # assert_equal 21, assigns(:posts).total_entries
228 | # assert_equal 3, assigns(:posts).total_pages
229 | # assert_response :success
230 | # end
231 | #
232 | # should "get posts by year" do
233 | # get :index, :year => @post.year
234 | # assert_not_nil assigns(:posts)
235 | # assert_equal 10, assigns(:posts).length
236 | # assert_equal 31, assigns(:posts).total_entries
237 | # assert_equal 4, assigns(:posts).total_pages
238 | # assert_response :success
239 | # end
240 | #
241 | #end
242 |
--------------------------------------------------------------------------------
<%= t('blog.comments') %>
5 | 6 | 17 | 18 | blog comments powered by Disqus 19 |