admin-theme 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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 [![build status](https://secure.travis-ci.org/simmetria/admin-theme.png)](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
|