comfy_blog 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. data/Gemfile +11 -0
  2. data/LICENSE +20 -0
  3. data/README.md +23 -0
  4. data/Rakefile +21 -0
  5. data/VERSION +1 -0
  6. data/app/assets/images/rails.png +0 -0
  7. data/app/assets/javascripts/application.js +8 -0
  8. data/app/assets/stylesheets/comfy_blog/admin.css +57 -0
  9. data/app/assets/stylesheets/comfy_blog/application.css +73 -0
  10. data/app/assets/stylesheets/comfy_blog/reset.css +1 -0
  11. data/app/controllers/application_controller.rb +5 -0
  12. data/app/controllers/blog/admin/base_controller.rb +3 -0
  13. data/app/controllers/blog/admin/comments_controller.rb +34 -0
  14. data/app/controllers/blog/admin/posts_controller.rb +57 -0
  15. data/app/controllers/blog/posts_controller.rb +33 -0
  16. data/app/helpers/blog/application_helper.rb +16 -0
  17. data/app/models/.gitkeep +0 -0
  18. data/app/models/blog/comment.rb +21 -0
  19. data/app/models/blog/post.rb +81 -0
  20. data/app/models/blog/tag.rb +25 -0
  21. data/app/models/blog/tagging.rb +22 -0
  22. data/app/views/blog/admin/_html_head.html.erb +1 -0
  23. data/app/views/blog/admin/_navigation.html.erb +1 -0
  24. data/app/views/blog/admin/comments/_comment.html.erb +22 -0
  25. data/app/views/blog/admin/comments/destroy.js.erb +3 -0
  26. data/app/views/blog/admin/comments/index.html.erb +10 -0
  27. data/app/views/blog/admin/comments/publish.js.erb +1 -0
  28. data/app/views/blog/admin/posts/_form.html.erb +24 -0
  29. data/app/views/blog/admin/posts/_post.html.erb +21 -0
  30. data/app/views/blog/admin/posts/edit.html.erb +5 -0
  31. data/app/views/blog/admin/posts/index.html.erb +9 -0
  32. data/app/views/blog/admin/posts/new.html.erb +5 -0
  33. data/app/views/blog/posts/_post.html.erb +18 -0
  34. data/app/views/blog/posts/index.html.erb +5 -0
  35. data/app/views/blog/posts/show.html.erb +1 -0
  36. data/app/views/layouts/application.html.erb +17 -0
  37. data/comfy_blog.gemspec +125 -0
  38. data/config.ru +4 -0
  39. data/config/application.rb +48 -0
  40. data/config/boot.rb +6 -0
  41. data/config/database.yml +25 -0
  42. data/config/environment.rb +5 -0
  43. data/config/environments/development.rb +33 -0
  44. data/config/environments/production.rb +51 -0
  45. data/config/environments/test.rb +42 -0
  46. data/config/initializers/comfy_blog.rb +18 -0
  47. data/config/initializers/secret_token.rb +3 -0
  48. data/config/initializers/wrap_parameters.rb +14 -0
  49. data/config/locales/en.yml +5 -0
  50. data/config/routes.rb +29 -0
  51. data/db/migrate/01_create_comfy_blog.rb +55 -0
  52. data/db/schema.rb +63 -0
  53. data/db/seeds.rb +7 -0
  54. data/lib/comfy_blog.rb +26 -0
  55. data/lib/comfy_blog/configuration.rb +33 -0
  56. data/lib/comfy_blog/core_ext/string.rb +8 -0
  57. data/lib/comfy_blog/engine.rb +20 -0
  58. data/lib/comfy_blog/form_builder.rb +50 -0
  59. data/lib/generators/README +10 -0
  60. data/lib/generators/blog_generator.rb +31 -0
  61. data/script/rails +6 -0
  62. data/test/fixtures/.gitkeep +0 -0
  63. data/test/fixtures/blog/comments.yml +6 -0
  64. data/test/fixtures/blog/posts.yml +8 -0
  65. data/test/fixtures/blog/taggings.yml +7 -0
  66. data/test/fixtures/blog/tags.yml +9 -0
  67. data/test/functional/.gitkeep +0 -0
  68. data/test/functional/blog/admin/comments_controller_test.rb +38 -0
  69. data/test/functional/blog/admin/posts_controller_test.rb +100 -0
  70. data/test/functional/blog/posts_controller_test.rb +93 -0
  71. data/test/test_helper.rb +40 -0
  72. data/test/unit/.gitkeep +0 -0
  73. data/test/unit/comment_test.rb +34 -0
  74. data/test/unit/configuration_test.rb +19 -0
  75. data/test/unit/post_test.rb +121 -0
  76. data/test/unit/tag_test.rb +44 -0
  77. data/test/unit/tagging_test.rb +30 -0
  78. metadata +172 -0
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'rails', '>=3.1.0'
4
+ gem 'will_paginate', '~>3.0.2'
5
+ gem 'rails_autolink', '~>1.0.4'
6
+ gem 'jquery-rails', '>=1.0.0'
7
+
8
+ group :test do
9
+ gem 'sqlite3'
10
+ gem 'jeweler'
11
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Oleg Khabarov, The Working Group
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,23 @@
1
+ # ComfyBlog
2
+
3
+ ComfyBlog is an simple blog management engine for Rails 3.1 apps. As a bonus it integrates seamlessly with [ComfortableMexicanSofa](https://github.com/comfy/comfortable-mexican-sofa) CMS Engine
4
+
5
+ ## Installation
6
+
7
+ Add gem definition to your Gemfile:
8
+
9
+ gem 'comfy_blog'
10
+
11
+ Then from the Rails project's root run:
12
+
13
+ bundle install
14
+ rails g blog
15
+ rake db:migrate
16
+
17
+ ## Usage
18
+
19
+ TODO
20
+
21
+ Sofa Blog is released under the [MIT license](https://github.com/comfy/comfy-blog/raw/master/LICENSE)
22
+
23
+ Copyright 2011 Oleg Khabarov, [The Working Group Inc](http://www.twg.ca)
@@ -0,0 +1,21 @@
1
+ require File.expand_path('../config/application', __FILE__)
2
+ require 'rubygems'
3
+ require 'rake'
4
+
5
+ ComfyBlog::Application.load_tasks
6
+
7
+ begin
8
+ require 'jeweler'
9
+ Jeweler::Tasks.new do |gem|
10
+ gem.name = 'comfy_blog'
11
+ gem.homepage = 'http://github.com/comfy/comfy-blog'
12
+ gem.license = 'MIT'
13
+ gem.summary = 'ComfyBlog is a blog engine for Rails 3.1 apps (and ComfortableMexicanSofa)'
14
+ gem.description = ''
15
+ gem.email = 'oleg@twg.ca'
16
+ gem.authors = ['Oleg Khabarov', 'The Working Group Inc.']
17
+ end
18
+ Jeweler::GemcutterTasks.new
19
+ rescue LoadError
20
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
21
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.0
@@ -0,0 +1,8 @@
1
+ // This is a manifest file that'll be compiled into including all the files listed below.
2
+ // Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
3
+ // be included in the compiled file accessible from http://example.com/assets/application.js
4
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
5
+ // the compiled file.
6
+ //
7
+ //= require jquery
8
+ //= require jquery_ujs
@@ -0,0 +1,57 @@
1
+ /* === List items === */
2
+ ul.list.posts li .item .label,
3
+ ul.list.comments li .item .label {
4
+ margin-left: 0px ! important;
5
+ }
6
+ ul.list.comments li .item .label .sublabel em {
7
+ margin-left: 5px;
8
+ }
9
+ ul.list.comments li .item .content {
10
+ font-size: 11px;
11
+ font-weight: normal;
12
+ margin: 5px 0px 0 35px;
13
+ }
14
+
15
+ /* === Tags === */
16
+ span.tag {
17
+ font-size: 10px;
18
+ padding: 1px 5px;
19
+ background-color: #d5d5d5;
20
+ border-radius: 2px;
21
+ -moz-border-radius: 2px;
22
+ }
23
+
24
+
25
+ /* === Autocomplete === */
26
+ .ac_results {
27
+ padding: 0px;
28
+ border: 1px solid #999;
29
+ background-color: white;
30
+ overflow: hidden;
31
+ z-index: 99999;
32
+ color: #666;
33
+ }
34
+ .ac_results ul {
35
+ width: 100%;
36
+ list-style-position: outside;
37
+ list-style: none;
38
+ padding: 0;
39
+ margin: 0;
40
+ }
41
+ .ac_results ul li {
42
+ margin: 0px;
43
+ padding: 2px 5px;
44
+ cursor: default;
45
+ display: block;
46
+ font-size: 12px;
47
+ line-height: 16px;
48
+ overflow: hidden;
49
+ }
50
+
51
+ .ac_odd {
52
+ background-color: #eee;
53
+ }
54
+ .ac_over {
55
+ background-color: #C4EAF6;
56
+ color: #000;
57
+ }
@@ -0,0 +1,73 @@
1
+ /*
2
+ = require ./reset
3
+ = require_self
4
+ */
5
+
6
+ body {
7
+ padding: 25px;
8
+ font: 13px/18px Arial, sans-serif;
9
+ }
10
+ h1 {
11
+ font: bold 26px/34px Arial, sans-serif;
12
+ margin-bottom: 15px;
13
+ }
14
+ h2 {
15
+ font: bold 18px/20px Arial, sans-serif;
16
+ margin-bottom: 10px;
17
+ }
18
+ a {
19
+ color: #3875D7;
20
+ text-decoration: none;
21
+ }
22
+ a:hover {
23
+ text-decoration: underline;
24
+ }
25
+ table.formatted td,
26
+ table.formatted th {
27
+ padding: 2px 5px;
28
+ border-bottom: 1px dotted #ccc;
29
+ }
30
+ table.formatted th {
31
+ background-color: #666;
32
+ color: #fff;
33
+ }
34
+ table.formatted td img {
35
+ float: left;
36
+ padding: 2px;
37
+ margin-right: 2px;
38
+ }
39
+ table.formatted td .sublabel {
40
+ font-size: 11px;
41
+ line-height: 11px;
42
+ color: #888;
43
+ }
44
+
45
+ .form_element {
46
+ overflow: hidden;
47
+ margin-bottom: 5px;
48
+ }
49
+ .form_element .label {
50
+ width: 125px;
51
+ float: left;
52
+ text-align: right;
53
+ }
54
+ .form_element .value,
55
+ .form_element .errors {
56
+ margin-left: 135px;
57
+ }
58
+ .form_element .value input[type='text'],
59
+ .form_element .value textarea {
60
+ width: 400px;
61
+ }
62
+
63
+ .jcrop-holder {
64
+ margin-bottom: 15px;
65
+ }
66
+
67
+ .flash{
68
+ border: 2px solid #ccc;
69
+ padding: 10px;
70
+ margin-bottom: 15px;
71
+ text-align: center;
72
+ }
73
+
@@ -0,0 +1 @@
1
+ html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var,optgroup{font-style:inherit;font-weight:inherit;}del,ins{text-decoration:none;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:baseline;}sub{vertical-align:baseline;}legend{color:#000;}input,button,textarea,select,optgroup,option{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;}input,button,textarea,select{*font-size:100%;}
@@ -0,0 +1,5 @@
1
+ class ApplicationController < ActionController::Base
2
+ protect_from_forgery
3
+
4
+ layout 'application'
5
+ end
@@ -0,0 +1,3 @@
1
+ class Blog::Admin::BaseController < ComfyBlog.config.admin_controller.to_s.constantize
2
+ # ...
3
+ end
@@ -0,0 +1,34 @@
1
+ class Blog::Admin::CommentsController < Blog::Admin::BaseController
2
+
3
+ before_filter :load_post, :only => [:index]
4
+ before_filter :load_comment, :only => [:destroy, :publish]
5
+
6
+ def index
7
+ @comments = @post ? @post.comments : Blog::Comment.all
8
+ end
9
+
10
+ def destroy
11
+ @comment.destroy
12
+ end
13
+
14
+ def publish
15
+ @comment.update_attribute(:is_published, true)
16
+ end
17
+
18
+ protected
19
+
20
+ def load_post
21
+ return unless params[:post_id]
22
+ @post = Blog::Post.find(params[:post_id])
23
+ rescue ActiveRecord::RecordNotFound
24
+ flash[:error] = 'Blog Post not found'
25
+ redirect_to :action => :index
26
+ end
27
+
28
+ def load_comment
29
+ @comment = Blog::Comment.find(params[:id])
30
+ rescue ActiveRecord::RecordNotFound
31
+ # ... do nothing
32
+ end
33
+
34
+ end
@@ -0,0 +1,57 @@
1
+ class Blog::Admin::PostsController < Blog::Admin::BaseController
2
+
3
+ before_filter :build_post, :only => [:new, :create]
4
+ before_filter :load_post, :only => [:edit, :update, :destroy]
5
+
6
+ def index
7
+ @posts = Blog::Post.paginate :page => params[:page]
8
+ end
9
+
10
+ def new
11
+ render
12
+ end
13
+
14
+ def create
15
+ @post.save!
16
+ flash.now[:notice] = 'Blog Post created'
17
+ render :action => :new
18
+
19
+ rescue ActiveRecord::RecordInvalid
20
+ flash.now[:error] = 'Failed to create Blog Post'
21
+ render :action => :new
22
+ end
23
+
24
+ def edit
25
+ render
26
+ end
27
+
28
+ def update
29
+ @post.update_attributes!(params[:post])
30
+ flash.now[:notice] = 'Blog Post updated'
31
+ render :action => :edit
32
+
33
+ rescue ActiveRecord::RecordInvalid
34
+ flash.now[:error] = 'Failed to update Blog Post'
35
+ render :action => :edit
36
+ end
37
+
38
+ def destroy
39
+ @post.destroy
40
+ flash[:notice] = 'Blog Post removed'
41
+ redirect_to :action => :index
42
+ end
43
+
44
+ protected
45
+
46
+ def load_post
47
+ @post = Blog::Post.find(params[:id])
48
+ rescue ActiveRecord::RecordNotFound
49
+ flash[:error] = 'Blog Post not found'
50
+ redirect_to :action => :index
51
+ end
52
+
53
+ def build_post
54
+ @post = Blog::Post.new(params[:post])
55
+ end
56
+
57
+ end
@@ -0,0 +1,33 @@
1
+ class Blog::PostsController < ApplicationController
2
+
3
+ layout ComfyBlog.config.public_layout
4
+
5
+ def index
6
+ scope = if params[:tag]
7
+ Blog::Post.published.tagged_with(params[:tag])
8
+ elsif params[:category]
9
+ Blog::Post.published.categorized_as(params[:category])
10
+ elsif params[:year]
11
+ scope = Blog::Post.published.for_year(params[:year])
12
+ params[:month] ? scope.for_month(params[:month]) : scope
13
+ else
14
+ Blog::Post.published
15
+ end
16
+
17
+ @posts = scope.paginate :per_page => ComfyBlog.config.posts_per_page, :page => params[:page]
18
+ end
19
+
20
+ def show
21
+ @post = if params[:slug] && params[:year] && params[:month]
22
+ Blog::Post.published.find_by_year_and_year_and_slug!(params[:year], params[:month], params[:slug])
23
+ else
24
+ Blog::Post.published.find(params[:id])
25
+ end
26
+ rescue ActiveRecord::RecordNotFound
27
+ if defined? ComfortableMexicanSofa
28
+ render :cms_page => '/404', :status => 404
29
+ else
30
+ render :text => 'Post not found', :status => 404
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,16 @@
1
+ module Blog::ApplicationHelper
2
+
3
+ def comfy_blog_form_for(record_or_name_or_array, *args, &proc)
4
+ options = args.extract_options!
5
+ form_for(
6
+ record_or_name_or_array,
7
+ *(args << options.merge(:builder => ComfyBlog.config.form_builder.to_s.constantize)),
8
+ &proc
9
+ )
10
+ end
11
+
12
+ def blog_post_path(post)
13
+
14
+ end
15
+
16
+ end
File without changes
@@ -0,0 +1,21 @@
1
+ class Blog::Comment < ActiveRecord::Base
2
+
3
+ set_table_name :blog_comments
4
+
5
+ attr_accessible :author,
6
+ :email,
7
+ :content
8
+
9
+ # -- Relationships --------------------------------------------------------
10
+ belongs_to :post
11
+
12
+ # -- Validations ----------------------------------------------------------
13
+ validates :post_id, :content, :author, :email,
14
+ :presence => true
15
+ validates :email,
16
+ :format => { :with => /^([\w.%-+]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i }
17
+
18
+ # -- Scopes ---------------------------------------------------------------
19
+ scope :published, where(:is_published => true)
20
+
21
+ end
@@ -0,0 +1,81 @@
1
+ class Blog::Post < ActiveRecord::Base
2
+
3
+ set_table_name :blog_posts
4
+
5
+ # -- Attributes -----------------------------------------------------------
6
+ attr_accessor :tag_names,
7
+ :category_ids
8
+
9
+ # -- Relationships --------------------------------------------------------
10
+ has_many :comments, :dependent => :destroy
11
+ has_many :taggings, :dependent => :destroy
12
+ has_many :tags, :through => :taggings
13
+
14
+ # -- Validations ----------------------------------------------------------
15
+ validates :title, :slug, :year, :month, :content,
16
+ :presence => true
17
+ validates :slug,
18
+ :uniqueness => { :scope => [:year, :month] }
19
+
20
+ # -- Scopes ---------------------------------------------------------------
21
+ default_scope order('created_at DESC')
22
+
23
+ scope :published, where(:is_published => true)
24
+ scope :for_year, lambda { |year|
25
+ where(:year => year)
26
+ }
27
+ scope :for_month, lambda { |month|
28
+ where(:month => month)
29
+ }
30
+ scope :tagged_with, lambda { |tag|
31
+ joins(:tags).where('blog_tags.name' => tag, 'blog_tags.is_category' => false)
32
+ }
33
+ scope :categorized_as, lambda { |tag|
34
+ joins(:tags).where('blog_tags.name' => tag, 'blog_tags.is_category' => true)
35
+ }
36
+
37
+ # -- Callbacks ------------------------------------------------------------
38
+ before_validation :set_slug,
39
+ :set_date
40
+ after_save :sync_tags,
41
+ :sync_categories
42
+
43
+ # -- Instance Methods -----------------------------------------------------
44
+ def tag_names(reload = false)
45
+ @tag_names = nil if reload
46
+ @tag_names ||= self.tags.tags.collect(&:name).join(', ')
47
+ end
48
+
49
+ protected
50
+
51
+ def set_slug
52
+ self.slug ||= self.title.to_s.slugify
53
+ end
54
+
55
+ def set_date
56
+ self.year ||= Time.zone.now.year
57
+ self.month ||= Time.zone.now.month
58
+ end
59
+
60
+ def sync_tags
61
+ return unless tag_names
62
+ self.taggings.for_tags.destroy_all
63
+ self.tag_names.split(',').map{ |t| t.strip }.uniq.each do |tag_name|
64
+ self.tags << Blog::Tag.find_or_create_by_name(tag_name)
65
+ end
66
+ end
67
+
68
+ def sync_categories
69
+ (self.category_ids || {}).each do |category_id, flag|
70
+ case flag.to_i
71
+ when 1
72
+ if category = Blog::Tag.categories.find_by_id(category_id)
73
+ category.taggings.create(:post => self)
74
+ end
75
+ when 0
76
+ self.taggings.for_categories.where(:tag_id => category_id).destroy_all
77
+ end
78
+ end
79
+ end
80
+
81
+ end