cardboard_cms 0.2.2 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +6 -0
  3. data/Gemfile +2 -2
  4. data/Gemfile.lock +47 -42
  5. data/README.md +12 -2
  6. data/app/assets/javascripts/cardboard/admin.js +1 -1
  7. data/app/controllers/pages_controller.rb +3 -35
  8. data/app/controllers/url_controller.rb +33 -0
  9. data/app/decorators/controllers/application_controller_decorator.rb +3 -1
  10. data/app/helpers/cardboard/public_helper.rb +1 -1
  11. data/app/helpers/cardboard/resource_helper.rb +6 -0
  12. data/app/models/cardboard/field.rb +2 -1
  13. data/app/models/cardboard/field/rich_text.rb +5 -5
  14. data/app/models/cardboard/page.rb +30 -72
  15. data/app/models/cardboard/setting.rb +0 -2
  16. data/app/models/cardboard/template.rb +5 -0
  17. data/app/models/cardboard/url.rb +91 -0
  18. data/app/views/cardboard/pages/_sidebar.html.slim +1 -1
  19. data/app/views/layouts/cardboard/_main_topbar.html.slim +1 -1
  20. data/cardboard.gemspec +4 -2
  21. data/config/routes.rb +3 -14
  22. data/db/migrate/1_create_cardboard.rb +13 -4
  23. data/lib/cardboard/concerns/url_concern.rb +29 -0
  24. data/lib/cardboard/constraints/page_constraint.rb +7 -0
  25. data/lib/cardboard/dynamic_router.rb +34 -0
  26. data/lib/cardboard/engine.rb +3 -1
  27. data/lib/cardboard/helpers/content_for_in_controllers.rb +23 -0
  28. data/lib/cardboard/helpers/seed.rb +17 -10
  29. data/lib/cardboard/version.rb +1 -1
  30. data/lib/generators/cardboard/resource/resource_generator.rb +2 -2
  31. data/lib/generators/cardboard/resource/templates/slim/edit.html.slim +1 -1
  32. data/lib/generators/cardboard/resource/templates/slim/new.html.slim +1 -1
  33. data/test/dummy/app/controllers/blog_controller.rb +2 -0
  34. data/test/dummy/app/views/blog/index.html.slim +1 -0
  35. data/test/dummy/app/views/layouts/application.html.slim +1 -0
  36. data/test/dummy/app/views/pages/about_us.html.slim +1 -0
  37. data/test/dummy/app/views/pages/history.html.slim +3 -0
  38. data/test/dummy/app/views/{templates → pages}/home.html.slim +0 -0
  39. data/test/dummy/config/cardboard.yml +6 -2
  40. data/test/dummy/db/migrate/20140312180204_create_cardboard.rb +72 -0
  41. data/test/dummy/db/schema.rb +19 -6
  42. data/test/models/url_test.rb +11 -0
  43. metadata +32 -28
  44. data/.ruby-gemset +0 -1
  45. data/.ruby-version +0 -1
  46. data/app/views/cardboard/pages/_error.html.slim +0 -7
  47. data/app/views/cardboard/pages/_seo.html.slim +0 -6
  48. data/app/views/cardboard/pages/show.html.slim +0 -9
  49. data/test/dummy/app/views/templates/about-us.html.slim +0 -1
  50. data/test/dummy/test/fixtures/admins.yml +0 -7
  51. data/test/dummy/test/functional/admins_controller_test.rb +0 -49
  52. data/test/dummy/test/unit/admin_test.rb +0 -7
  53. data/test/dummy/test/unit/helpers/admins_helper_test.rb +0 -4
@@ -1,7 +1,7 @@
1
1
  module Cardboard
2
2
  class Field::RichText < Field
3
3
  validate :is_required
4
- before_save :sanitize_value
4
+ # before_save :sanitize_value
5
5
 
6
6
  def value
7
7
  self.value_uid.try(:html_safe)
@@ -13,10 +13,10 @@ module Cardboard
13
13
 
14
14
  private
15
15
 
16
- def sanitize_value
17
- return nil unless value_changed?
18
- self.value = ActionController::Base.helpers.sanitize(self.value, :tags => %w(strong b i u em br p div span ul ol li a pre code blockquote h1 h2 h3), :attributes => %w(class style href src width height alt))
19
- end
16
+ # def sanitize_value
17
+ # return nil unless value_changed?
18
+ # self.value = ActionController::Base.helpers.sanitize(self.value, :tags => %w(strong b i u em br p div span ul ol li a pre code blockquote h1 h2 h3 table), :attributes => %w(class style href src width height alt))
19
+ # end
20
20
 
21
21
  def is_required
22
22
  errors.add(:value, "is required") if required_field? && ActionController::Base.helpers.strip_tags(value_uid).blank?
@@ -1,38 +1,32 @@
1
1
  module Cardboard
2
2
  class Page < ActiveRecord::Base
3
-
4
3
  has_many :parts, class_name: "Cardboard::PagePart", :dependent => :destroy, :validate => true
5
4
 
6
5
  belongs_to :template, class_name: "Cardboard::Template"
7
6
 
8
- attr_accessor :parent_url, :is_root
7
+ attr_accessor :parent_url
9
8
 
10
9
  accepts_nested_attributes_for :parts, allow_destroy: true, :reject_if => :all_blank
11
10
  # TODO: allow destroy and allow all blank only if repeatable
12
11
 
13
- serialize :meta_seo, Hash
14
- serialize :slugs_backup, Array
15
-
16
- before_validation :default_values
17
- before_save :update_slugs_backup
18
-
19
- #gems
20
- acts_as_url :title, :url_attribute => :slug, :scope => :path, only_when_blank: true
21
-
12
+ include UrlConcern
22
13
  include RankedModel
23
- ranks :position, :with_same => :path
14
+ ranks :position
24
15
 
25
16
  #validations
26
- # validates_associated :parts
27
- validates :title, :path, :template, presence:true
28
- validates :slug, uniqueness: { :case_sensitive => false, :scope => :path }, presence: true
17
+ validates :title, :template, presence:true
29
18
  validates :identifier, uniqueness: {:case_sensitive => false}, :format => { :with => /\A[a-z\_0-9]+\z/,
30
19
  :message => "Only downcase letters, numbers and underscores are allowed" }, presence: true
31
- #validate all seo keys are valid meta keys + title
32
20
 
33
21
  #scopes
34
- scope :preordered, -> {order("path ASC, position ASC, slug ASC")}
22
+ scope :preordered, -> {joins(:url_object).order("cardboard_urls.path ASC, position ASC, cardboard_urls.slug ASC")}
23
+ scope :with_path, -> (p) {joins(:url_object).where("cardboard_urls.path = ?",p) }
24
+
25
+
26
+ # Hooks
27
+ before_validation :default_values
35
28
 
29
+ #delegates
36
30
 
37
31
  #class variables
38
32
  after_commit do
@@ -40,51 +34,34 @@ module Cardboard
40
34
  end
41
35
 
42
36
  #overwritten setters/getters
43
- def slug=(value)
44
- # the user can overwrite the auto generated slug
45
- self[:slug] = value.present? ? value.to_url : nil
46
- end
47
37
 
48
- def is_root=(val)
49
- self.position_position = :first if val
38
+ def self.root
39
+ #TODO: check that join work correctly
40
+ with_path('/').rank(:position).first
50
41
  end
42
+ def self.homepage; self.root; end
51
43
 
52
- def using_slug_backup?
53
- @using_slug_backup || false
44
+ def root?
45
+ return @root unless @root.nil?
46
+ @root = self.id == Page.root.id
54
47
  end
55
48
 
56
- def using_slug_backup=(value)
57
- @using_slug_backup = value
49
+ def meta_seo=(hash)
50
+ self.meta_tags = meta_tags.merge(hash)
51
+ end
52
+ def meta_seo
53
+ meta_tags.slice("description", "title")
58
54
  end
59
55
 
60
56
  #class methods
61
57
  def self.find_by_url(full_url)
62
- return nil unless full_url
63
- path, slug = self.path_and_slug(full_url)
64
- page = self.where(path: path, slug: slug).first
65
-
66
- if slug && page.nil?
67
- #use arel instead of LIKE/ILIKE
68
- page = self.where(path: path).where(self.arel_table[:slugs_backup].matches("% #{slug}\n%")).first
69
- page.using_slug_backup = true if page
70
- end
71
-
72
- page
58
+ Cardboard::Url.urlable_for(full_url, type: self.name)
73
59
  end
74
60
 
75
- def self.root
76
- # Homepage is the highest position in the root path
77
- where(path: "/").rank(:position).first
78
- end
79
- def self.homepage; self.root; end
80
-
81
- def root?
82
- @root_id ||= Page.root.id
83
- @root_id == self.id
84
- end
85
61
 
86
62
  #instance methods
87
63
 
64
+
88
65
  def template_hash
89
66
  @template_hash ||= self.template.fields
90
67
  end
@@ -128,14 +105,8 @@ module Cardboard
128
105
  self.meta_seo = hash.to_hash
129
106
  @_seo = nil
130
107
  end
131
-
132
- def url
133
- return "/" if slug.blank? #|| self.root?
134
- "#{path}#{slug}/"
135
- end
136
108
 
137
109
  def split_path
138
- # path.sub(/^\//,'').split("/") # "/path/" => ["path"]
139
110
  path[1..-1].split("/")
140
111
  end
141
112
 
@@ -172,11 +143,11 @@ module Cardboard
172
143
  end
173
144
 
174
145
  def children
175
- Cardboard::Page.where(path: url)
146
+ Cardboard::Page.with_path(url)
176
147
  end
177
148
 
178
149
  def siblings
179
- Cardboard::Page.where("path = ? AND id != ?", path, id)
150
+ Cardboard::Page.with_path(path).where("cardboard_pages.id != ?", id)
180
151
  end
181
152
 
182
153
  def depth
@@ -218,25 +189,12 @@ module Cardboard
218
189
  Rails.cache.delete("arranged_pages")
219
190
  end
220
191
 
221
- # def to_param
222
- # "#{id}-#{slug}"
223
- # end
224
-
225
192
  private
226
- def self.path_and_slug(full_url)
227
- *path, slug = full_url.sub(/^\//, '').split('/')
228
- [path.blank? ? '/' : "/#{path.join('/')}/", slug]
229
- end
230
-
231
-
232
- def update_slugs_backup
233
- return nil if !self.slug_changed? || self.slug_was.nil?
234
- self.slugs_backup |= [self.slug_was] #Yes, that's a single pipe...
235
- end
236
193
 
237
194
  def default_values
238
- self.path ||= '/'
239
- self.title ||= self.identifier.parameterize("_")
195
+ self.title ||= self.identifier.try(:parameterize, "_")
196
+ self.path ||= "/"
197
+ self.slug = self.title.try(:to_url) if self.slug.blank?
240
198
  end
241
199
  end
242
200
  end
@@ -1,7 +1,5 @@
1
1
  module Cardboard
2
2
  class Setting < ActiveRecord::Base
3
- # self.table_name = "cardboard_settings"
4
- # attr_accessible :name, :fields_attributes
5
3
 
6
4
  has_many :fields, :as => :object_with_field
7
5
  accepts_nested_attributes_for :fields, :allow_destroy => true
@@ -7,6 +7,11 @@ module Cardboard
7
7
 
8
8
  validates :identifier, uniqueness: {:case_sensitive => false}, :format => { :with => /\A[a-z\_0-9]+\z/,
9
9
  :message => "Only downcase letters, numbers and underscores are allowed" }
10
+
11
+ after_save :reload_routes
12
+ def reload_routes
13
+ DynamicRouter.reload
14
+ end
10
15
 
11
16
  def name
12
17
  self[:name] || self.identifier
@@ -0,0 +1,91 @@
1
+ module Cardboard
2
+ class Url < ActiveRecord::Base
3
+ belongs_to :urlable, polymorphic: true
4
+
5
+ serialize :slugs_backup, Array
6
+ serialize :meta_tags, Hash
7
+
8
+ before_save :update_slugs_backup
9
+
10
+ validates :path, presence: true
11
+ validates :slug, uniqueness: { :case_sensitive => false, :scope => :path }, presence: true
12
+
13
+ after_save :reload_routes
14
+
15
+ # TODO: Should we use the homepage boolean?
16
+ # before_save :update_homepage
17
+ # def update_homepage
18
+ # return unless homepage_changed?
19
+ # self.class.where('id != ? AND homepage', self.id).update_all(homepage: false)
20
+ # end
21
+
22
+ def self.urlable_for(full_url, options = {})
23
+ #TODO: refactor
24
+ return nil unless full_url
25
+ path, slug = self.path_and_slug(full_url)
26
+ url_hash = {path: path, slug: slug}
27
+ url_hash.merge!(urlable_type: options[:type]) if options[:type]
28
+
29
+ page = self.where(url_hash).first
30
+
31
+ if slug && page.nil?
32
+ #use arel instead of LIKE/ILIKE
33
+ page = self.where(path: path).where(self.arel_table[:slugs_backup].matches("% #{slug}\n%")).where(urlable_type: options[:type]).first
34
+ page.using_slug_backup = true if page
35
+ end
36
+
37
+ page.try(:urlable)
38
+ end
39
+
40
+ def slug=(value)
41
+ # the user can overwrite the auto generated slug
42
+ self[:slug] = value.present? ? value.to_url : nil
43
+ end
44
+
45
+ def path=(value)
46
+ return if value.nil?
47
+ value = value.gsub(/\//, '')
48
+ self[:path] = value.blank?? "/" : "/#{value}/"
49
+ end
50
+
51
+ def using_slug_backup?
52
+ @using_slug_backup || false
53
+ end
54
+
55
+ def using_slug_backup=(value)
56
+ @using_slug_backup = value
57
+ end
58
+
59
+ def slugs_backup=(value)
60
+ if value.is_a?(String)
61
+ self[:slugs_backup] = value.split(",").map(&:strip)
62
+ else
63
+ self[:slugs_backup] = value
64
+ end
65
+ end
66
+
67
+ def to_s
68
+ return "/" if slug.blank?
69
+ "#{path}#{slug}/"
70
+ end
71
+
72
+ private
73
+
74
+ def reload_routes
75
+ DynamicRouter.reload
76
+ end
77
+
78
+ def self.path_and_slug(full_url)
79
+ *path, slug = full_url.sub(/^\//, '').split('/')
80
+ [path.blank? ? '/' : "/#{path.join('/')}/", slug]
81
+ end
82
+
83
+
84
+ def update_slugs_backup
85
+ return nil if !self.slug_changed? || self.slug_was.nil?
86
+ self.slugs_backup |= [self.slug_was] #Yes, that's a single pipe...
87
+ self.slugs_backup = slugs_backup - [self.slug] #in case we are going back to a link that was in the backup
88
+ end
89
+
90
+ end
91
+ end
@@ -1,6 +1,6 @@
1
1
  form class="form-inline"
2
2
  - if Cardboard.used_with_templates?
3
- => link_to pages_new_path, class:"btn btn-primary" do
3
+ => link_to new_page_path, class:"btn btn-primary" do
4
4
  i.icon-plus
5
5
 
6
6
  .input-append style=(Cardboard.used_with_templates?? "width: 160px" : nil)
@@ -3,7 +3,7 @@
3
3
  #cardboard_logo CB
4
4
 
5
5
  = link_to main_app.root_path, id: "brand" do
6
- = Cardboard::Setting.company_name || Cardboard.application.site_title
6
+ = Cardboard::Setting.company_name rescue Cardboard.application.site_title
7
7
 
8
8
  ul.nav.pull-right
9
9
 
@@ -13,6 +13,8 @@ Gem::Specification.new do |s|
13
13
  s.summary = "Rails CMS made simple"
14
14
  s.description = "Rails CMS made simple"
15
15
 
16
+ s.required_ruby_version = ">= 1.9.3"
17
+
16
18
  # s.files = Dir["{app,config,db,lib}/**/*"] + ["MIT-LICENSE", "Rakefile", "README.rdoc"]
17
19
  s.files = `git ls-files`.split("\n").sort - %w(.rvmrc .gitignore)
18
20
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -28,7 +30,7 @@ Gem::Specification.new do |s|
28
30
  s.add_dependency "jquery-rails"
29
31
  s.add_dependency 'bootstrap-sass', '~> 2.2'
30
32
  s.add_dependency 'bootstrap-datepicker-rails'
31
- s.add_dependency 'bootstrap-wysihtml5-rails'
33
+ s.add_dependency 'bootstrap-wysihtml5-rails', '~> 0.3.1.24'
32
34
  s.add_dependency 'kaminari-bootstrap', '~> 0.1.3'
33
35
  s.add_dependency 'font-awesome-sass-rails', '>= 3.0.0.1'
34
36
  s.add_dependency 'simple_form', '>= 3.0.0'
@@ -43,7 +45,7 @@ Gem::Specification.new do |s|
43
45
  s.add_dependency 'ransack', '>= 1.0.0'
44
46
  s.add_dependency 'turbolinks'
45
47
  s.add_dependency 'decorators'
46
- s.add_dependency 'jquery-ui-rails'
48
+ s.add_dependency 'jquery-ui-rails', '~> 5.0.0'
47
49
  s.add_dependency 'select2-rails'
48
50
 
49
51
  s.add_development_dependency "guard-minitest"
@@ -4,10 +4,7 @@ Cardboard::Engine.routes.draw do
4
4
  get "my_account", to: "my_account#edit"
5
5
  patch "my_account", to: "my_account#update"
6
6
 
7
- get "pages/new", to: "pages#new"
8
7
  post "pages/sort", to: "pages#sort"
9
- get "pages/:id", to: "pages#edit"
10
-
11
8
  resources :pages
12
9
 
13
10
  get "/yoda", to: "super_user#index"
@@ -15,10 +12,8 @@ Cardboard::Engine.routes.draw do
15
12
  get "/settings", to: "settings#index"
16
13
  patch "/settings/update", to: "settings#update", as: "setting"
17
14
 
18
- get "/", to: "dashboard#index", as: "dashboard"
19
- #Don't put a root path here, use "/" instead... (to be able to use root_path in the pages)
15
+ get "/", to: "dashboard#index", as: "dashboard" #Don't put a root path here
20
16
 
21
-
22
17
  scope as: 'cardboard' do
23
18
  #generate routes for custom cardboard resources controllers
24
19
  Cardboard.resource_controllers.each do |controller|
@@ -36,15 +31,9 @@ Cardboard::Engine.routes.draw do
36
31
  end
37
32
 
38
33
  # Routes for public pages
39
- Rails.application.routes.draw do
40
- scope :constraints => { :format => 'html' } do #:format => true,
41
- get "*id", to: "pages#show"
42
- end
43
-
44
- root :to => "pages#show" unless @set.named_routes.routes[:root] #has_named_route?
45
- end
46
-
34
+ Cardboard::DynamicRouter.load
47
35
 
36
+ #legacy support
48
37
  Rails.application.routes.named_routes.module.module_eval do
49
38
  def page_path(identifier, options = {})
50
39
  url = Cardboard::Page.where(identifier: identifier.to_s).first.try(:url)
@@ -27,9 +27,6 @@ class CreateCardboard < ActiveRecord::Migration
27
27
  #Pages
28
28
  create_table :cardboard_pages do |t|
29
29
  t.string :title
30
- t.string :path
31
- t.string :slug
32
- t.text :slugs_backup
33
30
  t.integer :position
34
31
  t.text :meta_seo
35
32
  t.boolean :in_menu, default: true
@@ -38,7 +35,6 @@ class CreateCardboard < ActiveRecord::Migration
38
35
 
39
36
  t.timestamps
40
37
  end
41
- add_index :cardboard_pages, [:path, :slug], :unique => true
42
38
  add_index :cardboard_pages, :identifier, :unique => true
43
39
 
44
40
  #Settings
@@ -57,7 +53,20 @@ class CreateCardboard < ActiveRecord::Migration
57
53
  t.text :fields
58
54
  t.string :identifier
59
55
  t.boolean :is_page
56
+ t.string :controller_action
57
+ t.timestamps
58
+ end
59
+ add_index :cardboard_templates, :identifier, :unique => true
60
+
61
+ create_table :cardboard_urls do |t|
62
+ t.string :slug, index: true
63
+ t.string :path, index: true
64
+ t.text :slugs_backup
65
+ t.text :meta_tags
66
+ t.references :urlable, polymorphic: true
67
+
60
68
  t.timestamps
61
69
  end
70
+ add_index :cardboard_urls, [:path, :slug], :unique => true
62
71
  end
63
72
  end
@@ -0,0 +1,29 @@
1
+ require 'active_support/concern'
2
+
3
+ module Cardboard
4
+ module UrlConcern
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ has_one :url_object, class_name: "Cardboard::Url", :as => :urlable, :autosave => true, :dependent => :destroy
9
+
10
+ def url_object_with_auto_build
11
+ build_url_object unless url_object_without_auto_build
12
+ url_object_without_auto_build #to continue the association chain
13
+ end
14
+ alias_method_chain :url_object, :auto_build
15
+
16
+ accepts_nested_attributes_for :url_object
17
+
18
+ delegate :slug, :slug=,
19
+ :path, :path=,
20
+ :meta_tags, :meta_tags=,
21
+ :using_slug_backup?,
22
+ to: :url_object, allow_nil: true
23
+ end
24
+
25
+ def url
26
+ url_object.to_s
27
+ end
28
+ end
29
+ end