add base goffee

This commit is contained in:
JACS 2026-05-01 11:56:07 -05:00
parent c95e8a9245
commit ac2cfa9fe1
108 changed files with 64018 additions and 0 deletions

604
controllers/adminusers.go Normal file
View file

@ -0,0 +1,604 @@
// Copyright (c) 2024 Zeni Kim <zenik@smarteching.com>
// Use of this source code is governed by MIT-style
// license that can be found in the LICENSE file.
package controllers
import (
"errors"
"fmt"
"strconv"
"git.smarteching.com/goffee/core"
"git.smarteching.com/goffee/core/template/components"
"git.smarteching.com/jacs/goffeetabler/events"
"git.smarteching.com/jacs/goffeetabler/models"
"git.smarteching.com/jacs/goffeetabler/utils"
"gorm.io/gorm"
)
func AdminUsersList(c *core.Context) *core.Response {
// initiate authority
auth := new(utils.Authority)
var session = new(utils.SessionUser)
// true if session is active
hassession := session.Init(c)
if !hassession {
type emptytemplate struct{}
emptyData := emptytemplate{}
return c.Response.Template("nopermission.html", emptyData)
}
session_uid := session.GetUserID()
// check if user has role admin
is_admin, _ := auth.CheckUserRole(c, session_uid, "admin")
if !is_admin {
type emptytemplate struct{}
emptyData := emptytemplate{}
return c.Response.Template("nopermission.html", emptyData)
}
// continue if has session and is admin
var users []models.User
db := c.GetGorm()
db.Find(&users)
// -- response template
type templateData struct {
TableUsers components.ContentTable
AddButton components.ContentHref
}
cols := []components.ContentTableTH{
{
Value: "UID",
ValueType: "number",
},
{
Value: "Name",
ValueType: "string",
},
{
Value: "Fullname",
ValueType: "string",
},
{
Value: "Email",
ValueType: "string",
},
{
Value: "Roles",
ValueType: "string",
},
{
Value: "Created",
},
{
Value: "Updated",
},
{
ValueType: "href",
},
}
var listroles string
rows := make([][]components.ContentTableTD, len(users))
for i, u := range users {
roles, _ := auth.GetUserRoles(c, u.ID)
listroles = ""
for _, role := range roles {
if listroles != "" {
listroles += ", "
}
listroles += role.Name
}
row := []components.ContentTableTD{
{Value: strconv.Itoa(int(u.ID))},
{Value: u.Name},
{Value: u.Fullname},
{Value: u.Email},
{Value: listroles},
{Value: utils.FormatUnix(u.Created)},
{Value: utils.FormatUnix(u.Updated)},
{Value: components.ContentHref{
Text: "edit",
Link: "/admin/users/edit/" + strconv.Itoa(int(u.ID)),
TypeClass: "outline-secondary",
}},
}
rows[i] = row
}
tmplData := templateData{
TableUsers: components.ContentTable{
ID: "table_demo",
AllTH: cols,
AllTD: rows,
},
AddButton: components.ContentHref{
Text: "Register",
Link: "/admin/users/add",
IsButton: true,
TypeClass: "outline-primary",
},
}
return c.Response.Template("admin_userlist.html", tmplData)
}
func AdminUsersAdd(c *core.Context) *core.Response {
// initiate authority
auth := new(utils.Authority)
var session = new(utils.SessionUser)
// true if session is active
hassession := session.Init(c)
if !hassession {
type emptytemplate struct{}
emptyData := emptytemplate{}
return c.Response.Template("nopermission.html", emptyData)
}
session_uid := session.GetUserID()
// check if user has role admin
is_admin, _ := auth.CheckUserRole(c, session_uid, "admin")
if !is_admin {
type emptytemplate struct{}
emptyData := emptytemplate{}
return c.Response.Template("nopermission.html", emptyData)
}
// check if is submit
submit := c.GetRequestParam("submit").(string)
errormessages := make([]string, 0)
var listroles []components.FormCheckboxItem
systemroles, _ := auth.GetAllRoles(c)
for _, systemrole := range systemroles {
var userrole components.FormCheckboxItem
userrole.Label = systemrole.Name
userrole.Name = "roles"
userrole.Value = systemrole.Slug
if systemrole.Slug == "authenticated" {
userrole.IsChecked = true
}
listroles = append(listroles, userrole)
}
name := ""
fullname := ""
email := ""
password := ""
if submit != "" {
name = c.GetRequestParam("name").(string)
fullname = c.GetRequestParam("fullname").(string)
email = c.GetRequestParam("email").(string)
password = c.GetRequestParam("password").(string)
roles := c.GetRequesForm("roles").([]string)
// check if email exists
var user models.User
res := c.GetGorm().Where("email = ?", c.CastToString(email)).First(&user)
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
c.GetLogger().Error(res.Error.Error())
errormessages = append(errormessages, "Error")
}
if res.Error == nil {
errormessages = append(errormessages, "Error, email already exists in the database")
}
// validation data
data := map[string]interface{}{
"name": name,
"fullname": fullname,
"email": email,
"password": password,
}
// validation rules
rules := map[string]interface{}{
"name": "required|alphaNumeric",
"fullname": "required",
"email": "required|email",
"password": "required|length:6,20",
}
// validate
v := c.GetValidator().Validate(data, rules)
if v.Failed() {
c.GetLogger().Error(v.GetErrorMessagesJson())
for _, v := range v.GetErrorMessagesMap() {
errormessages = append(errormessages, v)
}
}
if len(errormessages) == 0 {
//hash the password
passwordHashed, _ := c.GetHashing().HashPassword(c.CastToString(password))
// store the record in db
user = models.User{
Name: c.CastToString(name),
Fullname: c.CastToString(fullname),
Email: c.CastToString(email),
Password: passwordHashed,
}
res = c.GetGorm().Create(&user)
if res.Error != nil {
c.GetLogger().Error(res.Error.Error())
errormessages = append(errormessages, res.Error.Error())
} else {
// assign roles
for _, role := range roles {
auth.AssignRoleToUser(c, user.ID, role)
}
// fire user registered event
err := c.GetEventsManager().Fire(&core.Event{Name: events.USER_REGISTERED, Payload: map[string]interface{}{
"user": user,
}})
if err != nil {
c.GetLogger().Error(err.Error())
errormessages = append(errormessages, "Internal server error")
} else {
// redirect to list
return c.Response.Redirect("/admin/users")
}
}
}
}
// -- response template
type templateData struct {
FieldName components.FormInput
FieldFullname components.FormInput
FieldEmail components.FormInput
FieldRoles components.FormCheckbox
FieldPassword components.FormInput
ErrorMessages []string
SubmitButton components.FormButton
}
tmplData := templateData{
FieldName: components.FormInput{
ID: "name",
Label: "Name",
Type: "text",
Value: name,
IsRequired: true,
},
FieldFullname: components.FormInput{
ID: "fullname",
Label: "Full name",
Type: "text",
Value: fullname,
IsRequired: true,
},
FieldEmail: components.FormInput{
ID: "email",
Label: "e-mail",
Type: "text",
Value: email,
//Autocomplete: true,
IsRequired: true,
},
FieldRoles: components.FormCheckbox{
Label: "Roles",
AllCheckbox: listroles,
},
FieldPassword: components.FormInput{
ID: "password",
Label: "Password",
Type: "password",
Value: password,
IsRequired: true,
},
SubmitButton: components.FormButton{
ID: "submit",
Text: "Add user",
Value: "submit",
IsSubmit: true,
TypeClass: "primary",
},
ErrorMessages: errormessages,
}
return c.Response.Template("admin_useradd.html", tmplData)
}
func AdminUsersEdit(c *core.Context) *core.Response {
// check if is submit
submit := c.GetRequestParam("submit").(string)
user_id := c.GetPathParam("id")
errormessages := make([]string, 0)
// initiate authority
auth := new(utils.Authority)
var listroles []components.FormCheckboxItem
systemroles, _ := auth.GetAllRoles(c)
user_id_uint, _ := strconv.ParseUint(user_id.(string), 10, 32)
userroles, _ := auth.GetUserRoles(c, uint(user_id_uint))
for _, systemrole := range systemroles {
var userrole components.FormCheckboxItem
userrole.Label = systemrole.Name
userrole.Name = "roles"
userrole.Value = systemrole.Slug
for _, ur := range userroles {
if ur.Slug == systemrole.Slug {
userrole.IsChecked = true
break
}
}
listroles = append(listroles, userrole)
}
var origin_user models.User
db := c.GetGorm()
// check if existes
result_db := db.First(&origin_user, user_id)
if result_db.RowsAffected == 0 {
c.GetLogger().Error("User ID not found")
return c.Response.Redirect("/admin/users")
}
name := origin_user.Name
fullname := origin_user.Fullname
email := origin_user.Email
password := ""
user_id_string := c.GetPathParam("id").(string)
if submit != "" {
name = c.GetRequestParam("name").(string)
fullname = c.GetRequestParam("fullname").(string)
email = c.GetRequestParam("email").(string)
password = c.GetRequestParam("password").(string)
roles := c.GetRequesForm("roles").([]string)
key := c.GetRequestParam("key")
// check if email exists
var user models.User
res := c.GetGorm().Where("email = ? AND id != ?", c.CastToString(email), key).First(&user)
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
c.GetLogger().Error(res.Error.Error())
errormessages = append(errormessages, "Error")
}
if res.Error == nil {
errormessages = append(errormessages, "Error, email already exists to another user")
}
// validation data
data := map[string]interface{}{
"name": name,
"fullname": fullname,
"email": email,
}
// validation rules
rules := map[string]interface{}{
"name": "required|alphaNumeric",
"fullname": "required",
"email": "required|email",
}
// nee update password
if password != "" {
data["password"] = password
rules["password"] = "required|length:6,20"
}
// validate
v := c.GetValidator().Validate(data, rules)
if v.Failed() {
c.GetLogger().Error(v.GetErrorMessagesJson())
for _, v := range v.GetErrorMessagesMap() {
errormessages = append(errormessages, v)
}
}
if len(errormessages) == 0 {
//hash the password
if password != "" {
passwordHashed, _ := c.GetHashing().HashPassword(c.CastToString(password))
origin_user.Password = passwordHashed
}
// store the record in db
origin_user.Name = name
origin_user.Fullname = fullname
origin_user.Email = email
result_db = db.Save(&origin_user)
if result_db.RowsAffected == 0 {
c.GetLogger().Error("Admin user: error updating")
errormessages = append(errormessages, fmt.Sprintf("Error updating user %s:", user_id_string))
} else {
// delete roles
auth.RevokeAllUserRole(c, origin_user.ID)
// assign roles
for _, role := range roles {
auth.AssignRoleToUser(c, origin_user.ID, role)
}
return c.Response.Redirect("/admin/users")
}
}
}
// -- response template
type templateData struct {
FieldName components.FormInput
FieldFullname components.FormInput
FieldEmail components.FormInput
FieldRoles components.FormCheckbox
FieldPassword components.FormInput
FieldKey components.FormInput
ErrorMessages []string
SubmitButton components.FormButton
DeleteButton components.FormButton
}
tmplData := templateData{
FieldName: components.FormInput{
ID: "name",
Label: "Name",
Type: "text",
Value: name,
IsRequired: true,
},
FieldFullname: components.FormInput{
ID: "fullname",
Label: "Full name",
Type: "text",
Value: fullname,
IsRequired: true,
},
FieldEmail: components.FormInput{
ID: "email",
Label: "e-mail",
Type: "text",
Value: email,
//Autocomplete: true,
IsRequired: true,
},
FieldRoles: components.FormCheckbox{
Label: "Roles",
AllCheckbox: listroles,
},
FieldPassword: components.FormInput{
ID: "password",
Label: "Password",
Type: "password",
Hint: "Leave blank if you don't want to change it",
Value: password,
IsRequired: false,
},
FieldKey: components.FormInput{
ID: "key",
Type: "hidden",
Value: user_id_string,
},
SubmitButton: components.FormButton{
ID: "submit",
Text: "Update user",
Value: "submit",
IsSubmit: true,
TypeClass: "primary",
},
DeleteButton: components.FormButton{
ID: "submit",
Text: "Delete user",
Value: "submit",
IsSubmit: true,
TypeClass: "warning",
},
ErrorMessages: errormessages,
}
return c.Response.Template("admin_useredit.html", tmplData)
}
func AdminUsersDelete(c *core.Context) *core.Response {
user_id := c.GetRequestParam("key").(string)
errormessages := make([]string, 0)
warningmessages := make([]string, 0)
var origin_user models.User
db := c.GetGorm()
// check if existes
result_db := db.First(&origin_user, user_id)
if result_db.RowsAffected == 0 {
errormessages = append(errormessages, "User ID not found")
} else {
// check if is the seed user
seed := "1"
if user_id == seed {
errormessages = append(errormessages, "You can't delete the seed user")
}
}
// sample warning message
warningmessages = append(warningmessages, fmt.Sprintf("Are you sure you want to cancel the account %s?", origin_user.Name))
// -- response template
type templateData struct {
ErrorMessages []string
WarningMessages []string
FieldKey components.FormInput
ConfirmButton components.FormButton
BackButton components.ContentHref
}
tmplData := templateData{
FieldKey: components.FormInput{
ID: "key",
Type: "hidden",
Value: user_id,
},
ConfirmButton: components.FormButton{
ID: "submit",
Text: "Confirm",
Value: "submit",
IsSubmit: true,
TypeClass: "primary",
},
BackButton: components.ContentHref{
Link: "/admin/users",
Text: "Cancel",
IsButton: true,
},
ErrorMessages: errormessages,
WarningMessages: warningmessages,
}
return c.Response.Template("admin_confirmuserdel.html", tmplData)
}
func AdminUsersDelConfirm(c *core.Context) *core.Response {
user_id := c.GetRequestParam("key").(string)
var origin_user models.User
db := c.GetGorm()
// check if existes
result_db := db.First(&origin_user, user_id)
if result_db.RowsAffected != 0 {
// check if is the seed user
seed := "1"
if user_id != seed {
// initiate authority
auth := new(utils.Authority)
// Delete the user
// fire user delete event
err := c.GetEventsManager().Fire(&core.Event{Name: events.USER_DELETED, Payload: map[string]interface{}{
"user": origin_user,
}})
if err != nil {
c.GetLogger().Error(err.Error())
}
auth.RevokeAllUserRole(c, origin_user.ID)
result_db.Unscoped().Delete(&origin_user)
}
}
return c.Response.Redirect("/admin/users")
}

View file

@ -0,0 +1,478 @@
// Copyright 2023 Harran Ali <harran.m@gmail.com>. All rights reserved.
// Copyright (c) 2024 Zeni Kim <zenik@smarteching.com>
// Use of this source code is governed by MIT-style
// license that can be found in the LICENSE file.
package controllers
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"os"
"strconv"
"strings"
"time"
"git.smarteching.com/goffee/core"
"git.smarteching.com/jacs/goffeetabler/events"
"git.smarteching.com/jacs/goffeetabler/models"
"git.smarteching.com/jacs/goffeetabler/utils"
"github.com/google/uuid"
"gorm.io/gorm"
)
func Signup(c *core.Context) *core.Response {
name := c.GetRequestParam("name")
fullname := c.GetRequestParam("fullname")
email := c.GetRequestParam("email")
password := c.GetRequestParam("password")
// check if email exists
var user models.User
res := c.GetGorm().Where("email = ?", c.CastToString(email)).First(&user)
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
c.GetLogger().Error(res.Error.Error())
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]string{
"message": "internal error",
}))
}
if res.Error == nil {
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]string{
"message": "email already exists in the database",
}))
}
// validation data
data := map[string]interface{}{
"name": name,
"fullname": fullname,
"email": email,
"password": password,
}
// validation rules
rules := map[string]interface{}{
"name": "required|alphaNumeric",
"fullname": "required",
"email": "required|email",
"password": "required|length:6,20",
}
// validate
v := c.GetValidator().Validate(data, rules)
if v.Failed() {
c.GetLogger().Error(v.GetErrorMessagesJson())
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(v.GetErrorMessagesJson())
}
//hash the password
passwordHashed, err := c.GetHashing().HashPassword(c.CastToString(password))
if err != nil {
c.GetLogger().Error(err.Error())
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]interface{}{
"message": err.Error(),
}))
}
// store the record in db
user = models.User{
Name: c.CastToString(name),
Fullname: c.CastToString(fullname),
Email: c.CastToString(email),
Password: passwordHashed,
}
res = c.GetGorm().Create(&user)
if res.Error != nil {
c.GetLogger().Error(res.Error.Error())
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]string{
"message": res.Error.Error(),
}))
}
token, err := c.GetJWT().GenerateToken(map[string]interface{}{
"userID": user.ID,
})
if err != nil {
c.GetLogger().Error(err.Error())
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]string{
"message": "internal server error",
}))
}
// cache the token
userAgent := c.GetUserAgent()
hashedCacheKey := utils.CreateAuthTokenHashedCacheKey(user.ID, userAgent)
err = c.GetCache().Set(hashedCacheKey, token)
if err != nil {
c.GetLogger().Error(err.Error())
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]interface{}{
"message": "internal server error",
}))
}
// fire user registered event
err = c.GetEventsManager().Fire(&core.Event{Name: events.USER_REGISTERED, Payload: map[string]interface{}{
"user": user,
}})
if err != nil {
c.GetLogger().Error(err.Error())
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]string{
"message": "internal server error",
}))
}
return c.Response.Json(c.MapToJson(map[string]string{
"token": token,
}))
}
func Signin(c *core.Context) *core.Response {
email := c.GetRequestParam("email")
password := c.GetRequestParam("password")
// check if template engine is enable
TemplateEnableStr := os.Getenv("TEMPLATE_ENABLE")
if TemplateEnableStr == "" {
TemplateEnableStr = "false"
}
TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr)
data := map[string]interface{}{
"email": email,
"password": password,
}
rules := map[string]interface{}{
"email": "required|email",
"password": "required",
}
v := c.GetValidator().Validate(data, rules)
if v.Failed() {
c.GetLogger().Error(v.GetErrorMessagesJson())
if TemplateEnable {
// TODO set error in session
return c.Response.Redirect("/applogin")
} else {
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(v.GetErrorMessagesJson())
}
}
var user models.User
res := c.GetGorm().Where("email = ?", c.CastToString(email)).First(&user)
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
c.GetLogger().Error(res.Error.Error())
if TemplateEnable {
// TODO set error in session
return c.Response.Redirect("/applogin")
} else {
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]string{
"message": "internal server error",
}))
}
}
if res.Error != nil && errors.Is(res.Error, gorm.ErrRecordNotFound) {
if TemplateEnable {
// TODO set error in session
return c.Response.Redirect("/applogin")
} else {
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]string{
"message": "invalid email or password",
}))
}
}
ok, err := c.GetHashing().CheckPasswordHash(user.Password, c.CastToString(password))
if err != nil {
c.GetLogger().Error(err.Error())
if TemplateEnable {
// TODO set error in session
return c.Response.Redirect("/applogin")
} else {
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]string{
"message": err.Error(),
}))
}
}
if !ok {
if TemplateEnable {
// TODO set error in session
return c.Response.Redirect("/applogin")
} else {
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]string{
"message": "invalid email or password",
}))
}
}
token, err := c.GetJWT().GenerateToken(map[string]interface{}{
"userID": user.ID,
})
if err != nil {
c.GetLogger().Error(err.Error())
// TODO set error in session
if TemplateEnable {
return c.Response.Redirect("/applogin")
} else {
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]string{
"message": "internal server error",
}))
}
}
// cache the token
userAgent := c.GetUserAgent()
hashedCacheKey := utils.CreateAuthTokenHashedCacheKey(user.ID, userAgent)
err = c.GetCache().Set(hashedCacheKey, token)
// delete data from old sessions
sessionKey := fmt.Sprintf("sess_%v", userAgent)
hashedSessionKey := utils.CreateAuthTokenHashedCacheKey(user.ID, sessionKey)
_ = c.GetCache().Delete(hashedSessionKey)
if err != nil {
c.GetLogger().Error(err.Error())
if TemplateEnable {
// TODO set error in session
return c.Response.Redirect("/applogin")
} else {
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]interface{}{
"message": "internal server error",
}))
}
}
if TemplateEnable {
// create cookie
err = core.SetCookie(c.Response.HttpResponseWriter, email.(string), token)
if err != nil {
panic(fmt.Sprintf("Error write encrypted cookie: %v", err))
return c.Response.SetStatusCode(http.StatusInternalServerError)
}
// redirecto to app
return c.Response.Redirect("/appsample")
} else {
return c.Response.Json(c.MapToJson(map[string]string{
"token": token,
}))
}
}
func ResetPasswordRequest(c *core.Context) *core.Response {
email := c.GetRequestParam("email")
// validation data
data := map[string]interface{}{
"email": email,
}
// validation rules
rules := map[string]interface{}{
"email": "required|email",
}
// validate
v := c.GetValidator().Validate(data, rules)
if v.Failed() {
c.GetLogger().Error(v.GetErrorMessagesJson())
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(v.GetErrorMessagesJson())
}
// check email in the database
var user models.User
res := c.GetGorm().Where("email = ?", c.CastToString(email)).First(&user)
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
c.GetLogger().Error(res.Error.Error())
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]string{
"message": "internal server error",
}))
}
if res.Error != nil && errors.Is(res.Error, gorm.ErrRecordNotFound) {
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]string{
"message": "email not found in our database",
}))
}
// generate the link
expiresAt := time.Now().Add(time.Hour * 3).Unix()
linkCodeData := map[string]string{
"userID": c.CastToString(user.ID),
"expiresAt": c.CastToString(expiresAt),
}
code := uuid.NewString()
c.GetCache().SetWithExpiration(code, c.MapToJson(linkCodeData), time.Hour*3)
err := c.GetEventsManager().Fire(&core.Event{Name: events.USER_PASSWORD_RESET_REQUESTED, Payload: map[string]interface{}{
"user": user,
"code": code,
}})
if err != nil {
c.GetLogger().Error(err.Error())
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]string{
"message": "internal server error",
}))
}
return c.Response.Json(c.MapToJson(map[string]string{
"message": "reset password email sent successfully",
}))
}
func SetNewPassword(c *core.Context) *core.Response {
urlCode := c.CastToString(c.GetPathParam("code"))
linkCodeDataStr, err := c.GetCache().Get(urlCode)
if err != nil {
c.GetLogger().Error(err.Error())
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]string{
"message": "invalid link",
}))
}
var linkCode map[string]interface{}
json.Unmarshal([]byte(linkCodeDataStr), &linkCode)
expiresAtUnix, err := strconv.ParseInt(c.CastToString(linkCode["expiresAt"]), 10, 64)
if err != nil {
c.GetLogger().Error(err.Error())
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]string{
"message": "invalid link",
}))
}
expiresAt := time.Unix(expiresAtUnix, 0)
if time.Now().After(expiresAt) {
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]string{
"message": "invalid link",
}))
}
userID, err := strconv.ParseUint(c.CastToString(linkCode["userID"]), 10, 64)
if err != nil {
c.GetLogger().Error(err.Error())
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]string{
"message": "invalid link",
}))
}
oldPassword := c.CastToString(c.GetRequestParam("old_password"))
newPassword := c.CastToString(c.GetRequestParam("new_password"))
newPasswordConfirm := c.CastToString(c.GetRequestParam("new_password_confirm"))
// validation data
data := map[string]interface{}{
"old_password": oldPassword,
"new_password": newPassword,
"new_password_confirm": newPasswordConfirm,
}
// validation rules
rules := map[string]interface{}{
"old_password": "required|length:6,10",
"new_password": "required|length:6,10",
"new_password_confirm": "required|length:6,10",
}
// validate
v := c.GetValidator().Validate(data, rules)
if v.Failed() {
c.GetLogger().Error(v.GetErrorMessagesJson())
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(v.GetErrorMessagesJson())
}
var user models.User
res := c.GetGorm().Where("id = ?", userID).First(&user)
if res.Error != nil {
c.GetLogger().Error(res.Error.Error())
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]string{
"message": "invalid link",
}))
}
SamePassword, err := c.GetHashing().CheckPasswordHash(user.Password, oldPassword)
if err != nil {
c.GetLogger().Error(err.Error())
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]string{
"message": "invalid link",
}))
}
if !SamePassword {
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]string{
"message": "the old password is incorrect",
}))
}
if newPassword != newPasswordConfirm {
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]string{
"message": "new password does not match new password confirmation",
}))
}
hashedNewPassword, err := c.GetHashing().HashPassword(newPassword)
if err != nil {
c.GetLogger().Error(err.Error())
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]string{
"message": "invalid password",
}))
}
user.Password = hashedNewPassword
c.GetGorm().Save(&user)
err = c.GetEventsManager().Fire(&core.Event{Name: events.PASSWORD_CHANGED, Payload: map[string]interface{}{
"user": user,
}})
if err != nil {
c.GetLogger().Error(err.Error())
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]string{
"message": "internal server error",
}))
}
return c.Response.Json(c.MapToJson(map[string]string{
"message": "password changed successfully",
}))
}
func Signout(c *core.Context) *core.Response {
// check if template engine is enable
TemplateEnableStr := os.Getenv("TEMPLATE_ENABLE")
if TemplateEnableStr == "" {
TemplateEnableStr = "false"
}
TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr)
token := ""
if TemplateEnable {
// get cookie
usercookie, err := c.GetCookie()
if err != nil {
}
token = usercookie.Token
} else {
tokenRaw := c.GetHeader("Authorization")
token = strings.TrimSpace(strings.Replace(tokenRaw, "Bearer", "", 1))
}
if token == "" {
return c.Response.SetStatusCode(http.StatusUnauthorized).Json(c.MapToJson(map[string]interface{}{
"message": "unauthorized",
}))
}
payload, err := c.GetJWT().DecodeToken(token)
if err != nil {
return c.Response.SetStatusCode(http.StatusUnauthorized).Json(c.MapToJson(map[string]interface{}{
"message": "unauthorized",
}))
}
userAgent := c.GetUserAgent()
hashedCacheKey := utils.CreateAuthTokenHashedCacheKey(uint(c.CastToInt(payload["userID"])), userAgent)
err = c.GetCache().Delete(hashedCacheKey)
if err != nil {
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]interface{}{
"message": "internal error",
}))
}
return c.Response.SetStatusCode(http.StatusOK).Json(c.MapToJson(map[string]interface{}{
"message": "signed out successfully",
}))
}

54
controllers/home.go Normal file
View file

@ -0,0 +1,54 @@
// Copyright 2023 Harran Ali <harran.m@gmail.com>. All rights reserved.
// Copyright (c) 2024 Zeni Kim <zenik@smarteching.com>
// Use of this source code is governed by MIT-style
// license that can be found in the LICENSE file.
package controllers
import (
"os"
"strconv"
"git.smarteching.com/goffee/core"
"git.smarteching.com/goffee/core/template/components"
)
// Show home page
func WelcomeHome(c *core.Context) *core.Response {
// check if template engine is enable
TemplateEnableStr := os.Getenv("TEMPLATE_ENABLE")
if TemplateEnableStr == "" {
TemplateEnableStr = "false"
}
TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr)
if TemplateEnable {
// first, include all compoments
type templateData struct {
PageCard components.PageCard
}
// now fill data of the components
tmplData := templateData{
PageCard: components.PageCard{
CardTitle: "Golang Framework",
CardBody: "Welcome to Goffee",
},
}
return c.Response.Template("welcome.html", tmplData)
} else {
message := "{\"message\": \"Welcome to Goffee\"}"
return c.Response.Json(message)
}
}
// Show dashboard
func WelcomeToDashboard(c *core.Context) *core.Response {
message := "{\"message\": \"Welcome to Dashboard\"}"
return c.Response.Json(message)
}

View file

@ -0,0 +1,52 @@
// Copyright (c) 2025 Zeni Kim <zenik@smarteching.com>
// Use of this source code is governed by MIT-style
// license that can be found in the LICENSE file.
package controllers
import (
"encoding/json"
"log"
"time"
"git.smarteching.com/goffee/core"
"git.smarteching.com/jacs/goffeetabler/workers"
"github.com/hibiken/asynq"
)
// Make samples queues
func Queuesample(c *core.Context) *core.Response {
// Get client queue asynq
client := c.GetQueueClient()
// Create a task with typename and payload.
payload, err := json.Marshal(workers.EmailTaskPayload{UserID: 42})
if err != nil {
log.Fatal(err)
}
t1 := asynq.NewTask(workers.TypeWelcomeEmail, payload)
t2 := asynq.NewTask(workers.TypeReminderEmail, payload)
// Process the task immediately.
info, err := client.Enqueue(t1)
if err != nil {
log.Fatal(err)
}
log.Printf(" [*] Successfully enqueued task: %+v", info)
// Process 2 task 1 min later.
for i := 1; i < 3; i++ {
info, err = client.Enqueue(t2, asynq.ProcessIn(1*time.Minute))
if err != nil {
log.Fatal(err)
}
log.Printf(" [*] Successfully enqueued task: %+v", info)
}
message := "{\"message\": \"Task queued\"}"
return c.Response.Json(message)
}

149
controllers/sample.go Normal file
View file

@ -0,0 +1,149 @@
// Copyright (c) 2024 Zeni Kim <zenik@smarteching.com>
// Use of this source code is governed by MIT-style
// license that can be found in the LICENSE file.
package controllers
import (
"fmt"
"git.smarteching.com/goffee/core"
"git.smarteching.com/goffee/core/template/components"
"git.smarteching.com/jacs/goffeetabler/utils"
)
// Show basic template
func Sample(c *core.Context) *core.Response {
// first, include all compoments
type templateData struct {
PageCard components.PageCard
}
// now fill data of the components
tmplData := templateData{
PageCard: components.PageCard{
CardTitle: "Framework Goffee",
CardBody: "Powered by Golang",
},
}
return c.Response.Template("basic.html", tmplData)
}
// Show basic app login
func AppLogin(c *core.Context) *core.Response {
// first, include all compoments
// first, include all compoments
type templateData struct {
PageCard components.PageCard
}
// now fill data of the components
tmplData := templateData{
PageCard: components.PageCard{
CardTitle: "Card title",
CardBody: "Loerm ipsum at deim",
},
}
return c.Response.Template("login.html", tmplData)
}
// Show basic app login
func AppSession(c *core.Context) *core.Response {
var session = new(utils.SessionUser)
// true if session is active
hassession := session.Init(c)
//session.Set("numberdos", 66)
type templateData struct {
PageCard components.PageCard
}
// now fill data of the components
tmplData := templateData{}
if hassession {
sesiondata := ""
cardtitle := fmt.Sprintf("Session user id: %v", session.GetUserID())
numberdos, ok := session.Get("numberdos")
if numberdos != nil {
numberdos = numberdos.(float64) + 10
} else {
numberdos = 10
}
session.Set("numberdos", numberdos)
if ok {
sesiondata = fmt.Sprintf("OK, Session numberdos has %v", numberdos)
} else {
sesiondata = fmt.Sprintf("No ok, session numberdos has %v", numberdos)
}
// delete single
//session.Delete("numberdos")
// delete all data
//session.Flush()
tmplData = templateData{
PageCard: components.PageCard{
CardTitle: cardtitle,
CardBody: sesiondata,
},
}
return c.Response.Template("appsession.html", tmplData)
} else {
return c.Response.Template("login.html", tmplData)
}
}
// Show basic app sample
func AppSample(c *core.Context) *core.Response {
// first, include all compoments
type templateData struct {
PageCard components.PageCard
ContentDropdown components.ContentDropdown
}
// now fill data of the components
tmplData := templateData{
PageCard: components.PageCard{
CardTitle: "Protected page",
CardBody: "If you can see this page, your are loggedin",
},
ContentDropdown: components.ContentDropdown{
Label: "dropdown",
Items: []components.ContentDropdownItem{
{
Text: "Signout",
Link: "#",
ID: "signout",
},
{
Text: "item disabled",
Link: "#",
IsDisabled: true,
},
},
},
}
//fmt.Printf("Outside cookie user is: %s", user.Email)
return c.Response.Template("app.html", tmplData)
}

863
controllers/themedemo.go Normal file
View file

@ -0,0 +1,863 @@
// Copyright (c) 2024 Zeni Kim <zenik@smarteching.com>
// Use of this source code is governed by MIT-style
// license that can be found in the LICENSE file.
package controllers
import (
"fmt"
"math/rand/v2"
"os"
"strconv"
"git.smarteching.com/goffee/core"
"git.smarteching.com/goffee/core/template/components"
)
// Show home page
func Themedemo(c *core.Context) *core.Response {
// check if template engine is enabled
TemplateEnableStr := os.Getenv("TEMPLATE_ENABLE")
if TemplateEnableStr == "" {
TemplateEnableStr = "false"
}
TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr)
if TemplateEnable {
// first, include all compoments
type templateData struct {
PageCard components.PageCard
}
// now fill data of the components
tmplData := templateData{
PageCard: components.PageCard{
CardTitle: "Card title",
CardBody: "Loerm ipsum at deim",
},
}
return c.Response.Template("custom_theme_base.html", tmplData)
} else {
message := "{\"message\": \"Error, template not enabled\"}"
return c.Response.Json(message)
}
}
// Show form element page
func Themeform(c *core.Context) *core.Response {
// check if template engine is enabled
TemplateEnableStr := os.Getenv("TEMPLATE_ENABLE")
if TemplateEnableStr == "" {
TemplateEnableStr = "false"
}
TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr)
if TemplateEnable {
// first, include all compoments
type templateData struct {
FormText components.FormInput
FormEmail components.FormInput
FormButton components.FormButton
FormSelectCity components.FormSelect
FormTextarea components.FormTextarea
FormRadio components.FormRadio
FormCheckbox components.FormCheckbox
}
// for select options
var allOptions []components.FormSelectOption
var option components.FormSelectOption
option.Value = "ch"
option.Caption = "China"
allOptions = append(allOptions, option)
option.Value = "ba"
option.Caption = "Buenos Aires"
allOptions = append(allOptions, option)
option.Value = "fr"
option.Caption = "France"
selectedOption := option
allOptions = append(allOptions, option)
// for radio options
var allOptionsr []components.FormRadioItem
var optionr components.FormRadioItem
optionr.ID = "citysch"
optionr.Name = "citys"
optionr.Value = "china"
optionr.Label = "China"
allOptionsr = append(allOptionsr, optionr)
optionr.ID = "citysba"
optionr.Name = "citys"
optionr.Value = "buenosaires"
optionr.Label = "Buenos Aires"
//optionr.IsDisabled = true
allOptionsr = append(allOptionsr, optionr)
// for radio options
var allOptionsc []components.FormCheckboxItem
var optionc components.FormCheckboxItem
optionc.ID = "citysch"
optionc.Name = "citys"
optionc.Value = "china"
optionc.Label = "China"
allOptionsc = append(allOptionsc, optionc)
optionc.ID = "citysba"
optionc.Name = "citys"
optionc.Value = "buenosaires"
optionc.Label = "Buenos Aires"
allOptionsc = append(allOptionsc, optionc)
optionc.ID = "london"
optionc.Name = "london"
optionc.Value = "london"
optionc.Label = "London"
//optionc.IsChecked = true
allOptionsc = append(allOptionsc, optionc)
// now fill data of the components
tmplData := templateData{
FormText: components.FormInput{
ID: "text",
Label: "Name",
Type: "text",
Hint: "This is sample hint",
Placeholder: "Enter your name",
},
FormEmail: components.FormInput{
ID: "email",
Label: "Email",
Type: "email",
IsRequired: true,
Placeholder: "Enter your email address",
},
FormButton: components.FormButton{
Text: "Login",
IsSubmit: true,
TypeClass: "primary",
},
FormSelectCity: components.FormSelect{
ID: "city",
Label: "Select city",
AllOptions: allOptions,
SelectedOption: selectedOption,
},
FormTextarea: components.FormTextarea{
ID: "text",
Label: "Example textarea",
},
FormRadio: components.FormRadio{
Label: "Radio buttons",
AllRadios: allOptionsr,
},
FormCheckbox: components.FormCheckbox{
Label: "Checkbox options",
AllCheckbox: allOptionsc,
},
}
return c.Response.Template("custom_theme_formpage.html", tmplData)
} else {
message := "{\"message\": \"Error, template not enabled\"}"
return c.Response.Json(message)
}
}
func ThemeElements(c *core.Context) *core.Response {
// check if template engine is enabled
TemplateEnableStr := os.Getenv("TEMPLATE_ENABLE")
if TemplateEnableStr == "" {
TemplateEnableStr = "false"
}
TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr)
if TemplateEnable {
type templateData struct {
Buttons []components.FormButton
Hrefs []components.ContentHref
Badges []components.ContentBadge
Dropdowns []components.ContentDropdown
Lists []components.ContentList
Menus []components.PageNav
}
buttons := []components.FormButton{
{
Text: "primary",
TypeClass: "primary",
},
{
Text: "secondary",
TypeClass: "secondary",
},
{
Text: "success",
TypeClass: "success",
},
{
Text: "danger",
TypeClass: "danger",
},
{
Text: "warning",
TypeClass: "warning",
},
{
Text: "info",
TypeClass: "info",
},
{
Text: "light",
TypeClass: "light",
},
{
Text: "dark",
TypeClass: "dark",
},
{
Text: "link",
TypeClass: "link",
},
{
Text: "disabled",
TypeClass: "primary",
IsDisabled: true,
},
{
Text: "outline-primary",
TypeClass: "outline-primary",
},
{
Text: "outline-secondary",
TypeClass: "outline-secondary",
},
{
Text: "outline-success",
TypeClass: "outline-success",
},
{
Text: "outline-danger",
TypeClass: "outline-danger",
},
{
Text: "outline-warning",
TypeClass: "outline-warning",
},
{
Text: "outline-info",
TypeClass: "outline-info",
},
{
Text: "outline-light",
TypeClass: "outline-light",
},
{
Text: "outline-dark",
TypeClass: "outline-dark",
},
}
hrefs := []components.ContentHref{
{
Text: "href",
Link: "#",
IsButton: false,
},
{
Text: "link",
Link: "#",
IsButton: false,
TypeClass: "link",
},
{
Text: "button",
Link: "#",
IsButton: true,
TypeClass: "primary",
},
{
Text: "href disabled",
Link: "#",
IsButton: false,
IsDisabled: true,
},
{
Text: "link disabled",
Link: "#",
TypeClass: "link",
IsDisabled: true,
},
{
Text: "button disabled",
Link: "#",
IsButton: true,
TypeClass: "primary",
IsDisabled: true,
},
}
badges := []components.ContentBadge{
{
Text: "primary",
TypeClass: "primary",
},
{
Text: "secondary",
TypeClass: "secondary",
},
{
Text: "success",
TypeClass: "success",
},
{
Text: "danger",
TypeClass: "danger",
},
{
Text: "warning",
TypeClass: "warning",
},
{
Text: "info",
TypeClass: "info",
},
{
Text: "light",
TypeClass: "light",
},
{
Text: "dark",
TypeClass: "dark",
},
{
Text: "outline",
TypeClass: "primary",
IsOutline: true,
},
{
Text: "outline",
TypeClass: "success",
IsOutline: true,
},
{
Text: "outline",
TypeClass: "danger",
IsOutline: true,
},
{
Text: "outline",
TypeClass: "warning",
IsOutline: true,
},
}
dropdowns := []components.ContentDropdown{
// dropdown
{
Label: "dropdown",
Items: []components.ContentDropdownItem{
{
Text: "item ",
Link: "#",
},
{
Text: "item disabled",
Link: "#",
IsDisabled: true,
},
},
},
// dropdown
{
Label: "primary",
TypeClass: "primary",
Items: []components.ContentDropdownItem{
{
Text: "item ",
Link: "#",
},
{
Text: "item ",
Link: "#",
IsActive: true,
},
{
Text: "item disabled",
Link: "#",
IsDisabled: true,
},
},
},
// dropdown
{
Label: "outline",
TypeClass: "outline-primary",
Items: []components.ContentDropdownItem{
{
Text: "item ",
Link: "#",
},
},
},
// dropdown
{
Label: "disabled",
TypeClass: "primary",
IsDisabled: true,
// items
},
}
list := []components.ContentList{
// basic list
{
Items: []components.ContentListItem{
{
Text: "item 1",
},
{
Text: "item 2",
EndElement: "end text",
},
{
Text: "item disabled",
IsDisabled: true,
},
},
},
// description list
{
Items: []components.ContentListItem{
{
Text: "item 1",
Description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. ",
},
{
Text: "item 2",
Description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
},
{
Text: "item disabled",
Description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
IsDisabled: true,
},
},
},
// list with class
{
Items: []components.ContentListItem{
{
Text: "class primary",
TypeClass: "primary",
},
{
Text: "class success",
TypeClass: "success",
},
{
Text: "class danger",
TypeClass: "danger",
},
},
},
}
menus := []components.PageNav{
// nav
{
NavClass: "nav-pills",
NavItems: []components.PageNavItem{
{
Text: "item active",
Link: "#",
IsActive: true,
},
{
Text: "item",
Link: "#",
IsActive: false,
},
{
Text: "item",
Link: "#",
IsActive: false,
ChildItems: []components.PageNavItem{
{
Text: "item ",
Link: "#",
},
{
Text: "item disabled",
Link: "#",
IsDisabled: true,
},
},
},
{
Text: "item disabled",
Link: "#",
IsDisabled: true,
},
},
},
// nav
{
NavClass: "",
NavItems: []components.PageNavItem{
{
Text: "item active",
Link: "#",
IsActive: true,
},
{
Text: "item",
Link: "#",
IsActive: false,
},
{
Text: "item",
Link: "#",
IsActive: false,
},
{
Text: "item disabled",
Link: "#",
IsDisabled: true,
},
},
},
// nav underline
{
NavClass: "nav-underline",
NavItems: []components.PageNavItem{
{
Text: "item active",
Link: "#",
IsActive: true,
},
{
Text: "item",
Link: "#",
IsActive: false,
},
{
Text: "item",
Link: "#",
IsActive: false,
},
{
Text: "item disabled",
Link: "#",
IsDisabled: true,
},
},
},
// nav tabs
{
NavClass: "",
IsTab: true,
NavItems: []components.PageNavItem{
{
Text: "tab active",
Link: "#",
IsActive: true,
},
{
Text: "tab",
Link: "#",
IsActive: false,
},
{
Text: "tab",
Link: "#",
IsActive: false,
},
{
Text: "tab disabled",
Link: "#",
IsDisabled: true,
},
},
},
// nav vertical
{
NavClass: "",
IsVertical: true,
NavItems: []components.PageNavItem{
{
Text: "item active",
Link: "#",
IsActive: true,
},
{
Text: "item",
Link: "#",
IsActive: false,
},
{
Text: "item",
Link: "#",
IsActive: false,
},
{
Text: "item disabled",
Link: "#",
IsDisabled: true,
},
},
},
}
tmplData := templateData{
Buttons: buttons,
Hrefs: hrefs,
Badges: badges,
Dropdowns: dropdowns,
Lists: list,
Menus: menus,
}
return c.Response.Template("custom_theme_elements.html", tmplData)
} else {
message := "{\"message\": \"Error, template not enabled\"}"
return c.Response.Json(message)
}
}
// Show form element page
func Themecontent(c *core.Context) *core.Response {
// check if template engine is enabled
TemplateEnableStr := os.Getenv("TEMPLATE_ENABLE")
if TemplateEnableStr == "" {
TemplateEnableStr = "false"
}
TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr)
if TemplateEnable {
// first, include all compoments
type templateData struct {
ContentTable components.ContentTable
ContentTabledetail components.ContentTabledetail
ContentGraph components.ContentGraph
FieldText components.FormInput
FormSelectCityM components.FormSelect
Pagination components.ContentPagination
ShouldShowPagination bool
}
// for select options
var allOptions []components.FormSelectOption
var option components.FormSelectOption
option.Value = "ch"
option.Caption = "China"
allOptions = append(allOptions, option)
option.Value = "ba"
option.Caption = "Buenos Aires"
allOptions = append(allOptions, option)
option.Value = "fr"
option.Caption = "France"
selectedOption := option
allOptions = append(allOptions, option)
option.Value = "kr"
option.Caption = "Korea"
allOptions = append(allOptions, option)
// for custom attributes in form
var customAtt []components.CustomAtt
var attribute components.CustomAtt
attribute.AttName = "code"
attribute.AttValue = "five"
customAtt = append(customAtt, attribute)
attribute.AttName = "mytag"
attribute.AttValue = "My value"
customAtt = append(customAtt, attribute)
// TABLES
// for th head
var allTh []components.ContentTableTH
var th components.ContentTableTH
th.Value = "Column heading 1"
allTh = append(allTh, th)
th.Value = "Column heading 2"
allTh = append(allTh, th)
th.ID = "ba"
th.Value = "Column heading 3"
allTh = append(allTh, th)
th.Value = "Column badge"
th.ValueType = "badge" // column type badge
allTh = append(allTh, th)
th.Value = "Column action"
th.ValueType = "href" // column type href
allTh = append(allTh, th)
// for td items
var allTd [][]components.ContentTableTD
//var vals []components.ContentTableTD
// rows
for i := 1; i <= 28; i++ {
vals := make([]components.ContentTableTD, len(allTh))
for b := 0; b < len(allTh)-2; b++ {
vals[b].Value = fmt.Sprintf("%s%d%d", "TD data: ", i, b)
vals[b].ID = fmt.Sprintf("%s%d%d", "idtd_", i, b)
}
// column badge
vals[len(allTh)-2].Value = components.ContentBadge{
Text: "success",
TypeClass: "success",
}
// last column href
vals[len(allTh)-1].Value = components.ContentHref{
Text: "edit",
Link: "#",
}
allTd = append(allTd, vals)
}
// Pagination demo
// start config
limit := 10
shouldShowPagination := false
pageViewTableOffset := 0
// Get the length of AllOptions
totalrecords := len(allTd)
// get current table offset
tpage := c.RequestParamExists("tpage")
if tpage {
pageViewTableOffset, _ = strconv.Atoi(c.GetRequestParam("tpage").(string))
}
// start default option paginator
var pagination components.ContentPagination
pagination.PageStartRecord = pageViewTableOffset + 1
pagination.PageEndRecord = 0
pagination.TotalRecords = 0
pagination.PrevLink = ""
pagination.NextLink = ""
// check current page
// fake function to emulate a query offset
newTd := getPaginatedPageViews(allTd, limit, pageViewTableOffset)
if len(newTd) > 0 {
pagination.TotalRecords = totalrecords
pagination.PageStartRecord = pageViewTableOffset + 1
pagination.PageEndRecord = pageViewTableOffset + len(newTd)
shouldShowPagination = totalrecords > limit
}
if shouldShowPagination && pageViewTableOffset != 0 {
pagination.PrevLink = fmt.Sprintf("/themecontent?tpage=%d", pageViewTableOffset-limit)
}
if shouldShowPagination && pageViewTableOffset+limit < totalrecords {
pagination.NextLink = fmt.Sprintf("/themecontent?tpage=%d", pageViewTableOffset+limit)
}
// for td items in table detail
var allTdetail []components.ContentTabledetailTD
// table detail
var thd components.ContentTabledetailTD
thd.Caption = "Continent"
thd.Value = "Asia"
allTdetail = append(allTdetail, thd)
thd.Caption = "Country"
thd.Value = "South Korea"
allTdetail = append(allTdetail, thd)
thd.Caption = "Capital"
thd.Value = "Seoul"
allTdetail = append(allTdetail, thd)
thd.Caption = "Details"
thd.ValueType = "href" // column type href
thd.Value = components.ContentHref{
Text: "edit",
Link: "#",
}
allTdetail = append(allTdetail, thd)
thd.Caption = "Notifications"
thd.ValueType = "badge" // column type href
thd.Value = components.ContentBadge{
Text: "success",
TypeClass: "success",
}
allTdetail = append(allTdetail, thd)
// random values for pie
one := rand.IntN(50)
two := rand.IntN(50)
three := rand.IntN(50)
valuesgraph := fmt.Sprintf("%d|%d|%d", one, two, three)
// now fill data of the components
tmplData := templateData{
FormSelectCityM: components.FormSelect{
ID: "city",
Label: "Select city",
AllOptions: allOptions,
SelectedOption: selectedOption,
IsMultiple: true,
},
FieldText: components.FormInput{
ID: "text",
Label: "Name",
Type: "text",
Hint: "This is sample hint",
Placeholder: "Enter your name",
CustomAtt: customAtt,
},
ContentTable: components.ContentTable{
ID: "table_demo",
AllTH: allTh,
AllTD: newTd,
},
ContentTabledetail: components.ContentTabledetail{
ID: "table_demodetail",
Title: "Sample table detail",
HeadClass: "table-warning",
AllTD: allTdetail,
},
ContentGraph: components.ContentGraph{
Graph: "pie",
Labels: "Berlin|Paris|Venecia",
Values: valuesgraph,
},
Pagination: pagination,
ShouldShowPagination: shouldShowPagination,
}
return c.Response.Template("custom_theme_contentpage.html", tmplData)
} else {
message := "{\"message\": \"Error, template not enabled\"}"
return c.Response.Json(message)
}
}
func getPaginatedPageViews(values [][]components.ContentTableTD, limit int, offset int) [][]components.ContentTableTD {
// Validate the offset and adjust if necessary
if offset < 0 {
offset = 0 // Ensure offset is not negative
} else if offset >= len(values) {
var emptytd [][]components.ContentTableTD
return emptytd
}
// Calculate the end index (limit the slice to the size of the array)
end := offset + limit
if end > len(values) {
end = len(values) // Ensure end doesn't exceed the length of the array
}
return values[offset:end] // Slice the array
}