mirror of https://github.com/jlelse/GoBlog
Further improve counting of media uses
This commit is contained in:
parent
d98ae73af0
commit
529d8b616b
|
@ -185,6 +185,10 @@ func (db *database) exec(query string, args ...interface{}) (sql.Result, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *database) query(query string, args ...interface{}) (*sql.Rows, error) {
|
func (db *database) query(query string, args ...interface{}) (*sql.Rows, error) {
|
||||||
|
// Check if prepared cache should be skipped
|
||||||
|
if len(args) > 0 && args[0] == dbNoCache {
|
||||||
|
return db.db.Query(query, args[1:]...)
|
||||||
|
}
|
||||||
// Use prepared statement
|
// Use prepared statement
|
||||||
st, _ := db.prepare(query)
|
st, _ := db.prepare(query)
|
||||||
if st != nil {
|
if st != nil {
|
||||||
|
@ -195,6 +199,10 @@ func (db *database) query(query string, args ...interface{}) (*sql.Rows, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *database) queryRow(query string, args ...interface{}) (*sql.Row, error) {
|
func (db *database) queryRow(query string, args ...interface{}) (*sql.Row, error) {
|
||||||
|
// Check if prepared cache should be skipped
|
||||||
|
if len(args) > 0 && args[0] == dbNoCache {
|
||||||
|
return db.db.QueryRow(query, args[1:]...), nil
|
||||||
|
}
|
||||||
// Use prepared statement
|
// Use prepared statement
|
||||||
st, _ := db.prepare(query)
|
st, _ := db.prepare(query)
|
||||||
if st != nil {
|
if st != nil {
|
||||||
|
|
|
@ -3,6 +3,8 @@ package main
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
"github.com/thoas/go-funk"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (a *goBlog) serveEditorFiles(w http.ResponseWriter, r *http.Request) {
|
func (a *goBlog) serveEditorFiles(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -15,12 +17,26 @@ func (a *goBlog) serveEditorFiles(w http.ResponseWriter, r *http.Request) {
|
||||||
sort.Slice(files, func(i, j int) bool {
|
sort.Slice(files, func(i, j int) bool {
|
||||||
return files[i].Time.After(files[j].Time)
|
return files[i].Time.After(files[j].Time)
|
||||||
})
|
})
|
||||||
|
// Find uses
|
||||||
|
fileNames, ok := funk.Map(files, func(f *mediaFile) string {
|
||||||
|
return f.Name
|
||||||
|
}).([]string)
|
||||||
|
if !ok {
|
||||||
|
a.serveError(w, r, "Failed to get file names", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uses, err := a.db.usesOfMediaFile(fileNames...)
|
||||||
|
if err != nil {
|
||||||
|
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
// Serve HTML
|
// Serve HTML
|
||||||
blog := r.Context().Value(blogContextKey).(string)
|
blog := r.Context().Value(blogContextKey).(string)
|
||||||
a.render(w, r, templateEditorFiles, &renderData{
|
a.render(w, r, templateEditorFiles, &renderData{
|
||||||
BlogString: blog,
|
BlogString: blog,
|
||||||
Data: map[string]interface{}{
|
Data: map[string]interface{}{
|
||||||
"Files": files,
|
"Files": files,
|
||||||
|
"Uses": uses,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
45
postsDb.go
45
postsDb.go
|
@ -471,15 +471,44 @@ func (d *database) allPublishedDates(blog string) (dates []publishedDate, err er
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *database) usesOfMediaFile(name string) (count int, err error) {
|
const mediaUseSql = `
|
||||||
query := "select count(distinct path) from (select path from posts_fts where content match @fts_name union all select path from post_parameters where instr(value, @name) > 0)"
|
with mediafiles (name) as (values %s)
|
||||||
row, err := db.queryRow(query, sql.Named("fts_name", fmt.Sprintf("\"%s\"", name)), sql.Named("name", name))
|
select name, count(path) as count from (
|
||||||
if err != nil {
|
select distinct m.name, p.path
|
||||||
return 0, err
|
from mediafiles m, post_parameters p
|
||||||
|
where instr(p.value, m.name) > 0
|
||||||
|
union
|
||||||
|
select distinct m.name, p.path
|
||||||
|
from mediafiles m, posts_fts p
|
||||||
|
where p.content match '"' || m.name || '"'
|
||||||
|
)
|
||||||
|
group by name;
|
||||||
|
`
|
||||||
|
|
||||||
|
func (db *database) usesOfMediaFile(names ...string) (counts map[string]int, err error) {
|
||||||
|
sqlArgs := []interface{}{dbNoCache}
|
||||||
|
nameValues := ""
|
||||||
|
for i, n := range names {
|
||||||
|
if i > 0 {
|
||||||
|
nameValues += ", "
|
||||||
|
}
|
||||||
|
named := fmt.Sprintf("name%v", i)
|
||||||
|
nameValues += fmt.Sprintf("(@%s)", named)
|
||||||
|
sqlArgs = append(sqlArgs, sql.Named(named, n))
|
||||||
}
|
}
|
||||||
err = row.Scan(&count)
|
rows, err := db.query(fmt.Sprintf(mediaUseSql, nameValues), sqlArgs...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return count, nil
|
counts = map[string]int{}
|
||||||
|
var name string
|
||||||
|
var count int
|
||||||
|
for rows.Next() {
|
||||||
|
err = rows.Scan(&name, &count)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
counts[name] = count
|
||||||
|
}
|
||||||
|
return counts, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -338,7 +338,10 @@ func Test_usesOfMediaFile(t *testing.T) {
|
||||||
}, &postCreationOptions{new: true})
|
}, &postCreationOptions{new: true})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
count, err := app.db.usesOfMediaFile("test.jpg")
|
counts, err := app.db.usesOfMediaFile("test.jpg")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, 2, count)
|
assert.Len(t, counts, 1)
|
||||||
|
if assert.NotNil(t, counts["test.jpg"]) {
|
||||||
|
assert.Equal(t, 2, counts["test.jpg"])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
31
render.go
31
render.go
|
@ -56,22 +56,21 @@ func (a *goBlog) initRendering() error {
|
||||||
"translations": a.postTranslations,
|
"translations": a.postTranslations,
|
||||||
"shorturl": a.shortPostURL,
|
"shorturl": a.shortPostURL,
|
||||||
// Others
|
// Others
|
||||||
"dateformat": dateFormat,
|
"dateformat": dateFormat,
|
||||||
"isodate": isoDateFormat,
|
"isodate": isoDateFormat,
|
||||||
"unixtodate": unixToLocalDateString,
|
"unixtodate": unixToLocalDateString,
|
||||||
"now": localNowString,
|
"now": localNowString,
|
||||||
"asset": a.assetFileName,
|
"asset": a.assetFileName,
|
||||||
"string": a.ts.GetTemplateStringVariantFunc(),
|
"string": a.ts.GetTemplateStringVariantFunc(),
|
||||||
"include": a.includeRenderedTemplate,
|
"include": a.includeRenderedTemplate,
|
||||||
"urlize": urlize,
|
"urlize": urlize,
|
||||||
"sort": sortedStrings,
|
"sort": sortedStrings,
|
||||||
"absolute": a.getFullAddress,
|
"absolute": a.getFullAddress,
|
||||||
"mentions": a.db.getWebmentionsByAddress,
|
"mentions": a.db.getWebmentionsByAddress,
|
||||||
"geotitle": a.geoTitle,
|
"geotitle": a.geoTitle,
|
||||||
"geolink": geoOSMLink,
|
"geolink": geoOSMLink,
|
||||||
"opensearch": openSearchUrl,
|
"opensearch": openSearchUrl,
|
||||||
"mbytes": mBytesString,
|
"mbytes": mBytesString,
|
||||||
"mediafileuses": a.db.usesOfMediaFile,
|
|
||||||
}
|
}
|
||||||
baseTemplate, err := template.New("base").Funcs(templateFunctions).ParseFiles(path.Join(templatesDir, templateBase+templatesExt))
|
baseTemplate, err := template.New("base").Funcs(templateFunctions).ParseFiles(path.Join(templatesDir, templateBase+templatesExt))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -9,8 +9,9 @@
|
||||||
{{ if .Data.Files }}
|
{{ if .Data.Files }}
|
||||||
<form class="fw-form p" method="post">
|
<form class="fw-form p" method="post">
|
||||||
<select name="filename">
|
<select name="filename">
|
||||||
|
{{ $uses := .Data.Uses }}
|
||||||
{{ range $i, $file := .Data.Files }}
|
{{ range $i, $file := .Data.Files }}
|
||||||
<option value="{{ $file.Name }}">{{ $file.Name }} ({{ isodate $file.Time.String }}, {{ mbytes $file.Size }}, ~{{ mediafileuses $file.Name }} {{ string $blog.Lang "fileuses" }})</option>
|
<option value="{{ $file.Name }}">{{ $file.Name }} ({{ isodate $file.Time.String }}, {{ mbytes $file.Size }}, ~{{ index $uses $file.Name }} {{ string $blog.Lang "fileuses" }})</option>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</select>
|
</select>
|
||||||
<input type="submit" formaction="{{ .Blog.RelativePath "/editor/files/view" }}" value="{{ string .Blog.Lang "view" }}">
|
<input type="submit" formaction="{{ .Blog.RelativePath "/editor/files/view" }}" value="{{ string .Blog.Lang "view" }}">
|
||||||
|
|
Loading…
Reference in New Issue