mirror of
https://github.com/go-shiori/shiori.git
synced 2025-02-21 22:43:22 +08:00
Initial bookmark content
This commit is contained in:
parent
a7d59d55e3
commit
3345d8db29
8 changed files with 276 additions and 17 deletions
80
internal/view/content.html
Normal file
80
internal/view/content.html
Normal file
|
@ -0,0 +1,80 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>Shiori - Bookmarks Manager</title>
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<link rel="apple-touch-icon-precomposed" sizes="152x152" href="/res/apple-touch-icon-152x152.png">
|
||||
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="/res/apple-touch-icon-144x144.png">
|
||||
<link rel="icon" type="image/png" href="/res/favicon-32x32.png" sizes="32x32">
|
||||
<link rel="icon" type="image/png" href="/res/favicon-16x16.png" sizes="16x16">
|
||||
<link rel="icon" type="image/x-icon" href="/res/favicon.ico">
|
||||
|
||||
<link href="/css/fontawesome.min.css" rel="stylesheet">
|
||||
<link href="/css/stylesheet.css" rel="stylesheet">
|
||||
<link href="/css/custom-dialog.css" rel="stylesheet">
|
||||
<link href="/css/bookmark-item.css" rel="stylesheet">
|
||||
|
||||
<script src="/js/vue.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="content-scene" :class="{night: displayOptions.nightMode}">
|
||||
<div id="header">
|
||||
<p id="metadata">Added {{modified}} UTC</p>
|
||||
<p id="title">{{title}}</p>
|
||||
<div id="links">
|
||||
<a href="$$.URL$$" target="_blank">View Original</a>
|
||||
<a href="$$.URL$$" target="_blank">View Archive</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="content">
|
||||
$$html .HTML$$
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="module">
|
||||
// Create initial variable
|
||||
import basePage from "/js/page/base.js";
|
||||
|
||||
new Vue({
|
||||
el: '#content-scene',
|
||||
mixins: [basePage],
|
||||
data: {
|
||||
title: "$$.Title$$",
|
||||
author: "$$.Author$$",
|
||||
modified: "$$.Modified$$"
|
||||
},
|
||||
methods: {
|
||||
loadSetting() {
|
||||
var opts = JSON.parse(localStorage.getItem("shiori-setting")) || {},
|
||||
showId = (typeof opts.showId === "boolean") ? opts.showId : false,
|
||||
listMode = (typeof opts.listMode === "boolean") ? opts.listMode : false,
|
||||
nightMode = (typeof opts.nightMode === "boolean") ? opts.nightMode : false,
|
||||
useArchive = (typeof opts.useArchive === "boolean") ? opts.useArchive : false;
|
||||
|
||||
this.displayOptions = {
|
||||
showId: showId,
|
||||
listMode: listMode,
|
||||
nightMode: nightMode,
|
||||
useArchive: useArchive,
|
||||
};
|
||||
|
||||
document.body.className = nightMode ? "night" : "";
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// Set title
|
||||
document.title = `${this.title} - Shiori - Bookmarks Manager`;
|
||||
|
||||
// Load setting
|
||||
this.loadSetting();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
File diff suppressed because one or more lines are too long
|
@ -383,6 +383,114 @@ a {
|
|||
}
|
||||
}
|
||||
|
||||
#content-scene {
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
color: var(--color);
|
||||
background-color: var(--bg);
|
||||
flex-flow: column nowrap;
|
||||
align-items: center;
|
||||
|
||||
#header {
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
max-width: 840px;
|
||||
margin-bottom: 16px;
|
||||
background-color: var(--contentBg);
|
||||
border: 1px solid var(--border);
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
align-items: center;
|
||||
|
||||
#metadata {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
color: var(--colorLink);
|
||||
}
|
||||
|
||||
#title {
|
||||
padding: 8px 0;
|
||||
grid-column-start: 1;
|
||||
grid-column-end: -1;
|
||||
font-size: 36px;
|
||||
font-weight: 700;
|
||||
word-break: break-word;
|
||||
hyphens: none;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#links {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
|
||||
a {
|
||||
padding: 0 4px;
|
||||
color: var(--color);
|
||||
text-decoration: underline;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: var(--main);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#content {
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
max-width: 840px;
|
||||
background-color: var(--contentBg);
|
||||
border: 1px solid var(--border);
|
||||
|
||||
* {
|
||||
font-size: 18px;
|
||||
line-height: 180%;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color);
|
||||
text-decoration: underline;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: var(--main);
|
||||
}
|
||||
}
|
||||
|
||||
pre,
|
||||
code {
|
||||
overflow: auto;
|
||||
border: 1px solid var(--border);
|
||||
font-family: 'Ubuntu Mono', 'Courier New', Courier, monospace;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
pre {
|
||||
padding: 8px;
|
||||
|
||||
>code {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
ol,
|
||||
ul {
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#page-home {
|
||||
#edit-box {
|
||||
background-color: var(--selectedBg);
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -156,7 +156,7 @@ func (h *handler) apiGetBookmarks(w http.ResponseWriter, r *http.Request, ps htt
|
|||
for i := range bookmarks {
|
||||
strID := strconv.Itoa(bookmarks[i].ID)
|
||||
imgPath := fp.Join(h.DataDir, "thumb", strID)
|
||||
imgURL := path.Join("/", "thumb", strID)
|
||||
imgURL := path.Join("/", "bookmark", strID, "thumb")
|
||||
|
||||
if fileExists(imgPath) {
|
||||
bookmarks[i].ImageURL = imgURL
|
||||
|
@ -309,7 +309,7 @@ func (h *handler) apiInsertBookmark(w http.ResponseWriter, r *http.Request, ps h
|
|||
for _, imageURL := range imageURLs {
|
||||
err = downloadBookImage(imageURL, imgPath, time.Minute)
|
||||
if err == nil {
|
||||
book.ImageURL = path.Join("/", "thumb", strID)
|
||||
book.ImageURL = path.Join("/", "bookmark", strID, "thumb")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -488,7 +488,8 @@ func (h *handler) apiUpdateCache(w http.ResponseWriter, r *http.Request, ps http
|
|||
// Split response body so it can be processed twice
|
||||
archivalInput := bytes.NewBuffer(nil)
|
||||
readabilityInput := bytes.NewBuffer(nil)
|
||||
multiWriter := io.MultiWriter(archivalInput, readabilityInput)
|
||||
readabilityCheckInput := bytes.NewBuffer(nil)
|
||||
multiWriter := io.MultiWriter(archivalInput, readabilityInput, readabilityCheckInput)
|
||||
|
||||
_, err = io.Copy(multiWriter, resp.Body)
|
||||
if err != nil {
|
||||
|
@ -501,6 +502,8 @@ func (h *handler) apiUpdateCache(w http.ResponseWriter, r *http.Request, ps http
|
|||
contentType := resp.Header.Get("Content-Type")
|
||||
|
||||
if strings.Contains(contentType, "text/html") {
|
||||
isReadable := readability.IsReadable(readabilityCheckInput)
|
||||
|
||||
article, err := readability.FromReader(readabilityInput, book.URL)
|
||||
if err != nil {
|
||||
chProblem <- book.ID
|
||||
|
@ -510,7 +513,7 @@ func (h *handler) apiUpdateCache(w http.ResponseWriter, r *http.Request, ps http
|
|||
book.Author = article.Byline
|
||||
book.Content = article.TextContent
|
||||
book.HTML = article.Content
|
||||
book.HasContent = book.Content != ""
|
||||
book.HasContent = book.Content != "" && isReadable
|
||||
|
||||
if article.Title != "" {
|
||||
book.Title = article.Title
|
||||
|
@ -535,7 +538,7 @@ func (h *handler) apiUpdateCache(w http.ResponseWriter, r *http.Request, ps http
|
|||
for _, imageURL := range imageURLs {
|
||||
err = downloadBookImage(imageURL, imgPath, time.Minute)
|
||||
if err == nil {
|
||||
book.ImageURL = path.Join("/", "thumb", strID)
|
||||
book.ImageURL = path.Join("/", "bookmark", strID, "thumb")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -652,7 +655,7 @@ func (h *handler) apiUpdateBookmarkTags(w http.ResponseWriter, r *http.Request,
|
|||
for i := range bookmarks {
|
||||
strID := strconv.Itoa(bookmarks[i].ID)
|
||||
imgPath := fp.Join(h.DataDir, "thumb", strID)
|
||||
imgURL := path.Join("/", "thumb", strID)
|
||||
imgURL := path.Join("/", "bookmark", strID, "thumb")
|
||||
|
||||
if fileExists(imgPath) {
|
||||
bookmarks[i].ImageURL = imgURL
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
package webserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"net/http"
|
||||
nurl "net/url"
|
||||
"os"
|
||||
"path"
|
||||
fp "path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
@ -63,7 +67,42 @@ func (h *handler) serveLoginPage(w http.ResponseWriter, r *http.Request, ps http
|
|||
checkError(err)
|
||||
}
|
||||
|
||||
// serveThumbnailImage is handler for GET /thumb/:id
|
||||
// serveBookmarkContent is handler for GET /bookmark/:id/content
|
||||
func (h *handler) serveBookmarkContent(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
// Get bookmark ID from URL
|
||||
strID := ps.ByName("id")
|
||||
id, err := strconv.Atoi(strID)
|
||||
checkError(err)
|
||||
|
||||
// Get bookmark in database
|
||||
bookmark, exist := h.DB.GetBookmark(id, "")
|
||||
if !exist {
|
||||
panic(fmt.Errorf("Bookmark not found"))
|
||||
}
|
||||
|
||||
// Create template
|
||||
funcMap := template.FuncMap{
|
||||
"html": func(s string) template.HTML {
|
||||
return template.HTML(s)
|
||||
},
|
||||
"hostname": func(s string) string {
|
||||
parsed, err := nurl.ParseRequestURI(s)
|
||||
if err != nil || len(parsed.Scheme) == 0 {
|
||||
return s
|
||||
}
|
||||
return parsed.Hostname()
|
||||
},
|
||||
}
|
||||
|
||||
tplCache, err := createTemplate("content.html", funcMap)
|
||||
checkError(err)
|
||||
|
||||
// Execute template
|
||||
err = tplCache.Execute(w, &bookmark)
|
||||
checkError(err)
|
||||
}
|
||||
|
||||
// serveThumbnailImage is handler for GET /bookmark/:id/thumb
|
||||
func (h *handler) serveThumbnailImage(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
// Get bookmark ID from URL
|
||||
id := ps.ByName("id")
|
||||
|
|
|
@ -33,7 +33,8 @@ func ServeApp(DB database.DB, dataDir string, port int) error {
|
|||
|
||||
router.GET("/", hdl.serveIndexPage)
|
||||
router.GET("/login", hdl.serveLoginPage)
|
||||
router.GET("/thumb/:id", hdl.serveThumbnailImage)
|
||||
router.GET("/bookmark/:id/thumb", hdl.serveThumbnailImage)
|
||||
router.GET("/bookmark/:id/content", hdl.serveBookmarkContent)
|
||||
|
||||
router.POST("/api/login", hdl.apiLogin)
|
||||
router.POST("/api/logout", hdl.apiLogout)
|
||||
|
|
|
@ -2,11 +2,13 @@ package webserver
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
"image/jpeg"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"mime"
|
||||
"net/http"
|
||||
|
@ -162,6 +164,24 @@ func downloadBookImage(url, dstPath string, timeout time.Duration) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func createTemplate(filename string, funcMap template.FuncMap) (*template.Template, error) {
|
||||
// Open file
|
||||
src, err := assets.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
// Read file content
|
||||
srcContent, err := ioutil.ReadAll(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create template
|
||||
return template.New(filename).Delims("$$", "$$").Funcs(funcMap).Parse(string(srcContent))
|
||||
}
|
||||
|
||||
func checkError(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
|
Loading…
Reference in a new issue