wheelhouse-blog 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2011-12 Sam Pohlenz
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  Blog Plugin for Wheelhouse CMS
2
2
  ==============================
3
3
 
4
+ ![Screenshot of blog plugin](https://www.wheelhousecms.com/media/308e77b2/Blog-Plugin.png)
5
+
4
6
  This gem makes it easy to integrate a blog or news feed into your Wheelhouse CMS site.
5
7
 
6
8
  It currently supports:
@@ -9,6 +11,7 @@ It currently supports:
9
11
  - tags
10
12
  - categories
11
13
  - archives
14
+ - pagination
12
15
  - multiple blogs
13
16
 
14
17
  It does not yet support commenting but this will be implemented in the near future. In the meanwhile, it is fairly easy to integrate comments using a third-party JavaScript library such as [Disqus](http://disqus.com/) or [Facebook Comments](https://developers.facebook.com/docs/reference/plugins/comments/).
@@ -25,4 +28,4 @@ Then run `bundle install`.
25
28
 
26
29
  **2. Create a new blog from the New Page dropdown.**
27
30
 
28
- **3. Copy the templates from `app/templates/blog` to your theme templates folder to customize.**
31
+ **3. To customize, copy the partials (`_post.html.haml` and `_layout.html.haml`) from `app/templates/blog` to your theme templates folder.**
@@ -13,3 +13,13 @@
13
13
  li
14
14
  margin-top: 0
15
15
  margin-bottom: 3px
16
+
17
+ .posts-per-page
18
+ label
19
+ width: 40%
20
+ line-height: 2.5
21
+
22
+ input[type="text"]
23
+ width: 4em
24
+ float: left
25
+
@@ -1,34 +1,37 @@
1
1
  class Blog::BlogHandler < Wheelhouse::ResourceHandler
2
- get :cache => true do
3
- # Nothing extra required
2
+ get "/(page/:page)", :cache => true do
3
+ @posts = paginate(@blog.posts)
4
4
  end
5
5
 
6
6
  get "/feed.xml", :cache => true do
7
- request.format = :xml
8
- render :template => "feed.xml", :layout => false
7
+ render :template => "feed", :formats => [:rss], :layout => false
9
8
  end
10
9
 
11
- get '/tag/:tag', :cache => true do
12
- @posts = @blog.posts.tagged_with(params[:tag])
13
- render :template => "tag.html"
10
+ get '/tag/:tag(/page/:page)', :cache => true do
11
+ @posts = paginate(@blog.posts.tagged_with(params[:tag]))
12
+ render :template => "tag"
14
13
  end
15
14
 
16
- get '/category/:category', :cache => true do
17
- @posts = @blog.posts.in_category(params[:category])
18
- render :template => "category.html"
15
+ get '/category/:category(/page/:page)', :cache => true do
16
+ @posts = paginate(@blog.posts.in_category(params[:category]))
17
+ render :template => "category"
18
+ end
19
+
20
+ get "/:year(/:month)(/page/:page)", :cache => true do
21
+ @archive = Blog::Archive.new(@blog, params[:year], params[:month].presence)
22
+ raise ActionController::RoutingError, "No route matches #{request.path.inspect}" if @archive.invalid?
23
+
24
+ @posts = paginate(@archive.posts)
25
+ render :template => "archive"
19
26
  end
20
27
 
21
28
  get "/:year/:month/:permalink", :cache => true do
22
29
  @post = @blog.find_post(params[:year], params[:month], params[:permalink])
23
30
  render @post
24
31
  end
25
-
26
- get "/:year/:month", :cache => true do
27
- @year = params[:year].to_i
28
- @month = params[:month].to_i
29
- raise ActionController::RoutingError, "No route matches #{request.path.inspect}" if @year.zero? || @month.zero?
30
-
31
- @posts = @blog.posts.in_year_and_month(@year, @month)
32
- render :template => "archive.html"
32
+
33
+ private
34
+ def paginate(posts)
35
+ posts.paginate(:page => params[:page].presence || 1, :per_page => @blog.posts_per_page)
33
36
  end
34
37
  end
@@ -1,9 +1,15 @@
1
1
  module Blog::BlogHelper
2
+ def blog(label=nil)
3
+ @_blogs ||= {}
4
+
5
+ if label
6
+ @_blogs[label] ||= Blog::Blog.get(label)
7
+ else
8
+ @_blogs[:default] ||= Blog::Blog.first
9
+ end
10
+ end
11
+
2
12
  def blog_post_tag_links(post)
3
13
  post.tags.map { |tag| link_to(tag, post.blog.tag_path(tag), :title => "View all posts tagged #{tag}") }.join(', ').html_safe
4
14
  end
5
-
6
- def format_month(year, month)
7
- "#{Date::MONTHNAMES[month]} #{year}"
8
- end
9
15
  end
@@ -0,0 +1,32 @@
1
+ module Blog::PaginationHelper
2
+ WILL_PAGINATE_BASE = defined?(WillPaginate::ViewHelpers) ? WillPaginate::ViewHelpers::LinkRenderer : WillPaginate::LinkRenderer
3
+
4
+ class LinkRenderer < WILL_PAGINATE_BASE
5
+ PAGE_PARAMETER = /\/page\/\d+$/
6
+
7
+ protected
8
+ def url(page)
9
+ path = @template.request.path
10
+
11
+ if page == 1
12
+ path.sub(PAGE_PARAMETER, "")
13
+ elsif path =~ PAGE_PARAMETER
14
+ path.sub(PAGE_PARAMETER, "/page/#{page}")
15
+ else
16
+ path + "/page/#{page}"
17
+ end
18
+ end
19
+ alias url_for url
20
+ end
21
+
22
+ DEFAULTS = {
23
+ :renderer => LinkRenderer,
24
+ :next_label => "Older Posts &raquo;",
25
+ :previous_label => "&laquo; Newer Posts",
26
+ :page_links => false
27
+ }
28
+
29
+ def blog_pagination(options={})
30
+ will_paginate(@posts, DEFAULTS.merge(options)) if @posts
31
+ end
32
+ end
@@ -0,0 +1,75 @@
1
+ class Blog::Archive
2
+ attr_reader :year, :month
3
+ attr_accessor :count
4
+
5
+ def initialize(blog, year, month=nil)
6
+ @blog, @year = blog, year.to_i
7
+ @month = month.to_i if month
8
+ yield self if block_given?
9
+ end
10
+
11
+ def to_s
12
+ format.to_s
13
+ end
14
+
15
+ def format(format="%B %Y")
16
+ month ? to_date.strftime(format) : year
17
+ end
18
+
19
+ def path
20
+ @blog.path(year, month)
21
+ end
22
+
23
+ def posts
24
+ @posts ||= begin
25
+ posts = @blog.posts.where(:year => year)
26
+ posts = posts.where(:month => month) if month
27
+ posts
28
+ end
29
+ end
30
+
31
+ def invalid?
32
+ month.present? && !valid_month?
33
+ end
34
+
35
+ def to_date
36
+ Date.new(year, month, 1)
37
+ end
38
+
39
+ def <=>(other)
40
+ other.to_date <=> to_date
41
+ end
42
+
43
+ def self.from_mongo(blog, hash)
44
+ new(blog, hash["year"], hash["month"]) do |archive|
45
+ archive.count = hash["count"].to_i
46
+ end
47
+ end
48
+
49
+ def self.build(blog, options)
50
+ options[:group] ||= [:year, :month]
51
+
52
+ group(blog.posts, :key => options[:group]).map { |hash|
53
+ Blog::Archive.from_mongo(blog, hash)
54
+ }.sort
55
+ end
56
+
57
+ # Backwards-compatiblity with old-style archives
58
+ def to_ary
59
+ [to_date, path, count]
60
+ end
61
+
62
+ private
63
+ def valid_month?
64
+ month > 0 && month < 12
65
+ end
66
+
67
+ def self.group(base, options={})
68
+ scope = base.current_scope
69
+ selector, _ = MongoModel::MongoOptions.new(scope.klass, scope.finder_options).to_a
70
+
71
+ scope.collection.group({ :cond => selector,
72
+ :initial => { :count => 0 },
73
+ :reduce => "function(doc, out) { out.count++; }" }.merge(options))
74
+ end
75
+ end
@@ -3,9 +3,12 @@ class Blog::Blog < Wheelhouse::Resource
3
3
  include Wheelhouse::Resource::Content
4
4
 
5
5
  property :title, String, :translate => true, :required => true
6
+ property :posts_per_page, Integer, :default => 20
6
7
 
7
8
  has_many :posts, :class => "Blog::Post", :conditions => { :state => 'Published' }, :order => :published_at.desc
8
- has_many :all_posts, :class => "Blog::Post", :order => :created_at.desc
9
+ has_many :all_posts, :class => "Blog::Post"
10
+ has_many :posts_for_admin, :class => "Blog::Post", :order => [:_admin_sort_index.desc, :created_at.desc],
11
+ :select => [:id, :type, :title, :state, :published_at, :created_by_id, :author_name, :blog_id]
9
12
 
10
13
  activities :all
11
14
 
@@ -21,6 +24,10 @@ class Blog::Blog < Wheelhouse::Resource
21
24
  posts.find_by_year_and_month_and_permalink!(year.to_i, month.to_i, permalink)
22
25
  end
23
26
 
27
+ def feed_path
28
+ path('feed.xml')
29
+ end
30
+
24
31
  def tag_path(tag)
25
32
  path('tag', tag.parameterize)
26
33
  end
@@ -29,27 +36,20 @@ class Blog::Blog < Wheelhouse::Resource
29
36
  path('category', category.parameterize)
30
37
  end
31
38
 
32
- def archive_path(year, month)
33
- path(year.to_i, month.to_i)
39
+ def archive_path(year, month=nil)
40
+ month ? path(year.to_i, month.to_i) : path(year.to_i)
34
41
  end
35
42
 
36
- def archives
37
- selector = MongoModel::MongoOptions.new(posts.klass, posts.finder_options).to_a.first
38
- posts.collection.group(:key => [:year, :month], :cond => selector, :initial => { :count => 0 }, :reduce => "function(doc, out) { out.count++ }").map { |hash|
39
- year, month, count = hash["year"], hash["month"], hash["count"]
40
-
41
- [Date.civil(year, month, 1), archive_path(year, month), count.to_i]
42
- }.sort { |a, b| b.first <=> a.first }
43
+ def archives(options={})
44
+ Blog::Archive.build(self, options)
43
45
  end
44
46
 
45
47
  def tags
46
- selector = MongoModel::MongoOptions.new(posts.klass, posts.finder_options).to_a.first
47
- posts.collection.distinct(:tags, selector)
48
+ Blog::Tag.for_blog(self).cached
48
49
  end
49
50
 
50
51
  def categories
51
- selector = MongoModel::MongoOptions.new(posts.klass, posts.finder_options).to_a.first
52
- posts.collection.distinct(:categories, selector)
52
+ Blog::Category.for_blog(self).cached
53
53
  end
54
54
 
55
55
  def additional_children
@@ -0,0 +1 @@
1
+ Blog::Category = Blog::Taxonomy.new(:categories)
@@ -29,17 +29,19 @@ class Blog::Post < Wheelhouse::Resource
29
29
 
30
30
  scope :tagged_with, lambda { |tag| where(:_tags => tag.parameterize) }
31
31
  scope :in_category, lambda { |category| where(:_categories => category.parameterize) }
32
- scope :in_year_and_month, lambda { |year, month| where(:year => year, :month => month) }
33
-
34
- scope :properties_for_admin, select(:id, :type, :title, :state, :published_at, :created_by_id, :author_name, :blog_id)
35
32
 
36
33
  before_save :set_published_timestamp, :if => :published?
37
34
  before_save :cache_author_name
35
+ before_save :set_admin_sort_index
38
36
 
39
37
  delegate :site, :to => :blog
38
+
40
39
  after_save :clear_cache!
41
40
  after_destroy :clear_cache!
42
41
 
42
+ after_save :update_taxonomies
43
+ after_destroy :update_taxonomies
44
+
43
45
  self.parent_resource = :blog
44
46
  self.default_template = "post"
45
47
 
@@ -81,4 +83,13 @@ private
81
83
  def cache_author_name
82
84
  write_attribute(:author_name, author.name) if author
83
85
  end
86
+
87
+ def update_taxonomies
88
+ Blog::Tag.refresh
89
+ Blog::Category.refresh
90
+ end
91
+
92
+ def set_admin_sort_index
93
+ attributes[:_admin_sort_index] = published? ? [0, published_at.to_i] : [1, updated_at.to_i]
94
+ end
84
95
  end
@@ -0,0 +1 @@
1
+ Blog::Tag = Blog::Taxonomy.new(:tags)
@@ -0,0 +1,112 @@
1
+ class Blog::Taxonomy
2
+ include MongoModel::MapReduce
3
+
4
+ class_attribute :taxonomy_type
5
+
6
+ class << self
7
+ alias_method :subclass_new, :new
8
+ end
9
+
10
+ def self.new(type)
11
+ Class.new(self) do
12
+ self.taxonomy_type = type
13
+
14
+ def self.new(*args, &block)
15
+ subclass_new(*args, &block)
16
+ end
17
+ end
18
+ end
19
+
20
+ self.parent_collection = Blog::Post.collection
21
+
22
+ attr_reader :name, :count
23
+ attr_accessor :blog
24
+
25
+ def initialize(name, count)
26
+ @name, @count = name, count.to_i
27
+ end
28
+
29
+ def path
30
+ raise "#path requires a blog to be set" unless blog
31
+ blog.send(path_method, name)
32
+ end
33
+
34
+ def to_s
35
+ name
36
+ end
37
+
38
+ def parameterize
39
+ name.parameterize
40
+ end
41
+
42
+ def hash
43
+ name.hash
44
+ end
45
+
46
+ def inspect
47
+ "#<#{self.class.name} \"#{name}\">"
48
+ end
49
+
50
+ def ==(other)
51
+ if other === self.class
52
+ self == other
53
+ else
54
+ name == other
55
+ end
56
+ end
57
+ alias eql? ==
58
+
59
+ def self.alphabetical
60
+ order(:"_id.name".asc)
61
+ end
62
+
63
+ def self.top(n=nil)
64
+ ordered = order(:value.desc)
65
+ n ? ordered.first(n) : ordered
66
+ end
67
+
68
+ def self.refresh
69
+ scoped.to_a
70
+ end
71
+
72
+ def self.for_blog(blog)
73
+ where('_id.blog_id' => blog.id).on_load do |taxonomy|
74
+ taxonomy.blog = blog
75
+ end
76
+ end
77
+
78
+ def self.from_mongo(attrs)
79
+ new(attrs['_id']['name'], attrs['value'])
80
+ end
81
+
82
+ def self.map_function
83
+ <<-MAP
84
+ function() {
85
+ if (!this.#{taxonomy_type}) { return; }
86
+
87
+ for (i in this.#{taxonomy_type}) {
88
+ emit({ blog_id: this.blog_id, name: this.#{taxonomy_type}[i] }, 1);
89
+ }
90
+ }
91
+ MAP
92
+ end
93
+
94
+ def self.reduce_function
95
+ <<-REDUCE
96
+ function(key, values) {
97
+ var count = 0;
98
+
99
+ for (i in values) {
100
+ count += values[i];
101
+ }
102
+
103
+ return count;
104
+ }
105
+ REDUCE
106
+ end
107
+
108
+ private
109
+ def path_method
110
+ "#{self.class.taxonomy_type.to_s.singularize}_path"
111
+ end
112
+ end
@@ -4,3 +4,5 @@
4
4
  %p= description
5
5
 
6
6
  = yield
7
+
8
+ = blog_pagination
@@ -1,5 +1,5 @@
1
- - title [format_month(@year, @month), @blog.title].join(' - ')
2
- - breadcrumb format_month(@year, @month)
1
+ - title [@archive.format, @blog.title].join(' - ')
2
+ - breadcrumb @archive.format
3
3
 
4
- = render :layout => "layout", :locals => { :description => "Posts from: #{format_month(@year, @month)}" } do
4
+ = render :layout => "layout", :locals => { :description => "Posts from: #{@archive.format}" } do
5
5
  = render :partial => "post", :collection => @posts
@@ -3,4 +3,4 @@
3
3
  - title @blog.title
4
4
 
5
5
  = render :layout => "layout" do
6
- = render :partial => "post", :collection => @blog.posts
6
+ = render :partial => "post", :collection => @posts
@@ -4,7 +4,7 @@
4
4
  .buttons
5
5
  = link_to "New Post", new_blog_post_path(@blog), :class => 'add'
6
6
 
7
- - @posts = @blog.all_posts.properties_for_admin.paginate(:page => params[:p])
7
+ - @posts = @blog.posts_for_admin.paginate(:page => params[:p])
8
8
 
9
9
  = table_for Blog::Post, :id => "blog-posts" do |table|
10
10
  - table.rows(@posts) do |row, post|
@@ -15,6 +15,9 @@
15
15
  %dl
16
16
  = navigation_options
17
17
  = template_options
18
+ = sidebar_expander :pagination_options do
19
+ = field :posts_per_page, :class => "fixed posts-per-page" do
20
+ = form.text_field :posts_per_page
18
21
 
19
22
  - if @blog.persisted?
20
23
  %hr
@@ -2,6 +2,10 @@ en:
2
2
  resource:
3
3
  models:
4
4
  blog/post: Blog Post
5
+
6
+ attributes:
7
+ blog/blog:
8
+ posts_per_page: Posts per page
5
9
 
6
10
  helpers:
7
11
  label:
@@ -14,6 +18,9 @@ en:
14
18
  tags_string: Tags
15
19
  published_at: Publish Date
16
20
 
17
- tabs:
18
- blog/blog:
19
- blog_settings: Blog Settings
21
+ tabs:
22
+ blog/blog:
23
+ blog_settings: Blog Settings
24
+
25
+ sidebar:
26
+ pagination_options: Pagination Options
@@ -1,4 +1,5 @@
1
1
  require "wheelhouse"
2
+ require "will_paginate"
2
3
 
3
4
  module Blog
4
5
  class Plugin < Wheelhouse::Plugin
@@ -7,6 +8,8 @@ module Blog
7
8
  # Enable blog sections by default
8
9
  config.wheelhouse.blog.sections = true
9
10
 
11
+ config.precompile << "wheelhouse-blog/admin.*"
12
+
10
13
  isolate_namespace Blog
11
14
 
12
15
  resources do
@@ -22,9 +25,5 @@ module Blog
22
25
  Section(blog.label, blog)
23
26
  end if ::Blog::Plugin.config.wheelhouse.blog.sections
24
27
  end
25
-
26
- initializer "precompile assets" do |app|
27
- app.config.assets.precompile << "wheelhouse-blog/admin.*"
28
- end
29
28
  end
30
29
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wheelhouse-blog
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,24 +9,56 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-08-02 00:00:00.000000000 Z
12
+ date: 2012-12-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: wheelhouse
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
- - - ~>
19
+ - - ! '>='
20
20
  - !ruby/object:Gem::Version
21
- version: '1.0'
21
+ version: 1.0.1
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  none: false
26
26
  requirements:
27
- - - ~>
27
+ - - ! '>='
28
28
  - !ruby/object:Gem::Version
29
- version: '1.0'
29
+ version: 1.0.1
30
+ - !ruby/object:Gem::Dependency
31
+ name: mongomodel
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 0.4.6
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 0.4.6
46
+ - !ruby/object:Gem::Dependency
47
+ name: will_paginate
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
30
62
  description: Integrates blogs into your Wheelhouse CMS site.
31
63
  email: info@wheelhousecms.com
32
64
  executables: []
@@ -41,13 +73,18 @@ files:
41
73
  - app/handlers/blog/blog_handler.rb
42
74
  - app/handlers/blog/post_handler.rb
43
75
  - app/helpers/blog/blog_helper.rb
76
+ - app/helpers/blog/pagination_helper.rb
77
+ - app/models/blog/archive.rb
44
78
  - app/models/blog/blog.rb
79
+ - app/models/blog/category.rb
45
80
  - app/models/blog/post.rb
81
+ - app/models/blog/tag.rb
82
+ - app/models/blog/taxonomy.rb
46
83
  - app/templates/blog/_layout.html.haml
47
84
  - app/templates/blog/_post.html.haml
48
85
  - app/templates/blog/archive.html.haml
49
86
  - app/templates/blog/category.html.haml
50
- - app/templates/blog/feed.xml.builder
87
+ - app/templates/blog/feed.rss.builder
51
88
  - app/templates/blog/index.html.haml
52
89
  - app/templates/blog/post.html.haml
53
90
  - app/templates/blog/tag.html.haml
@@ -58,6 +95,7 @@ files:
58
95
  - config/routes.rb
59
96
  - lib/wheelhouse-blog.rb
60
97
  - README.md
98
+ - LICENSE
61
99
  homepage: https://www.wheelhousecms.com
62
100
  licenses: []
63
101
  post_install_message: