spud_admin 0.1.0
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/README.markdown +39 -0
- data/app/assets/images/back_disabled.jpg +0 -0
- data/app/assets/images/back_enabled.jpg +0 -0
- data/app/assets/images/favicon.ico +0 -0
- data/app/assets/images/forward_disabled.jpg +0 -0
- data/app/assets/images/forward_enabled.jpg +0 -0
- data/app/assets/images/sort_asc.png +0 -0
- data/app/assets/images/sort_asc_disabled.png +0 -0
- data/app/assets/images/sort_both.png +0 -0
- data/app/assets/images/sort_desc.png +0 -0
- data/app/assets/images/sort_desc_disabled.png +0 -0
- data/app/assets/images/spud/admin/flick/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/app/assets/images/spud/admin/flick/ui-bg_flat_0_eeeeee_40x100.png +0 -0
- data/app/assets/images/spud/admin/flick/ui-bg_flat_55_ffffff_40x100.png +0 -0
- data/app/assets/images/spud/admin/flick/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/app/assets/images/spud/admin/flick/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/app/assets/images/spud/admin/flick/ui-bg_highlight-soft_100_f6f6f6_1x100.png +0 -0
- data/app/assets/images/spud/admin/flick/ui-bg_highlight-soft_25_0073ea_1x100.png +0 -0
- data/app/assets/images/spud/admin/flick/ui-bg_highlight-soft_50_dddddd_1x100.png +0 -0
- data/app/assets/images/spud/admin/flick/ui-icons_0073ea_256x240.png +0 -0
- data/app/assets/images/spud/admin/flick/ui-icons_454545_256x240.png +0 -0
- data/app/assets/images/spud/admin/flick/ui-icons_666666_256x240.png +0 -0
- data/app/assets/images/spud/admin/flick/ui-icons_ff0084_256x240.png +0 -0
- data/app/assets/images/spud/admin/flick/ui-icons_ffffff_256x240.png +0 -0
- data/app/assets/images/spud/admin/users_thumb.png +0 -0
- data/app/assets/javascripts/spud/admin/application.js +73 -0
- data/app/assets/javascripts/spud/admin/jquery.dataTables.min.js +151 -0
- data/app/assets/javascripts/spud/admin/settings.js +2 -0
- data/app/assets/javascripts/spud/admin/users.js +2 -0
- data/app/assets/javascripts/spud/setup.js +2 -0
- data/app/assets/javascripts/spud/user_sessions.js +2 -0
- data/app/assets/javascripts/spud/users.js +2 -0
- data/app/assets/stylesheets/spud/admin/application.css +432 -0
- data/app/assets/stylesheets/spud/admin/jquery-ui.css +568 -0
- data/app/assets/stylesheets/spud/admin/settings.css +4 -0
- data/app/assets/stylesheets/spud/admin/users.css +4 -0
- data/app/assets/stylesheets/spud/forms.css +108 -0
- data/app/assets/stylesheets/spud/login.css +58 -0
- data/app/assets/stylesheets/spud/login/application.css +302 -0
- data/app/assets/stylesheets/spud/setup.css +4 -0
- data/app/assets/stylesheets/spud/user_sessions.css +4 -0
- data/app/assets/stylesheets/spud/users.css +4 -0
- data/app/controllers/spud/admin/application_controller.rb +17 -0
- data/app/controllers/spud/admin/dashboard_controller.rb +6 -0
- data/app/controllers/spud/admin/settings_controller.rb +15 -0
- data/app/controllers/spud/admin/users_controller.rb +109 -0
- data/app/controllers/spud/application_controller.rb +44 -0
- data/app/controllers/spud/setup_controller.rb +21 -0
- data/app/controllers/spud/user_sessions_controller.rb +28 -0
- data/app/controllers/spud/users_controller.rb +21 -0
- data/app/helpers/spud/admin/application_helper.rb +17 -0
- data/app/helpers/spud/admin/settings_helper.rb +2 -0
- data/app/helpers/spud/admin/users_helper.rb +2 -0
- data/app/helpers/spud/setup_helper.rb +2 -0
- data/app/helpers/spud/user_sessions_helper.rb +2 -0
- data/app/helpers/spud/users_helper.rb +2 -0
- data/app/models/spud_admin_permission.rb +2 -0
- data/app/models/spud_setting.rb +2 -0
- data/app/models/spud_user.rb +14 -0
- data/app/models/spud_user_session.rb +3 -0
- data/app/views/layouts/spud/admin/application.html.erb +44 -0
- data/app/views/layouts/spud/admin/detail.html.erb +31 -0
- data/app/views/layouts/spud/login/application.html.erb +17 -0
- data/app/views/layouts/spud/setup.html.erb +47 -0
- data/app/views/spud/admin/dashboard/index.html.erb +11 -0
- data/app/views/spud/admin/users/_edit.html.erb +14 -0
- data/app/views/spud/admin/users/_form.html.erb +38 -0
- data/app/views/spud/admin/users/_new.html.erb +14 -0
- data/app/views/spud/admin/users/index.html.erb +41 -0
- data/app/views/spud/admin/users/show.html.erb +17 -0
- data/app/views/spud/setup/index.html.erb +25 -0
- data/app/views/spud/user_sessions/new.html.erb +22 -0
- data/app/views/spud/users/_form.html.erb +35 -0
- data/app/views/spud/users/settings.html.erb +14 -0
- data/config/application.rb +48 -0
- data/config/boot.rb +6 -0
- data/config/routes.rb +15 -0
- data/lib/spud_admin.rb +7 -0
- data/lib/spud_admin/configuration.rb +12 -0
- data/lib/spud_admin/engine.rb +45 -0
- metadata +180 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
class Spud::Admin::ApplicationController < Spud::ApplicationController
|
|
2
|
+
before_filter :require_admin_user
|
|
3
|
+
add_breadcrumb "Dashboard", :spud_admin_root_path
|
|
4
|
+
layout 'spud/admin/application'
|
|
5
|
+
unloadable
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
def require_admin_user
|
|
10
|
+
return false if !require_user
|
|
11
|
+
if !@current_user.super_admin
|
|
12
|
+
flash[:error] = "User must be an administrator to view this area."
|
|
13
|
+
redirect_to root_url() and return false
|
|
14
|
+
end
|
|
15
|
+
return true
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
class Spud::Admin::SettingsController < Spud::Admin::ApplicationController
|
|
2
|
+
layout 'spud/admin/detail'
|
|
3
|
+
add_breadcrumb "Settings", :spud_admin_settings_path
|
|
4
|
+
def index
|
|
5
|
+
@spud_settings = SpudSetting.all
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def create
|
|
9
|
+
@spud_settings = SpudSetting.all
|
|
10
|
+
if params[:spud_settings].blank?
|
|
11
|
+
flash[:error] = "Spud Settings not specified!"
|
|
12
|
+
redirect_to spud_admin_settings_url()
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
class Spud::Admin::UsersController < Spud::Admin::ApplicationController
|
|
2
|
+
layout 'spud/admin/detail'
|
|
3
|
+
add_breadcrumb "Users", :spud_admin_users_path
|
|
4
|
+
|
|
5
|
+
# filters
|
|
6
|
+
before_filter :load_user,:only => [:edit,:update,:show,:destroy]
|
|
7
|
+
|
|
8
|
+
def index
|
|
9
|
+
@page_thumbnail = "spud/admin/users_thumb.png"
|
|
10
|
+
@page_name = "Users"
|
|
11
|
+
|
|
12
|
+
@users = SpudUser.order(:login).paginate :page => params[:page]
|
|
13
|
+
respond_to do |format|
|
|
14
|
+
format.json {render :json => @users.to_json}
|
|
15
|
+
format.js {render :json => @users.to_json}
|
|
16
|
+
format.xml {render :xml => @users.to_xml}
|
|
17
|
+
format.html { render }
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def show
|
|
22
|
+
add_breadcrumb @user.full_name, :spud_admin_user_path
|
|
23
|
+
@page_thumbnail = "spud/admin/users_thumb.png"
|
|
24
|
+
@page_name = @user.full_name
|
|
25
|
+
end
|
|
26
|
+
def new
|
|
27
|
+
@user = SpudUser.new
|
|
28
|
+
|
|
29
|
+
respond_to do |format|
|
|
30
|
+
format.js { render :partial => "new"}
|
|
31
|
+
format.html { render }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def create
|
|
37
|
+
status = 500
|
|
38
|
+
@user = SpudUser.new(params[:spud_user])
|
|
39
|
+
@user.super_admin = params[:spud_user][:super_admin]
|
|
40
|
+
|
|
41
|
+
if @user.save
|
|
42
|
+
status = 200
|
|
43
|
+
flash[:notice] = "User created successfully"
|
|
44
|
+
|
|
45
|
+
else
|
|
46
|
+
flash[:error] = "There was an error while saving the user."
|
|
47
|
+
render :action => "new"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
respond_to do |format|
|
|
51
|
+
format.js { render :status => status, :json => @user.to_json}
|
|
52
|
+
format.html {
|
|
53
|
+
if status == 200
|
|
54
|
+
redirect_to spud_admin_users_url()
|
|
55
|
+
else
|
|
56
|
+
render :action => "new"
|
|
57
|
+
end
|
|
58
|
+
}
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def edit
|
|
64
|
+
respond_to do |format|
|
|
65
|
+
format.js { render :partial => "edit"}
|
|
66
|
+
format.html { render }
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def update
|
|
71
|
+
@user.attributes = params[:spud_user]
|
|
72
|
+
@user.super_admin = params[:spud_user][:super_admin]
|
|
73
|
+
if @user.save
|
|
74
|
+
flash[:notice] = "User saved successfully."
|
|
75
|
+
redirect_to spud_admin_users_url()
|
|
76
|
+
else
|
|
77
|
+
flash[:error] = "There was an error while saving the user."
|
|
78
|
+
render :action => "edit"
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def destroy
|
|
83
|
+
status = 500
|
|
84
|
+
if @user.destroy
|
|
85
|
+
status = 200
|
|
86
|
+
flash[:notice] = "User removed."
|
|
87
|
+
else
|
|
88
|
+
flash[:error] = "There was an error removing the user"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
respond_to do |format|
|
|
92
|
+
format.js { render :text => nil,:status => status }
|
|
93
|
+
format.html {redirect_to spud_admin_users_url()}
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
private
|
|
99
|
+
|
|
100
|
+
def load_user
|
|
101
|
+
@user = SpudUser.where(:id => params[:id]).first
|
|
102
|
+
if @user.blank?
|
|
103
|
+
flash[:error] = "User not found!"
|
|
104
|
+
redirect_to spud_admin_users_url() and return false
|
|
105
|
+
end
|
|
106
|
+
return true
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
class Spud::ApplicationController < ActionController::Base
|
|
2
|
+
unloadable
|
|
3
|
+
protect_from_forgery
|
|
4
|
+
helper_method :current_user_session, :current_user
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
private
|
|
8
|
+
def current_user_session
|
|
9
|
+
return @current_user_session if defined?(@current_user_session)
|
|
10
|
+
@current_user_session = SpudUserSession.find
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def current_user
|
|
14
|
+
return @current_user if defined?(@current_user)
|
|
15
|
+
@current_user = current_user_session && current_user_session.spud_user
|
|
16
|
+
end
|
|
17
|
+
def require_user
|
|
18
|
+
unless current_user
|
|
19
|
+
store_location
|
|
20
|
+
flash[:notice] = "You must be logged in to access this page"
|
|
21
|
+
redirect_to new_spud_user_session_url
|
|
22
|
+
return false
|
|
23
|
+
end
|
|
24
|
+
return true
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def require_no_user
|
|
28
|
+
if current_user
|
|
29
|
+
store_location
|
|
30
|
+
flash[:notice] = "You must be logged out to access this page"
|
|
31
|
+
redirect_to root_url
|
|
32
|
+
return false
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def store_location
|
|
37
|
+
session[:return_to] = request.url
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def redirect_back_or_default(default)
|
|
41
|
+
redirect_to(session[:return_to] || default)
|
|
42
|
+
session[:return_to] = nil
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
class Spud::SetupController < Spud::ApplicationController
|
|
2
|
+
layout 'spud/setup'
|
|
3
|
+
def index
|
|
4
|
+
@page_thumbnail = "spud/admin/users_thumb.png"
|
|
5
|
+
@page_name = "First Time Setup"
|
|
6
|
+
if SpudUser.count != 0
|
|
7
|
+
flash[:error] = "Access Denied! This wizard may only be executed when the database is empty."
|
|
8
|
+
redirect_to root_url() and return
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
if request.post?
|
|
12
|
+
@user = SpudUser.new(:login => params[:login],:email => params[:email],:password => params[:password],:password_confirmation => params[:password_confirmation],:super_admin => true)
|
|
13
|
+
if @user.save
|
|
14
|
+
redirect_to spud_admin_root_url() and return
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
flash[:error] = "Error creating administrative account!"
|
|
18
|
+
else
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
class Spud::UserSessionsController < Spud::ApplicationController
|
|
2
|
+
before_filter :require_no_user, :only => [:new, :create]
|
|
3
|
+
before_filter :require_user, :only => :destroy
|
|
4
|
+
layout 'spud/login/application'
|
|
5
|
+
def new
|
|
6
|
+
if SpudUser.count == 0
|
|
7
|
+
redirect_to spud_setup_url() and return
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
@user_session = SpudUserSession.new
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def create
|
|
14
|
+
@user_session = SpudUserSession.new(params[:spud_user_session])
|
|
15
|
+
if @user_session.save
|
|
16
|
+
flash[:notice] = "Login successful!"
|
|
17
|
+
redirect_back_or_default root_url
|
|
18
|
+
else
|
|
19
|
+
render :action => :new
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def destroy
|
|
24
|
+
current_user_session.destroy
|
|
25
|
+
flash[:notice] = "Logout successful!"
|
|
26
|
+
redirect_back_or_default new_spud_user_session_url
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
class Spud::UsersController < Spud::ApplicationController
|
|
2
|
+
before_filter :require_user
|
|
3
|
+
layout 'spud/admin/detail'
|
|
4
|
+
add_breadcrumb "Settings", :spud_settings_path
|
|
5
|
+
def settings
|
|
6
|
+
@page_thumbnail = "spud/admin/users_thumb.png"
|
|
7
|
+
@page_name = "Settings"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def update
|
|
11
|
+
@page_thumbnail = "spud/admin/users_thumb.png"
|
|
12
|
+
@page_name = "Settings"
|
|
13
|
+
if @current_user.update_attributes(params[:spud_user])
|
|
14
|
+
flash[:notice] = "User settings saved successfully."
|
|
15
|
+
redirect_to spud_admin_root_url()
|
|
16
|
+
else
|
|
17
|
+
flash[:error] = "There was an error while saving the user."
|
|
18
|
+
render :action => "settings"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Spud::Admin::ApplicationHelper
|
|
2
|
+
def timestamp(timedate)
|
|
3
|
+
return "Never" if timedate.blank?
|
|
4
|
+
return Time.now() - timedate > 604800 ? timedate.strftime("%B %d") + ' at ' + timedate.strftime("%I:%M %p") : time_ago_in_words(timedate) + ' ago'
|
|
5
|
+
end
|
|
6
|
+
def link_to_remove_fields(name, f)
|
|
7
|
+
f.hidden_field(:_destroy) + link_to_function(name, "remove_fields(this)")
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def link_to_add_fields(name, f, association)
|
|
11
|
+
new_object = f.object.class.reflect_on_association(association).klass.new
|
|
12
|
+
fields = f.fields_for(association, new_object, :child_index => "new_#{association}") do |builder|
|
|
13
|
+
render(association.to_s.singularize + "_fields", :f => builder)
|
|
14
|
+
end
|
|
15
|
+
link_to_function(name, "add_fields(this, \"#{association}\", \"#{escape_javascript(fields)}\")")
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
class SpudUser < ActiveRecord::Base
|
|
2
|
+
|
|
3
|
+
attr_protected :super_admin
|
|
4
|
+
acts_as_authentic do |c|
|
|
5
|
+
# c.my_config_option = my_value # for available options see documentation in: Authlogic::ActsAsAuthentic
|
|
6
|
+
end # block optional
|
|
7
|
+
|
|
8
|
+
def full_name
|
|
9
|
+
if first_name.blank? && last_name.blank?
|
|
10
|
+
return self.login
|
|
11
|
+
end
|
|
12
|
+
return "#{self.first_name} #{self.last_name}"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>Spud Admin</title>
|
|
5
|
+
<%= stylesheet_link_tag "spud/admin/application" %>
|
|
6
|
+
<%= javascript_include_tag "spud/admin/application" %>
|
|
7
|
+
<%= csrf_meta_tags %>
|
|
8
|
+
<%=yield :head%>
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<div id="header">
|
|
12
|
+
<h1>Spud Admin</h1>
|
|
13
|
+
<div id="user_meta">
|
|
14
|
+
<span class="greeting">Hello <%=@current_user.login%></span> | <%=link_to "Settings", spud_settings_path%> | <%=link_to "Logout",spud_user_session_url,:method => :delete%></a>
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
<div id="breadcrumbs">
|
|
18
|
+
<%= render_breadcrumbs :separator => ' / ' %>
|
|
19
|
+
</div>
|
|
20
|
+
<div id="content">
|
|
21
|
+
<% if flash[:notice] %>
|
|
22
|
+
<div class="flash notice"><%= flash[:notice] %></div>
|
|
23
|
+
<% end %>
|
|
24
|
+
<% if flash[:warning] %>
|
|
25
|
+
<div class="flash warning"><%= flash[:warning] %></div>
|
|
26
|
+
<% end %>
|
|
27
|
+
<% if flash[:error] || @error_object_name.nil? == false %>
|
|
28
|
+
<div class="flash error">
|
|
29
|
+
<%= flash[:error] %>
|
|
30
|
+
<%-unless @error_object_name.nil?%>
|
|
31
|
+
<%-if @error_object_name.is_a?(Array)%>
|
|
32
|
+
<%=error_messages_for @error_object_name[0], :header_message => @error_object_name[1], :message => @error_object_name[2]%>
|
|
33
|
+
<%-else%>
|
|
34
|
+
<%=error_messages_for @error_object_name%>
|
|
35
|
+
<%-end%>
|
|
36
|
+
|
|
37
|
+
<%-end%>
|
|
38
|
+
</div>
|
|
39
|
+
<%-end%>
|
|
40
|
+
<%= content_for?(:content) ? yield(:content) : yield %>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
</body>
|
|
44
|
+
</html>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<%=content_for :content do %>
|
|
2
|
+
<div id="detail_wrapper">
|
|
3
|
+
<span class="data_controls">
|
|
4
|
+
<%=yield :data_controls%>
|
|
5
|
+
</span>
|
|
6
|
+
<h2>
|
|
7
|
+
<%if @page_thumbnail%>
|
|
8
|
+
<span class="thumb_wrapper"><%=image_tag(@page_thumbnail)%></span>
|
|
9
|
+
<%end%>
|
|
10
|
+
<%=@page_name%>
|
|
11
|
+
</h2>
|
|
12
|
+
|
|
13
|
+
<div id="details">
|
|
14
|
+
<%if content_for?(:detail_nav)%>
|
|
15
|
+
<div id="detail_nav_wrapper">
|
|
16
|
+
<div id="detail_nav">
|
|
17
|
+
<%= yield(:detail_nav)%>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
<div id="detail_main_wrapper">
|
|
21
|
+
<div id="detail_main">
|
|
22
|
+
<%= content_for?(:detail) ? yield(:detail) : yield %></div>
|
|
23
|
+
</div>
|
|
24
|
+
<br style="clear:both;"/>
|
|
25
|
+
<%else%>
|
|
26
|
+
<%= content_for?(:detail) ? yield(:detail) : yield %>
|
|
27
|
+
<%end%>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
<%end%>
|
|
31
|
+
<%= render :template => 'layouts/spud/admin/application' %>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>Spud Admin</title>
|
|
5
|
+
<%= stylesheet_link_tag("spud/login/application") %>
|
|
6
|
+
|
|
7
|
+
<%= csrf_meta_tags %>
|
|
8
|
+
<%=content_for :head%>
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
|
|
12
|
+
<div id="content_wrapper">
|
|
13
|
+
<div id="content"><%=yield%></div>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
</body>
|
|
17
|
+
</html>
|