jakewendt-simply_pages 2.0.0
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.
- data/README.rdoc +65 -0
- data/generators/simply_pages/USAGE +0 -0
- data/generators/simply_pages/simply_pages_generator.rb +120 -0
- data/generators/simply_pages/templates/autotest_simply_pages.rb +2 -0
- data/generators/simply_pages/templates/controllers/locales_controller.rb +16 -0
- data/generators/simply_pages/templates/controllers/pages_controller.rb +160 -0
- data/generators/simply_pages/templates/functional/locales_controller_test.rb +58 -0
- data/generators/simply_pages/templates/functional/pages_controller_test.rb +237 -0
- data/generators/simply_pages/templates/images/drag.gif +0 -0
- data/generators/simply_pages/templates/javascripts/pages.js +48 -0
- data/generators/simply_pages/templates/migrations/create_pages.rb +26 -0
- data/generators/simply_pages/templates/models/page.rb +107 -0
- data/generators/simply_pages/templates/models/page_sweeper.rb +74 -0
- data/generators/simply_pages/templates/simply_pages.rake +5 -0
- data/generators/simply_pages/templates/stylesheets/page.css +17 -0
- data/generators/simply_pages/templates/stylesheets/pages.css +22 -0
- data/generators/simply_pages/templates/unit/page_test.rb +149 -0
- data/generators/simply_pages/templates/unit/redcloth_extension_test.rb +64 -0
- data/generators/simply_pages/templates/views/pages/_child.html.erb +20 -0
- data/generators/simply_pages/templates/views/pages/_form.html.erb +44 -0
- data/generators/simply_pages/templates/views/pages/_page.html.erb +34 -0
- data/generators/simply_pages/templates/views/pages/all.html.erb +39 -0
- data/generators/simply_pages/templates/views/pages/edit.html.erb +10 -0
- data/generators/simply_pages/templates/views/pages/index.html.erb +29 -0
- data/generators/simply_pages/templates/views/pages/new.html.erb +10 -0
- data/generators/simply_pages/templates/views/pages/show.html.erb +31 -0
- data/generators/simply_pages/templates/views/pages/translate.js.erb +42 -0
- metadata +93 -0
Binary file
|
@@ -0,0 +1,48 @@
|
|
1
|
+
var initial_page_order;
|
2
|
+
jQuery(function(){
|
3
|
+
jQuery('#pages').sortable({
|
4
|
+
axis:'y',
|
5
|
+
dropOnEmpty:false,
|
6
|
+
handle:'img.handle',
|
7
|
+
update:function(event,ui){compare_page_order()},
|
8
|
+
items:'tr.page.row'
|
9
|
+
});
|
10
|
+
|
11
|
+
jQuery('#save_order').disable();
|
12
|
+
|
13
|
+
initial_page_order = page_order();
|
14
|
+
|
15
|
+
jQuery('form#order_pages').submit(function(){
|
16
|
+
if( initial_page_order == page_order() ) {
|
17
|
+
/*
|
18
|
+
Shouldn't get here as button should
|
19
|
+
be disabled if not different!
|
20
|
+
*/
|
21
|
+
alert("Page order hasn't changed. Nothing to save.");
|
22
|
+
return false
|
23
|
+
} else {
|
24
|
+
new_action = jQuery(this).attr('action');
|
25
|
+
if( (/\?/).test(new_action) ){
|
26
|
+
new_action += '&';
|
27
|
+
} else {
|
28
|
+
new_action += '?';
|
29
|
+
}
|
30
|
+
new_action += page_order();
|
31
|
+
jQuery(this).attr('action',new_action);
|
32
|
+
}
|
33
|
+
})
|
34
|
+
|
35
|
+
});
|
36
|
+
|
37
|
+
function page_order() {
|
38
|
+
return jQuery('#pages').sortable('serialize',{key:'pages[]'});
|
39
|
+
}
|
40
|
+
|
41
|
+
function compare_page_order(){
|
42
|
+
if( initial_page_order == page_order() ) {
|
43
|
+
jQuery('#save_order').disable();
|
44
|
+
} else {
|
45
|
+
jQuery('#save_order').highlight(4000);
|
46
|
+
jQuery('#save_order').enable();
|
47
|
+
}
|
48
|
+
}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class CreatePages < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :pages do |t|
|
4
|
+
t.integer :position
|
5
|
+
t.integer :parent_id
|
6
|
+
t.boolean :hide_menu, :default => false
|
7
|
+
t.string :path
|
8
|
+
t.string :title_en
|
9
|
+
t.string :title_es
|
10
|
+
t.string :menu_en
|
11
|
+
t.string :menu_es
|
12
|
+
t.text :body_en
|
13
|
+
t.text :body_es
|
14
|
+
t.timestamps
|
15
|
+
end
|
16
|
+
add_index :pages, :path, :unique => true
|
17
|
+
add_index :pages, :parent_id
|
18
|
+
# acts_as_list doesn't like the uniqueness
|
19
|
+
# when it reorders, positions are temporarily not unique
|
20
|
+
# add_index :pages, :position, :unique => true, :name => 'by_position'
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.down
|
24
|
+
drop_table :pages
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# == requires
|
2
|
+
# * path ( unique, > 1 char and starts with a / )
|
3
|
+
# * menu ( unique and > 3 chars )
|
4
|
+
# * title ( > 3 chars )
|
5
|
+
# * body ( > 3 chars )
|
6
|
+
#
|
7
|
+
# == named_scope(s)
|
8
|
+
# * not_home (returns those pages where path is not just '/')
|
9
|
+
# * roots
|
10
|
+
#
|
11
|
+
# uses acts_as_list for parent / child relationship. As this
|
12
|
+
# is only a parent and child and no deeper, its ok. If it
|
13
|
+
# were to get any deeper, the list should probably be changed
|
14
|
+
# to something like a nested set.
|
15
|
+
class Page < ActiveRecord::Base
|
16
|
+
default_scope :order => :position
|
17
|
+
|
18
|
+
acts_as_list :scope => :parent_id
|
19
|
+
# acts_as_list :scope => "parent_id \#{(parent_id.nil?)?'IS NULL':'= parent_id'} AND locale = '\#{locale}'"
|
20
|
+
|
21
|
+
validates_presence_of :path
|
22
|
+
validates_length_of :path, :minimum => 1
|
23
|
+
validates_format_of :path, :with => /^\//
|
24
|
+
# validates_presence_of :menu_en
|
25
|
+
validates_length_of :menu_en, :minimum => 4
|
26
|
+
# validates_presence_of :title_en
|
27
|
+
validates_length_of :title_en, :minimum => 4
|
28
|
+
# validates_presence_of :body_en
|
29
|
+
validates_length_of :body_en, :minimum => 4
|
30
|
+
validates_uniqueness_of :menu_en
|
31
|
+
validates_uniqueness_of :path
|
32
|
+
|
33
|
+
belongs_to :parent, :class_name => 'Page'
|
34
|
+
has_many :children, :class_name => 'Page', :foreign_key => 'parent_id',
|
35
|
+
:dependent => :nullify
|
36
|
+
|
37
|
+
named_scope :roots, :conditions => {
|
38
|
+
:parent_id => nil, :hide_menu => false }
|
39
|
+
|
40
|
+
named_scope :hidden, :conditions => {
|
41
|
+
:hide_menu => true }
|
42
|
+
|
43
|
+
named_scope :not_home, :conditions => [ "path != '/'" ]
|
44
|
+
|
45
|
+
attr_accessible :path, :parent_id, :hide_menu,
|
46
|
+
:menu, :menu_en, :menu_es,
|
47
|
+
:title, :title_en, :title_es,
|
48
|
+
:body, :body_en, :body_es
|
49
|
+
|
50
|
+
before_validation :adjust_path
|
51
|
+
|
52
|
+
def to_s
|
53
|
+
title
|
54
|
+
end
|
55
|
+
|
56
|
+
def adjust_path
|
57
|
+
unless self.path.nil?
|
58
|
+
# remove any duplicate /'s
|
59
|
+
# self.path = path.gsub(/\/+/,'/')
|
60
|
+
self.path.gsub!(/\/+/,'/')
|
61
|
+
|
62
|
+
# add leading / if none
|
63
|
+
# self.path = path.downcase
|
64
|
+
self.path.downcase!
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# named_scopes ALWAYS return an "Array"
|
69
|
+
# so if ONLY want one, MUST use a method.
|
70
|
+
# by_path returns the one(max) page that
|
71
|
+
# matches the given path.
|
72
|
+
def self.by_path(path)
|
73
|
+
page = find(:first,
|
74
|
+
:conditions => {
|
75
|
+
:path => path.downcase
|
76
|
+
}
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
def root
|
81
|
+
page = self
|
82
|
+
until page.parent == nil
|
83
|
+
page = page.parent
|
84
|
+
end
|
85
|
+
page
|
86
|
+
end
|
87
|
+
|
88
|
+
def is_home?
|
89
|
+
self.path == "/"
|
90
|
+
end
|
91
|
+
|
92
|
+
# Virtual attributes
|
93
|
+
%w( menu title body ).each do |attr|
|
94
|
+
define_method "#{attr}" do |*args|
|
95
|
+
r = send("#{attr}_#{args[0]||'en'}")
|
96
|
+
(r.blank?) ? send("#{attr}_en") : r
|
97
|
+
end
|
98
|
+
define_method "#{attr}=" do |new_val|
|
99
|
+
self.send("#{attr}_en=",new_val)
|
100
|
+
end
|
101
|
+
# attr_accessible attr.to_sym
|
102
|
+
# %w( en es ).each do |lang|
|
103
|
+
# attr_accessible "#{attr}_#{lang}".to_sym
|
104
|
+
# end
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# While sweepers are model observers, they only seem
|
2
|
+
# to be so within the scope of a given controller.
|
3
|
+
# If the 'cache_sweeper' line is not given in the
|
4
|
+
# controller, these observers don't see it.
|
5
|
+
# In addition, saving or destroying a page outside
|
6
|
+
# of the scope of the controller (ie. from console)
|
7
|
+
# will also go unnoticed.
|
8
|
+
#
|
9
|
+
# Interestingly, in unit/model testing, this fails
|
10
|
+
# as request is nil. This means that I don't quite
|
11
|
+
# understand how this works. Perhaps because the cache
|
12
|
+
# is in memory and not a file, the server and console
|
13
|
+
# are completely separate. Destroying in the console
|
14
|
+
# doesn't effect the server's cache. That makes sense.
|
15
|
+
# So then. How to get the host_with_port without a
|
16
|
+
# request then?
|
17
|
+
class PageSweeper < ActionController::Caching::Sweeper
|
18
|
+
observe Page
|
19
|
+
|
20
|
+
# After saving (creating or updating) a page,
|
21
|
+
# expire any associated caches.
|
22
|
+
def after_save(page)
|
23
|
+
expire_cache(page)
|
24
|
+
end
|
25
|
+
|
26
|
+
# After destroying a page, expire any associated
|
27
|
+
# caches.
|
28
|
+
def after_destroy(page)
|
29
|
+
expire_cache(page)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Always expire the menu cache. This may not
|
33
|
+
# be necessary as it only relies on the menu,
|
34
|
+
# path and position attributes which may have
|
35
|
+
# remained unchanged. Also expire the "show"
|
36
|
+
# action cache. As the pages are rarely accessed
|
37
|
+
# via /pages/:id, we need to expire the special
|
38
|
+
# routes used by the catch-all route. In the
|
39
|
+
# real world, this works just fine, but in testing
|
40
|
+
# the app tries to expire cache when in unit tests
|
41
|
+
# as well as functional tests. There is no
|
42
|
+
# "request" in a unit test causing failure and
|
43
|
+
# my usage of ".try"
|
44
|
+
def expire_cache(page)
|
45
|
+
# Please note that the "views/" prefix is
|
46
|
+
# internal to rails. Don't meddle with it.
|
47
|
+
|
48
|
+
# Expired fragment: views/page_menu (0.0ms)
|
49
|
+
expire_fragment 'page_menu'
|
50
|
+
|
51
|
+
# We don't really access the pages via :id
|
52
|
+
# but they can be so we need to deal with the cache.
|
53
|
+
# Expired fragment: views/dev.sph.berkeley.edu:3000/pages/1 (0.0ms)
|
54
|
+
# Expired fragment: views/dev.sph.berkeley.edu:3000/pages/5 (0.0ms)
|
55
|
+
expire_action
|
56
|
+
|
57
|
+
# Expired fragment: views/dev.sph.berkeley.edu:3000/alpha (0.0ms)
|
58
|
+
# Expired fragment: views/dev.sph.berkeley.edu:3000/ (0.0ms)
|
59
|
+
# This fails for the home page as "/" is
|
60
|
+
# called "index" by the server
|
61
|
+
# NEEDS to change page.path to /index for home page
|
62
|
+
# be views/dev.sph.berkeley.edu:3000/index !
|
63
|
+
page_path = ( page.path == "/" ) ? "/index" : page.path
|
64
|
+
# expire_fragment "#{request.host_with_port}#{page_path}"
|
65
|
+
expire_fragment "#{request.try(:host_with_port)}#{page_path}"
|
66
|
+
|
67
|
+
# In production, the page caches weren't expiring
|
68
|
+
# Adding the relative_url_root should fix it.
|
69
|
+
page_path = ActionController::Base.relative_url_root.to_s + page_path
|
70
|
+
expire_fragment "#{request.try(:host_with_port)}#{page_path}"
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
.page img.handle {
|
2
|
+
}
|
3
|
+
.page .position {
|
4
|
+
font-weight:bold;
|
5
|
+
text-align:center;
|
6
|
+
}
|
7
|
+
.page .path {
|
8
|
+
width: 140px;
|
9
|
+
}
|
10
|
+
.page .menu {
|
11
|
+
width: 140px;
|
12
|
+
}
|
13
|
+
.page .title {
|
14
|
+
width: 140px;
|
15
|
+
}
|
16
|
+
.page .manage {
|
17
|
+
text-align:right;
|
18
|
+
}
|
19
|
+
|
20
|
+
.page.row.ui-sortable-helper {
|
21
|
+
border: 1px dashed #E7C254;
|
22
|
+
}
|
@@ -0,0 +1,149 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class PageTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
assert_should_require(:path,:menu_en,:title_en,:body_en)
|
6
|
+
assert_should_require_unique(:path,:menu_en)
|
7
|
+
assert_should_require_attribute_length(:path,:minimum => 1)
|
8
|
+
assert_should_require_attribute_length(:menu_en,:title_en,:body_en,
|
9
|
+
:minimum => 4)
|
10
|
+
|
11
|
+
test "should create page" do
|
12
|
+
assert_difference 'Page.count' do
|
13
|
+
page = create_page
|
14
|
+
assert !page.new_record?,
|
15
|
+
"#{page.errors.full_messages.to_sentence}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
test "should require path begin with slash" do
|
20
|
+
assert_no_difference 'Page.count' do
|
21
|
+
page = create_page(:path => 'Hey')
|
22
|
+
assert page.errors.on(:path)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
test "should filter out multiple continguous slashes" do
|
27
|
+
page = create_page(:path => "///a//b///c" )
|
28
|
+
assert_equal "/a/b/c", page.path
|
29
|
+
end
|
30
|
+
|
31
|
+
test "should downcase path" do
|
32
|
+
page = create_page(:path => "/A/B/C")
|
33
|
+
assert_equal "/a/b/c", page.path
|
34
|
+
end
|
35
|
+
|
36
|
+
test "can have a parent" do
|
37
|
+
parent = create_page
|
38
|
+
page = create_page( :parent_id => parent.id )
|
39
|
+
assert_equal page.reload.parent, parent
|
40
|
+
end
|
41
|
+
|
42
|
+
test "should return self as root with no parent" do
|
43
|
+
page = create_page
|
44
|
+
assert_equal page, page.root
|
45
|
+
end
|
46
|
+
|
47
|
+
test "should return parent as root with parent" do
|
48
|
+
parent = create_page
|
49
|
+
page = create_page( :parent_id => parent.id )
|
50
|
+
assert_equal parent, page.reload.root
|
51
|
+
end
|
52
|
+
|
53
|
+
test "should nullify parent_id of children when parent destroyed" do
|
54
|
+
parent = create_page
|
55
|
+
child = create_page( :parent_id => parent.id )
|
56
|
+
assert_equal child.reload.parent_id, parent.id
|
57
|
+
parent.destroy
|
58
|
+
assert_nil child.reload.parent_id
|
59
|
+
end
|
60
|
+
|
61
|
+
test "should return false if page is not home" do
|
62
|
+
page = create_page
|
63
|
+
assert !page.is_home?
|
64
|
+
end
|
65
|
+
|
66
|
+
test "should return true if page is home" do
|
67
|
+
page = create_page(:path => '/')
|
68
|
+
assert page.is_home?
|
69
|
+
end
|
70
|
+
|
71
|
+
test "should create page with hide_menu true" do
|
72
|
+
assert_difference('Page.count',1){
|
73
|
+
assert_difference('Page.roots.count',0){
|
74
|
+
page = create_page(:hide_menu => true)
|
75
|
+
# assert_equal 1, Page.count
|
76
|
+
# assert_equal 0, Page.roots.count
|
77
|
+
assert_not_nil Page.find(page)
|
78
|
+
assert_not_nil Page.find(page.id)
|
79
|
+
assert_not_nil Page.find_by_path(page.path)
|
80
|
+
} }
|
81
|
+
end
|
82
|
+
|
83
|
+
test "should find page by path" do
|
84
|
+
p = create_page
|
85
|
+
page = Page.by_path(p.path)
|
86
|
+
assert_equal p, page
|
87
|
+
end
|
88
|
+
|
89
|
+
test "should return english menu without locale" do
|
90
|
+
p = create_page
|
91
|
+
assert_equal p.menu, p.menu_en
|
92
|
+
end
|
93
|
+
|
94
|
+
test "should return english title without locale" do
|
95
|
+
p = create_page
|
96
|
+
assert_equal p.title, p.title_en
|
97
|
+
end
|
98
|
+
|
99
|
+
test "should return english body without locale" do
|
100
|
+
p = create_page
|
101
|
+
assert_equal p.body, p.body_en
|
102
|
+
end
|
103
|
+
|
104
|
+
test "should return english menu with locale" do
|
105
|
+
p = create_page
|
106
|
+
assert_equal p.menu('en'), p.menu_en
|
107
|
+
end
|
108
|
+
|
109
|
+
test "should return english title with locale" do
|
110
|
+
p = create_page
|
111
|
+
assert_equal p.title('en'), p.title_en
|
112
|
+
end
|
113
|
+
|
114
|
+
test "should return english body with locale" do
|
115
|
+
p = create_page
|
116
|
+
assert_equal p.body('en'), p.body_en
|
117
|
+
end
|
118
|
+
|
119
|
+
test "should return spanish menu with locale" do
|
120
|
+
p = create_page(:menu_es => 'spanish menu')
|
121
|
+
assert_equal p.menu('es'), p.menu_es
|
122
|
+
end
|
123
|
+
|
124
|
+
test "should return spanish title with locale" do
|
125
|
+
p = create_page(:title_es => 'spanish title')
|
126
|
+
assert_equal p.title('es'), p.title_es
|
127
|
+
end
|
128
|
+
|
129
|
+
test "should return spanish body with locale" do
|
130
|
+
p = create_page(:body_es => 'spanish body')
|
131
|
+
assert_equal p.body('es'), p.body_es
|
132
|
+
end
|
133
|
+
|
134
|
+
test "should return english menu with missing spanish locale" do
|
135
|
+
p = create_page(:menu_es => '')
|
136
|
+
assert_equal p.menu('es'), p.menu_en
|
137
|
+
end
|
138
|
+
|
139
|
+
test "should return english title with missing spanish locale" do
|
140
|
+
p = create_page(:title_es => '')
|
141
|
+
assert_equal p.title('es'), p.title_en
|
142
|
+
end
|
143
|
+
|
144
|
+
test "should return english body with missing spanish locale" do
|
145
|
+
p = create_page(:body_es => '')
|
146
|
+
assert_equal p.body('es'), p.body_en
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|