cardboard_cms 0.2.2 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|