Browse Source

Admin panel, logout action, save

Vova Tkach 6 years ago
parent
commit
c38d9bf76d

+ 1 - 1
constants/constants.go

@@ -2,4 +2,4 @@ package constants
 
 const Debug = !false
 const ServerVersion = "1.0.1"
-const AssetsVersion = "4"
+const AssetsVersion = "5"

+ 2 - 2
engine/actions/action_first_user.go

@@ -5,8 +5,8 @@ import (
 )
 
 func (this *Action) Action_first_user() {
-	if dbe := this.use_database(); dbe != nil {
-		this.msg_error(dbe.Error())
+	if err := this.use_database(); err != nil {
+		this.msg_error(err.Error())
 		return
 	} else {
 		defer this.db.Close()

+ 2 - 2
engine/actions/action_signin.go

@@ -8,8 +8,8 @@ import (
 )
 
 func (this *Action) Action_signin() {
-	if dbe := this.use_database(); dbe != nil {
-		this.msg_error(dbe.Error())
+	if err := this.use_database(); err != nil {
+		this.msg_error(err.Error())
 		return
 	} else {
 		defer this.db.Close()

+ 28 - 0
engine/actions/action_singout.go

@@ -0,0 +1,28 @@
+package actions
+
+import (
+//"database/sql"
+//_ "github.com/go-sql-driver/mysql"
+
+//utils "golang-fave/engine/wrapper/utils"
+)
+
+func (this *Action) Action_singout() {
+	if err := this.use_database(); err != nil {
+		this.msg_error(err.Error())
+		return
+	} else {
+		defer this.db.Close()
+	}
+
+	if err := this.load_session_user(); err != nil {
+		this.msg_error(err.Error())
+		return
+	}
+
+	// Set to zero
+	this.wrapper.Session.SetInt("UserId", 0)
+
+	// Reload current page
+	this.write(`window.location.reload(false);`)
+}

+ 24 - 1
engine/actions/actions.go

@@ -17,6 +17,7 @@ import (
 type Action struct {
 	wrapper *wrapper.Wrapper
 	db      *sql.DB
+	user    *utils.MySql_user
 }
 
 func (this *Action) write(data string) {
@@ -61,8 +62,30 @@ func (this *Action) use_database() error {
 	return nil
 }
 
+func (this *Action) load_session_user() error {
+	if this.db == nil {
+		return errors.New("not connected to database")
+	}
+	if this.user != nil {
+		return errors.New("user already loaded")
+	}
+	if this.wrapper.Session.GetIntDef("UserId", 0) <= 0 {
+		return errors.New("session user id is not defined")
+	}
+	this.user = &utils.MySql_user{}
+	err := this.db.QueryRow("SELECT `id`, `first_name`, `last_name`, `email`, `password` FROM `users` WHERE `id` = ? LIMIT 1;", this.wrapper.Session.GetIntDef("UserId", 0)).Scan(
+		&this.user.A_id, &this.user.A_first_name, &this.user.A_last_name, &this.user.A_email, &this.user.A_password)
+	if err != nil {
+		return err
+	}
+	if this.user.A_id != this.wrapper.Session.GetIntDef("UserId", 0) {
+		return errors.New("can't load user from session user id")
+	}
+	return nil
+}
+
 func New(wrapper *wrapper.Wrapper) *Action {
-	return &Action{wrapper, nil}
+	return &Action{wrapper, nil, nil}
 }
 
 func (this *Action) Run() bool {

+ 91 - 2
engine/backend/backend.go

@@ -3,19 +3,31 @@ package backend
 import (
 	"database/sql"
 	_ "github.com/go-sql-driver/mysql"
+	"html/template"
 
+	"golang-fave/constants"
 	"golang-fave/engine/wrapper"
 
 	templates "golang-fave/engine/wrapper/resources/templates"
+	utils "golang-fave/engine/wrapper/utils"
 )
 
 type Backend struct {
 	wrapper *wrapper.Wrapper
 	db      *sql.DB
+	user    *utils.MySql_user
+}
+
+type TmplData struct {
+	Title        string
+	UserEmail    string
+	SidebarLeft  template.HTML
+	Content      template.HTML
+	SidebarRight template.HTML
 }
 
 func New(wrapper *wrapper.Wrapper, db *sql.DB) *Backend {
-	return &Backend{wrapper, db}
+	return &Backend{wrapper, db, nil}
 }
 
 func (this *Backend) Run() bool {
@@ -34,6 +46,83 @@ func (this *Backend) Run() bool {
 		return this.wrapper.TmplBackEnd(templates.CpLogin, nil)
 	}
 
-	(*this.wrapper.W).Write([]byte(`Admin panel here...`))
+	// Load current user, if not, show login page
+	this.user = &utils.MySql_user{}
+	err = this.db.QueryRow("SELECT `id`, `first_name`, `last_name`, `email`, `password` FROM `users` WHERE `id` = ? LIMIT 1;", this.wrapper.Session.GetIntDef("UserId", 0)).Scan(
+		&this.user.A_id, &this.user.A_first_name, &this.user.A_last_name, &this.user.A_email, &this.user.A_password)
+	if this.wrapper.EngineErrMsgOnError(err) {
+		return true
+	}
+	if this.user.A_id != this.wrapper.Session.GetIntDef("UserId", 0) {
+		return this.wrapper.TmplBackEnd(templates.CpLogin, nil)
+	}
+
+	// Display cp page
+	/*
+		(*this.wrapper.W).Write([]byte(`Admin panel here...`))
+		return true
+	*/
+	// return this.wrapper.TmplBackEnd(templates.CpBase, nil)
+
+	/*
+		tmpl, err := template.New("template").Parse(string(templates.CpBase))
+		if err == nil {
+			(*this.wrapper.W).Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
+			tmpl.Execute(*this.wrapper.W, wrapper.TmplDataAll{
+				System: this.wrapper.TmplGetSystemData(),
+				Data: TmplData{
+					Title: "Fave " + constants.ServerVersion,
+					UserEmail: this.user.A_email,
+					SidebarLeft: "Sidebar left",
+					Content: "Content",
+					SidebarRight: "Sidebar right",
+				},
+			})
+			return true
+		}
+	*/
+
+	// Get parsed template as string
+	// https://coderwall.com/p/ns60fq/simply-output-go-html-template-execution-to-strings
+
+	// http://localhost:8080/admin/
+
+	sidebar_left := string(`<ul class="nav flex-column">
+		<li class="nav-item active">
+			<a class="nav-link" href="#">Pages</a>
+			<ul class="nav flex-column">
+				<li class="nav-item active">
+					<a class="nav-link" href="#">List of pages</a>
+				</li>
+				<li class="nav-item">
+					<a class="nav-link" href="#">Add new page</a>
+				</li>
+			</ul>
+		</li>
+		<li class="nav-item">
+			<a class="nav-link" href="#">Link 2</a>
+		</li>
+		<li class="nav-item">
+			<a class="nav-link" href="#">Link 3</a>
+		</li>
+		<li class="nav-item">
+			<a class="nav-link" href="#">Link 4</a>
+		</li>
+	</ul>`)
+
+	page := this.wrapper.TmplParseToString(templates.CpBase, wrapper.TmplDataAll{
+		System: this.wrapper.TmplGetSystemData(),
+		Data: TmplData{
+			Title:        "Fave " + constants.ServerVersion,
+			UserEmail:    this.user.A_email,
+			SidebarLeft:  template.HTML(sidebar_left),
+			Content:      template.HTML("Content"),
+			SidebarRight: template.HTML("Sidebar right"),
+		},
+	})
+	(*this.wrapper.W).Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
+	(*this.wrapper.W).Write([]byte(page))
 	return true
+
+	return false
 }

+ 50 - 10
engine/wrapper/resources/scripts/assets.cp.scripts.js

@@ -26,6 +26,36 @@ function ModalShowMsg(title, message) {
 	dialog.modal('show');
 }
 
+function AjaxDone(data) {
+	try {
+		eval(data);
+	} catch(e) {
+		if(e instanceof SyntaxError) {
+			console.log(data);
+			ModalShowMsg('JavaScript Eval Error', e.message)
+		}
+	}
+}
+
+function AjaxFail() {
+	console.log('Form send fail, page will be reloaded');
+	window.location.reload(false);
+}
+
+function ActionSingOut() {
+	$.ajax({
+		type: "POST",
+		url: '/cp/',
+		data: {
+			action: 'singout',
+		}
+	}).done(function(data) {
+		AjaxDone(data)
+	}).fail(function() {
+		AjaxFail();
+	});
+}
+
 $(document).ready(function() {
 	// Fix body scroll
 	$(window).resize(function() {
@@ -51,17 +81,9 @@ $(document).ready(function() {
 				url: form.attr('action'),
 				data: form.serialize()
 			}).done(function(data) {
-				try {
-					eval(data);
-				} catch(e) {
-					if(e instanceof SyntaxError) {
-						console.log(data);
-						ModalShowMsg('JavaScript Eval Error', e.message)
-					}
-				}
+				AjaxDone(data)
 			}).fail(function() {
-				console.log('Form send fail, page will be reloaded');
-				window.location.reload(false);
+				AjaxFail();
 			}).always(function() {
 				$(form).removeClass('loading');
 				$(button).removeClass('progress-bar-striped').removeClass('progress-bar-animated');
@@ -70,4 +92,22 @@ $(document).ready(function() {
 			e.preventDefault();
 		});
 	});
+
+	// Fix navbar bottstrap menu hover
+	/*
+	$('#navbarCollapse ul').each(function() {
+		var ul = $(this);
+		ul.find('li').mouseover(function() {
+			if(ul.find('li.show').length > 0) {
+				var li = $(this);
+				ul.find('li').removeClass('show');
+				ul.find('li div.dropdown-menu').removeClass('show');
+				ul.find('li a[role=button]').attr('aria-expanded', false);
+				li.addClass('show');
+				li.find('div.dropdown-menu').addClass('show');
+				li.find('a[role=button]:first').attr('aria-expanded', true);
+			}
+		});
+	});
+	*/
 });

+ 1 - 1
engine/wrapper/resources/scripts/assets.cp.scripts.js.go

@@ -1,4 +1,4 @@
 package scripts
 
 // https://jscompress.com/
-var File_assets_cp_scripts_js = []byte(`function DetectBodyScroll(){var o=$("body");$(o).hasScrollBar()?$(o).removeClass("no-scroll"):$(o).addClass("no-scroll")}function ModalSysMsg(o,a){DetectBodyScroll();var t=$("#sys-modal-msg");return $("#sysModalMsgLabel").text(o),$("#sysModalMsgBody").html(a),t}function ModalShowMsg(o,a){ModalSysMsg(o,a).modal("show")}jQuery.fn.hasScrollBar=function(){return this.get(0).scrollHeight>this.get(0).clientHeight},$(document).ready(function(){$(window).resize(function(){DetectBodyScroll()}),DetectBodyScroll(),$("form").each(function(){$(this).submit(function(e){var form=$(this);if($(form).hasClass("loading"))e.preventDefault();else{$(form).addClass("loading");var button=$(this).find("button[type=submit]");$(button).addClass("progress-bar-striped").addClass("progress-bar-animated"),$.ajax({type:"POST",url:form.attr("action"),data:form.serialize()}).done(function(data){try{eval(data)}catch(o){o instanceof SyntaxError&&(console.log(data),ModalShowMsg("JavaScript Eval Error",o.message))}}).fail(function(){console.log("Form send fail, page will be reloaded"),window.location.reload(!1)}).always(function(){$(form).removeClass("loading"),$(button).removeClass("progress-bar-striped").removeClass("progress-bar-animated")}),e.preventDefault()}})})});`)
+var File_assets_cp_scripts_js = []byte(`function DetectBodyScroll(){var a=$("body");$(a).hasScrollBar()?$(a).removeClass("no-scroll"):$(a).addClass("no-scroll")}function ModalSysMsg(a,o){DetectBodyScroll();var t=$("#sys-modal-msg");return $("#sysModalMsgLabel").text(a),$("#sysModalMsgBody").html(o),t}function ModalShowMsg(a,o){ModalSysMsg(a,o).modal("show")}function AjaxDone(data){try{eval(data)}catch(a){a instanceof SyntaxError&&(console.log(data),ModalShowMsg("JavaScript Eval Error",a.message))}}function AjaxFail(){console.log("Form send fail, page will be reloaded"),window.location.reload(!1)}function ActionSingOut(){$.ajax({type:"POST",url:"/cp/",data:{action:"singout"}}).done(function(a){AjaxDone(a)}).fail(function(){AjaxFail()})}jQuery.fn.hasScrollBar=function(){return this.get(0).scrollHeight>this.get(0).clientHeight},$(document).ready(function(){$(window).resize(function(){DetectBodyScroll()}),DetectBodyScroll(),$("form").each(function(){$(this).submit(function(a){var o=$(this);if($(o).hasClass("loading"))a.preventDefault();else{$(o).addClass("loading");var t=$(this).find("button[type=submit]");$(t).addClass("progress-bar-striped").addClass("progress-bar-animated"),$.ajax({type:"POST",url:o.attr("action"),data:o.serialize()}).done(function(a){AjaxDone(a)}).fail(function(){AjaxFail()}).always(function(){$(o).removeClass("loading"),$(t).removeClass("progress-bar-striped").removeClass("progress-bar-animated")}),a.preventDefault()}})})});`)

+ 130 - 0
engine/wrapper/resources/styles/assets.cp.styles.css

@@ -3,6 +3,15 @@ body.no-scroll {
 	padding-right: 0px !important;
 }
 
+/* Bootstrap dropdown hover fix */
+.dropdown-item:focus, .dropdown-item:hover {
+	background-color: #f1f1f1;
+}
+
+.dropdown-item.active, .dropdown-item:active {
+	background-color: #007bff;
+}
+
 /* Login/MySQL form */
 body.cp-login,
 body.cp-mysql,
@@ -64,4 +73,125 @@ body.cp-first-user {
 	margin-bottom: 10px;
 	border-top-left-radius: 0;
 	border-top-right-radius: 0;
+}
+
+/* Control panel skeleton */
+body.cp {
+	font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+	background: initial;
+	background-color: #fff;
+	font-size: 1rem;
+	font-weight: 400;
+	line-height: 1.5;
+	color: #212529;
+	width: 100%;
+	height: 100%;
+	overflow: hidden;
+}
+
+body.cp nav.main {
+	height: 56px;
+	box-shadow: 0 0 5px 4px rgba(0,0,0,0.25);
+}
+
+body.cp nav.main.bg-dark {
+	background: #205081 url(/assets/sys/bg.png) repeat 0 0!important;
+}
+
+body.cp nav.main a.navbar-brand {
+	font-weight: bold;
+}
+
+body.cp nav.main .navbar-nav .nav-item a img {
+	width: 35px;
+	width: 35px;
+	margin-right: 10px;
+	margin-top: -30px;
+	margin-bottom: -30px;
+}
+
+body.cp .wrap {
+	width: 100%;
+	height: 100%;
+	display: table;
+	align-items: stretch;
+}
+
+body.cp .wrap .sidebar,
+body.cp .wrap .content {
+	display: table-cell;
+	position: relative;
+	padding-top: 56px;
+	vertical-align: top;
+}
+
+body.cp .wrap .sidebar {
+	display: none;
+}
+
+body.cp.cp-sidebar-left .wrap .sidebar.sidebar-left {
+	display: table-cell;
+}
+
+body.cp.cp-sidebar-right .wrap .sidebar.sidebar-right {
+	display: table-cell;
+}
+
+body.cp .wrap .sidebar-right .padd,
+body.cp .wrap .content .padd {
+	padding: 1rem 1rem;
+}
+
+body.cp .wrap .scroll {
+	height: 100%;
+	overflow: hidden;
+	overflow-y: auto;
+}
+
+body.cp .wrap .sidebar {
+	width: 245px;
+	background: #eee;
+	box-shadow: 0 0.5em 0.5em rgba(0,0,0,.3);
+}
+
+body.cp .wrap .sidebar.sidebar-left ul.nav {
+	padding: 1rem 0px;
+}
+
+body.cp .wrap .sidebar.sidebar-left ul.nav li.nav-item a {
+	color: #444;
+}
+
+body.cp .wrap .sidebar.sidebar-left ul.nav li.nav-item.active {
+	background-color: #417cb9;
+}
+
+body.cp .wrap .sidebar.sidebar-left ul.nav li.nav-item.active a {
+	color: #fff;
+}
+
+body.cp .wrap .sidebar.sidebar-left ul.nav li.nav-item:hover {
+	background-color: #e7e7e7;
+}
+
+body.cp .wrap .sidebar.sidebar-left ul.nav li.nav-item.active:hover {
+	background-color: #417cb9;
+}
+
+body.cp .wrap .sidebar.sidebar-left ul.nav ul.nav {
+	background: #eee;
+	padding-top: 0px;
+}
+
+body.cp .wrap .sidebar.sidebar-left ul.nav ul.nav li.nav-item a {
+	color: #444;
+	padding-left: 2rem;
+}
+
+body.cp .wrap .sidebar.sidebar-left ul.nav ul.nav li.nav-item.active {
+	background-color: #e7e7e7;
+}
+
+body.cp .wrap .sidebar.sidebar-left ul.nav ul.nav li.nav-item.active a {
+	color: #417cb9;
 }

File diff suppressed because it is too large
+ 0 - 1
engine/wrapper/resources/styles/assets.cp.styles.css.go


+ 84 - 0
engine/wrapper/resources/templates/cp.base.go

@@ -0,0 +1,84 @@
+package templates
+
+var CpBase = []byte(`<!doctype html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8">
+		<meta name="theme-color" content="#205081" />
+		<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+		<title>{{$.Data.Title}}</title>
+		<link rel="stylesheet" href="{{$.System.PathCssBootstrap}}">
+		<link rel="stylesheet" href="{{$.System.PathCssStyles}}" />
+		<link rel="stylesheet" href="{{$.System.PathCssCpStyles}}">
+		<link rel="shortcut icon" href="{{$.System.PathIcoFav}}" type="image/x-icon" />
+	</head>
+	<body class="cp cp-sidebar-left cp-sidebar-right">
+		<nav class="navbar main navbar-expand-md navbar-dark fixed-top bg-dark">
+			<a class="navbar-brand" href="/">{{$.Data.Title}}</a>
+			<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
+				<span class="navbar-toggler-icon"></span>
+			</button>
+			<div class="collapse navbar-collapse" id="navbarCollapse">
+				<ul class="navbar-nav mr-auto">
+					<li class="nav-item dropdown">
+						<a class="nav-link dropdown-toggle" href="javascript:;" id="nbModulesDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+							Modules
+						</a>
+						<div class="dropdown-menu" aria-labelledby="nbModulesDropdown">
+							<a class="dropdown-item active" href="/cp/">Pages</a>
+						</div>
+					</li>
+					<li class="nav-item dropdown">
+						<a class="nav-link dropdown-toggle" href="javascript:;" id="nbSystemDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+							System
+						</a>
+						<div class="dropdown-menu" aria-labelledby="nbSystemDropdown">
+							<a class="dropdown-item" href="/cp/users/">Users</a>
+							<div class="dropdown-divider"></div>
+							<a class="dropdown-item" href="/cp/settings/">Settings</a>
+						</div>
+					</li>
+				</ul>
+				<ul class="navbar-nav ml-auto">
+					<li class="nav-item dropdown">
+						<a class="nav-link dropdown-toggle" href="javascript:;" id="nbAccountDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+							<img class="rounded-circle" src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==">{{$.Data.UserEmail}}
+						</a>
+						<div class="dropdown-menu dropdown-menu-right" aria-labelledby="nbAccountDropdown">
+							<a class="dropdown-item" href="javascript:;">Settings</a>
+							<div class="dropdown-divider"></div>
+							<a class="dropdown-item" href="javascript:ActionSingOut();">Sing out</a>
+						</div>
+					</li>
+				</ul>
+			</div>
+		</nav>
+		<div class="wrap">
+			<div class="sidebar sidebar-left">
+				<div class="scroll">
+					<div class="padd">
+						{{$.Data.SidebarLeft}}
+					</div>
+				</div>
+			</div>
+			<div class="content">
+				<div class="scroll">
+					<div class="padd">
+						{{$.Data.Content}}
+					</div>
+				</div>
+			</div>
+			<div class="sidebar sidebar-right">
+				<div class="scroll">
+					<div class="padd">
+						{{$.Data.SidebarRight}}
+					</div>
+				</div>
+			</div>
+		</div>
+		<script src="{{$.System.PathJsJquery}}"></script>
+		<script src="{{$.System.PathJsPopper}}"></script>
+		<script src="{{$.System.PathJsBootstrap}}"></script>
+		<script src="{{$.System.PathJsCpScripts}}"></script>
+	</body>
+</html>`)

+ 82 - 0
engine/wrapper/resources/templates/cp.base.html

@@ -0,0 +1,82 @@
+<!doctype html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8">
+		<meta name="theme-color" content="#205081" />
+		<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+		<title>{{$.Data.Title}}</title>
+		<link rel="stylesheet" href="{{$.System.PathCssBootstrap}}">
+		<link rel="stylesheet" href="{{$.System.PathCssStyles}}" />
+		<link rel="stylesheet" href="{{$.System.PathCssCpStyles}}">
+		<link rel="shortcut icon" href="{{$.System.PathIcoFav}}" type="image/x-icon" />
+	</head>
+	<body class="cp cp-sidebar-left cp-sidebar-right">
+		<nav class="navbar main navbar-expand-md navbar-dark fixed-top bg-dark">
+			<a class="navbar-brand" href="/">{{$.Data.Title}}</a>
+			<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
+				<span class="navbar-toggler-icon"></span>
+			</button>
+			<div class="collapse navbar-collapse" id="navbarCollapse">
+				<ul class="navbar-nav mr-auto">
+					<li class="nav-item dropdown">
+						<a class="nav-link dropdown-toggle" href="javascript:;" id="nbModulesDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+							Modules
+						</a>
+						<div class="dropdown-menu" aria-labelledby="nbModulesDropdown">
+							<a class="dropdown-item active" href="/cp/">Pages</a>
+						</div>
+					</li>
+					<li class="nav-item dropdown">
+						<a class="nav-link dropdown-toggle" href="javascript:;" id="nbSystemDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+							System
+						</a>
+						<div class="dropdown-menu" aria-labelledby="nbSystemDropdown">
+							<a class="dropdown-item" href="/cp/users/">Users</a>
+							<div class="dropdown-divider"></div>
+							<a class="dropdown-item" href="/cp/settings/">Settings</a>
+						</div>
+					</li>
+				</ul>
+				<ul class="navbar-nav ml-auto">
+					<li class="nav-item dropdown">
+						<a class="nav-link dropdown-toggle" href="javascript:;" id="nbAccountDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+							<img class="rounded-circle" src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==">{{$.Data.UserEmail}}
+						</a>
+						<div class="dropdown-menu dropdown-menu-right" aria-labelledby="nbAccountDropdown">
+							<a class="dropdown-item" href="javascript:;">Settings</a>
+							<div class="dropdown-divider"></div>
+							<a class="dropdown-item" href="javascript:ActionSingOut();">Sing out</a>
+						</div>
+					</li>
+				</ul>
+			</div>
+		</nav>
+		<div class="wrap">
+			<div class="sidebar sidebar-left">
+				<div class="scroll">
+					<div class="padd">
+						{{$.Data.SidebarLeft}}
+					</div>
+				</div>
+			</div>
+			<div class="content">
+				<div class="scroll">
+					<div class="padd">
+						{{$.Data.Content}}
+					</div>
+				</div>
+			</div>
+			<div class="sidebar sidebar-right">
+				<div class="scroll">
+					<div class="padd">
+						{{$.Data.SidebarRight}}
+					</div>
+				</div>
+			</div>
+		</div>
+		<script src="{{$.System.PathJsJquery}}"></script>
+		<script src="{{$.System.PathJsPopper}}"></script>
+		<script src="{{$.System.PathJsBootstrap}}"></script>
+		<script src="{{$.System.PathJsCpScripts}}"></script>
+	</body>
+</html>

+ 8 - 8
engine/wrapper/static.go

@@ -143,8 +143,8 @@ func (this *Wrapper) printPageDefault() {
 	}
 	(*this.W).Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
 	(*this.W).Header().Set("Content-Type", "text/html")
-	tmpl.Execute(*this.W, tmplDataAll{
-		System: this.tmplGetSystemData(),
+	tmpl.Execute(*this.W, TmplDataAll{
+		System: this.TmplGetSystemData(),
 	})
 }
 
@@ -167,8 +167,8 @@ func (this *Wrapper) printPage404() {
 	(*this.W).Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
 	(*this.W).WriteHeader(http.StatusNotFound)
 	(*this.W).Header().Set("Content-Type", "text/html")
-	tmpl.Execute(*this.W, tmplDataAll{
-		System: this.tmplGetSystemData(),
+	tmpl.Execute(*this.W, TmplDataAll{
+		System: this.TmplGetSystemData(),
 	})
 }
 
@@ -185,8 +185,8 @@ func (this *Wrapper) printTmplPageError(perr error) {
 	(*this.W).Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
 	(*this.W).WriteHeader(http.StatusInternalServerError)
 	(*this.W).Header().Set("Content-Type", "text/html")
-	tmpl.Execute(*this.W, tmplDataAll{
-		System: this.tmplGetSystemData(),
+	tmpl.Execute(*this.W, TmplDataAll{
+		System: this.TmplGetSystemData(),
 		Data: tmplDataErrorMsg{
 			ErrorMessage: perr.Error(),
 		},
@@ -206,8 +206,8 @@ func (this *Wrapper) printEnginePageError(perr error) {
 	(*this.W).Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
 	(*this.W).WriteHeader(http.StatusInternalServerError)
 	(*this.W).Header().Set("Content-Type", "text/html")
-	tmpl.Execute(*this.W, tmplDataAll{
-		System: this.tmplGetSystemData(),
+	tmpl.Execute(*this.W, TmplDataAll{
+		System: this.TmplGetSystemData(),
 		Data: tmplDataErrorMsg{
 			ErrorMessage: perr.Error(),
 		},

+ 9 - 0
engine/wrapper/utils/struct_mysql.go

@@ -0,0 +1,9 @@
+package utils
+
+type MySql_user struct {
+	A_id         int
+	A_first_name string
+	A_last_name  string
+	A_email      string
+	A_password   string
+}

+ 33 - 20
engine/wrapper/wrapper.go

@@ -1,6 +1,7 @@
 package wrapper
 
 import (
+	"bytes"
 	"html/template"
 	"log"
 	"net"
@@ -28,7 +29,7 @@ type tmplDataSystem struct {
 	BlockModalSysMsg template.HTML
 }
 
-type tmplDataAll struct {
+type TmplDataAll struct {
 	System tmplDataSystem
 	Data   interface{}
 }
@@ -46,21 +47,6 @@ type Wrapper struct {
 	Session      *sessions.Session
 }
 
-func (this *Wrapper) tmplGetSystemData() tmplDataSystem {
-	return tmplDataSystem{
-		PathIcoFav:       this.R.URL.Scheme + "://" + this.R.Host + "/assets/sys/fave.ico?v=" + constants.AssetsVersion,
-		PathSvgLogo:      this.R.URL.Scheme + "://" + this.R.Host + "/assets/sys/logo.svg?v=" + constants.AssetsVersion,
-		PathCssStyles:    this.R.URL.Scheme + "://" + this.R.Host + "/assets/sys/styles.css?v=" + constants.AssetsVersion,
-		PathCssCpStyles:  this.R.URL.Scheme + "://" + this.R.Host + "/assets/cp/styles.css?v=" + constants.AssetsVersion,
-		PathCssBootstrap: this.R.URL.Scheme + "://" + this.R.Host + "/assets/sys/bootstrap.css?v=" + constants.AssetsVersion,
-		PathJsJquery:     this.R.URL.Scheme + "://" + this.R.Host + "/assets/sys/jquery.js?v=" + constants.AssetsVersion,
-		PathJsPopper:     this.R.URL.Scheme + "://" + this.R.Host + "/assets/sys/popper.js?v=" + constants.AssetsVersion,
-		PathJsBootstrap:  this.R.URL.Scheme + "://" + this.R.Host + "/assets/sys/bootstrap.js?v=" + constants.AssetsVersion,
-		PathJsCpScripts:  this.R.URL.Scheme + "://" + this.R.Host + "/assets/cp/scripts.js?v=" + constants.AssetsVersion,
-		BlockModalSysMsg: template.HTML(templates.BlockModalSysMsg),
-	}
-}
-
 func New(w *http.ResponseWriter, r *http.Request, vhost string, port string, wwwdir string, vhosthome string) *Wrapper {
 	return &Wrapper{
 		VHost:        vhost,
@@ -167,6 +153,33 @@ func (this *Wrapper) LogError(value string) {
 		"] [" + this.R.Header.Get("User-Agent") + "]")
 }
 
+func (this *Wrapper) TmplGetSystemData() tmplDataSystem {
+	return tmplDataSystem{
+		PathIcoFav:       this.R.URL.Scheme + "://" + this.R.Host + "/assets/sys/fave.ico?v=" + constants.AssetsVersion,
+		PathSvgLogo:      this.R.URL.Scheme + "://" + this.R.Host + "/assets/sys/logo.svg?v=" + constants.AssetsVersion,
+		PathCssStyles:    this.R.URL.Scheme + "://" + this.R.Host + "/assets/sys/styles.css?v=" + constants.AssetsVersion,
+		PathCssCpStyles:  this.R.URL.Scheme + "://" + this.R.Host + "/assets/cp/styles.css?v=" + constants.AssetsVersion,
+		PathCssBootstrap: this.R.URL.Scheme + "://" + this.R.Host + "/assets/sys/bootstrap.css?v=" + constants.AssetsVersion,
+		PathJsJquery:     this.R.URL.Scheme + "://" + this.R.Host + "/assets/sys/jquery.js?v=" + constants.AssetsVersion,
+		PathJsPopper:     this.R.URL.Scheme + "://" + this.R.Host + "/assets/sys/popper.js?v=" + constants.AssetsVersion,
+		PathJsBootstrap:  this.R.URL.Scheme + "://" + this.R.Host + "/assets/sys/bootstrap.js?v=" + constants.AssetsVersion,
+		PathJsCpScripts:  this.R.URL.Scheme + "://" + this.R.Host + "/assets/cp/scripts.js?v=" + constants.AssetsVersion,
+		BlockModalSysMsg: template.HTML(templates.BlockModalSysMsg),
+	}
+}
+
+func (this *Wrapper) TmplParseToString(tcont []byte, data interface{}) string {
+	tmpl, err := template.New("template").Parse(string(tcont))
+	if err != nil {
+		return err.Error()
+	}
+	var tpl bytes.Buffer
+	if err := tmpl.Execute(&tpl, data); err != nil {
+		return err.Error()
+	}
+	return tpl.String()
+}
+
 func (this *Wrapper) TmplFrontEnd(tname string, data interface{}) bool {
 	tmpl, err := template.ParseFiles(
 		this.DirVHostHome+"/template"+"/"+tname+".html",
@@ -179,8 +192,8 @@ func (this *Wrapper) TmplFrontEnd(tname string, data interface{}) bool {
 		return true
 	}
 	(*this.W).Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
-	tmpl.Execute(*this.W, tmplDataAll{
-		System: this.tmplGetSystemData(),
+	tmpl.Execute(*this.W, TmplDataAll{
+		System: this.TmplGetSystemData(),
 		Data:   data,
 	})
 	return true
@@ -193,8 +206,8 @@ func (this *Wrapper) TmplBackEnd(tcont []byte, data interface{}) bool {
 		return true
 	}
 	(*this.W).Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
-	tmpl.Execute(*this.W, tmplDataAll{
-		System: this.tmplGetSystemData(),
+	tmpl.Execute(*this.W, TmplDataAll{
+		System: this.TmplGetSystemData(),
 		Data:   data,
 	})
 	return true

+ 29 - 84
hosts/localhost/htdocs/admin/index.html

@@ -7,83 +7,15 @@
 		
 		<link rel="stylesheet" href="/assets/sys/bootstrap.css">
 		<link rel="stylesheet" href="/assets/sys/styles.css">
-		<style type="text/css">
-			body.cp {
-				font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
-				background: initial;
-				background-color: #fff;
-				font-size: 1rem;
-				font-weight: 400;
-				line-height: 1.5;
-				color: #212529;
-				width: 100%;
-				height: 100%;
-				overflow: hidden;
-			}
+		<link rel="stylesheet" href="/assets/cp/styles.css">
 
-			body.cp nav.main {
-				height: 56px;
-			}
-
-			body.cp .wrap {
-				width: 100%;
-				height: 100%;
-				display: table;
-				align-items: stretch;
-			}
-
-			body.cp .wrap .sidebar,
-			body.cp .wrap .content {
-				display: table-cell;
-				position: relative;
-				padding-top: 56px;
-			}
-
-			body.cp .wrap .sidebar {
-				display: none;
-			}
-
-			body.cp.cp-sidebar-left .wrap .sidebar.sidebar-left {
-				display: table-cell;
-			}
-
-			body.cp.cp-sidebar-right .wrap .sidebar.sidebar-right {
-				display: table-cell;
-			}
-
-			body.cp .wrap .sidebar .padd,
-			body.cp .wrap .content .padd {
-				padding: .5rem 1rem;
-			}
-
-			body.cp .wrap .scroll {
-				height: 100%;
-				overflow: hidden;
-				overflow-y: auto;
-			}
-
-			body.cp .wrap .sidebar {
-				width: 245px;
-				background: #eee;
-				box-shadow: 0 0.5em 0.5em rgba(0,0,0,.3);
-			}
-
-			body.cp .wrap .content {
-				/**/
-			}
-
-			body.cp .wrap .sidebar.sidebar-right {
-				/**/
-			}
-		</style>
-
-		<title>Control Panel</title>
+		<title>Fave 1.0.1</title>
 
 		<link rel="shortcut icon" href="/assets/sys/fave.ico" type="image/x-icon" />
 	</head>
 	<body class="cp cp-sidebar-left cp-sidebar-right">
 		<nav class="navbar main navbar-expand-md navbar-dark fixed-top bg-dark">
-			<a class="navbar-brand" href="/">Control Panel</a>
+			<a class="navbar-brand" href="/">Fave 1.0.1</a>
 			<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
 				<span class="navbar-toggler-icon"></span>
 			</button>
@@ -94,7 +26,7 @@
 							Modules
 						</a>
 						<div class="dropdown-menu" aria-labelledby="nbModulesDropdown">
-							<a class="dropdown-item" href="#">Pages</a>
+							<a class="dropdown-item active" href="#">Pages</a>
 						</div>
 					</li>
 					<li class="nav-item dropdown">
@@ -111,12 +43,11 @@
 				<ul class="navbar-nav ml-auto">
 					<li class="nav-item dropdown">
 						<a class="nav-link dropdown-toggle" href="javascript:;" id="nbAccountDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
-							example@example.com
+							<img class="rounded-circle" src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==">admin@admin.com
 						</a>
 						<div class="dropdown-menu dropdown-menu-right" aria-labelledby="nbAccountDropdown">
-							<a class="dropdown-item" href="#">Change password</a>
-							<div class="dropdown-divider"></div>
 							<a class="dropdown-item" href="#">Settings</a>
+							<div class="dropdown-divider"></div>
 							<a class="dropdown-item" href="#">Sing out</a>
 						</div>
 					</li>
@@ -127,15 +58,28 @@
 			<div class="sidebar sidebar-left">
 				<div class="scroll">
 					<div class="padd">
-						Sidebar left<br />Sidebar left<br />Sidebar left<br />Sidebar left<br />Sidebar left<br />
-						Sidebar left<br />Sidebar left<br />Sidebar left<br />Sidebar left<br />Sidebar left<br />
-						Sidebar left<br />Sidebar left<br />Sidebar left<br />Sidebar left<br />Sidebar left<br />
-						Sidebar left<br />Sidebar left<br />Sidebar left<br />Sidebar left<br />Sidebar left<br />
-						Sidebar left<br />Sidebar left<br />Sidebar left<br />Sidebar left<br />Sidebar left<br />
-						Sidebar left<br />Sidebar left<br />Sidebar left<br />Sidebar left<br />Sidebar left<br />
-						Sidebar left<br />Sidebar left<br />Sidebar left<br />Sidebar left<br />Sidebar left<br />
-						Sidebar left<br />Sidebar left<br />Sidebar left<br />Sidebar left<br />Sidebar left<br />
-						Sidebar left<br />Sidebar left<br />Sidebar left<br />Sidebar left<br />Sidebar left<br />
+						<ul class="nav flex-column">
+							<li class="nav-item active">
+								<a class="nav-link" href="#">Pages</a>
+								<ul class="nav flex-column">
+									<li class="nav-item active">
+										<a class="nav-link" href="#">List of pages</a>
+									</li>
+									<li class="nav-item">
+										<a class="nav-link" href="#">Add new page</a>
+									</li>
+								</ul>
+							</li>
+							<li class="nav-item">
+								<a class="nav-link" href="#">Link 2</a>
+							</li>
+							<li class="nav-item">
+								<a class="nav-link" href="#">Link 3</a>
+							</li>
+							<li class="nav-item">
+								<a class="nav-link" href="#">Link 4</a>
+							</li>
+						</ul>
 					</div>
 				</div>
 			</div>
@@ -175,5 +119,6 @@
 		<script src="/assets/sys/jquery.js"></script>
 		<script src="/assets/sys/popper.js"></script>
 		<script src="/assets/sys/bootstrap.js"></script>
+		<script src="/assets/cp/scripts.js"></script>
 	</body>
 </html>

Some files were not shown because too many files changed in this diff