Merge pull request #33 from Floriansylvain/feature/postCreation
Feature/post creation
This commit is contained in:
16 files changed
+376
-26
No files matched your search
@@ -0,0 +1,51 @@
|
|||||||
|
root = "."
|
||||||
|
testdata_dir = "testdata"
|
||||||
|
tmp_dir = "tmp"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
args_bin = []
|
||||||
|
bin = "tmp\\main.exe"
|
||||||
|
cmd = "go build -o ./tmp/main.exe ./main"
|
||||||
|
delay = 1000
|
||||||
|
exclude_dir = ["assets", "tmp", "vendor", "testdata", "api\\static\\tinymce", "api\\static\\bootstrap-icons"]
|
||||||
|
exclude_file = []
|
||||||
|
exclude_regex = ["_test.go"]
|
||||||
|
exclude_unchanged = false
|
||||||
|
follow_symlink = false
|
||||||
|
full_bin = ""
|
||||||
|
include_dir = []
|
||||||
|
include_ext = ["go", "tpl", "tmpl", "html"]
|
||||||
|
include_file = []
|
||||||
|
kill_delay = "0s"
|
||||||
|
log = "build-errors.log"
|
||||||
|
poll = false
|
||||||
|
poll_interval = 0
|
||||||
|
post_cmd = []
|
||||||
|
pre_cmd = []
|
||||||
|
rerun = false
|
||||||
|
rerun_delay = 500
|
||||||
|
send_interrupt = false
|
||||||
|
stop_on_error = false
|
||||||
|
|
||||||
|
[color]
|
||||||
|
app = ""
|
||||||
|
build = "yellow"
|
||||||
|
main = "magenta"
|
||||||
|
runner = "green"
|
||||||
|
watcher = "cyan"
|
||||||
|
|
||||||
|
[log]
|
||||||
|
main_only = false
|
||||||
|
time = false
|
||||||
|
|
||||||
|
[misc]
|
||||||
|
clean_on_exit = false
|
||||||
|
|
||||||
|
[proxy]
|
||||||
|
app_port = 0
|
||||||
|
enabled = false
|
||||||
|
proxy_port = 0
|
||||||
|
|
||||||
|
[screen]
|
||||||
|
clear_on_rebuild = false
|
||||||
|
keep_scroll = true
|
||||||
+3
-1
@@ -9,4 +9,6 @@
|
|||||||
bin
|
bin
|
||||||
|
|
||||||
# Environment
|
# Environment
|
||||||
.env
|
.env
|
||||||
|
|
||||||
|
tmp/
|
||||||
@@ -29,6 +29,16 @@ func (a *PostRepository) Get(id uint32) (domain.Post, error) {
|
|||||||
return mapPostToDomain(post), nil
|
return mapPostToDomain(post), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *PostRepository) GetByName(name string) (domain.Post, error) {
|
||||||
|
var post entity.Post
|
||||||
|
err := a.db.Model(&entity.Post{}).Where("title = ?", name).First(&post).Error
|
||||||
|
if err != nil {
|
||||||
|
return domain.Post{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapPostToDomain(post), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (a *PostRepository) Create(post domain.Post) (domain.Post, error) {
|
func (a *PostRepository) Create(post domain.Post) (domain.Post, error) {
|
||||||
creationResult := a.db.Create(&entity.Post{
|
creationResult := a.db.Create(&entity.Post{
|
||||||
Title: post.Title,
|
Title: post.Title,
|
||||||
@@ -59,4 +69,25 @@ func (a *PostRepository) GetAll() []domain.Post {
|
|||||||
return domainPosts
|
return domainPosts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *PostRepository) UpdateBody(id uint32, body string) error {
|
||||||
|
var localPost entity.Post
|
||||||
|
err := a.db.Model(&entity.Post{}).First(&localPost, id).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
localPost.Body = body
|
||||||
|
err = a.db.Save(&localPost).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *PostRepository) Delete(id uint32) error {
|
||||||
|
//TODO implement me
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
var _ gateways.IPostRepository = &PostRepository{}
|
var _ gateways.IPostRepository = &PostRepository{}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>GoCMS | Create post</title>
|
||||||
|
{{.Head}}
|
||||||
|
<style>
|
||||||
|
form {
|
||||||
|
max-width: 24rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="text-dark">
|
||||||
|
{{.Navbar}}
|
||||||
|
<div class="container mt-3 text-black-50">
|
||||||
|
<h1>Post - creation</h1>
|
||||||
|
<p>Create a new post</p>
|
||||||
|
<form action="create" method="post" class="d-flex align-items-center flex-column gap-3" id="createPostForm">
|
||||||
|
<div class="form-floating w-100">
|
||||||
|
<input type="text" class="form-control {{ if .PageError.IsError }} is-invalid {{ end }}" id="name"
|
||||||
|
name="name" placeholder="Chose the post name" value="{{ .Name }}">
|
||||||
|
<label for="name">Name</label>
|
||||||
|
<div class="invalid-feedback">{{ .PageError.Message }}</div>
|
||||||
|
</div>
|
||||||
|
<button id="createPostButton" class="btn btn-primary w-100" disabled type="submit">
|
||||||
|
<span class="createPostFormButtonLoading visually-hidden spinner-border spinner-border-sm"
|
||||||
|
role="status"></span>
|
||||||
|
<span class="createPostFormButtonDefault">Create</span>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const button = document.querySelector("#createPostButton")
|
||||||
|
const inputs = document.querySelectorAll('input')
|
||||||
|
const form = document.querySelector("#createPostForm")
|
||||||
|
|
||||||
|
function formFieldsEmpty() {
|
||||||
|
return Array.from(inputs).some((input) => input.value === "")
|
||||||
|
}
|
||||||
|
|
||||||
|
function setButtonDisabled() {
|
||||||
|
button.disabled = formFieldsEmpty() ? "disabled" : ""
|
||||||
|
}
|
||||||
|
|
||||||
|
function setButtonLoading() {
|
||||||
|
button.classList.add("disabled")
|
||||||
|
button.querySelector(".createPostFormButtonDefault").classList.add("visually-hidden")
|
||||||
|
button.querySelector(".createPostFormButtonLoading").classList.remove("visually-hidden")
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCreatePostFormSubmit(event) {
|
||||||
|
setButtonLoading()
|
||||||
|
event.target.submit()
|
||||||
|
}
|
||||||
|
|
||||||
|
function onInput(event) {
|
||||||
|
if (event.target.tagName === "INPUT") setButtonDisabled()
|
||||||
|
}
|
||||||
|
|
||||||
|
form.addEventListener('submit', onCreatePostFormSubmit)
|
||||||
|
window.addEventListener('input', onInput)
|
||||||
|
setButtonDisabled()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -6,28 +6,36 @@
|
|||||||
</head>
|
</head>
|
||||||
<body class="text-dark vh-100 d-flex flex-column">
|
<body class="text-dark vh-100 d-flex flex-column">
|
||||||
{{.Navbar}}
|
{{.Navbar}}
|
||||||
<div class="container mt-3 text-black-50 d-flex flex-column h-100">
|
<form class="container mt-3 text-black-50 d-flex flex-column h-100" action="edit" method="post">
|
||||||
<div class="d-flex align-items-center justify-content-between">
|
<div class="d-flex align-items-center justify-content-between">
|
||||||
<div>
|
<div>
|
||||||
<h1>Post - edition</h1>
|
<h1>Post - edition</h1>
|
||||||
<label for="postContent">Edition</label>
|
<label for="postBody">Edition</label>
|
||||||
</div>
|
</div>
|
||||||
|
{{ if .Alert.Message }}
|
||||||
|
<div class="alert {{ if .Alert.IsError }} alert-danger {{ else }} alert-success {{ end }} alert-dismissible" role="alert">
|
||||||
|
{{ .Alert.Message }}
|
||||||
|
<button type="button" class="btn-close" aria-label="Close" data-bs-dismiss="alert"></button>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
<div class="d-flex justify-content-center align-items-center">
|
<div class="d-flex justify-content-center align-items-center">
|
||||||
<button class="btn btn-success d-flex justify-content-center align-items-center gap-1" style="fill: white;">
|
<button class="btn btn-success d-flex justify-content-center align-items-center gap-1" style="fill: white;" type="submit">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M840-680v480q0 33-23.5 56.5T760-120H200q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h480l160 160Zm-80 34L646-760H200v560h560v-446ZM480-240q50 0 85-35t35-85q0-50-35-85t-85-35q-50 0-85 35t-35 85q0 50 35 85t85 35ZM240-560h360v-160H240v160Zm-40-86v446-560 114Z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24">
|
||||||
|
<path d="M840-680v480q0 33-23.5 56.5T760-120H200q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h480l160 160Zm-80 34L646-760H200v560h560v-446ZM480-240q50 0 85-35t35-85q0-50-35-85t-85-35q-50 0-85 35t-35 85q0 50 35 85t85 35ZM240-560h360v-160H240v160Zm-40-86v446-560 114Z"/>
|
||||||
|
</svg>
|
||||||
Save
|
Save
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-black-50 d-flex align-items-center justify-content-center h-100 py-3">
|
<div class="text-black-50 d-flex align-items-center justify-content-center h-100 py-3">
|
||||||
<textarea id="postContent" name="postContent"></textarea>
|
<textarea id="postBody" name="postBody">{{.Body}}</textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</form>
|
||||||
|
|
||||||
<script src="/static/tinymce/js/tinymce/tinymce.min.js"></script>
|
<script src="/static/tinymce/js/tinymce/tinymce.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
tinymce.init({
|
tinymce.init({
|
||||||
selector: '#postContent',
|
selector: '#postBody',
|
||||||
promotion: false,
|
promotion: false,
|
||||||
plugins: 'image',
|
plugins: 'image',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
|
|||||||
@@ -1,21 +1,73 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<title>GoCMS | Posts</title>
|
<title>GoCMS | Posts</title>
|
||||||
{{.Head}}
|
{{.Head}}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.btn-outline-danger:hover > * {
|
||||||
|
fill: #fff !important;
|
||||||
|
transition: fill ease-in-out 50ms;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="text-dark">
|
<body class="text-dark">
|
||||||
{{.Navbar}}
|
{{.Navbar}}
|
||||||
<div class="container mt-3 text-black-50">
|
<div class="container mt-3 text-black-50">
|
||||||
<h1>Posts</h1>
|
<h1>Posts</h1>
|
||||||
<p>List of your posts</p>
|
<p>List of your posts</p>
|
||||||
<table class="table table-striped">
|
<table class="table table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
</thead>
|
<tr>
|
||||||
<tbody>
|
<th scope="col">Name</th>
|
||||||
</tbody>
|
<th scope="col">Creation Date</th>
|
||||||
</table>
|
<th scope="col">Edition Date</th>
|
||||||
|
<th scope="col">Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="4" class="text-center">
|
||||||
|
<form action="post/create" method="get">
|
||||||
|
<button class="btn btn-sm btn-link w-100 h-100" type="submit">Create a new post...</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{ range $post := .Posts }}
|
||||||
|
<tr>
|
||||||
|
<td>{{ $post.Title }}</td>
|
||||||
|
<td class="date">{{ $post.CreatedAt }}</td>
|
||||||
|
<td class="date">{{ $post.UpdatedAt }}</td>
|
||||||
|
<td>
|
||||||
|
<a href="/post/{{ $post.Title }}/edit" class="btn btn-secondary btn-sm">
|
||||||
|
<svg width="12px" height="12px" fill="currentColor">
|
||||||
|
<use xlink:href="/static/bootstrap-icons.svg#pen"/>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
<a href="/post/{{ $post.Title }}/delete" class="btn btn-outline-danger btn-sm">
|
||||||
|
<svg width="12px" height="12px" fill="currentColor">
|
||||||
|
<use xlink:href="/static/bootstrap-icons.svg#trash"/>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const datesElements = document.querySelectorAll("td.date")
|
||||||
|
datesElements.forEach(date => {
|
||||||
|
date.innerText = new Date(date.innerText).toLocaleDateString(undefined, {
|
||||||
|
year: "2-digit",
|
||||||
|
month: "2-digit",
|
||||||
|
day: "2-digit",
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit"
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -14,6 +14,7 @@ type UseCases struct {
|
|||||||
CreatePostUseCase *useCases.CreatePostUseCase
|
CreatePostUseCase *useCases.CreatePostUseCase
|
||||||
GetPostUseCase *useCases.GetPostUseCase
|
GetPostUseCase *useCases.GetPostUseCase
|
||||||
ListPostsUseCase *useCases.ListPostsUseCase
|
ListPostsUseCase *useCases.ListPostsUseCase
|
||||||
|
UpdatePostUseCase *useCases.UpdatePostUseCase
|
||||||
GetUserUseCase *useCases.GetUserUseCase
|
GetUserUseCase *useCases.GetUserUseCase
|
||||||
CreateUserUseCase *useCases.CreateUserUseCase
|
CreateUserUseCase *useCases.CreateUserUseCase
|
||||||
UpdateUserUseCase *useCases.UpdateUserUseCase
|
UpdateUserUseCase *useCases.UpdateUserUseCase
|
||||||
@@ -49,6 +50,7 @@ func InitContainer() {
|
|||||||
CreatePostUseCase: useCases.NewCreatePostUseCase(db),
|
CreatePostUseCase: useCases.NewCreatePostUseCase(db),
|
||||||
GetPostUseCase: useCases.NewGetPostUseCase(db),
|
GetPostUseCase: useCases.NewGetPostUseCase(db),
|
||||||
ListPostsUseCase: useCases.NewListPostsUseCase(db),
|
ListPostsUseCase: useCases.NewListPostsUseCase(db),
|
||||||
|
UpdatePostUseCase: useCases.NewUpdatePostUseCase(db),
|
||||||
GetUserUseCase: useCases.NewGetUserUseCase(db),
|
GetUserUseCase: useCases.NewGetUserUseCase(db),
|
||||||
CreateUserUseCase: useCases.NewCreateUserUseCase(db),
|
CreateUserUseCase: useCases.NewCreateUserUseCase(db),
|
||||||
UpdateUserUseCase: useCases.NewUpdateUserUseCase(db),
|
UpdateUserUseCase: useCases.NewUpdateUserUseCase(db),
|
||||||
|
|||||||
+4
-1
@@ -122,7 +122,10 @@ func NewPageRouter() http.Handler {
|
|||||||
r.Use(IsVerifiedMiddleware)
|
r.Use(IsVerifiedMiddleware)
|
||||||
r.Get("/home", GetHomePage)
|
r.Get("/home", GetHomePage)
|
||||||
r.Get("/post", GetPostsPage)
|
r.Get("/post", GetPostsPage)
|
||||||
r.Get("/post/edit", GetPostEditPage)
|
r.Get("/post/{name}/edit", GetPostEditPage)
|
||||||
|
r.Post("/post/{name}/edit", PostPostEditPage)
|
||||||
|
r.Get("/post/create", GetPostCreatePage)
|
||||||
|
r.Post("/post/create", PostPostCreatePage)
|
||||||
})
|
})
|
||||||
|
|
||||||
return r
|
return r
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"GoCMS/useCases"
|
||||||
|
"html/template"
|
||||||
|
"net/http"
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PostCreatePageError struct {
|
||||||
|
IsError bool
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPostCreatePageTemplate(postName string, errorMessage string) ([]byte, error) {
|
||||||
|
navbarTmpl, _ := Container.GetPageUseCase.GetPage("componentNavbar", nil)
|
||||||
|
return Container.GetPageUseCase.GetPage("postCreate", map[string]interface{}{
|
||||||
|
"Navbar": template.HTML(navbarTmpl),
|
||||||
|
"Head": headTmpl,
|
||||||
|
"PageError": PostCreatePageError{
|
||||||
|
IsError: errorMessage != "",
|
||||||
|
Message: errorMessage,
|
||||||
|
},
|
||||||
|
"Name": postName,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func PostPostCreatePage(w http.ResponseWriter, r *http.Request) {
|
||||||
|
_ = r.ParseForm()
|
||||||
|
|
||||||
|
postName := r.FormValue("name")
|
||||||
|
pattern := regexp.MustCompile("^[a-zA-Z0-9À-ÖØ-öø-ÿĀ-ſḀ-ỿ ]{3,50}$")
|
||||||
|
|
||||||
|
if !pattern.MatchString(postName) {
|
||||||
|
postsTmpl, _ := GetPostCreatePageTemplate(postName, "Name should be alphanumeric, and between 3 and 50 characters.")
|
||||||
|
_, _ = w.Write(postsTmpl)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
post, err := Container.CreatePostUseCase.CreatePost(useCases.CreatePostCommand{
|
||||||
|
Title: postName,
|
||||||
|
Body: "",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
postsTmpl, _ := GetPostCreatePageTemplate(postName, "Something went wrong when creating the post, please contact admin.")
|
||||||
|
_, _ = w.Write(postsTmpl)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
http.Redirect(w, r, "/post/"+post.Title+"/edit", http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPostCreatePage(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
postsTmpl, _ := GetPostCreatePageTemplate("", "")
|
||||||
|
_, _ = w.Write(postsTmpl)
|
||||||
|
}
|
||||||
+56
-3
@@ -1,15 +1,68 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
"html/template"
|
"html/template"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetPostEditPage(w http.ResponseWriter, _ *http.Request) {
|
type PostEditPageAlert struct {
|
||||||
|
IsError bool
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPostEditPageTemplate(body string, alert PostEditPageAlert) []byte {
|
||||||
navbarTmpl, _ := Container.GetPageUseCase.GetPage("componentNavbar", nil)
|
navbarTmpl, _ := Container.GetPageUseCase.GetPage("componentNavbar", nil)
|
||||||
postsTmpl, _ := Container.GetPageUseCase.GetPage("postEdit", map[string]interface{}{
|
postTmpl, _ := Container.GetPageUseCase.GetPage("postEdit", map[string]interface{}{
|
||||||
"Navbar": template.HTML(navbarTmpl),
|
"Navbar": template.HTML(navbarTmpl),
|
||||||
"Head": headTmpl,
|
"Head": headTmpl,
|
||||||
|
"Body": body,
|
||||||
|
"Alert": alert,
|
||||||
})
|
})
|
||||||
_, _ = w.Write(postsTmpl)
|
return postTmpl
|
||||||
|
}
|
||||||
|
|
||||||
|
func PostPostEditPage(w http.ResponseWriter, r *http.Request) {
|
||||||
|
postName := chi.URLParam(r, "name")
|
||||||
|
if len(postName) == 0 {
|
||||||
|
_, _ = w.Write(getPostEditPageTemplate("", PostEditPageAlert{
|
||||||
|
IsError: true,
|
||||||
|
Message: "Could not find the requested post.",
|
||||||
|
}))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = r.ParseForm()
|
||||||
|
postBody := r.FormValue("postBody")
|
||||||
|
|
||||||
|
parsedName, _ := url.PathUnescape(postName)
|
||||||
|
post, _ := Container.GetPostUseCase.GetPostByName(parsedName)
|
||||||
|
err := Container.UpdatePostUseCase.UpdateBody(post.ID, postBody)
|
||||||
|
if err != nil {
|
||||||
|
_, _ = w.Write(getPostEditPageTemplate(postBody, PostEditPageAlert{
|
||||||
|
IsError: true,
|
||||||
|
Message: "Could not save the post: " + err.Error(),
|
||||||
|
}))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _ = w.Write(getPostEditPageTemplate(postBody, PostEditPageAlert{
|
||||||
|
IsError: false,
|
||||||
|
Message: "Post successfully edited!",
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPostEditPage(w http.ResponseWriter, r *http.Request) {
|
||||||
|
postName := chi.URLParam(r, "name")
|
||||||
|
if len(postName) == 0 {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
parsedName, _ := url.PathUnescape(postName)
|
||||||
|
post, _ := Container.GetPostUseCase.GetPostByName(parsedName)
|
||||||
|
_, _ = w.Write(getPostEditPageTemplate(post.Body, PostEditPageAlert{
|
||||||
|
IsError: false,
|
||||||
|
Message: "",
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
@@ -6,10 +6,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func GetPostsPage(w http.ResponseWriter, _ *http.Request) {
|
func GetPostsPage(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
posts := Container.ListPostsUseCase.ListPosts()
|
||||||
navbarTmpl, _ := Container.GetPageUseCase.GetPage("componentNavbar", nil)
|
navbarTmpl, _ := Container.GetPageUseCase.GetPage("componentNavbar", nil)
|
||||||
postsTmpl, _ := Container.GetPageUseCase.GetPage("posts", map[string]interface{}{
|
postsTmpl, _ := Container.GetPageUseCase.GetPage("posts", map[string]interface{}{
|
||||||
"Navbar": template.HTML(navbarTmpl),
|
"Navbar": template.HTML(navbarTmpl),
|
||||||
"Head": headTmpl,
|
"Head": headTmpl,
|
||||||
|
"Posts": posts,
|
||||||
})
|
})
|
||||||
_, _ = w.Write(postsTmpl)
|
_, _ = w.Write(postsTmpl)
|
||||||
}
|
}
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><symbol class="bi bi-0-circle" viewBox="0 0 16 16" id="0-circle"><path d="M7.988 12.158c-1.851 0-2.941-1.57-2.941-3.99V7.84c0-2.408 1.101-3.996 2.965-3.996 1.857 0 2.935 1.57 2.935 3.996v.328c0 2.408-1.101 3.99-2.959 3.99M8 4.951c-1.008 0-1.629 1.09-1.629 2.895v.31c0 1.81.627 2.895 1.629 2.895s1.623-1.09 1.623-2.895v-.31c0-1.8-.621-2.895-1.623-2.895"/><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0M1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8"/></symbol><symbol class="bi bi-0-circle-fill" viewBox="0 0 16 16" id="0-circle-fill"><path d="M8 4.951c-1.008 0-1.629 1.09-1.629 2.895v.31c0 1.81.627 2.895 1.629 2.895s1.623-1.09 1.623-2.895v-.31c0-1.8-.621-2.895-1.623-2.895"/><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0m-8.012 4.158c1.858 0 2.96-1.582 2.96-3.99V7.84c0-2.426-1.079-3.996-2.936-3.996-1.864 0-2.965 1.588-2.965 3.996v.328c0 2.42 1.09 3.99 2.941 3.99"/></symbol><symbol class="bi bi-0-square" viewBox="0 0 16 16" id="0-square"><path d="M7.988 12.158c-1.851 0-2.941-1.57-2.941-3.99V7.84c0-2.408 1.101-3.996 2.965-3.996 1.857 0 2.935 1.57 2.935 3.996v.328c0 2.408-1.101 3.99-2.959 3.99M8 4.951c-1.008 0-1.629 1.09-1.629 2.895v.31c0 1.81.627 2.895 1.629 2.895s1.623-1.09 1.623-2.895v-.31c0-1.8-.621-2.895-1.623-2.895"/><path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm15 0a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1z"/></symbol><symbol class="bi bi-0-square-fill" viewBox="0 0 16 16" id="0-square-fill"><path d="M8 4.951c-1.008 0-1.629 1.09-1.629 2.895v.31c0 1.81.627 2.895 1.629 2.895s1.623-1.09 1.623-2.895v-.31c0-1.8-.621-2.895-1.623-2.895"/><path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm5.988 12.158c-1.851 0-2.941-1.57-2.941-3.99V7.84c0-2.408 1.101-3.996 2.965-3.996 1.857 0 2.935 1.57 2.935 3.996v.328c0 2.408-1.101 3.99-2.959 3.99"/></symbol><symbol class="bi bi-1-circle" viewBox="0 0 16 16" id="1-circle"><path d="M1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8m15 0A8 8 0 1 1 0 8a8 8 0 0 1 16 0M9.283 4.002V12H7.971V5.338h-.065L6.072 6.656V5.385l1.899-1.383z"/></symbol><symbol class="bi bi-1-circle-fill" viewBox="0 0 16 16" id="1-circle-fill"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0M9.283 4.002H7.971L6.072 5.385v1.271l1.834-1.318h.065V12h1.312z"/></symbol><symbol class="bi bi-1-square" viewBox="0 0 16 16" id="1-square"><path d="M9.283 4.002V12H7.971V5.338h-.065L6.072 6.656V5.385l1.899-1.383z"/><path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm15 0a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1z"/></symbol><symbol class="bi bi-1-square-fill" viewBox="0 0 16 16" id="1-square-fill"><path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm7.283 4.002V12H7.971V5.338h-.065L6.072 6.656V5.385l1.899-1.383z"/></symbol><symbol class="bi bi-123" viewBox="0 0 16 16" id="123"><path d="M2.873 11.297V4.142H1.699L0 5.379v1.137l1.64-1.18h.06v5.961zm3.213-5.09v-.063c0-.618.44-1.169 1.196-1.169.676 0 1.174.44 1.174 1.106 0 .624-.42 1.101-.807 1.526L4.99 10.553v.744h4.78v-.99H6.643v-.069L8.41 8.252c.65-.724 1.237-1.332 1.237-2.27C9.646 4.849 8.723 4 7.308 4c-1.573 0-2.36 1.064-2.36 2.15v.057zm6.559 1.883h.786c.823 0 1.374.481 1.379 1.179.01.707-.55 1.216-1.421 1.21-.77-.005-1.326-.419-1.379-.953h-1.095c.042 1.053.938 1.918 2.464 1.918 1.478 0 2.642-.839 2.62-2.144-.02-1.143-.922-1.651-1.551-1.714v-.063c.535-.09 1.347-.66 1.326-1.678-.026-1.053-.933-1.855-2.359-1.845-1.5.005-2.317.88-2.348 1.898h1.116c.032-.498.498-.944 1.206-.944.703 0 1.206.435 1.206 1.07.005.64-.504 1.106-1.2 1.106h-.75z"/></symbol><symbol class="bi bi-2-circle" viewBox="0 0 16 16" id="2-circle"><path d="M1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8m15 0A8 8 0 1 1 0 8a8 8 0 0 1 16 0M6.646 6.24v.07H5.375v-.064c0-1.213.879-2.402 2.637-2.402 1.582 0 2.613.949 2.613 2.215 0 1.002-.6 1.667-1.287 2.43l-.096.107-1.974 2.22v.077h3.498V12H5.422v-.832l2.97-3.293c.434-.475.903-1.008.903-1.705 0-.744-.557-1.236-1.313-1.236-.843 0-1.336.615-1.336 1.306"/></symbol><symbol class="bi bi-2-circle-fill" viewBox="0 0 16 16" id="2-circle-fill"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0M6.646 6.24c0-.691.493-1.306 1.336-1.306.756 0 1.313.492 1.313 1.236 0 .697-.469 1.23-.902 1.705l-2.971 3.293V12h5.344v-1.107H7.268v-.077l1.974-2.22.096-.107c.688-.763 1.287-1.428 1.287-2.43 0-1.266-1.031-2.215-2.613-2.215-1.758 0-2.637 1.19-2.637 2.402v.065h1.271v-.07Z"/></symbol><symbol class="bi bi-2-square" viewBox="0 0 16 16" id="2-square"><path d="M6.646 6.24v.07H5.375v-.064c0-1.213.879-2.402 2.637-2.402 1.582 0 2.613.949 2.613 2.215 0 1.002-.6 1.667-1.287 2.43l-.096.107-1.974 2.22v.077h3.498V12H5.422v-.832l2.97-3.293c.434-.475.903-1.008.903-1.705 0-.744-.557-1.236-1.313-1.236-.843 0-1.336.615-1.336 1.306"/><path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm15 0a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1z"/></symbol><symbol class="bi bi-2-square-fill" viewBox="0 0 Line truncated
|
||||||
|
After Width: | Height: | Size: 1.0 MiB |
@@ -6,6 +6,9 @@ import (
|
|||||||
|
|
||||||
type IPostRepository interface {
|
type IPostRepository interface {
|
||||||
Get(id uint32) (post.Post, error)
|
Get(id uint32) (post.Post, error)
|
||||||
|
GetByName(name string) (post.Post, error)
|
||||||
GetAll() []post.Post
|
GetAll() []post.Post
|
||||||
Create(post post.Post) (post.Post, error)
|
Create(post post.Post) (post.Post, error)
|
||||||
|
UpdateBody(id uint32, body string) error
|
||||||
|
Delete(id uint32) error
|
||||||
}
|
}
|
||||||
@@ -19,3 +19,7 @@ func NewGetPostUseCase(db *gorm.DB) *GetPostUseCase {
|
|||||||
func (g *GetPostUseCase) GetPost(id uint32) (post.Post, error) {
|
func (g *GetPostUseCase) GetPost(id uint32) (post.Post, error) {
|
||||||
return g.postRepository.Get(id)
|
return g.postRepository.Get(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *GetPostUseCase) GetPostByName(name string) (post.Post, error) {
|
||||||
|
return g.postRepository.GetByName(name)
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package useCases
|
||||||
|
|
||||||
|
import (
|
||||||
|
"GoCMS/adapters/secondary/gateways"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UpdatePostUseCase struct {
|
||||||
|
postRepository gateways.PostRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUpdatePostUseCase(db *gorm.DB) *UpdatePostUseCase {
|
||||||
|
return &UpdatePostUseCase{
|
||||||
|
postRepository: *gateways.NewPostRepository(db),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *UpdatePostUseCase) UpdateBody(id uint32, body string) error {
|
||||||
|
return g.postRepository.UpdateBody(id, body)
|
||||||
|
}
|
||||||
@@ -10,10 +10,6 @@ type UpdateUserUseCase struct {
|
|||||||
userRepository gateways.UserRepository
|
userRepository gateways.UserRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateVerificationStatusCommand struct {
|
|
||||||
isVerified bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewUpdateUserUseCase(db *gorm.DB) *UpdateUserUseCase {
|
func NewUpdateUserUseCase(db *gorm.DB) *UpdateUserUseCase {
|
||||||
return &UpdateUserUseCase{
|
return &UpdateUserUseCase{
|
||||||
userRepository: *gateways.NewUserRepository(db),
|
userRepository: *gateways.NewUserRepository(db),
|
||||||
|
|||||||
Reference in new issue
Block a user