admin-theme 1.0.1
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/.gitignore +17 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +32 -0
- data/README.md +115 -0
- data/Rakefile +9 -0
- data/admin-theme.gemspec +22 -0
- data/config/locales/en.yml +18 -0
- data/features/resource_generator.feature +9 -0
- data/features/setup_generator.feature +8 -0
- data/features/steps/test_admin_theme_resource_generation.rb +31 -0
- data/features/steps/test_admin_theme_setup.rb +36 -0
- data/features/support/env.rb +1 -0
- data/features/support/helpers.rb +39 -0
- data/lib/admin-theme.rb +7 -0
- data/lib/admin-theme/version.rb +5 -0
- data/lib/generators/admin_theme/resource/controllers/admin_resource_controller.erb +46 -0
- data/lib/generators/admin_theme/resource/resource_generator.rb +93 -0
- data/lib/generators/admin_theme/resource/templates/view_edit.html.erb +17 -0
- data/lib/generators/admin_theme/resource/templates/view_form.html.erb +14 -0
- data/lib/generators/admin_theme/resource/templates/view_index.html.erb +38 -0
- data/lib/generators/admin_theme/resource/templates/view_new.html.erb +16 -0
- data/lib/generators/admin_theme/resource/templates/view_show.html.erb +36 -0
- data/lib/generators/admin_theme/setup/controllers/admin_base_controller.rb +18 -0
- data/lib/generators/admin_theme/setup/controllers/admin_dashboard_controller.rb +4 -0
- data/lib/generators/admin_theme/setup/setup_generator.rb +37 -0
- data/lib/generators/admin_theme/setup/templates/layout_admin.html.erb +46 -0
- data/lib/generators/admin_theme/setup/templates/view_dashboard.html.erb +7 -0
- data/lib/generators/admin_theme/setup/templates/view_sidebar.html.erb +15 -0
- data/vendor/assets/stylesheets/web-app-theme.css +410 -0
- data/vendor/assets/stylesheets/web-app-theme/default.scss +463 -0
- metadata +136 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
Except for those files subject to other licenses (as noted in the file or in
|
2
|
+
documentation accompanying the file), all files contained within this directory
|
3
|
+
are distributed under the following license:
|
4
|
+
|
5
|
+
- * -
|
6
|
+
|
7
|
+
Copyright (c) 2012 Nihad Abbasov <http://github.com/narkoz>
|
8
|
+
All rights reserved.
|
9
|
+
|
10
|
+
Redistribution and use in source and binary forms, with or without
|
11
|
+
modification, are permitted provided that the following conditions are met:
|
12
|
+
|
13
|
+
1. Redistributions of source code must retain the above copyright notice,
|
14
|
+
this list of conditions and the following disclaimer.
|
15
|
+
|
16
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
17
|
+
this list of conditions and the following disclaimer in the documentation
|
18
|
+
and/or other materials provided with the distribution.
|
19
|
+
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
23
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
24
|
+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
25
|
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
26
|
+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
27
|
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
28
|
+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
29
|
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
30
|
+
POSSIBILITY OF SUCH DAMAGE.
|
31
|
+
|
32
|
+
- * -
|
data/README.md
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
# Admin Theme [](https://secure.travis-ci.org/simmetria/admin-theme)
|
2
|
+
|
3
|
+
Admin theme is a fork of [Web App Theme](https://github.com/pilu/web-app-theme) generator. See [demo](http://pilu.github.com/web-app-theme).
|
4
|
+
It aims the same goal as a main branch: to generate admin panels quickly.
|
5
|
+
|
6
|
+
Changes from main branch:
|
7
|
+
|
8
|
+
* Rails 3.1+ compatibility
|
9
|
+
* Asset pipeline integration
|
10
|
+
* Auto generation of resources
|
11
|
+
* Improved I18n support
|
12
|
+
* Only default theme included
|
13
|
+
* Kaminari for pagination (optional)
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
Add to your Gemfile:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem 'admin-theme'
|
21
|
+
```
|
22
|
+
|
23
|
+
If you want to paginate records in admin panel also add:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
gem 'kaminari'
|
27
|
+
gem 'kaminari-admin-theme'
|
28
|
+
```
|
29
|
+
|
30
|
+
Run `bundle install`
|
31
|
+
|
32
|
+
## Usage
|
33
|
+
|
34
|
+
1. First time you need to setup admin-theme:
|
35
|
+
|
36
|
+
```sh
|
37
|
+
rails generate admin_theme:setup
|
38
|
+
```
|
39
|
+
|
40
|
+
This will generate some controllers, views for admin panel and prepare assets:
|
41
|
+
|
42
|
+
```sh
|
43
|
+
create app/views/layouts/admin.html.erb
|
44
|
+
create app/views/admin/shared/_sidebar.html.erb
|
45
|
+
create app/views/admin/dashboard/show.html.erb
|
46
|
+
create app/controllers/admin/base_controller.rb
|
47
|
+
create app/controllers/admin/dashboard_controller.rb
|
48
|
+
insert config/routes.rb
|
49
|
+
create app/assets/stylesheets/admin.css
|
50
|
+
create app/assets/javascripts/admin.js
|
51
|
+
```
|
52
|
+
|
53
|
+
You can pass `app_name` option to specify the application name:
|
54
|
+
|
55
|
+
```sh
|
56
|
+
rails generate admin_theme:setup --app_name="Custom name"
|
57
|
+
```
|
58
|
+
|
59
|
+
After you will be able to access admin panel at `localhost:3000/admin`.
|
60
|
+
|
61
|
+
2. Move application stylesheets to `another_dir` and change `application.css` to not load admin-theme stylesheets:
|
62
|
+
|
63
|
+
```patch
|
64
|
+
--*= require_tree .
|
65
|
+
++*= require_tree ./another_dir
|
66
|
+
```
|
67
|
+
|
68
|
+
3. You can now use admin-theme resource generator to generate necessary resources in admin panel:
|
69
|
+
|
70
|
+
```sh
|
71
|
+
rails generate admin_theme:resource posts # assuming you have a model named Post
|
72
|
+
```
|
73
|
+
|
74
|
+
If you want to enable pagination:
|
75
|
+
|
76
|
+
```sh
|
77
|
+
rails generate admin_theme:resource posts --pagination
|
78
|
+
```
|
79
|
+
|
80
|
+
If you want to generate resource with different name you can explicitly specify the model name in the second parameter:
|
81
|
+
|
82
|
+
```sh
|
83
|
+
rails generate admin_theme:resource items post # assuming you have a model named Post
|
84
|
+
```
|
85
|
+
|
86
|
+
## i18n
|
87
|
+
|
88
|
+
See [`config/locales`](https://github.com/simmetria/admin-theme/tree/master/config/locales) for available translations.
|
89
|
+
You can add one by yourself in `config/locales`:
|
90
|
+
|
91
|
+
```yaml
|
92
|
+
time:
|
93
|
+
formats:
|
94
|
+
admin-theme: "%d %b %Y [%H:%M]"
|
95
|
+
|
96
|
+
admin-theme:
|
97
|
+
save: Save
|
98
|
+
cancel: Cancel
|
99
|
+
list: List
|
100
|
+
edit: Edit
|
101
|
+
new: New
|
102
|
+
show: Show
|
103
|
+
delete: Delete
|
104
|
+
confirm: Are you sure?
|
105
|
+
created_at: Created at
|
106
|
+
all: All
|
107
|
+
or: or
|
108
|
+
```
|
109
|
+
|
110
|
+
## Credits
|
111
|
+
|
112
|
+
* Web App Theme: [Andrea Franz](http://gravityblast.com)
|
113
|
+
* Icons: [FAMFAMFAM Silk icons](http://www.famfamfam.com/lab/icons/silk)
|
114
|
+
* Buttons: Particletree - Rediscovering the Button Element
|
115
|
+
|
data/Rakefile
ADDED
data/admin-theme.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/admin-theme/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Nihad Abbasov"]
|
6
|
+
gem.email = ["mail@narkoz.me"]
|
7
|
+
gem.description = %q{Allows to generate admin panels quickly}
|
8
|
+
gem.summary = %q{Admin panel generator}
|
9
|
+
gem.homepage = "https://github.com/simmetria/admin-theme"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "admin-theme"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Admin::Theme::VERSION
|
17
|
+
|
18
|
+
gem.add_runtime_dependency 'rails', '~> 3.1'
|
19
|
+
gem.add_runtime_dependency 'thor', '~> 0.14'
|
20
|
+
|
21
|
+
gem.add_development_dependency 'spinach'
|
22
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
en:
|
2
|
+
time:
|
3
|
+
formats:
|
4
|
+
admin-theme: "%d %b %Y [%H:%M]"
|
5
|
+
|
6
|
+
admin-theme:
|
7
|
+
save: Save
|
8
|
+
cancel: Cancel
|
9
|
+
list: List
|
10
|
+
edit: Edit
|
11
|
+
new: New
|
12
|
+
show: Show
|
13
|
+
delete: Delete
|
14
|
+
confirm: Are you sure?
|
15
|
+
created_at: Created at
|
16
|
+
updated_at: Updated at
|
17
|
+
all: All
|
18
|
+
or: or
|
@@ -0,0 +1,9 @@
|
|
1
|
+
Feature: Test admin-theme resource generation
|
2
|
+
|
3
|
+
Scenario: Generate an admin-theme resource
|
4
|
+
Given a new rails app
|
5
|
+
And I install 'admin-theme' gem
|
6
|
+
And I run 'rails generate admin_theme:setup'
|
7
|
+
When I run 'rails generate admin_theme:resource posts'
|
8
|
+
Then the posts resource in admin panel should be generated
|
9
|
+
And the routes file should be modified
|
@@ -0,0 +1,8 @@
|
|
1
|
+
Feature: Test admin-theme setup
|
2
|
+
|
3
|
+
Scenario: Setup admin-theme
|
4
|
+
Given a new rails app
|
5
|
+
And I install 'admin-theme' gem
|
6
|
+
When I run 'rails generate admin_theme:setup'
|
7
|
+
Then the necessary files should be generated
|
8
|
+
And the assets and routes files should be modified
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class TestAdminThemeResourceGeneration < Spinach::FeatureSteps
|
2
|
+
include Helpers
|
3
|
+
|
4
|
+
Given 'a new rails app' do
|
5
|
+
`rails new #{app_name}`
|
6
|
+
end
|
7
|
+
|
8
|
+
And 'I install \'admin-theme\' gem' do
|
9
|
+
in_app_dir { `echo "'admin-theme', github: 'simmetria/admin-theme'" >> Gemfile && bundle` }
|
10
|
+
end
|
11
|
+
|
12
|
+
And 'I run \'rails generate admin_theme:setup\'' do
|
13
|
+
in_app_dir { `rails g admin_theme:setup` }
|
14
|
+
end
|
15
|
+
|
16
|
+
When 'I run \'rails generate admin_theme:resource posts\'' do
|
17
|
+
in_app_dir { @output = `rails g admin_theme:resource posts` }
|
18
|
+
end
|
19
|
+
|
20
|
+
Then 'the posts resource in admin panel should be generated' do
|
21
|
+
resource_files.each { |f| @output.must_include f }
|
22
|
+
@output.scan('create').size.must_equal 6
|
23
|
+
@output.scan('insert').size.must_equal 1
|
24
|
+
@output.scan('gsub').size.must_equal 1
|
25
|
+
end
|
26
|
+
|
27
|
+
And 'the routes file should be modified' do
|
28
|
+
routes = read_file('config/routes.rb')
|
29
|
+
routes.must_include "resources :posts"
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class TestAdminThemeSetup < Spinach::FeatureSteps
|
2
|
+
include Helpers
|
3
|
+
|
4
|
+
Given 'a new rails app' do
|
5
|
+
`rails new #{app_name}`
|
6
|
+
end
|
7
|
+
|
8
|
+
And 'I install \'admin-theme\' gem' do
|
9
|
+
in_app_dir { `echo "'admin-theme', github: 'simmetria/admin-theme'" >> Gemfile && bundle` }
|
10
|
+
end
|
11
|
+
|
12
|
+
When 'I run \'rails generate admin_theme:setup\'' do
|
13
|
+
in_app_dir { @output = `rails g admin_theme:setup` }
|
14
|
+
end
|
15
|
+
|
16
|
+
Then 'the necessary files should be generated' do
|
17
|
+
setup_files.each { |f| @output.must_include f }
|
18
|
+
@output.scan('create').size.must_equal 7
|
19
|
+
@output.scan('insert').size.must_equal 1
|
20
|
+
end
|
21
|
+
|
22
|
+
And 'the assets and routes files should be modified' do
|
23
|
+
routes = read_file('config/routes.rb')
|
24
|
+
routes.must_include "namespace :admin do"
|
25
|
+
routes.must_include "root :to => 'dashboard#show', :as => 'dashboard'"
|
26
|
+
|
27
|
+
stylesheet = read_file('app/assets/stylesheets/admin.css')
|
28
|
+
stylesheet.must_include "*= require web-app-theme"
|
29
|
+
stylesheet.must_include "*= require web-app-theme/default"
|
30
|
+
stylesheet.must_include "*= require_self"
|
31
|
+
|
32
|
+
javascript = read_file('app/assets/javascripts/admin.js')
|
33
|
+
javascript.must_include "//= require jquery"
|
34
|
+
javascript.must_include "//= require jquery_ujs"
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'minitest/spec'
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Helpers
|
2
|
+
def app_name
|
3
|
+
@app_name = "tmp/test_app#{rand(10..100)}"
|
4
|
+
end
|
5
|
+
|
6
|
+
def in_app_dir(&block)
|
7
|
+
Dir.chdir(@app_name, &block)
|
8
|
+
end
|
9
|
+
|
10
|
+
def read_file(file)
|
11
|
+
in_app_dir { File.read(file) }
|
12
|
+
end
|
13
|
+
|
14
|
+
def setup_files
|
15
|
+
[
|
16
|
+
"app/views/layouts/admin.html.erb",
|
17
|
+
"app/views/admin/shared/_sidebar.html.erb",
|
18
|
+
"app/views/admin/dashboard/show.html.erb",
|
19
|
+
"app/controllers/admin/base_controller.rb",
|
20
|
+
"app/controllers/admin/dashboard_controller.rb",
|
21
|
+
"config/routes.rb",
|
22
|
+
"app/assets/stylesheets/admin.css",
|
23
|
+
"app/assets/javascripts/admin.js"
|
24
|
+
]
|
25
|
+
end
|
26
|
+
|
27
|
+
def resource_files
|
28
|
+
[
|
29
|
+
"app/controllers/admin/posts_controller.rb",
|
30
|
+
"config/routes.rb",
|
31
|
+
"app/views/admin/posts/_form.html.erb",
|
32
|
+
"app/views/admin/posts/edit.html.erb",
|
33
|
+
"app/views/admin/posts/index.html.erb",
|
34
|
+
"app/views/admin/posts/new.html.erb",
|
35
|
+
"app/views/admin/posts/show.html.erb",
|
36
|
+
"app/views/layouts/admin.html.erb"
|
37
|
+
]
|
38
|
+
end
|
39
|
+
end
|
data/lib/admin-theme.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
class Admin::<%= plural_model_name %>Controller < Admin::BaseController
|
2
|
+
before_filter :find_<%= resource_name %>, :only => [:show, :edit, :update, :destroy]
|
3
|
+
|
4
|
+
def index
|
5
|
+
@<%= plural_resource_name %> = <%= model_name %>.<%= model_scope %>
|
6
|
+
end
|
7
|
+
|
8
|
+
def show
|
9
|
+
end
|
10
|
+
|
11
|
+
def new
|
12
|
+
@<%= resource_name %> = <%= model_name %>.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def create
|
16
|
+
@<%= resource_name %> = <%= model_name %>.new(params[:<%= resource_name %>])
|
17
|
+
|
18
|
+
if @<%= resource_name %>.save
|
19
|
+
redirect_to <%= singular_controller_routing_path %>_path(@<%= resource_name %>), :notice => "<%= model_name %> was successfully created."
|
20
|
+
else
|
21
|
+
render :new
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def edit
|
26
|
+
end
|
27
|
+
|
28
|
+
def update
|
29
|
+
if @<%= resource_name %>.update_attributes(params[:<%= resource_name %>])
|
30
|
+
redirect_to <%= singular_controller_routing_path %>_path(@<%= resource_name %>), :notice => "<%= model_name %> was successfully updated."
|
31
|
+
else
|
32
|
+
render :edit
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def destroy
|
37
|
+
@<%= resource_name %>.destroy
|
38
|
+
redirect_to <%= controller_routing_path %>_path, :notice => "<%= model_name %> was successfully deleted."
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def find_<%= resource_name %>
|
44
|
+
@<%= resource_name %> = <%= model_name %>.find params[:id]
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'rails/generators/generated_attribute'
|
2
|
+
|
3
|
+
module AdminTheme
|
4
|
+
class ResourceGenerator < Rails::Generators::Base
|
5
|
+
desc "Creates the resource in admin panel."
|
6
|
+
source_root File.expand_path('../templates', __FILE__)
|
7
|
+
|
8
|
+
argument :resource, :type => :string
|
9
|
+
argument :model_name, :type => :string, :required => false
|
10
|
+
|
11
|
+
class_option :pagination, :type => :boolean, :default => false, :desc => 'Specify if you use kaminari to paginate'
|
12
|
+
|
13
|
+
def initialize(args, *options)
|
14
|
+
super(args, *options)
|
15
|
+
initialize_views_variables
|
16
|
+
end
|
17
|
+
|
18
|
+
def generate_controller
|
19
|
+
template "../controllers/admin_resource_controller.erb", "app/controllers/admin/#{plural_resource_name}_controller.rb"
|
20
|
+
inject_into_file "config/routes.rb", " resources :#{plural_resource_name}\n", :after => "namespace :admin do\n"
|
21
|
+
end
|
22
|
+
|
23
|
+
def generate_views
|
24
|
+
template "view_form.html.erb", "app/views/admin/#{plural_resource_name}/_form.html.erb"
|
25
|
+
template "view_edit.html.erb", "app/views/admin/#{plural_resource_name}/edit.html.erb"
|
26
|
+
template "view_index.html.erb", "app/views/admin/#{plural_resource_name}/index.html.erb"
|
27
|
+
template "view_new.html.erb", "app/views/admin/#{plural_resource_name}/new.html.erb"
|
28
|
+
template "view_show.html.erb", "app/views/admin/#{plural_resource_name}/show.html.erb"
|
29
|
+
|
30
|
+
# Add link to resource in admin layout
|
31
|
+
gsub_file("app/views/layouts/admin.html.erb", /\<div\s+id=\"main-navigation\">.*\<\/ul\>/mi) do |match|
|
32
|
+
match.gsub!(/\<\/ul\>/, "")
|
33
|
+
%|#{match} <li class="<%= controller_name == '#{plural_resource_name}' ? 'active' : '' %>">
|
34
|
+
<%= link_to "#{plural_model_name}", #{controller_routing_path}_path %>
|
35
|
+
</li>
|
36
|
+
</ul>|
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def initialize_views_variables
|
43
|
+
@base_name, @controller_class_path, @controller_file_path,
|
44
|
+
@controller_class_nesting, @controller_class_nesting_depth = extract_modules("admin/#{resource}")
|
45
|
+
|
46
|
+
@controller_routing_path = @controller_file_path.gsub(/\//, '_')
|
47
|
+
@model_name = @base_name.singularize unless @model_name
|
48
|
+
@model_name = @model_name.camelize
|
49
|
+
end
|
50
|
+
|
51
|
+
def controller_routing_path
|
52
|
+
@controller_routing_path
|
53
|
+
end
|
54
|
+
|
55
|
+
def singular_controller_routing_path
|
56
|
+
@controller_routing_path.singularize
|
57
|
+
end
|
58
|
+
|
59
|
+
def model_name
|
60
|
+
@model_name
|
61
|
+
end
|
62
|
+
|
63
|
+
def plural_model_name
|
64
|
+
@model_name.pluralize
|
65
|
+
end
|
66
|
+
|
67
|
+
def resource_name
|
68
|
+
@model_name.underscore
|
69
|
+
end
|
70
|
+
|
71
|
+
def plural_resource_name
|
72
|
+
resource_name.pluralize
|
73
|
+
end
|
74
|
+
|
75
|
+
def model_scope
|
76
|
+
if options.pagination
|
77
|
+
'page(params[:page]).per(20)'
|
78
|
+
else
|
79
|
+
'all'
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def extract_modules(name)
|
84
|
+
modules = name.include?('/') ? name.split('/') : name.split('::')
|
85
|
+
name = modules.pop
|
86
|
+
path = modules.map { |m| m.underscore }
|
87
|
+
file_path = (path + [name.underscore]).join('/')
|
88
|
+
nesting = modules.map { |m| m.camelize }.join('::')
|
89
|
+
|
90
|
+
[name, path, file_path, nesting, modules.size]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|