jakewendt-simply_pages 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|