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.
- checksums.yaml +4 -4
- data/.travis.yml +6 -0
- data/Gemfile +2 -2
- data/Gemfile.lock +47 -42
- data/README.md +12 -2
- data/app/assets/javascripts/cardboard/admin.js +1 -1
- data/app/controllers/pages_controller.rb +3 -35
- data/app/controllers/url_controller.rb +33 -0
- data/app/decorators/controllers/application_controller_decorator.rb +3 -1
- data/app/helpers/cardboard/public_helper.rb +1 -1
- data/app/helpers/cardboard/resource_helper.rb +6 -0
- data/app/models/cardboard/field.rb +2 -1
- data/app/models/cardboard/field/rich_text.rb +5 -5
- data/app/models/cardboard/page.rb +30 -72
- data/app/models/cardboard/setting.rb +0 -2
- data/app/models/cardboard/template.rb +5 -0
- data/app/models/cardboard/url.rb +91 -0
- data/app/views/cardboard/pages/_sidebar.html.slim +1 -1
- data/app/views/layouts/cardboard/_main_topbar.html.slim +1 -1
- data/cardboard.gemspec +4 -2
- data/config/routes.rb +3 -14
- data/db/migrate/1_create_cardboard.rb +13 -4
- data/lib/cardboard/concerns/url_concern.rb +29 -0
- data/lib/cardboard/constraints/page_constraint.rb +7 -0
- data/lib/cardboard/dynamic_router.rb +34 -0
- data/lib/cardboard/engine.rb +3 -1
- data/lib/cardboard/helpers/content_for_in_controllers.rb +23 -0
- data/lib/cardboard/helpers/seed.rb +17 -10
- data/lib/cardboard/version.rb +1 -1
- data/lib/generators/cardboard/resource/resource_generator.rb +2 -2
- data/lib/generators/cardboard/resource/templates/slim/edit.html.slim +1 -1
- data/lib/generators/cardboard/resource/templates/slim/new.html.slim +1 -1
- data/test/dummy/app/controllers/blog_controller.rb +2 -0
- data/test/dummy/app/views/blog/index.html.slim +1 -0
- data/test/dummy/app/views/layouts/application.html.slim +1 -0
- data/test/dummy/app/views/pages/about_us.html.slim +1 -0
- data/test/dummy/app/views/pages/history.html.slim +3 -0
- data/test/dummy/app/views/{templates → pages}/home.html.slim +0 -0
- data/test/dummy/config/cardboard.yml +6 -2
- data/test/dummy/db/migrate/20140312180204_create_cardboard.rb +72 -0
- data/test/dummy/db/schema.rb +19 -6
- data/test/models/url_test.rb +11 -0
- metadata +32 -28
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
- data/app/views/cardboard/pages/_error.html.slim +0 -7
- data/app/views/cardboard/pages/_seo.html.slim +0 -6
- data/app/views/cardboard/pages/show.html.slim +0 -9
- data/test/dummy/app/views/templates/about-us.html.slim +0 -1
- data/test/dummy/test/fixtures/admins.yml +0 -7
- data/test/dummy/test/functional/admins_controller_test.rb +0 -49
- data/test/dummy/test/unit/admin_test.rb +0 -7
- 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
|
-
|
18
|
-
|
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
|
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
|
-
|
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
|
14
|
+
ranks :position
|
24
15
|
|
25
16
|
#validations
|
26
|
-
|
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
|
49
|
-
|
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
|
53
|
-
@
|
44
|
+
def root?
|
45
|
+
return @root unless @root.nil?
|
46
|
+
@root = self.id == Page.root.id
|
54
47
|
end
|
55
48
|
|
56
|
-
def
|
57
|
-
|
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
|
-
|
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.
|
146
|
+
Cardboard::Page.with_path(url)
|
176
147
|
end
|
177
148
|
|
178
149
|
def siblings
|
179
|
-
Cardboard::Page.where("
|
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.
|
239
|
-
self.
|
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
|
@@ -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
|
data/cardboard.gemspec
CHANGED
@@ -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"
|
data/config/routes.rb
CHANGED
@@ -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
|
-
|
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
|