postablr 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.md +22 -0
  3. data/Rakefile +40 -0
  4. data/app/assets/javascripts/postablr/application.js +15 -0
  5. data/app/assets/javascripts/postablr/blog.js +2 -0
  6. data/app/assets/javascripts/postablr/entries.js +2 -0
  7. data/app/assets/stylesheets/postablr/application.css +13 -0
  8. data/app/assets/stylesheets/postablr/blog.css +4 -0
  9. data/app/assets/stylesheets/postablr/entries.css +4 -0
  10. data/app/controllers/postablr/application_controller.rb +4 -0
  11. data/app/controllers/postablr/blog_controller.rb +21 -0
  12. data/app/controllers/postablr/entries_controller.rb +43 -0
  13. data/app/helpers/postablr/application_helper.rb +4 -0
  14. data/app/helpers/postablr/blog_helper.rb +27 -0
  15. data/app/helpers/postablr/entries_helper.rb +4 -0
  16. data/app/helpers/postablr/truncate_html_helper.rb +52 -0
  17. data/app/models/postablr/ar_publish.rb +172 -0
  18. data/app/models/postablr/entry.rb +37 -0
  19. data/app/models/postablr/entry/audio.rb +23 -0
  20. data/app/models/postablr/entry/image.rb +17 -0
  21. data/app/models/postablr/entry/link.rb +8 -0
  22. data/app/models/postablr/entry/post.rb +8 -0
  23. data/app/models/postablr/entry/quote.rb +7 -0
  24. data/app/models/postablr/entry/video.rb +29 -0
  25. data/app/uploaders/entry_audio_uploader.rb +55 -0
  26. data/app/uploaders/entry_photo_uploader.rb +42 -0
  27. data/app/views/layouts/postablr/application.html.erb +14 -0
  28. data/app/views/postablr/blog/_entry_display.haml +3 -0
  29. data/app/views/postablr/blog/comments/_comment.haml +10 -0
  30. data/app/views/postablr/blog/comments/_form.haml +6 -0
  31. data/app/views/postablr/blog/comments/create.js.haml +5 -0
  32. data/app/views/postablr/blog/comments/new.html.haml +0 -0
  33. data/app/views/postablr/blog/comments/new.js.haml +2 -0
  34. data/app/views/postablr/blog/entries/show.haml +39 -0
  35. data/app/views/postablr/blog/show.html.haml +46 -0
  36. data/app/views/postablr/blog/show/_aside.haml +19 -0
  37. data/app/views/postablr/blog/show/_audio.haml +13 -0
  38. data/app/views/postablr/blog/show/_image.haml +13 -0
  39. data/app/views/postablr/blog/show/_link.haml +11 -0
  40. data/app/views/postablr/blog/show/_post.haml +14 -0
  41. data/app/views/postablr/blog/show/_quote.haml +12 -0
  42. data/app/views/postablr/blog/show/_video.haml +16 -0
  43. data/app/views/postablr/entries/_entries_menu.haml +14 -0
  44. data/app/views/postablr/entries/_entry.haml +4 -0
  45. data/app/views/postablr/entries/_form.haml +58 -0
  46. data/app/views/postablr/entries/edit.haml +1 -0
  47. data/app/views/postablr/entries/fields/_audio.haml +4 -0
  48. data/app/views/postablr/entries/fields/_image.haml +2 -0
  49. data/app/views/postablr/entries/fields/_link.haml +3 -0
  50. data/app/views/postablr/entries/fields/_post.haml +3 -0
  51. data/app/views/postablr/entries/fields/_quote.haml +3 -0
  52. data/app/views/postablr/entries/fields/_video.haml +2 -0
  53. data/app/views/postablr/entries/index.html.haml +11 -0
  54. data/app/views/postablr/entries/index.js.haml +2 -0
  55. data/app/views/postablr/entries/list/_audio.haml +14 -0
  56. data/app/views/postablr/entries/list/_image.haml +12 -0
  57. data/app/views/postablr/entries/list/_link.haml +11 -0
  58. data/app/views/postablr/entries/list/_post.haml +14 -0
  59. data/app/views/postablr/entries/list/_quote.haml +11 -0
  60. data/app/views/postablr/entries/list/_video.haml +17 -0
  61. data/app/views/postablr/entries/new.html.haml +2 -0
  62. data/app/views/postablr/entries/show.html.haml +5 -0
  63. data/config/routes.rb +20 -0
  64. data/db/migrate/20121222195404_create_postablr_entries.rb +18 -0
  65. data/db/migrate/20121222195417_create_postablr_entry_videos.rb +16 -0
  66. data/db/migrate/20121222200141_create_postablr_entry_quotes.rb +10 -0
  67. data/db/migrate/20121222200237_create_postablr_entry_posts.rb +10 -0
  68. data/db/migrate/20121222200303_create_postablr_entry_links.rb +10 -0
  69. data/db/migrate/20121222200402_create_postablr_entry_images.rb +12 -0
  70. data/db/migrate/20121222200810_create_postablr_entry_audios.rb +12 -0
  71. data/lib/postablr.rb +4 -0
  72. data/lib/postablr/engine.rb +11 -0
  73. data/lib/postablr/version.rb +3 -0
  74. data/lib/tasks/postablr_tasks.rake +4 -0
  75. data/test/factories/postablr_entries.rb +16 -0
  76. data/test/factories/postablr_entry_audios.rb +9 -0
  77. data/test/factories/postablr_entry_images.rb +10 -0
  78. data/test/factories/postablr_entry_links.rb +8 -0
  79. data/test/factories/postablr_entry_posts.rb +9 -0
  80. data/test/factories/postablr_entry_quotes.rb +8 -0
  81. data/test/factories/postablr_entry_videos.rb +14 -0
  82. metadata +358 -0
@@ -0,0 +1,20 @@
1
+ Copyright 2012 YOURNAME
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,22 @@
1
+ # Postablr
2
+
3
+ Postablr is a mountable blog engine for rails 3
4
+
5
+ but is a work in progress, don´t use in production!
6
+
7
+ ## Niceties
8
+
9
+ + Devise compatible
10
+ + Tumblr style for post types (image, text, video, quote)
11
+ + Sanitize html
12
+
13
+ + TODO
14
+ + tagging support - depend on acts_as_taggable_on
15
+ + Rich text editor support
16
+ + blogs support if many blogs in site
17
+
18
+ # instalation
19
+
20
+ gem install postablr
21
+
22
+ rake postablr:install:migrations
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'Postablr'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
24
+ load 'rails/tasks/engine.rake'
25
+
26
+
27
+
28
+ Bundler::GemHelper.install_tasks
29
+
30
+ require 'rake/testtask'
31
+
32
+ Rake::TestTask.new(:test) do |t|
33
+ t.libs << 'lib'
34
+ t.libs << 'test'
35
+ t.pattern = 'test/**/*_test.rb'
36
+ t.verbose = false
37
+ end
38
+
39
+
40
+ task :default => :test
@@ -0,0 +1,15 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // the compiled file.
9
+ //
10
+ // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11
+ // GO AFTER THE REQUIRES BELOW.
12
+ //
13
+ //= require jquery
14
+ //= require jquery_ujs
15
+ //= require_tree .
@@ -0,0 +1,2 @@
1
+ // Place all the behaviors and hooks related to the matching controller here.
2
+ // All this logic will automatically be available in application.js.
@@ -0,0 +1,2 @@
1
+ // Place all the behaviors and hooks related to the matching controller here.
2
+ // All this logic will automatically be available in application.js.
@@ -0,0 +1,13 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the top of the
9
+ * compiled file, but it's generally better to create a new file per style scope.
10
+ *
11
+ *= require_self
12
+ *= require_tree .
13
+ */
@@ -0,0 +1,4 @@
1
+ /*
2
+ Place all the styles related to the matching controller here.
3
+ They will automatically be included in application.css.
4
+ */
@@ -0,0 +1,4 @@
1
+ /*
2
+ Place all the styles related to the matching controller here.
3
+ They will automatically be included in application.css.
4
+ */
@@ -0,0 +1,4 @@
1
+ module Postablr
2
+ class ApplicationController < ActionController::Base
3
+ end
4
+ end
@@ -0,0 +1,21 @@
1
+ require_dependency "postablr/application_controller"
2
+
3
+ module Postablr
4
+ class BlogController < ApplicationController
5
+
6
+ def show
7
+ @entries = Postablr::Entry.includes(:postable).published.order("postablr_entries.postable_type.created_at desc").page(params[:page]).per(6)
8
+ end
9
+
10
+ def filter
11
+ @entries = Postablr::Entry.includes(:postable).published.where("postablr_entries.postable_type =?", "Entry::#{params[:filter].capitalize}").order("postablr_entries.created_at desc").page(params[:page]).per(6)
12
+ render :show
13
+ end
14
+
15
+ def tags_get
16
+ @q = params[:tag]
17
+ @tags = ActsAsTaggableOn::Tag.where{( name =~ "#{@q}%")}
18
+ render :json=> @tags.collect{ |tag| {"caption"=> tag.name, "value"=>tag.id.to_s } }
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,43 @@
1
+ require_dependency "postablr/application_controller"
2
+
3
+ module Postablr
4
+ class EntriesController < ApplicationController
5
+
6
+ before_filter :authenticate_user!
7
+
8
+ inherit_resources
9
+
10
+ respond_to :js
11
+
12
+ def new
13
+ @entry = current_user.entries.new
14
+ @entry.postable = "Postablr::Entry::#{params[:type].capitalize}".constantize.send(:new)
15
+ end
16
+
17
+ def create
18
+ @entry = current_user.entries.new(params[:entry].except("postable_attributes"))
19
+ @entry.tag_list = ActsAsTaggableOn::Tag.find(params[:tag_list_input]).map(&:name) unless params[:tag_list_input].blank?
20
+ @entry.postable = "Postablr::Entry::#{params[:type].capitalize}".constantize.new(params[:entry][:postable_attributes])
21
+ create!{
22
+ if @entry.errors.blank?
23
+ entry_url(@entry)
24
+ end
25
+ }
26
+ end
27
+
28
+ def update
29
+ @entry = current_user.entries.find(params[:post_id])
30
+ @entry.tag_list = ActsAsTaggableOn::Tag.find(params[:tag_list_input]).map(&:name) unless params[:tag_list_input].blank?
31
+ update!{ entry_url(@entry) if @entry.errors.blank? }
32
+ end
33
+
34
+ protected
35
+ def begin_of_association_chain
36
+ current_user
37
+ end
38
+
39
+ def collection
40
+ @entries ||= end_of_association_chain.order("created_at desc") #.page(params[:page]).per(6)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,4 @@
1
+ module Postablr
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,27 @@
1
+ module Postablr
2
+ module BlogHelper
3
+ def sanitize_clean(html, truncate = false, link=nil)
4
+ unless truncate
5
+ Sanitize.clean(html, Sanitize::Config::RESTRICTED)
6
+ else
7
+ truncate_html(clean_html( html ), truncate , link )
8
+ end
9
+ end
10
+
11
+ def sanitize_strict(html, truncate = false, link=nil)
12
+ unless truncate
13
+ Sanitize.clean(html, Sanitize::Config::RESTRICTED)
14
+ else
15
+ truncate_html(Sanitize.clean(html, Sanitize::Config::RESTRICTED), truncate , link )
16
+ end
17
+ end
18
+
19
+ def clean_html(html)
20
+ Sanitize.clean(html, Sanitize::Config::BASIC)
21
+ end
22
+
23
+ def sanitize_relaxed(html)
24
+ Sanitize.clean(html, Sanitize::Config::RELAXED)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,4 @@
1
+ module Postablr
2
+ module EntriesHelper
3
+ end
4
+ end
@@ -0,0 +1,52 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require "rubygems"
3
+ require "nokogiri"
4
+
5
+ module Postablr::TruncateHtmlHelper
6
+
7
+ def truncate_html(text, max_length = 10, ellipsis = "...")
8
+ doc = Nokogiri::HTML::DocumentFragment.parse text
9
+ # remove syntax highlighting
10
+ doc.css("div.CodeRay").each do |node|
11
+ node.remove
12
+ end
13
+ content_length = doc.inner_text.length
14
+ if content_length > max_length
15
+ doc = doc.truncate(max_length)
16
+ more = Nokogiri::HTML::DocumentFragment.parse ellipsis
17
+ doc.children.last.add_child(more.children)
18
+ doc.to_html.html_safe
19
+ else
20
+ text.to_s
21
+ end
22
+ end
23
+
24
+ end
25
+
26
+ module NokogiriTruncator
27
+ module NodeWithChildren
28
+ def truncate(max_length)
29
+ return self if inner_text.length <= max_length
30
+ truncated_node = self.dup
31
+ truncated_node.children.remove
32
+
33
+ self.children.each do |node|
34
+ remaining_length = max_length - truncated_node.inner_text.length
35
+ break if remaining_length <= 0
36
+ truncated_node.add_child node.truncate(remaining_length)
37
+ end
38
+ truncated_node
39
+ end
40
+ end
41
+
42
+ module TextNode
43
+ def truncate(max_length)
44
+ Nokogiri::XML::Text.new(content[0..(max_length - 1)], parent)
45
+ end
46
+ end
47
+
48
+ end
49
+
50
+ Nokogiri::HTML::DocumentFragment.send(:include, NokogiriTruncator::NodeWithChildren)
51
+ Nokogiri::XML::Element.send(:include, NokogiriTruncator::NodeWithChildren)
52
+ Nokogiri::XML::Text.send(:include, NokogiriTruncator::TextNode)
@@ -0,0 +1,172 @@
1
+ # -*- encoding : utf-8 -*-
2
+ # extracted from https://github.com/ismasan/ar_publish_control
3
+ module Postablr
4
+ module ArPublish
5
+
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ #extend ClassMethods
10
+ @statuses = [:published, :draft, :upcoming, :expired]
11
+
12
+ after_initialize :fill_default_publish
13
+ end
14
+
15
+ module ClassMethods
16
+ # == Configuration options
17
+ #
18
+ # Right now this plugin has only one configuration option. Models with no publication dates
19
+ # are by default published, not unpublished. If you want to hide your model when it has no
20
+ # explicit publication date set, you can turn off this behaviour with the
21
+ # +publish_by_default+ (defaults to <tt>true</tt>) option like so:
22
+ #
23
+ # class Post < ActiveRecord::Base
24
+ # publish_control :publish_by_default => false
25
+ # end
26
+ #
27
+ # == Database Schema
28
+ #
29
+ # The model that you're publishing needs to have two special date attributes:
30
+ #
31
+ # * publish_at
32
+ # * unpublish_at
33
+ #
34
+ # These attributes have no further requirements or required validations; they
35
+ # just need to be <tt>datetime</tt>-columns.
36
+ #
37
+ # You can use a migration like this to add these columns to your model:
38
+ #
39
+ # class AddPublicationDatesToPosts < ActiveRecord::Migration
40
+ # def self.up
41
+ # add_column :posts, :publish_at, :datetime
42
+ # add_column :posts, :unpublish_at, :datetime
43
+ # end
44
+ #
45
+ # def self.down
46
+ # remove_column :posts, :publish_at
47
+ # remove_column :posts, :unpublish_at
48
+ # end
49
+ # end
50
+ #
51
+ def publish_control(options = { :publish_by_default => true })
52
+ # don't allow multiple calls
53
+ #return if self.included_modules.include?(ArPublishControl::Publishable::InstanceMethods)
54
+ #send :include, ArPublishControl::Publishable::InstanceMethods
55
+
56
+ scope :published, lambda{{:conditions => published_conditions}}
57
+ scope :unpublished, lambda{{:conditions => unpublished_conditions}}
58
+ scope :upcoming, lambda{{:conditions => upcoming_conditions}}
59
+ scope :expired, lambda {{:conditions => expired_conditions}}
60
+ scope :draft, :conditions => {:is_published => false}
61
+
62
+ scope :published_only, lambda {|*args|
63
+ bool = (args.first.nil? ? true : (args.first)) # nil = true by default
64
+ bool ? {:conditions => published_conditions} : {}
65
+ }
66
+
67
+ validate :validate_publish_date_consistency
68
+ before_create :publish_by_default if options[:publish_by_default]
69
+ end
70
+
71
+ # Takes a block whose containing SQL queries are limited to
72
+ # published objects. You can pass a boolean flag indicating
73
+ # whether this scope should be applied or not--for example,
74
+ # you might want to disable it when the user is an administrator.
75
+ # By default the scope is applied.
76
+ #
77
+ # Example usage:
78
+ #
79
+ # Post.published_only(!logged_in?)
80
+ # @post.comments.published_only(!logged_in?)
81
+ #
82
+
83
+ protected
84
+
85
+ # returns a string for use in SQL to filter the query to unpublished results only
86
+ # Meant for internal use only
87
+ def unpublished_conditions
88
+ t = Time.now
89
+ ["(#{table_name}.is_published = ? OR #{table_name}.publish_at > ?) OR (#{table_name}.unpublish_at IS NOT NULL AND #{table_name}.unpublish_at < ?)",false,t,t]
90
+ end
91
+
92
+ # return a string for use in SQL to filter the query to published results only
93
+ # Meant for internal use only
94
+ def published_conditions
95
+ t = Time.now
96
+ ["(#{table_name}.is_published = ? AND #{table_name}.publish_at <= ?) AND (#{table_name}.unpublish_at IS NULL OR #{table_name}.unpublish_at > ?)",true,t,t]
97
+ end
98
+
99
+ def upcoming_conditions
100
+ t = Time.now
101
+ ["(#{table_name}.is_published = ? AND #{table_name}.publish_at > ?)",true,t]
102
+ end
103
+
104
+ def expired_conditions
105
+ t = Time.now
106
+ ["(#{table_name}.unpublish_at IS NOT NULL AND #{table_name}.unpublish_at < ?)",t]
107
+ end
108
+ end
109
+
110
+
111
+ def fill_default_publish
112
+ self.publish_at = Time.now if self.publish_at.nil?
113
+ end
114
+
115
+ # ActiveRecrod callback fired on +before_create+ to make
116
+ # sure a new object always gets a publication date; if
117
+ # none is supplied it defaults to the creation date.
118
+ def publish_by_default
119
+ self.is_published = true if is_published.nil?
120
+ end
121
+
122
+ # Validate that unpublish_at is greater than publish_at
123
+ # publish_at must not be nil
124
+ def validate_publish_date_consistency
125
+ if unpublish_at && publish_at && unpublish_at <= publish_at
126
+ errors.add(:unpublish_at,"should be greater than publication date or empty")
127
+ end
128
+ end
129
+
130
+ # Return whether the current object is published or not
131
+ def published?
132
+ (is_published? && (publish_at <=> Time.now) <= 0) && (unpublish_at.nil? || (unpublish_at <=> Time.now) >= 0)
133
+ end
134
+
135
+ def upcoming?
136
+ (is_published? && publish_at > Time.now)
137
+ end
138
+
139
+ def expired?
140
+ (!unpublish_at.nil? && unpublish_at < Time.now)
141
+ end
142
+
143
+ # Indefinitely publish the current object right now
144
+ def publish
145
+ return if published?
146
+ self.is_published = true
147
+ self.publish_at = Time.now
148
+ self.unpublish_at = nil
149
+ end
150
+
151
+ # Same as publish, but immediatly saves the object.
152
+ # Raises an error when saving fails.
153
+ def publish!
154
+ publish
155
+ save!
156
+ end
157
+
158
+ # Immediatly unpublish the current object
159
+ def unpublish
160
+ return unless published?
161
+ self.is_published = false
162
+ end
163
+
164
+ # Same as unpublish, but immediatly saves the object.
165
+ # Raises an error when saving files.
166
+ def unpublish!
167
+ unpublish
168
+ save!
169
+ end
170
+
171
+ end
172
+ end