merb-admin 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.markdown +58 -0
- data/Rakefile +64 -0
- data/app/controllers/application.rb +6 -0
- data/app/controllers/main.rb +145 -0
- data/app/helpers/application_helper.rb +64 -0
- data/app/helpers/main_helper.rb +122 -0
- data/app/views/layout/_message.html.erb +10 -0
- data/app/views/layout/dashboard.html.erb +34 -0
- data/app/views/layout/form.html.erb +48 -0
- data/app/views/layout/list.html.erb +42 -0
- data/app/views/main/_belongs_to.html.erb +29 -0
- data/app/views/main/_big_decimal.html.erb +12 -0
- data/app/views/main/_boolean.html.erb +7 -0
- data/app/views/main/_date.html.erb +12 -0
- data/app/views/main/_datetime.html.erb +12 -0
- data/app/views/main/_float.html.erb +12 -0
- data/app/views/main/_has_many.html.erb +16 -0
- data/app/views/main/_has_one.html.erb +30 -0
- data/app/views/main/_integer.html.erb +18 -0
- data/app/views/main/_properties.html.erb +18 -0
- data/app/views/main/_string.html.erb +12 -0
- data/app/views/main/_text.html.erb +11 -0
- data/app/views/main/_time.html.erb +12 -0
- data/app/views/main/_timestamp.html.erb +12 -0
- data/app/views/main/delete.html.erb +12 -0
- data/app/views/main/edit.html.erb +25 -0
- data/app/views/main/index.html.erb +22 -0
- data/app/views/main/list.html.erb +72 -0
- data/app/views/main/new.html.erb +22 -0
- data/lib/abstract_model.rb +67 -0
- data/lib/activerecord_support.rb +169 -0
- data/lib/datamapper_support.rb +157 -0
- data/lib/generic_support.rb +17 -0
- data/lib/merb-admin/merbtasks.rb +103 -0
- data/lib/merb-admin/slicetasks.rb +20 -0
- data/lib/merb-admin/spectasks.rb +53 -0
- data/lib/merb-admin.rb +99 -0
- data/public/images/arrow-down.gif +0 -0
- data/public/images/arrow-up.gif +0 -0
- data/public/images/changelist-bg.gif +0 -0
- data/public/images/changelist-bg_rtl.gif +0 -0
- data/public/images/chooser-bg.gif +0 -0
- data/public/images/chooser_stacked-bg.gif +0 -0
- data/public/images/default-bg-reverse.gif +0 -0
- data/public/images/default-bg.gif +0 -0
- data/public/images/deleted-overlay.gif +0 -0
- data/public/images/icon-no.gif +0 -0
- data/public/images/icon-unknown.gif +0 -0
- data/public/images/icon-yes.gif +0 -0
- data/public/images/icon_addlink.gif +0 -0
- data/public/images/icon_alert.gif +0 -0
- data/public/images/icon_calendar.gif +0 -0
- data/public/images/icon_changelink.gif +0 -0
- data/public/images/icon_clock.gif +0 -0
- data/public/images/icon_deletelink.gif +0 -0
- data/public/images/icon_error.gif +0 -0
- data/public/images/icon_searchbox.png +0 -0
- data/public/images/icon_success.gif +0 -0
- data/public/images/inline-delete-8bit.png +0 -0
- data/public/images/inline-delete.png +0 -0
- data/public/images/inline-restore-8bit.png +0 -0
- data/public/images/inline-restore.png +0 -0
- data/public/images/inline-splitter-bg.gif +0 -0
- data/public/images/nav-bg-grabber.gif +0 -0
- data/public/images/nav-bg-reverse.gif +0 -0
- data/public/images/nav-bg.gif +0 -0
- data/public/images/selector-add.gif +0 -0
- data/public/images/selector-addall.gif +0 -0
- data/public/images/selector-remove.gif +0 -0
- data/public/images/selector-removeall.gif +0 -0
- data/public/images/selector-search.gif +0 -0
- data/public/images/selector_stacked-add.gif +0 -0
- data/public/images/selector_stacked-remove.gif +0 -0
- data/public/images/tool-left.gif +0 -0
- data/public/images/tool-left_over.gif +0 -0
- data/public/images/tool-right.gif +0 -0
- data/public/images/tool-right_over.gif +0 -0
- data/public/images/tooltag-add.gif +0 -0
- data/public/images/tooltag-add_over.gif +0 -0
- data/public/images/tooltag-arrowright.gif +0 -0
- data/public/images/tooltag-arrowright_over.gif +0 -0
- data/public/javascripts/CollapsedFieldsets.js +85 -0
- data/public/javascripts/DateTimeShortcuts.js +255 -0
- data/public/javascripts/RelatedObjectLookups.js +96 -0
- data/public/javascripts/SelectBox.js +111 -0
- data/public/javascripts/SelectFilter2.js +113 -0
- data/public/javascripts/actions.js +39 -0
- data/public/javascripts/calendar.js +143 -0
- data/public/javascripts/core.js +176 -0
- data/public/javascripts/dateparse.js +233 -0
- data/public/javascripts/getElementsBySelector.js +167 -0
- data/public/javascripts/i18n.js +33 -0
- data/public/javascripts/master.js +0 -0
- data/public/javascripts/ordering.js +137 -0
- data/public/javascripts/timeparse.js +94 -0
- data/public/javascripts/urlify.js +140 -0
- data/public/stylesheets/base.css +746 -0
- data/public/stylesheets/changelists.css +269 -0
- data/public/stylesheets/dashboard.css +24 -0
- data/public/stylesheets/forms.css +327 -0
- data/public/stylesheets/global.css +142 -0
- data/public/stylesheets/ie.css +51 -0
- data/public/stylesheets/layout.css +29 -0
- data/public/stylesheets/login.css +54 -0
- data/public/stylesheets/master.css +2 -0
- data/public/stylesheets/null.css +1 -0
- data/public/stylesheets/patch-iewin.css +8 -0
- data/public/stylesheets/rtl.css +206 -0
- data/public/stylesheets/widgets.css +506 -0
- data/schema/migrations/001_create_divisions_migration.rb +15 -0
- data/schema/migrations/002_create_drafts_migration.rb +21 -0
- data/schema/migrations/003_create_leagues_migration.rb +13 -0
- data/schema/migrations/004_create_players_migration.rb +23 -0
- data/schema/migrations/005_create_teams_migration.rb +18 -0
- data/spec/controllers/main_spec.rb +25 -0
- data/spec/models/activerecord/division.rb +8 -0
- data/spec/models/activerecord/draft.rb +16 -0
- data/spec/models/activerecord/league.rb +6 -0
- data/spec/models/activerecord/player.rb +10 -0
- data/spec/models/activerecord/team.rb +11 -0
- data/spec/models/datamapper/division.rb +12 -0
- data/spec/models/datamapper/draft.rb +18 -0
- data/spec/models/datamapper/league.rb +11 -0
- data/spec/models/datamapper/player.rb +21 -0
- data/spec/models/datamapper/team.rb +15 -0
- data/spec/requests/main_spec.rb +475 -0
- data/spec/spec_helper.rb +112 -0
- metadata +209 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Erik Michaels-Ober
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# MerbAdmin
|
2
|
+
|
3
|
+
**MerbAdmin is a Merb plugin that provides an easy-to-use interface for managing your data.**
|
4
|
+
|
5
|
+
It currently offers the features listed [here](http://sferik.tadalist.com/lists/1352791/public).
|
6
|
+
|
7
|
+
## Get it
|
8
|
+
|
9
|
+
At the command prompt, type:
|
10
|
+
|
11
|
+
sudo gem install sferik-merb-admin -s http://gems.github.com
|
12
|
+
|
13
|
+
## Install it
|
14
|
+
|
15
|
+
In your app, add the following dependency to `config/dependencies.rb`:
|
16
|
+
|
17
|
+
dependency "sferik-merb-admin", "0.4.3", :require_as => "merb-admin"
|
18
|
+
|
19
|
+
Add the following route to `config/router.rb`:
|
20
|
+
|
21
|
+
add_slice(:merb_admin, :name_prefix => nil, :path_prefix => "admin", :default_routes => false)
|
22
|
+
|
23
|
+
Then, run the following rake task:
|
24
|
+
|
25
|
+
rake slices:merb-admin:install
|
26
|
+
|
27
|
+
## Configure it (optional)
|
28
|
+
|
29
|
+
If you're feeling crafty, you can set a couple configuration options in `config/init.rb`:
|
30
|
+
|
31
|
+
Merb::BootLoader.before_app_loads do
|
32
|
+
Merb::Slices::config[:merb_admin][:app_name] = "My App"
|
33
|
+
Merb::Slices::config[:merb_admin][:per_page] = 100
|
34
|
+
end
|
35
|
+
|
36
|
+
## Run it
|
37
|
+
|
38
|
+
Start the server:
|
39
|
+
|
40
|
+
merb
|
41
|
+
|
42
|
+
You should now be able to administer your site at [http://localhost:4000/admin](http://localhost:4000/admin).
|
43
|
+
|
44
|
+
Please report any problems you encounter to <sferik@gmail.com> or [@sferik](http://twitter.com/home/?status=@sferik%20) on Twitter.
|
45
|
+
|
46
|
+
## WARNING
|
47
|
+
|
48
|
+
MerbAdmin does not implement any authorization scheme. Make sure to apply authorization logic before deploying to production!
|
49
|
+
|
50
|
+
## Acknowledgements
|
51
|
+
|
52
|
+
Many thanks to:
|
53
|
+
|
54
|
+
* [Wilson Miner](http://www.wilsonminer.com) for contributing the stylesheets and javascripts from [Django](http://www.djangoproject.com)
|
55
|
+
* [Aaron Wheeler](http://fightinjoe.com/) for contributing libraries from [Merb AutoScaffold](http://github.com/fightinjoe/merb-autoscaffold)
|
56
|
+
* [Lori Holden](http://loriholden.com/) for contributing the [merb-pagination](http://github.com/lholden/merb-pagination) helper
|
57
|
+
|
58
|
+
Also, thanks to [beer](http://www.21st-amendment.com).
|
data/Rakefile
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "rake/gempackagetask"
|
3
|
+
|
4
|
+
require "merb-core"
|
5
|
+
require "merb-core/tasks/merb"
|
6
|
+
|
7
|
+
GEM_NAME = "merb-admin"
|
8
|
+
AUTHOR = "Erik Michaels-Ober"
|
9
|
+
EMAIL = "sferik@gmail.com"
|
10
|
+
HOMEPAGE = "http://twitter.com/sferik"
|
11
|
+
SUMMARY = "MerbAdmin is a Merb plugin that provides an easy-to-use interface for managing your data."
|
12
|
+
GEM_VERSION = "0.4.3"
|
13
|
+
|
14
|
+
spec = Gem::Specification.new do |s|
|
15
|
+
s.rubyforge_project = "merb"
|
16
|
+
s.name = GEM_NAME
|
17
|
+
s.version = GEM_VERSION
|
18
|
+
s.platform = Gem::Platform::RUBY
|
19
|
+
s.has_rdoc = false
|
20
|
+
s.extra_rdoc_files = ["README.markdown", "LICENSE"]
|
21
|
+
s.summary = SUMMARY
|
22
|
+
s.description = s.summary
|
23
|
+
s.author = AUTHOR
|
24
|
+
s.email = EMAIL
|
25
|
+
s.homepage = HOMEPAGE
|
26
|
+
s.add_dependency("merb-slices", ">= 1.0.12")
|
27
|
+
s.add_dependency("builder", ">= 2.1.2")
|
28
|
+
s.require_path = "lib"
|
29
|
+
s.files = %w(LICENSE README.markdown Rakefile) + Dir.glob("{app,lib,public,schema,spec,stubs}/**/*")
|
30
|
+
s.post_install_message = <<-POST_INSTALL_MESSAGE
|
31
|
+
#{"*" * 80}
|
32
|
+
|
33
|
+
WARNING: MerbAdmin does not implement any authorization scheme.
|
34
|
+
Make sure to apply authorization logic before deploying to production!
|
35
|
+
|
36
|
+
#{"*" * 80}
|
37
|
+
POST_INSTALL_MESSAGE
|
38
|
+
end
|
39
|
+
|
40
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
41
|
+
pkg.gem_spec = spec
|
42
|
+
end
|
43
|
+
|
44
|
+
desc "Install the gem"
|
45
|
+
task :install do
|
46
|
+
Merb::RakeHelper.install(GEM_NAME, :version => GEM_VERSION)
|
47
|
+
end
|
48
|
+
|
49
|
+
desc "Uninstall the gem"
|
50
|
+
task :uninstall do
|
51
|
+
Merb::RakeHelper.uninstall(GEM_NAME, :version => GEM_VERSION)
|
52
|
+
end
|
53
|
+
|
54
|
+
desc "Create a gemspec file"
|
55
|
+
task :gemspec do
|
56
|
+
File.open("#{GEM_NAME}.gemspec", "w") do |file|
|
57
|
+
file.puts spec.to_ruby
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
require "spec/rake/spectask"
|
62
|
+
require "merb-core/test/tasks/spectasks"
|
63
|
+
desc "Default: run spec examples"
|
64
|
+
task :default => "spec"
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require File.join( File.dirname(__FILE__), '..', '..', 'lib', 'abstract_model' )
|
2
|
+
|
3
|
+
class MerbAdmin::Main < MerbAdmin::Application
|
4
|
+
include Merb::MerbAdmin::MainHelper
|
5
|
+
|
6
|
+
before :find_models, :only => ['index']
|
7
|
+
before :find_model, :exclude => ['index']
|
8
|
+
before :find_object, :only => ['edit', 'update', 'delete', 'destroy']
|
9
|
+
|
10
|
+
def index
|
11
|
+
render(:layout => 'dashboard')
|
12
|
+
end
|
13
|
+
|
14
|
+
def list
|
15
|
+
options = {}
|
16
|
+
|
17
|
+
if params[:all]
|
18
|
+
options = {
|
19
|
+
:limit => MerbAdmin[:per_page] * 2,
|
20
|
+
}.merge(options)
|
21
|
+
@objects = @abstract_model.all(options).reverse
|
22
|
+
else
|
23
|
+
@current_page = (params[:page] || 1).to_i
|
24
|
+
options = {
|
25
|
+
:page => @current_page,
|
26
|
+
:per_page => MerbAdmin[:per_page],
|
27
|
+
}.merge(options)
|
28
|
+
@page_count, @objects = @abstract_model.paginated(options)
|
29
|
+
options.delete(:page)
|
30
|
+
options.delete(:per_page)
|
31
|
+
options.delete(:offset)
|
32
|
+
options.delete(:limit)
|
33
|
+
end
|
34
|
+
|
35
|
+
@record_count = @abstract_model.count(options)
|
36
|
+
render(:layout => 'list')
|
37
|
+
end
|
38
|
+
|
39
|
+
def new
|
40
|
+
@object = @abstract_model.new
|
41
|
+
render(:layout => 'form')
|
42
|
+
end
|
43
|
+
|
44
|
+
def edit
|
45
|
+
render(:layout => 'form')
|
46
|
+
end
|
47
|
+
|
48
|
+
def create
|
49
|
+
object = params[@abstract_model.singular_name] || {}
|
50
|
+
# Delete fields that are blank
|
51
|
+
object.each do |key, value|
|
52
|
+
object[key] = nil if value.blank?
|
53
|
+
end
|
54
|
+
has_one_associations = @abstract_model.has_one_associations.map{|association| [association, (params[:associations] || {}).delete(association[:name])]}
|
55
|
+
has_many_associations = @abstract_model.has_many_associations.map{|association| [association, (params[:associations] || {}).delete(association[:name])]}
|
56
|
+
@object = @abstract_model.new(object)
|
57
|
+
if @object.save && has_one_associations.each{|association, id| update_has_one_association(association, id)} && has_many_associations.each{|association, ids| update_has_many_association(association, ids)}
|
58
|
+
if params[:_continue]
|
59
|
+
redirect(url(:admin_edit, :model_name => @abstract_model.singular_name, :id => @object.id), :message => {:notice => "#{@abstract_model.pretty_name} was successfully created"})
|
60
|
+
elsif params[:_add_another]
|
61
|
+
redirect(url(:admin_new, :model_name => @abstract_model.singular_name), :message => {:notice => "#{@abstract_model.pretty_name} was successfully created"})
|
62
|
+
else
|
63
|
+
redirect(url(:admin_list, :model_name => @abstract_model.singular_name), :message => {:notice => "#{@abstract_model.pretty_name} was successfully created"})
|
64
|
+
end
|
65
|
+
else
|
66
|
+
message[:error] = "#{@abstract_model.pretty_name} failed to be created"
|
67
|
+
render(:new, :layout => 'form')
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def update
|
72
|
+
object = params[@abstract_model.singular_name] || {}
|
73
|
+
# Delete fields that are blank
|
74
|
+
object.each do |key, value|
|
75
|
+
object[key] = nil if value.blank?
|
76
|
+
end
|
77
|
+
has_one_associations = @abstract_model.has_one_associations.map{|association| [association, (params[:associations] || {}).delete(association[:name])]}
|
78
|
+
has_many_associations = @abstract_model.has_many_associations.map{|association| [association, (params[:associations] || {}).delete(association[:name])]}
|
79
|
+
if @object.update_attributes(object) && has_one_associations.each{|association, id| update_has_one_association(association, id)} && has_many_associations.each{|association, ids| update_has_many_association(association, ids)}
|
80
|
+
if params[:_continue]
|
81
|
+
redirect(url(:admin_edit, :model_name => @abstract_model.singular_name, :id => @object.id), :message => {:notice => "#{@abstract_model.pretty_name} was successfully updated"})
|
82
|
+
elsif params[:_add_another]
|
83
|
+
redirect(url(:admin_new, :model_name => @abstract_model.singular_name), :message => {:notice => "#{@abstract_model.pretty_name} was successfully updated"})
|
84
|
+
else
|
85
|
+
redirect(url(:admin_list, :model_name => @abstract_model.singular_name), :message => {:notice => "#{@abstract_model.pretty_name} was successfully updated"})
|
86
|
+
end
|
87
|
+
else
|
88
|
+
message[:error] = "#{@abstract_model.pretty_name} failed to be updated"
|
89
|
+
render(:edit, :layout => 'form')
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def delete
|
94
|
+
render(:layout => 'form')
|
95
|
+
end
|
96
|
+
|
97
|
+
def destroy
|
98
|
+
if @object.destroy
|
99
|
+
redirect(url(:admin_list, :model_name => @abstract_model.singular_name), :message => {:notice => "#{@abstract_model.pretty_name} was successfully destroyed"})
|
100
|
+
else
|
101
|
+
raise BadRequest
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def find_models
|
108
|
+
@abstract_models = MerbAdmin::AbstractModel.all
|
109
|
+
end
|
110
|
+
|
111
|
+
def find_model
|
112
|
+
model_name = params[:model_name].camel_case
|
113
|
+
@abstract_model = MerbAdmin::AbstractModel.new(model_name)
|
114
|
+
find_properties
|
115
|
+
end
|
116
|
+
|
117
|
+
def find_properties
|
118
|
+
@properties = @abstract_model.properties
|
119
|
+
end
|
120
|
+
|
121
|
+
def find_object
|
122
|
+
@object = @abstract_model.get(params[:id])
|
123
|
+
raise NotFound unless @object
|
124
|
+
end
|
125
|
+
|
126
|
+
def update_has_one_association(association, id)
|
127
|
+
model = MerbAdmin::AbstractModel.new(association[:child_model])
|
128
|
+
if object = model.get(id)
|
129
|
+
object.update_attributes(association[:child_key].first => @object.id)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def update_has_many_association(association, ids)
|
134
|
+
# Remove all of the associated items
|
135
|
+
relationship = @object.send(association[:name])
|
136
|
+
@object.clear_association(relationship)
|
137
|
+
# Add all of the objects to the relationship
|
138
|
+
model = MerbAdmin::AbstractModel.new(association[:child_model])
|
139
|
+
for object in model.all_in(ids)
|
140
|
+
relationship << object
|
141
|
+
end
|
142
|
+
@object.save
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Merb
|
2
|
+
module MerbAdmin
|
3
|
+
module ApplicationHelper
|
4
|
+
|
5
|
+
# @param *segments<Array[#to_s]> Path segments to append.
|
6
|
+
#
|
7
|
+
# @return <String>
|
8
|
+
# A path relative to the public directory, with added segments.
|
9
|
+
def image_path(*segments)
|
10
|
+
public_path_for(:image, *segments)
|
11
|
+
end
|
12
|
+
|
13
|
+
# @param *segments<Array[#to_s]> Path segments to append.
|
14
|
+
#
|
15
|
+
# @return <String>
|
16
|
+
# A path relative to the public directory, with added segments.
|
17
|
+
def javascript_path(*segments)
|
18
|
+
public_path_for(:javascript, *segments)
|
19
|
+
end
|
20
|
+
|
21
|
+
# @param *segments<Array[#to_s]> Path segments to append.
|
22
|
+
#
|
23
|
+
# @return <String>
|
24
|
+
# A path relative to the public directory, with added segments.
|
25
|
+
def stylesheet_path(*segments)
|
26
|
+
public_path_for(:stylesheet, *segments)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Construct a path relative to the public directory
|
30
|
+
#
|
31
|
+
# @param <Symbol> The type of component.
|
32
|
+
# @param *segments<Array[#to_s]> Path segments to append.
|
33
|
+
#
|
34
|
+
# @return <String>
|
35
|
+
# A path relative to the public directory, with added segments.
|
36
|
+
def public_path_for(type, *segments)
|
37
|
+
::MerbAdmin.public_path_for(type, *segments)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Construct an app-level path.
|
41
|
+
#
|
42
|
+
# @param <Symbol> The type of component.
|
43
|
+
# @param *segments<Array[#to_s]> Path segments to append.
|
44
|
+
#
|
45
|
+
# @return <String>
|
46
|
+
# A path within the host application, with added segments.
|
47
|
+
def app_path_for(type, *segments)
|
48
|
+
::MerbAdmin.app_path_for(type, *segments)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Construct a slice-level path.
|
52
|
+
#
|
53
|
+
# @param <Symbol> The type of component.
|
54
|
+
# @param *segments<Array[#to_s]> Path segments to append.
|
55
|
+
#
|
56
|
+
# @return <String>
|
57
|
+
# A path within the slice source (Gem), with added segments.
|
58
|
+
def slice_path_for(type, *segments)
|
59
|
+
::MerbAdmin.slice_path_for(type, *segments)
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'builder'
|
2
|
+
module Merb
|
3
|
+
module MerbAdmin
|
4
|
+
module MainHelper
|
5
|
+
def object_title(object)
|
6
|
+
if object.respond_to?(:name)
|
7
|
+
object.name
|
8
|
+
elsif object.respond_to?(:title)
|
9
|
+
object.title
|
10
|
+
else
|
11
|
+
"#{object.class.to_s} ##{object.id}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Given a page count and the current page, we generate a set of pagination
|
16
|
+
# links.
|
17
|
+
#
|
18
|
+
# * We use an inner and outer window into a list of links. For a set of
|
19
|
+
# 20 pages with the current page being 10:
|
20
|
+
# outer_window:
|
21
|
+
# 1 2 ..... 19 20
|
22
|
+
# inner_window
|
23
|
+
# 5 6 7 8 9 10 11 12 13 14
|
24
|
+
#
|
25
|
+
# This is totally adjustable, or can be turned off by giving the
|
26
|
+
# :inner_window setting a value of nil.
|
27
|
+
#
|
28
|
+
# * Options
|
29
|
+
# :left_cut_label => <em>text_for_cut</em>::
|
30
|
+
# Used when the page numbers need to be cut off to prevent the set of
|
31
|
+
# pagination links from being too long.
|
32
|
+
# Defaults to '…'
|
33
|
+
# :right_cut_label => <em>text_for_cut</em>::
|
34
|
+
# Same as :left_cut_label but for the right side of numbers.
|
35
|
+
# Defaults to '…'
|
36
|
+
# :outer_window => <em>number_of_pages</em>::
|
37
|
+
# Sets the number of pages to include in the outer 'window'
|
38
|
+
# Defaults to 2
|
39
|
+
# :inner_window => <em>number_of_pages</em>::
|
40
|
+
# Sets the number of pags to include in the inner 'window'
|
41
|
+
# Defaults to 7
|
42
|
+
# :page_param => <em>name_of_page_paramiter</em>
|
43
|
+
# Sets the name of the paramiter the paginator uses to return what
|
44
|
+
# page is being requested.
|
45
|
+
# Defaults to 'page'
|
46
|
+
# :url => <em>url_for_links</em>
|
47
|
+
# Provides the base url to use in the page navigation links.
|
48
|
+
# Defaults to ''
|
49
|
+
def paginate(current_page, page_count, options = {})
|
50
|
+
options[:left_cut_label] ||= '…'
|
51
|
+
options[:right_cut_label] ||= '…'
|
52
|
+
options[:outer_window] ||= 2
|
53
|
+
options[:inner_window] ||= 7
|
54
|
+
options[:page_param] ||= 'page'
|
55
|
+
options[:url] ||= ''
|
56
|
+
|
57
|
+
url = options.delete(:url)
|
58
|
+
url << (url.include?('?') ? '&' : '?') << options[:page_param]
|
59
|
+
|
60
|
+
pages = {
|
61
|
+
:all => (1..page_count).to_a,
|
62
|
+
:left => [],
|
63
|
+
:center => [],
|
64
|
+
:right => []
|
65
|
+
}
|
66
|
+
|
67
|
+
# Only worry about using our 'windows' if the page count is less then
|
68
|
+
# our windows combined.
|
69
|
+
if options[:inner_window].nil? || ((options[:outer_window] * 2) + options[:inner_window] + 2) >= page_count
|
70
|
+
pages[:center] = pages[:all]
|
71
|
+
else
|
72
|
+
pages[:left] = pages[:all][0, options[:outer_window]]
|
73
|
+
pages[:right] = pages[:all][page_count - options[:outer_window], options[:outer_window]]
|
74
|
+
pages[:center] = case current_page
|
75
|
+
# allow the inner 'window' to shift to right when close to the left edge
|
76
|
+
# Ex: 1 2 [3] 4 5 6 7 8 9 ... 20
|
77
|
+
when -infinity .. (options[:inner_window] / 2) + 3
|
78
|
+
pages[:all][options[:outer_window], options[:inner_window]] +
|
79
|
+
[options[:right_cut_label]]
|
80
|
+
# allow the inner 'window' to shift left when close to the right edge
|
81
|
+
# Ex: 1 2 ... 12 13 14 15 16 [17] 18 19 20
|
82
|
+
when (page_count - (options[:inner_window] / 2.0).ceil) - 1 .. infinity
|
83
|
+
[options[:left_cut_label]] +
|
84
|
+
pages[:all][page_count - options[:inner_window] - options[:outer_window], options[:inner_window]]
|
85
|
+
# Display the unshifed window
|
86
|
+
# ex: 1 2 ... 5 6 7 [8] 9 10 11 ... 19 20
|
87
|
+
else
|
88
|
+
[options[:left_cut_label]] +
|
89
|
+
pages[:all][current_page - (options[:inner_window] / 2) - 1, options[:inner_window]] +
|
90
|
+
[options[:right_cut_label]]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
b = []
|
95
|
+
|
96
|
+
[pages[:left], pages[:center], pages[:right]].each do |p|
|
97
|
+
p.each do |page_number|
|
98
|
+
case page_number
|
99
|
+
when String
|
100
|
+
b << page_number
|
101
|
+
when current_page
|
102
|
+
b << Builder::XmlMarkup.new.span(page_number, :class => "this-page")
|
103
|
+
when page_count
|
104
|
+
b << Builder::XmlMarkup.new.a(page_number, :class => "end", :href => "#{url}=#{page_number}")
|
105
|
+
else
|
106
|
+
b << Builder::XmlMarkup.new.a(page_number, :href => "#{url}=#{page_number}")
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
b.join(" ")
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def infinity
|
117
|
+
1.0 / 0
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
<%
|
2
|
+
slice_name = "MerbAdmin" + (MerbAdmin[:app_name].blank? ? "" : " for #{MerbAdmin[:app_name]}")
|
3
|
+
page_name = "Site administration"
|
4
|
+
%>
|
5
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
6
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-us" lang="en-us">
|
7
|
+
<head>
|
8
|
+
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
9
|
+
<meta name="robots" content="NONE,NOARCHIVE" />
|
10
|
+
<title><%= page_name %> | <%= slice_name %></title>
|
11
|
+
<link href="<%= public_path_for :stylesheet, 'base.css' %>" type="text/css" charset="utf-8" rel="stylesheet" media="all" />
|
12
|
+
<link href="<%= public_path_for :stylesheet, 'dashboard.css' %>" type="text/css" charset="utf-8" rel="stylesheet" media="all" />
|
13
|
+
<!--[if lte IE 7]><link href="<%= public_path_for :stylesheet, 'ie.css' %>" type="text/css" charset="utf-8" rel="stylesheet" media="all" /><![endif]-->
|
14
|
+
</head>
|
15
|
+
<body class="admin-slice dashboard">
|
16
|
+
<div id="container">
|
17
|
+
<div id="header">
|
18
|
+
<div id="branding">
|
19
|
+
<h1 id="site-name">
|
20
|
+
<%= slice_name %>
|
21
|
+
</h1>
|
22
|
+
</div>
|
23
|
+
</div>
|
24
|
+
<%= partial('layout/message', :message => message) unless message.blank? -%>
|
25
|
+
<div id="content" class="colMS">
|
26
|
+
<h1>
|
27
|
+
<%= page_name %>
|
28
|
+
</h1>
|
29
|
+
<%= catch_content(:for_layout) -%>
|
30
|
+
</div>
|
31
|
+
<div id="footer"></div>
|
32
|
+
</div>
|
33
|
+
</body>
|
34
|
+
</html>
|
@@ -0,0 +1,48 @@
|
|
1
|
+
<%
|
2
|
+
slice_name = "MerbAdmin" + (MerbAdmin[:app_name].blank? ? "" : " for #{MerbAdmin[:app_name]}")
|
3
|
+
page_name = action_name.capitalize + " " + @abstract_model.pretty_name.downcase
|
4
|
+
%>
|
5
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
6
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-us" lang="en-us">
|
7
|
+
<head>
|
8
|
+
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
9
|
+
<meta name="robots" content="NONE,NOARCHIVE" />
|
10
|
+
<title><%= page_name %> | <%= slice_name %></title>
|
11
|
+
<link href="<%= public_path_for :stylesheet, 'base.css' %>" type="text/css" charset="utf-8" rel="stylesheet" media="all" />
|
12
|
+
<link href="<%= public_path_for :stylesheet, 'forms.css' %>" type="text/css" charset="utf-8" rel="stylesheet" media="all" />
|
13
|
+
<!--[if lte IE 7]><link href="<%= public_path_for :stylesheet, 'ie.css' %>" type="text/css" charset="utf-8" rel="stylesheet" media="all" /><![endif]-->
|
14
|
+
<script src="<%= public_path_for :javascript, 'i18n.js' %>" type="text/javascript" charset="utf-8"></script>
|
15
|
+
<script src="<%= public_path_for :javascript, 'core.js' %>" type="text/javascript" charset="utf-8"></script>
|
16
|
+
<script src="<%= public_path_for :javascript, 'RelatedObjectLookups.js' %>" type="text/javascript" charset="utf-8"></script>
|
17
|
+
<script src="<%= public_path_for :javascript, 'getElementsBySelector.js' %>" type="text/javascript" charset="utf-8"></script>
|
18
|
+
<script src="<%= public_path_for :javascript, 'actions.js' %>" type="text/javascript" charset="utf-8"></script>
|
19
|
+
<script src="<%= public_path_for :javascript, 'SelectBox.js' %>" type="text/javascript" charset="utf-8"></script>
|
20
|
+
<script src="<%= public_path_for :javascript, 'SelectFilter2.js' %>" type="text/javascript" charset="utf-8"></script>
|
21
|
+
<script src="<%= public_path_for :javascript, 'calendar.js' %>" type="text/javascript" charset="utf-8"></script>
|
22
|
+
<script src="<%= public_path_for :javascript, 'DateTimeShortcuts.js' %>" type="text/javascript" charset="utf-8"></script>
|
23
|
+
</head>
|
24
|
+
<body class="admin-slice change-form">
|
25
|
+
<div id="container">
|
26
|
+
<div id="header">
|
27
|
+
<div id="branding">
|
28
|
+
<h1 id="site-name">
|
29
|
+
<%= slice_name %>
|
30
|
+
</h1>
|
31
|
+
</div>
|
32
|
+
</div>
|
33
|
+
<div class="breadcrumbs">
|
34
|
+
<%= link_to("Home", url(:admin_dashboard)) %> ›
|
35
|
+
<%= link_to(@abstract_model.plural_name.to_s.capitalize, url(:admin_list, :model_name => @abstract_model.singular_name)) %> ›
|
36
|
+
<%= page_name %>
|
37
|
+
</div>
|
38
|
+
<%= partial('layout/message', :message => message) unless message.blank? -%>
|
39
|
+
<div id="content" class="colM">
|
40
|
+
<h1>
|
41
|
+
<%= page_name %>
|
42
|
+
</h1>
|
43
|
+
<%= catch_content(:for_layout) -%>
|
44
|
+
</div>
|
45
|
+
<div id="footer"></div>
|
46
|
+
</div>
|
47
|
+
</body>
|
48
|
+
</html>
|
@@ -0,0 +1,42 @@
|
|
1
|
+
<%
|
2
|
+
slice_name = "MerbAdmin" + (MerbAdmin[:app_name].blank? ? "" : " for #{MerbAdmin[:app_name]}")
|
3
|
+
page_name = "Select " + @abstract_model.pretty_name.downcase + " to edit"
|
4
|
+
%>
|
5
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
6
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-us" lang="en-us">
|
7
|
+
<head>
|
8
|
+
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
9
|
+
<meta name="robots" content="NONE,NOARCHIVE" />
|
10
|
+
<title><%= page_name %> | <%= slice_name %></title>
|
11
|
+
<link href="<%= public_path_for :stylesheet, 'base.css' %>" type="text/css" charset="utf-8" rel="stylesheet" media="all" />
|
12
|
+
<link href="<%= public_path_for :stylesheet, 'changelists.css' %>" type="text/css" charset="utf-8" rel="stylesheet" media="all" />
|
13
|
+
<!--[if lte IE 7]><link href="<%= public_path_for :stylesheet, 'ie.css' %>" type="text/css" charset="utf-8" rel="stylesheet" media="all" /><![endif]-->
|
14
|
+
<script src="<%= public_path_for :javascript, 'core.js' %>" type="text/javascript" charset="utf-8"></script>
|
15
|
+
<script src="<%= public_path_for :javascript, 'RelatedObjectLookups.js' %>" type="text/javascript" charset="utf-8"></script>
|
16
|
+
<script src="<%= public_path_for :javascript, 'getElementsBySelector.js' %>" type="text/javascript" charset="utf-8"></script>
|
17
|
+
<script src="<%= public_path_for :javascript, 'actions.js' %>" type="text/javascript" charset="utf-8"></script>
|
18
|
+
</head>
|
19
|
+
<body class="admin-slice change-list">
|
20
|
+
<div id="container">
|
21
|
+
<div id="header">
|
22
|
+
<div id="branding">
|
23
|
+
<h1 id="site-name">
|
24
|
+
<%= slice_name %>
|
25
|
+
</h1>
|
26
|
+
</div>
|
27
|
+
</div>
|
28
|
+
<div class="breadcrumbs">
|
29
|
+
<%= link_to("Home", url(:admin_dashboard)) %> ›
|
30
|
+
<%= @abstract_model.plural_name.to_s.capitalize %>
|
31
|
+
</div>
|
32
|
+
<%= partial('layout/message', :message => message) unless message.blank? -%>
|
33
|
+
<div id="content" class="flex">
|
34
|
+
<h1>
|
35
|
+
<%= page_name %>
|
36
|
+
</h1>
|
37
|
+
<%= catch_content(:for_layout) -%>
|
38
|
+
</div>
|
39
|
+
<div id="footer"></div>
|
40
|
+
</div>
|
41
|
+
</body>
|
42
|
+
</html>
|
@@ -0,0 +1,29 @@
|
|
1
|
+
<%
|
2
|
+
child_key = association[:child_key].first
|
3
|
+
collection = MerbAdmin::AbstractModel.new(association[:parent_model]).all.map{|o| [o.id, object_title(o)]}.sort_by{|o| o[1]}
|
4
|
+
selected = @object.send(child_key)
|
5
|
+
label = association[:pretty_name]
|
6
|
+
required = false
|
7
|
+
@properties.each do |property|
|
8
|
+
next unless property[:name] == child_key
|
9
|
+
required = true unless property[:nullable?]
|
10
|
+
end
|
11
|
+
%>
|
12
|
+
<fieldset class="module aligned">
|
13
|
+
<h2><%= label %></h2>
|
14
|
+
<div class="<%= @object.errors[child_key] ? "form-row errors" : "form-row"%>">
|
15
|
+
<% if @object.errors[child_key] %>
|
16
|
+
<ul class="errorlist">
|
17
|
+
<% @object.errors[child_key].each do |error| %>
|
18
|
+
<li><%= error %></li>
|
19
|
+
<% end %>
|
20
|
+
</ul>
|
21
|
+
<% end %>
|
22
|
+
<div>
|
23
|
+
<%= select(child_key, :collection => collection, :include_blank => true, :selected => selected.to_s, :label => label) %>
|
24
|
+
<p class="help">
|
25
|
+
<%= required ? "Required." : "Optional." %>
|
26
|
+
</p>
|
27
|
+
</div>
|
28
|
+
</div>
|
29
|
+
</fieldset>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<%
|
2
|
+
name = property[:name]
|
3
|
+
length = property[:length]
|
4
|
+
label = property[:pretty_name]
|
5
|
+
required = !property[:nullable?] || property[:serial?]
|
6
|
+
%>
|
7
|
+
<div>
|
8
|
+
<%= text_field(name, :maxlength => length, :label => label) %>
|
9
|
+
<p class="help">
|
10
|
+
<%= required ? "Required." : "Optional." %>
|
11
|
+
</p>
|
12
|
+
</div>
|