mirror of
https://github.com/Eggbertx/gochan.git
synced 2025-08-24 08:46:24 -07:00
Make template for recent posts page
Also make manage page h1 headers automatic
This commit is contained in:
parent
33388612d7
commit
8e706a313c
15 changed files with 128 additions and 113 deletions
|
@ -534,6 +534,11 @@ div.section-block div.section-body {
|
|||
padding: 8px;
|
||||
}
|
||||
|
||||
.centered {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#footer {
|
||||
bottom: 0px;
|
||||
clear: both;
|
||||
|
|
|
@ -206,25 +206,22 @@ func getSpecificPostStringDecorated(ID string, onlyNotDeleted bool) (Post, error
|
|||
// getRecentPostsInternal returns the most recent N posts, on a specific board if specified, only with files if specified
|
||||
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
|
||||
// The code should be changed to reflect the new database design
|
||||
// TODO: rework so it uses all features/better sql
|
||||
// get recent posts
|
||||
func getRecentPostsInternal(amount int, onlyWithFile bool, boardID int, onSpecificBoard bool) ([]RecentPost, error) {
|
||||
//TODO: rework so it uses all features/better sql
|
||||
//get recent posts
|
||||
recentQueryStr := `
|
||||
/*
|
||||
recentposts = join all non-deleted posts with the post id of their thread and the board it belongs on, sort by date and grab top x posts
|
||||
singlefiles = the top file per post id
|
||||
|
||||
Left join singlefiles on recentposts where recentposts.selfid = singlefiles.post_id
|
||||
Coalesce filenames to "" (if filename = null -> "" else filename)
|
||||
|
||||
Query might benefit from [filter on posts with at least one file -> ] filter N most recent -> manually loop N results for file/board/parentthreadid
|
||||
*/
|
||||
|
||||
Select
|
||||
// recentposts = join all non-deleted posts with the post id of their thread and the board it belongs on, sort by date and grab top x posts
|
||||
// singlefiles = the top file per post id
|
||||
|
||||
// Left join singlefiles on recentposts where recentposts.selfid = singlefiles.post_id
|
||||
// Coalesce filenames to "" (if filename = null -> "" else filename)
|
||||
|
||||
// Query might benefit from [filter on posts with at least one file -> ] filter N most recent -> manually loop N results for file/board/parentthreadid
|
||||
recentQueryStr := `SELECT
|
||||
recentposts.selfid AS id,
|
||||
recentposts.toppostid AS parentid,
|
||||
recentposts.boardname,
|
||||
recentposts.boardid,
|
||||
recentposts.ip,
|
||||
recentposts.name,
|
||||
recentposts.tripcode,
|
||||
recentposts.message,
|
||||
|
@ -237,11 +234,12 @@ func getRecentPostsInternal(amount int, onlyWithFile bool, boardID int, onSpecif
|
|||
topposts.id AS toppostid,
|
||||
boards.dir AS boardname,
|
||||
boards.id AS boardid,
|
||||
posts.ip,
|
||||
posts.name,
|
||||
posts.tripcode,
|
||||
posts.message,
|
||||
posts.email,
|
||||
posts.created_on
|
||||
posts.created_on
|
||||
FROM
|
||||
DBPREFIXposts AS posts
|
||||
JOIN DBPREFIXthreads AS threads
|
||||
|
@ -252,7 +250,6 @@ func getRecentPostsInternal(amount int, onlyWithFile bool, boardID int, onSpecif
|
|||
ON threads.board_id = boards.id
|
||||
WHERE
|
||||
topposts.is_top_post = TRUE AND posts.is_deleted = FALSE
|
||||
|
||||
) as recentposts
|
||||
LEFT JOIN
|
||||
(SELECT files.post_id, filename, files.thumbnail_width, files.thumbnail_height
|
||||
|
@ -299,7 +296,8 @@ func getRecentPostsInternal(amount int, onlyWithFile bool, boardID int, onSpecif
|
|||
var formattedHTML template.HTML
|
||||
if err = rows.Scan(
|
||||
&recentPost.PostID, &recentPost.ParentID, &recentPost.BoardName, &recentPost.BoardID,
|
||||
&recentPost.Name, &recentPost.Tripcode, &formattedHTML, &recentPost.Filename, &recentPost.ThumbW, &recentPost.ThumbH,
|
||||
&recentPost.IP, &recentPost.Name, &recentPost.Tripcode, &formattedHTML, &recentPost.Filename,
|
||||
&recentPost.ThumbW, &recentPost.ThumbH,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ import (
|
|||
"github.com/gochan-org/gochan/pkg/config"
|
||||
"github.com/gochan-org/gochan/pkg/gcsql"
|
||||
"github.com/gochan-org/gochan/pkg/gcutil"
|
||||
x_html "golang.org/x/net/html"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -63,7 +62,6 @@ var funcMap = template.FuncMap{
|
|||
},
|
||||
|
||||
// String functions
|
||||
// "arrToString": arrToString,
|
||||
"intToString": strconv.Itoa,
|
||||
"escapeString": html.EscapeString,
|
||||
"formatFilesize": func(sizeInt int) string {
|
||||
|
@ -128,19 +126,7 @@ var funcMap = template.FuncMap{
|
|||
},
|
||||
"truncateHTMLMessage": truncateHTML,
|
||||
"stripHTML": func(htmlStr template.HTML) string {
|
||||
dom := x_html.NewTokenizer(strings.NewReader(string(htmlStr)))
|
||||
for tokenType := dom.Next(); tokenType != x_html.ErrorToken; {
|
||||
if tokenType != x_html.TextToken {
|
||||
tokenType = dom.Next()
|
||||
continue
|
||||
}
|
||||
txtContent := strings.TrimSpace(x_html.UnescapeString(string(dom.Text())))
|
||||
if len(txtContent) > 0 {
|
||||
return x_html.EscapeString(txtContent)
|
||||
}
|
||||
tokenType = dom.Next()
|
||||
}
|
||||
return ""
|
||||
return gcutil.StripHTML(string(htmlStr))
|
||||
},
|
||||
"truncateString": func(msg string, limit int, ellipsis bool) string {
|
||||
if len(msg) > limit {
|
||||
|
|
|
@ -11,23 +11,24 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
Banpage *template.Template
|
||||
Captcha *template.Template
|
||||
Catalog *template.Template
|
||||
ErrorPage *template.Template
|
||||
FrontPage *template.Template
|
||||
BoardPage *template.Template
|
||||
JsConsts *template.Template
|
||||
ManageBans *template.Template
|
||||
ManageBoards *template.Template
|
||||
ManageConfig *template.Template
|
||||
ManageDashboard *template.Template
|
||||
ManageLogin *template.Template
|
||||
ManageStaff *template.Template
|
||||
PageHeader *template.Template
|
||||
PageFooter *template.Template
|
||||
PostEdit *template.Template
|
||||
ThreadPage *template.Template
|
||||
Banpage *template.Template
|
||||
Captcha *template.Template
|
||||
Catalog *template.Template
|
||||
ErrorPage *template.Template
|
||||
FrontPage *template.Template
|
||||
BoardPage *template.Template
|
||||
JsConsts *template.Template
|
||||
ManageBans *template.Template
|
||||
ManageBoards *template.Template
|
||||
ManageConfig *template.Template
|
||||
ManageDashboard *template.Template
|
||||
ManageRecentPosts *template.Template
|
||||
ManageLogin *template.Template
|
||||
ManageStaff *template.Template
|
||||
PageHeader *template.Template
|
||||
PageFooter *template.Template
|
||||
PostEdit *template.Template
|
||||
ThreadPage *template.Template
|
||||
)
|
||||
|
||||
func loadTemplate(files ...string) (*template.Template, error) {
|
||||
|
@ -153,6 +154,12 @@ func templateLoading(t string, buildAll bool) error {
|
|||
return templateError("manage_login.html", err)
|
||||
}
|
||||
}
|
||||
if buildAll || t == "managerecents" {
|
||||
ManageRecentPosts, err = loadTemplate("manage_recentposts.html")
|
||||
if err != nil {
|
||||
return templateError("manage_recentposts.html", err)
|
||||
}
|
||||
}
|
||||
if buildAll || t == "managestaff" {
|
||||
ManageStaff, err = loadTemplate("manage_staff.html")
|
||||
if err != nil {
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
|
||||
"github.com/aquilax/tripcode"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
x_html "golang.org/x/net/html"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -258,6 +259,22 @@ func RandomString(length int) string {
|
|||
return str
|
||||
}
|
||||
|
||||
func StripHTML(htmlIn string) string {
|
||||
dom := x_html.NewTokenizer(strings.NewReader(htmlIn))
|
||||
for tokenType := dom.Next(); tokenType != x_html.ErrorToken; {
|
||||
if tokenType != x_html.TextToken {
|
||||
tokenType = dom.Next()
|
||||
continue
|
||||
}
|
||||
txtContent := strings.TrimSpace(x_html.UnescapeString(string(dom.Text())))
|
||||
if len(txtContent) > 0 {
|
||||
return x_html.EscapeString(txtContent)
|
||||
}
|
||||
tokenType = dom.Next()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func ThumbnailExtension(filename string) string {
|
||||
ext := filepath.Ext(strings.ToLower(filename))
|
||||
switch ext {
|
||||
|
|
|
@ -85,7 +85,7 @@ var actions = []Action{
|
|||
Title: "Cleanup",
|
||||
Permissions: AdminPerms,
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
|
||||
outputStr := `<h2>Cleanup</h2><br />`
|
||||
outputStr := ""
|
||||
if request.FormValue("run") == "Run Cleanup" {
|
||||
outputStr += "Removing deleted posts from the database.<hr />"
|
||||
if err = gcsql.PermanentlyRemoveDeletedPosts(); err != nil {
|
||||
|
@ -117,46 +117,25 @@ var actions = []Action{
|
|||
Permissions: JanitorPerms,
|
||||
JSONoutput: OptionalJSON,
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
|
||||
var outputStr string
|
||||
systemCritical := config.GetSystemCriticalConfig()
|
||||
limit := gcutil.HackyStringToInt(request.FormValue("limit"))
|
||||
if limit == 0 {
|
||||
limit = 50
|
||||
}
|
||||
output = `<h1 class="manage-header">Recent posts</h1>` +
|
||||
`Limit by: <select id="limit">` +
|
||||
`<option>25</option><option>50</option><option>100</option><option>200</option>` +
|
||||
`</select><br /><table width="100%%d" border="1">` +
|
||||
`<colgroup><col width="25%%" /><col width="50%%" /><col width="17%%" /></colgroup>` +
|
||||
`<tr><th></th><th>Message</th><th>Time</th></tr>`
|
||||
recentposts, err := gcsql.GetRecentPostsGlobal(limit, false) //only uses boardname, boardid, postid, parentid, message, ip and timestamp
|
||||
if wantsJSON {
|
||||
if wantsJSON || err != nil {
|
||||
return recentposts, err
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
errMsg := gclog.Println(gclog.LErrorLog, "Error getting recent posts:", err.Error())
|
||||
err = errors.New(errMsg)
|
||||
if wantsJSON {
|
||||
return ErrStaffAction{
|
||||
ErrorField: "recentpostserror",
|
||||
Action: "recentposts",
|
||||
Message: errMsg,
|
||||
}, err
|
||||
}
|
||||
return errMsg, err
|
||||
manageRecentsBuffer := bytes.NewBufferString("")
|
||||
if err = serverutil.MinifyTemplate(gctemplates.ManageRecentPosts,
|
||||
map[string]interface{}{
|
||||
"recentposts": recentposts,
|
||||
"webroot": config.GetSystemCriticalConfig().WebRoot,
|
||||
},
|
||||
manageRecentsBuffer, "text/html"); err != nil {
|
||||
return "", errors.New(gclog.Print(gclog.LErrorLog,
|
||||
"Error executing ban management page template: "+err.Error()))
|
||||
}
|
||||
|
||||
for _, recentpost := range recentposts {
|
||||
outputStr += fmt.Sprintf(
|
||||
`<tr><td><b>Post:</b> <a href="%s">%s/%d</a><br /><b>IP:</b> %s</td><td>%s</td><td>%s</td></tr>`,
|
||||
path.Join(systemCritical.WebRoot, recentpost.BoardName, "/res/", strconv.Itoa(recentpost.ParentID)+".html#"+strconv.Itoa(recentpost.PostID)),
|
||||
recentpost.BoardName, recentpost.PostID, recentpost.IP, string(recentpost.Message),
|
||||
recentpost.Timestamp.Format("01/02/06, 15:04"),
|
||||
)
|
||||
}
|
||||
outputStr += "</table>"
|
||||
return
|
||||
return manageRecentsBuffer.String(), nil
|
||||
}},
|
||||
{
|
||||
ID: "bans",
|
||||
|
@ -457,7 +436,7 @@ var actions = []Action{
|
|||
"front": "Built front page successfully",
|
||||
}, err
|
||||
}
|
||||
return "<h2>Build front page</h2>Built front page successfully", err
|
||||
return "Built front page successfully", err
|
||||
}},
|
||||
{
|
||||
ID: "rebuildall",
|
||||
|
@ -513,33 +492,32 @@ var actions = []Action{
|
|||
if wantsJSON {
|
||||
return buildMap, nil
|
||||
}
|
||||
buildStr := "<h2>Rebuilding everything</h2>"
|
||||
buildStr := ""
|
||||
for _, msg := range buildMap {
|
||||
buildStr += fmt.Sprintln(msg, "<hr />")
|
||||
}
|
||||
return buildStr, nil
|
||||
}},
|
||||
{
|
||||
ID: "rebuildboard",
|
||||
Title: "Rebuild board",
|
||||
Permissions: AdminPerms,
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
|
||||
return "Not implemented (yet)", gcutil.ErrNotImplemented
|
||||
// if err = gctemplates.InitTemplates(); err != nil {
|
||||
// return "", err
|
||||
// }
|
||||
// {
|
||||
// ID: "rebuildboard",
|
||||
// Title: "Rebuild board",
|
||||
// Permissions: AdminPerms,
|
||||
// Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
|
||||
// if err = gctemplates.InitTemplates(); err != nil {
|
||||
// return "", err
|
||||
// }
|
||||
|
||||
// for b, board := range request.Form {
|
||||
// if b == "board" {
|
||||
// return board[0], nil
|
||||
// }
|
||||
// }
|
||||
// return "", &ErrStaffAction{
|
||||
// ErrorField: "staffaction",
|
||||
// Action: "rebuildboard",
|
||||
// Message: fmt.Sprintf("/%s/ is not a board"),
|
||||
// }
|
||||
}},
|
||||
// for b, board := range request.Form {
|
||||
// if b == "board" {
|
||||
// return board[0], nil
|
||||
// }
|
||||
// }
|
||||
// return "", &ErrStaffAction{
|
||||
// ErrorField: "staffaction",
|
||||
// Action: "rebuildboard",
|
||||
// Message: fmt.Sprintf("/%s/ is not a board"),
|
||||
// }
|
||||
// }},
|
||||
{
|
||||
ID: "rebuildboards",
|
||||
Title: "Rebuild boards",
|
||||
|
@ -555,7 +533,7 @@ var actions = []Action{
|
|||
"message": "Boards built successfully",
|
||||
}, building.BuildBoards(false)
|
||||
}
|
||||
return "<h2>Rebuild boards</h2>Boards built successfully", building.BuildBoards(false)
|
||||
return "Boards built successfully", building.BuildBoards(false)
|
||||
}},
|
||||
{
|
||||
ID: "reparsehtml",
|
||||
|
@ -607,7 +585,7 @@ var actions = []Action{
|
|||
Title: "Temporary posts lists",
|
||||
Permissions: AdminPerms,
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
|
||||
outputStr := `<h1>Temporary posts</h1>`
|
||||
outputStr := ""
|
||||
if len(gcsql.TempPosts) == 0 {
|
||||
outputStr += "No temporary posts<br />"
|
||||
return
|
||||
|
|
|
@ -64,6 +64,11 @@ div.section-block {
|
|||
}
|
||||
}
|
||||
|
||||
.centered {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#footer {
|
||||
bottom:0px;
|
||||
clear:both;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
<h1>Ban user</h1>
|
||||
<form method="POST" action="/manage?action=bans">
|
||||
<input type="hidden" name="do" value="add" />
|
||||
<b>User filter:</b><br />
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
<h2>Manage Boards</h2>
|
||||
<form action="{{$.webroot}}manage?action=boards" method="GET">
|
||||
<input type="hidden" name="action" value="boards">
|
||||
{{with $.boards}}{{else}}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
<h1>Config editor</h1>
|
||||
{{if ne .status ""}}{{.status}}<hr />{{end}}
|
||||
Some fields omitted because they can not be (safely) edited from the web interface while Gochan is running.
|
||||
Edit these directly in gochan.json, then restart Gochan.<br />
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
<h2>Dashboard</h2>
|
||||
<fieldset><legend>Announcements</legend>
|
||||
{{range $a, $announcement := $.announcements}}
|
||||
<div class="announcement">
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
<h1>Login</h1><br />
|
||||
<form method="POST" action="{{.webroot}}manage?action=login" id="login-box" class="staff-form">
|
||||
<input type="hidden" name="redirect" value="{{.redirect}}" />
|
||||
<table>
|
||||
|
|
24
templates/manage_recentposts.html
Normal file
24
templates/manage_recentposts.html
Normal file
|
@ -0,0 +1,24 @@
|
|||
<!-- Limit by: <select id="limitby">
|
||||
<option>25</option>
|
||||
<option>50</option>
|
||||
<option>100</option>
|
||||
<option>200</option>
|
||||
</select><br /><br /> -->
|
||||
<table width="100%" border="1">
|
||||
<colgroup><col width="5%"><col width="15%"><col width="60%"><col width="15%"></colgroup>
|
||||
<tr><th></th><th>Name</th><th>Message</th><th>Thumb</th></tr>
|
||||
{{range $rp, $post := $.recentposts}}
|
||||
<tr><td><a href="{{$post.BoardName}}/res/{{if eq $post.ParentID 0}}{{$post.ID}}{{else}}{{$post.ParentID}}{{end}}.html#{{$post.PostID}}" class="centered">Post</a></td>
|
||||
<td><b>Name: </b> {{- if and (eq $post.Name "") (eq $post.Tripcode "")}}<span class="postername">Anonymous</span>{{end}}
|
||||
{{- if ne $post.Name ""}}<span class="postername">{{$post.Name}}</span>{{end -}}
|
||||
{{- if ne $post.Tripcode ""}}!<span class="tripcode">{{$post.Tripcode}}</span>{{end -}}<br />
|
||||
<b>IP: </b> {{$post.IP}}</td>
|
||||
<td>{{truncateMessage (stripHTML $post.Message) 300 16}}</td><td>
|
||||
{{- if eq $post.Filename "deleted" -}}
|
||||
<div class="file-deleted-box centered" style="text-align:center;">File removed</div>
|
||||
{{- else if ne $post.Filename "" -}}
|
||||
{{- $thumbURL := stringAppend $.webroot $post.BoardName "/thumb/" (getThreadThumbnail $post.Filename) -}}
|
||||
{{- $uploadURL := stringAppend $.webroot $post.BoardName "/src/" $post.Filename -}}
|
||||
<a href="{{$uploadURL}}" target="_blank" class="centered"><img src="{{$thumbURL}}"></a>
|
||||
{{end}}</td></tr>{{end}}
|
||||
</table>
|
|
@ -1,4 +1,3 @@
|
|||
<h1 class="manage-header">Staff</h1><br />
|
||||
<table id="stafftable" border="1">
|
||||
<tr>
|
||||
<td><b>Username</b></td>
|
||||
|
|
|
@ -20,4 +20,5 @@
|
|||
<div id="topbar">
|
||||
<a href="{{$.webroot}}" class="topbar-item">home</a>
|
||||
{{range $i, $board := .boards}}<a href="{{$.webroot}}{{$board.Dir}}/" class="topbar-item" title="{{$board.Title}}">/{{$board.Dir}}/</a>{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{with $.page_title}}<br /><h1>{{$.page_title}}</h1>{{end}}
|
Loading…
Add table
Add a link
Reference in a new issue