Initial bookmark content

This commit is contained in:
Radhi Fadlillah 2019-08-04 21:34:23 +07:00
parent a7d59d55e3
commit 3345d8db29
8 changed files with 276 additions and 17 deletions

View 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

View file

@ -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

View file

@ -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

View file

@ -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")

View file

@ -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)

View file

@ -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)