merb-admin 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (129) hide show
  1. data/LICENSE +20 -0
  2. data/README.markdown +58 -0
  3. data/Rakefile +64 -0
  4. data/app/controllers/application.rb +6 -0
  5. data/app/controllers/main.rb +145 -0
  6. data/app/helpers/application_helper.rb +64 -0
  7. data/app/helpers/main_helper.rb +122 -0
  8. data/app/views/layout/_message.html.erb +10 -0
  9. data/app/views/layout/dashboard.html.erb +34 -0
  10. data/app/views/layout/form.html.erb +48 -0
  11. data/app/views/layout/list.html.erb +42 -0
  12. data/app/views/main/_belongs_to.html.erb +29 -0
  13. data/app/views/main/_big_decimal.html.erb +12 -0
  14. data/app/views/main/_boolean.html.erb +7 -0
  15. data/app/views/main/_date.html.erb +12 -0
  16. data/app/views/main/_datetime.html.erb +12 -0
  17. data/app/views/main/_float.html.erb +12 -0
  18. data/app/views/main/_has_many.html.erb +16 -0
  19. data/app/views/main/_has_one.html.erb +30 -0
  20. data/app/views/main/_integer.html.erb +18 -0
  21. data/app/views/main/_properties.html.erb +18 -0
  22. data/app/views/main/_string.html.erb +12 -0
  23. data/app/views/main/_text.html.erb +11 -0
  24. data/app/views/main/_time.html.erb +12 -0
  25. data/app/views/main/_timestamp.html.erb +12 -0
  26. data/app/views/main/delete.html.erb +12 -0
  27. data/app/views/main/edit.html.erb +25 -0
  28. data/app/views/main/index.html.erb +22 -0
  29. data/app/views/main/list.html.erb +72 -0
  30. data/app/views/main/new.html.erb +22 -0
  31. data/lib/abstract_model.rb +67 -0
  32. data/lib/activerecord_support.rb +169 -0
  33. data/lib/datamapper_support.rb +157 -0
  34. data/lib/generic_support.rb +17 -0
  35. data/lib/merb-admin/merbtasks.rb +103 -0
  36. data/lib/merb-admin/slicetasks.rb +20 -0
  37. data/lib/merb-admin/spectasks.rb +53 -0
  38. data/lib/merb-admin.rb +99 -0
  39. data/public/images/arrow-down.gif +0 -0
  40. data/public/images/arrow-up.gif +0 -0
  41. data/public/images/changelist-bg.gif +0 -0
  42. data/public/images/changelist-bg_rtl.gif +0 -0
  43. data/public/images/chooser-bg.gif +0 -0
  44. data/public/images/chooser_stacked-bg.gif +0 -0
  45. data/public/images/default-bg-reverse.gif +0 -0
  46. data/public/images/default-bg.gif +0 -0
  47. data/public/images/deleted-overlay.gif +0 -0
  48. data/public/images/icon-no.gif +0 -0
  49. data/public/images/icon-unknown.gif +0 -0
  50. data/public/images/icon-yes.gif +0 -0
  51. data/public/images/icon_addlink.gif +0 -0
  52. data/public/images/icon_alert.gif +0 -0
  53. data/public/images/icon_calendar.gif +0 -0
  54. data/public/images/icon_changelink.gif +0 -0
  55. data/public/images/icon_clock.gif +0 -0
  56. data/public/images/icon_deletelink.gif +0 -0
  57. data/public/images/icon_error.gif +0 -0
  58. data/public/images/icon_searchbox.png +0 -0
  59. data/public/images/icon_success.gif +0 -0
  60. data/public/images/inline-delete-8bit.png +0 -0
  61. data/public/images/inline-delete.png +0 -0
  62. data/public/images/inline-restore-8bit.png +0 -0
  63. data/public/images/inline-restore.png +0 -0
  64. data/public/images/inline-splitter-bg.gif +0 -0
  65. data/public/images/nav-bg-grabber.gif +0 -0
  66. data/public/images/nav-bg-reverse.gif +0 -0
  67. data/public/images/nav-bg.gif +0 -0
  68. data/public/images/selector-add.gif +0 -0
  69. data/public/images/selector-addall.gif +0 -0
  70. data/public/images/selector-remove.gif +0 -0
  71. data/public/images/selector-removeall.gif +0 -0
  72. data/public/images/selector-search.gif +0 -0
  73. data/public/images/selector_stacked-add.gif +0 -0
  74. data/public/images/selector_stacked-remove.gif +0 -0
  75. data/public/images/tool-left.gif +0 -0
  76. data/public/images/tool-left_over.gif +0 -0
  77. data/public/images/tool-right.gif +0 -0
  78. data/public/images/tool-right_over.gif +0 -0
  79. data/public/images/tooltag-add.gif +0 -0
  80. data/public/images/tooltag-add_over.gif +0 -0
  81. data/public/images/tooltag-arrowright.gif +0 -0
  82. data/public/images/tooltag-arrowright_over.gif +0 -0
  83. data/public/javascripts/CollapsedFieldsets.js +85 -0
  84. data/public/javascripts/DateTimeShortcuts.js +255 -0
  85. data/public/javascripts/RelatedObjectLookups.js +96 -0
  86. data/public/javascripts/SelectBox.js +111 -0
  87. data/public/javascripts/SelectFilter2.js +113 -0
  88. data/public/javascripts/actions.js +39 -0
  89. data/public/javascripts/calendar.js +143 -0
  90. data/public/javascripts/core.js +176 -0
  91. data/public/javascripts/dateparse.js +233 -0
  92. data/public/javascripts/getElementsBySelector.js +167 -0
  93. data/public/javascripts/i18n.js +33 -0
  94. data/public/javascripts/master.js +0 -0
  95. data/public/javascripts/ordering.js +137 -0
  96. data/public/javascripts/timeparse.js +94 -0
  97. data/public/javascripts/urlify.js +140 -0
  98. data/public/stylesheets/base.css +746 -0
  99. data/public/stylesheets/changelists.css +269 -0
  100. data/public/stylesheets/dashboard.css +24 -0
  101. data/public/stylesheets/forms.css +327 -0
  102. data/public/stylesheets/global.css +142 -0
  103. data/public/stylesheets/ie.css +51 -0
  104. data/public/stylesheets/layout.css +29 -0
  105. data/public/stylesheets/login.css +54 -0
  106. data/public/stylesheets/master.css +2 -0
  107. data/public/stylesheets/null.css +1 -0
  108. data/public/stylesheets/patch-iewin.css +8 -0
  109. data/public/stylesheets/rtl.css +206 -0
  110. data/public/stylesheets/widgets.css +506 -0
  111. data/schema/migrations/001_create_divisions_migration.rb +15 -0
  112. data/schema/migrations/002_create_drafts_migration.rb +21 -0
  113. data/schema/migrations/003_create_leagues_migration.rb +13 -0
  114. data/schema/migrations/004_create_players_migration.rb +23 -0
  115. data/schema/migrations/005_create_teams_migration.rb +18 -0
  116. data/spec/controllers/main_spec.rb +25 -0
  117. data/spec/models/activerecord/division.rb +8 -0
  118. data/spec/models/activerecord/draft.rb +16 -0
  119. data/spec/models/activerecord/league.rb +6 -0
  120. data/spec/models/activerecord/player.rb +10 -0
  121. data/spec/models/activerecord/team.rb +11 -0
  122. data/spec/models/datamapper/division.rb +12 -0
  123. data/spec/models/datamapper/draft.rb +18 -0
  124. data/spec/models/datamapper/league.rb +11 -0
  125. data/spec/models/datamapper/player.rb +21 -0
  126. data/spec/models/datamapper/team.rb +15 -0
  127. data/spec/requests/main_spec.rb +475 -0
  128. data/spec/spec_helper.rb +112 -0
  129. 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,6 @@
1
+ class MerbAdmin::Application < Merb::Controller
2
+ include Merb::MerbAdmin::MainHelper
3
+
4
+ controller_for_slice
5
+
6
+ end
@@ -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 '&hellip;'
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 '&hellip;'
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] ||= '&hellip;'
51
+ options[:right_cut_label] ||= '&hellip;'
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,10 @@
1
+ <% if message && message[:error] %>
2
+ <p class="errornote">
3
+ <%=h message[:error] %>
4
+ </p>
5
+ <% end %>
6
+ <% if message && message[:notice] %>
7
+ <ul class="messagelist">
8
+ <li><%=h message[:notice] %></li>
9
+ </ul>
10
+ <% 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)) %> &rsaquo;
35
+ <%= link_to(@abstract_model.plural_name.to_s.capitalize, url(:admin_list, :model_name => @abstract_model.singular_name)) %> &rsaquo;
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)) %> &rsaquo;
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>