Add bookmarklet

This commit is contained in:
Frédéric Guillot 2017-11-21 19:37:47 -08:00
parent 1bc43ec2bc
commit 6690f6a70e
26 changed files with 244 additions and 48 deletions

View file

@ -26,7 +26,7 @@ TODO
- [ ] Custom entries sorting - [ ] Custom entries sorting
- [ ] Webpage scraper (Readability) - [ ] Webpage scraper (Readability)
- [ ] Bookmarklet - [X] Bookmarklet
- [ ] External integrations (Pinboard, Wallabag...) - [ ] External integrations (Pinboard, Wallabag...)
- [ ] Gzip compression - [ ] Gzip compression
- [ ] Integration tests - [ ] Integration tests

View file

@ -9,6 +9,10 @@ import (
"strconv" "strconv"
) )
const (
DefaultBaseURL = "http://localhost"
)
type Config struct { type Config struct {
} }

View file

@ -1,5 +1,5 @@
// Code generated by go generate; DO NOT EDIT. // Code generated by go generate; DO NOT EDIT.
// 2017-11-21 15:41:59.495654213 -0800 PST m=+0.041889871 // 2017-11-21 19:31:59.645632989 -0800 PST m=+0.024631837
package locale package locale
@ -139,12 +139,17 @@ var Translations = map[string]string{
"Work in progress...": "Travail en cours...", "Work in progress...": "Travail en cours...",
"This user already exists.": "Cet utilisateur existe déjà.", "This user already exists.": "Cet utilisateur existe déjà.",
"This category already exists.": "Cette catégorie existe déjà.", "This category already exists.": "Cette catégorie existe déjà.",
"Unable to update this category.": "Impossible de mettre à jour cette catégorie." "Unable to update this category.": "Impossible de mettre à jour cette catégorie.",
"Integrations": "Intégrations",
"Bookmarklet": "Bookmarklet",
"Drag and drop this link to your bookmarks.": "Glisser-déposer ce lien dans vos favoris.",
"This special link allows you to subscribe to a website directly by using a bookmark in your web browser.": "Ce lien spécial vous permet de vous abonner à un site web directement en utilisant un marque page dans votre navigateur web.",
"Add to Miniflux": "Ajouter à Miniflux"
} }
`, `,
} }
var TranslationsChecksums = map[string]string{ var TranslationsChecksums = map[string]string{
"en_US": "6fe95384260941e8a5a3c695a655a932e0a8a6a572c1e45cb2b1ae8baa01b897", "en_US": "6fe95384260941e8a5a3c695a655a932e0a8a6a572c1e45cb2b1ae8baa01b897",
"fr_FR": "5c8c2c5e35a17a7dd3c30596b73342f70950a3bbce00786d43ccba01b96ea672", "fr_FR": "f1ddbcfb8ffd837a2df69e8506d023e4254ead2f0b94e518ab595df97d32c87a",
} }

View file

@ -123,5 +123,10 @@
"Work in progress...": "Travail en cours...", "Work in progress...": "Travail en cours...",
"This user already exists.": "Cet utilisateur existe déjà.", "This user already exists.": "Cet utilisateur existe déjà.",
"This category already exists.": "Cette catégorie existe déjà.", "This category already exists.": "Cette catégorie existe déjà.",
"Unable to update this category.": "Impossible de mettre à jour cette catégorie." "Unable to update this category.": "Impossible de mettre à jour cette catégorie.",
"Integrations": "Intégrations",
"Bookmarklet": "Bookmarklet",
"Drag and drop this link to your bookmarks.": "Glisser-déposer ce lien dans vos favoris.",
"This special link allows you to subscribe to a website directly by using a bookmark in your web browser.": "Ce lien spécial vous permet de vous abonner à un site web directement en utilisant un marque page dans votre navigateur web.",
"Add to Miniflux": "Ajouter à Miniflux"
} }

View file

@ -18,16 +18,19 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
) )
// HandlerFunc is an application HTTP handler.
type HandlerFunc func(ctx *Context, request *Request, response *Response) type HandlerFunc func(ctx *Context, request *Request, response *Response)
// Handler manages HTTP handlers and middlewares.
type Handler struct { type Handler struct {
store *storage.Storage store *storage.Storage
translator *locale.Translator translator *locale.Translator
template *template.TemplateEngine template *template.Engine
router *mux.Router router *mux.Router
middleware *middleware.MiddlewareChain middleware *middleware.MiddlewareChain
} }
// Use is a wrapper around an HTTP handler.
func (h *Handler) Use(f HandlerFunc) http.Handler { func (h *Handler) Use(f HandlerFunc) http.Handler {
return h.middleware.WrapFunc(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return h.middleware.WrapFunc(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer helper.ExecutionTime(time.Now(), r.URL.Path) defer helper.ExecutionTime(time.Now(), r.URL.Path)
@ -47,7 +50,8 @@ func (h *Handler) Use(f HandlerFunc) http.Handler {
})) }))
} }
func NewHandler(store *storage.Storage, router *mux.Router, template *template.TemplateEngine, translator *locale.Translator, middleware *middleware.MiddlewareChain) *Handler { // NewHandler returns a new Handler.
func NewHandler(store *storage.Storage, router *mux.Router, template *template.Engine, translator *locale.Translator, middleware *middleware.MiddlewareChain) *Handler {
return &Handler{ return &Handler{
store: store, store: store,
translator: translator, translator: translator,

View file

@ -15,7 +15,7 @@ import (
type HTMLResponse struct { type HTMLResponse struct {
writer http.ResponseWriter writer http.ResponseWriter
request *http.Request request *http.Request
template *template.TemplateEngine template *template.Engine
} }
// Render execute a template and send to the client the generated HTML. // Render execute a template and send to the client the generated HTML.

View file

@ -15,7 +15,7 @@ import (
type Response struct { type Response struct {
writer http.ResponseWriter writer http.ResponseWriter
request *http.Request request *http.Request
template *template.TemplateEngine template *template.Engine
} }
// SetCookie send a cookie to the client. // SetCookie send a cookie to the client.
@ -67,6 +67,6 @@ func (r *Response) commonHeaders() {
} }
// NewResponse returns a new Response. // NewResponse returns a new Response.
func NewResponse(w http.ResponseWriter, r *http.Request, template *template.TemplateEngine) *Response { func NewResponse(w http.ResponseWriter, r *http.Request, template *template.Engine) *Response {
return &Response{writer: w, request: r, template: template} return &Response{writer: w, request: r, template: template}
} }

View file

@ -7,6 +7,7 @@ package server
import ( import (
"net/http" "net/http"
"github.com/miniflux/miniflux2/config"
"github.com/miniflux/miniflux2/locale" "github.com/miniflux/miniflux2/locale"
"github.com/miniflux/miniflux2/reader/feed" "github.com/miniflux/miniflux2/reader/feed"
"github.com/miniflux/miniflux2/reader/opml" "github.com/miniflux/miniflux2/reader/opml"
@ -20,10 +21,10 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
) )
func getRoutes(store *storage.Storage, feedHandler *feed.Handler) *mux.Router { func getRoutes(cfg *config.Config, store *storage.Storage, feedHandler *feed.Handler) *mux.Router {
router := mux.NewRouter() router := mux.NewRouter()
translator := locale.Load() translator := locale.Load()
templateEngine := template.NewTemplateEngine(router, translator) templateEngine := template.NewEngine(cfg, router, translator)
apiController := api_controller.NewController(store, feedHandler) apiController := api_controller.NewController(store, feedHandler)
uiController := ui_controller.NewController(store, feedHandler, opml.NewHandler(store)) uiController := ui_controller.NewController(store, feedHandler, opml.NewHandler(store))
@ -110,6 +111,9 @@ func getRoutes(store *storage.Storage, feedHandler *feed.Handler) *mux.Router {
router.Handle("/settings", uiHandler.Use(uiController.ShowSettings)).Name("settings").Methods("GET") router.Handle("/settings", uiHandler.Use(uiController.ShowSettings)).Name("settings").Methods("GET")
router.Handle("/settings", uiHandler.Use(uiController.UpdateSettings)).Name("updateSettings").Methods("POST") router.Handle("/settings", uiHandler.Use(uiController.UpdateSettings)).Name("updateSettings").Methods("POST")
router.Handle("/bookmarklet", uiHandler.Use(uiController.Bookmarklet)).Name("bookmarklet").Methods("GET")
router.Handle("/integrations", uiHandler.Use(uiController.ShowIntegrations)).Name("integrations").Methods("GET")
router.Handle("/sessions", uiHandler.Use(uiController.ShowSessions)).Name("sessions").Methods("GET") router.Handle("/sessions", uiHandler.Use(uiController.ShowSessions)).Name("sessions").Methods("GET")
router.Handle("/sessions/{sessionID}/remove", uiHandler.Use(uiController.RemoveSession)).Name("removeSession").Methods("POST") router.Handle("/sessions/{sessionID}/remove", uiHandler.Use(uiController.RemoveSession)).Name("removeSession").Methods("POST")

View file

@ -5,21 +5,23 @@
package server package server
import ( import (
"github.com/miniflux/miniflux2/config"
"github.com/miniflux/miniflux2/reader/feed"
"github.com/miniflux/miniflux2/storage"
"log" "log"
"net/http" "net/http"
"time" "time"
"github.com/miniflux/miniflux2/config"
"github.com/miniflux/miniflux2/reader/feed"
"github.com/miniflux/miniflux2/storage"
) )
// NewServer returns a new HTTP server.
func NewServer(cfg *config.Config, store *storage.Storage, feedHandler *feed.Handler) *http.Server { func NewServer(cfg *config.Config, store *storage.Storage, feedHandler *feed.Handler) *http.Server {
server := &http.Server{ server := &http.Server{
ReadTimeout: 5 * time.Second, ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second,
IdleTimeout: 60 * time.Second, IdleTimeout: 60 * time.Second,
Addr: cfg.Get("LISTEN_ADDR", "127.0.0.1:8080"), Addr: cfg.Get("LISTEN_ADDR", "127.0.0.1:8080"),
Handler: getRoutes(store, feedHandler), Handler: getRoutes(cfg, store, feedHandler),
} }
go func() { go func() {

View file

@ -1,5 +1,5 @@
// Code generated by go generate; DO NOT EDIT. // Code generated by go generate; DO NOT EDIT.
// 2017-11-21 15:41:59.461181295 -0800 PST m=+0.007416953 // 2017-11-21 19:31:59.626742594 -0800 PST m=+0.005741442
package static package static

File diff suppressed because one or more lines are too long

View file

@ -659,3 +659,18 @@ a.button {
.loading { .loading {
font-style: italic; font-style: italic;
} }
/* Bookmarlet */
.bookmarklet {
border: 1px dashed #ccc;
border-radius: 5px;
padding: 15px;
margin: 15px;
text-align: center;
}
.bookmarklet a {
font-weight: 600;
text-decoration: none;
font-size: 1.2em;
}

View file

@ -1,5 +1,5 @@
// Code generated by go generate; DO NOT EDIT. // Code generated by go generate; DO NOT EDIT.
// 2017-11-21 15:41:59.4687788 -0800 PST m=+0.015014458 // 2017-11-21 19:31:59.631783539 -0800 PST m=+0.010782387
package static package static

View file

@ -1,5 +1,5 @@
// Code generated by go generate; DO NOT EDIT. // Code generated by go generate; DO NOT EDIT.
// 2017-11-21 15:41:59.491806442 -0800 PST m=+0.038042100 // 2017-11-21 19:31:59.643994492 -0800 PST m=+0.022993340
package template package template

View file

@ -7,6 +7,9 @@
<li> <li>
<a href="{{ route "settings" }}">{{ t "Settings" }}</a> <a href="{{ route "settings" }}">{{ t "Settings" }}</a>
</li> </li>
<li>
<a href="{{ route "integrations" }}">{{ t "Integrations" }}</a>
</li>
<li> <li>
<a href="{{ route "sessions" }}">{{ t "Sessions" }}</a> <a href="{{ route "sessions" }}">{{ t "Sessions" }}</a>
</li> </li>

View file

@ -7,6 +7,9 @@
<li> <li>
<a href="{{ route "settings" }}">{{ t "Settings" }}</a> <a href="{{ route "settings" }}">{{ t "Settings" }}</a>
</li> </li>
<li>
<a href="{{ route "integrations" }}">{{ t "Integrations" }}</a>
</li>
<li> <li>
<a href="{{ route "sessions" }}">{{ t "Sessions" }}</a> <a href="{{ route "sessions" }}">{{ t "Sessions" }}</a>
</li> </li>

View file

@ -7,6 +7,9 @@
<li> <li>
<a href="{{ route "settings" }}">{{ t "Settings" }}</a> <a href="{{ route "settings" }}">{{ t "Settings" }}</a>
</li> </li>
<li>
<a href="{{ route "integrations" }}">{{ t "Integrations" }}</a>
</li>
<li> <li>
<a href="{{ route "sessions" }}">{{ t "Sessions" }}</a> <a href="{{ route "sessions" }}">{{ t "Sessions" }}</a>
</li> </li>

View file

@ -0,0 +1,35 @@
{{ define "title"}}{{ t "Integrations" }}{{ end }}
{{ define "content"}}
<section class="page-header">
<h1>{{ t "Integrations" }}</h1>
<ul>
<li>
<a href="{{ route "settings" }}">{{ t "Settings" }}</a>
</li>
<li>
<a href="{{ route "sessions" }}">{{ t "Sessions" }}</a>
</li>
{{ if .user.IsAdmin }}
<li>
<a href="{{ route "users" }}">{{ t "Users" }}</a>
</li>
{{ end }}
<li>
<a href="{{ route "about" }}">{{ t "About" }}</a>
</li>
</ul>
</section>
<div class="panel">
<h3>{{ t "Bookmarklet" }}</h3>
<p>{{ t "This special link allows you to subscribe to a website directly by using a bookmark in your web browser." }}</p>
<div class="bookmarklet">
<a href="javascript:location.href='{{ baseURL }}{{ route "bookmarklet" }}?uri='+encodeURIComponent(window.location.href)">{{ t "Add to Miniflux" }}</a>
</div>
<p>{{ t "Drag and drop this link to your bookmarks." }}</p>
</div>
{{ end }}

View file

@ -7,6 +7,9 @@
<li> <li>
<a href="{{ route "settings" }}">{{ t "Settings" }}</a> <a href="{{ route "settings" }}">{{ t "Settings" }}</a>
</li> </li>
<li>
<a href="{{ route "integrations" }}">{{ t "Integrations" }}</a>
</li>
<li> <li>
<a href="{{ route "users" }}">{{ t "Users" }}</a> <a href="{{ route "users" }}">{{ t "Users" }}</a>
</li> </li>

View file

@ -4,6 +4,9 @@
<section class="page-header"> <section class="page-header">
<h1>{{ t "Settings" }}</h1> <h1>{{ t "Settings" }}</h1>
<ul> <ul>
<li>
<a href="{{ route "integrations" }}">{{ t "Integrations" }}</a>
</li>
<li> <li>
<a href="{{ route "sessions" }}">{{ t "Sessions" }}</a> <a href="{{ route "sessions" }}">{{ t "Sessions" }}</a>
</li> </li>

View file

@ -7,6 +7,9 @@
<li> <li>
<a href="{{ route "settings" }}">{{ t "Settings" }}</a> <a href="{{ route "settings" }}">{{ t "Settings" }}</a>
</li> </li>
<li>
<a href="{{ route "integrations" }}">{{ t "Integrations" }}</a>
</li>
<li> <li>
<a href="{{ route "sessions" }}">{{ t "Sessions" }}</a> <a href="{{ route "sessions" }}">{{ t "Sessions" }}</a>
</li> </li>

View file

@ -1,4 +1,4 @@
// Copyright 2017 Frédéric Guillot. All rights reserved. // Copyright 2017 Frédéric Guilloe. All rights reserved.
// Use of this source code is governed by the Apache 2.0 // Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -13,6 +13,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/miniflux/miniflux2/config"
"github.com/miniflux/miniflux2/errors" "github.com/miniflux/miniflux2/errors"
"github.com/miniflux/miniflux2/locale" "github.com/miniflux/miniflux2/locale"
"github.com/miniflux/miniflux2/server/route" "github.com/miniflux/miniflux2/server/route"
@ -22,23 +23,28 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
) )
type TemplateEngine struct { // Engine handles the templating system.
type Engine struct {
templates map[string]*template.Template templates map[string]*template.Template
router *mux.Router router *mux.Router
translator *locale.Translator translator *locale.Translator
currentLocale *locale.Language currentLocale *locale.Language
cfg *config.Config
} }
func (t *TemplateEngine) ParseAll() { func (e *Engine) parseAll() {
funcMap := template.FuncMap{ funcMap := template.FuncMap{
"baseURL": func() string {
return e.cfg.Get("BASE_URL", config.DefaultBaseURL)
},
"route": func(name string, args ...interface{}) string { "route": func(name string, args ...interface{}) string {
return route.GetRoute(t.router, name, args...) return route.GetRoute(e.router, name, args...)
}, },
"noescape": func(str string) template.HTML { "noescape": func(str string) template.HTML {
return template.HTML(str) return template.HTML(str)
}, },
"proxyFilter": func(data string) string { "proxyFilter": func(data string) string {
return filter.ImageProxyFilter(t.router, data) return filter.ImageProxyFilter(e.router, data)
}, },
"domain": func(websiteURL string) string { "domain": func(websiteURL string) string {
parsedURL, err := url.Parse(websiteURL) parsedURL, err := url.Parse(websiteURL)
@ -58,15 +64,15 @@ func (t *TemplateEngine) ParseAll() {
return ts.Format("2006-01-02 15:04:05") return ts.Format("2006-01-02 15:04:05")
}, },
"elapsed": func(ts time.Time) string { "elapsed": func(ts time.Time) string {
return helper.GetElapsedTime(t.currentLocale, ts) return helper.GetElapsedTime(e.currentLocale, ts)
}, },
"t": func(key interface{}, args ...interface{}) string { "t": func(key interface{}, args ...interface{}) string {
switch key.(type) { switch key.(type) {
case string: case string:
return t.currentLocale.Get(key.(string), args...) return e.currentLocale.Get(key.(string), args...)
case errors.LocalizedError: case errors.LocalizedError:
err := key.(errors.LocalizedError) err := key.(errors.LocalizedError)
return err.Localize(t.currentLocale) return err.Localize(e.currentLocale)
case error: case error:
return key.(error).Error() return key.(error).Error()
default: default:
@ -74,7 +80,7 @@ func (t *TemplateEngine) ParseAll() {
} }
}, },
"plural": func(key string, n int, args ...interface{}) string { "plural": func(key string, n int, args ...interface{}) string {
return t.currentLocale.Plural(key, n, args...) return e.currentLocale.Plural(key, n, args...)
}, },
} }
@ -85,16 +91,18 @@ func (t *TemplateEngine) ParseAll() {
for name, content := range templateViewsMap { for name, content := range templateViewsMap {
log.Println("Parsing template:", name) log.Println("Parsing template:", name)
t.templates[name] = template.Must(template.New("main").Funcs(funcMap).Parse(commonTemplates + content)) e.templates[name] = template.Must(template.New("main").Funcs(funcMap).Parse(commonTemplates + content))
} }
} }
func (t *TemplateEngine) SetLanguage(language string) { // SetLanguage change the language for template processing.
t.currentLocale = t.translator.GetLanguage(language) func (e *Engine) SetLanguage(language string) {
e.currentLocale = e.translator.GetLanguage(language)
} }
func (t *TemplateEngine) Execute(w io.Writer, name string, data interface{}) { // Execute process a template.
tpl, ok := t.templates[name] func (e *Engine) Execute(w io.Writer, name string, data interface{}) {
tpl, ok := e.templates[name]
if !ok { if !ok {
log.Fatalf("The template %s does not exists.\n", name) log.Fatalf("The template %s does not exists.\n", name)
} }
@ -108,13 +116,15 @@ func (t *TemplateEngine) Execute(w io.Writer, name string, data interface{}) {
b.WriteTo(w) b.WriteTo(w)
} }
func NewTemplateEngine(router *mux.Router, translator *locale.Translator) *TemplateEngine { // NewEngine returns a new template Engine.
tpl := &TemplateEngine{ func NewEngine(cfg *config.Config, router *mux.Router, translator *locale.Translator) *Engine {
tpl := &Engine{
templates: make(map[string]*template.Template), templates: make(map[string]*template.Template),
router: router, router: router,
translator: translator, translator: translator,
cfg: cfg,
} }
tpl.ParseAll() tpl.parseAll()
return tpl return tpl
} }

View file

@ -1,5 +1,5 @@
// Code generated by go generate; DO NOT EDIT. // Code generated by go generate; DO NOT EDIT.
// 2017-11-21 15:41:59.472545112 -0800 PST m=+0.018780770 // 2017-11-21 19:31:59.633723314 -0800 PST m=+0.012722162
package template package template
@ -13,6 +13,9 @@ var templateViewsMap = map[string]string{
<li> <li>
<a href="{{ route "settings" }}">{{ t "Settings" }}</a> <a href="{{ route "settings" }}">{{ t "Settings" }}</a>
</li> </li>
<li>
<a href="{{ route "integrations" }}">{{ t "Integrations" }}</a>
</li>
<li> <li>
<a href="{{ route "sessions" }}">{{ t "Sessions" }}</a> <a href="{{ route "sessions" }}">{{ t "Sessions" }}</a>
</li> </li>
@ -267,6 +270,9 @@ var templateViewsMap = map[string]string{
<li> <li>
<a href="{{ route "settings" }}">{{ t "Settings" }}</a> <a href="{{ route "settings" }}">{{ t "Settings" }}</a>
</li> </li>
<li>
<a href="{{ route "integrations" }}">{{ t "Integrations" }}</a>
</li>
<li> <li>
<a href="{{ route "sessions" }}">{{ t "Sessions" }}</a> <a href="{{ route "sessions" }}">{{ t "Sessions" }}</a>
</li> </li>
@ -401,6 +407,9 @@ var templateViewsMap = map[string]string{
<li> <li>
<a href="{{ route "settings" }}">{{ t "Settings" }}</a> <a href="{{ route "settings" }}">{{ t "Settings" }}</a>
</li> </li>
<li>
<a href="{{ route "integrations" }}">{{ t "Integrations" }}</a>
</li>
<li> <li>
<a href="{{ route "sessions" }}">{{ t "Sessions" }}</a> <a href="{{ route "sessions" }}">{{ t "Sessions" }}</a>
</li> </li>
@ -725,6 +734,42 @@ var templateViewsMap = map[string]string{
</div> </div>
</form> </form>
{{ end }}
`,
"integrations": `{{ define "title"}}{{ t "Integrations" }}{{ end }}
{{ define "content"}}
<section class="page-header">
<h1>{{ t "Integrations" }}</h1>
<ul>
<li>
<a href="{{ route "settings" }}">{{ t "Settings" }}</a>
</li>
<li>
<a href="{{ route "sessions" }}">{{ t "Sessions" }}</a>
</li>
{{ if .user.IsAdmin }}
<li>
<a href="{{ route "users" }}">{{ t "Users" }}</a>
</li>
{{ end }}
<li>
<a href="{{ route "about" }}">{{ t "About" }}</a>
</li>
</ul>
</section>
<div class="panel">
<h3>{{ t "Bookmarklet" }}</h3>
<p>{{ t "This special link allows you to subscribe to a website directly by using a bookmark in your web browser." }}</p>
<div class="bookmarklet">
<a href="javascript:location.href='{{ baseURL }}{{ route "bookmarklet" }}?uri='+encodeURIComponent(window.location.href)">{{ t "Add to Miniflux" }}</a>
</div>
<p>{{ t "Drag and drop this link to your bookmarks." }}</p>
</div>
{{ end }} {{ end }}
`, `,
"login": `{{ define "title"}}{{ t "Sign In" }}{{ end }} "login": `{{ define "title"}}{{ t "Sign In" }}{{ end }}
@ -760,6 +805,9 @@ var templateViewsMap = map[string]string{
<li> <li>
<a href="{{ route "settings" }}">{{ t "Settings" }}</a> <a href="{{ route "settings" }}">{{ t "Settings" }}</a>
</li> </li>
<li>
<a href="{{ route "integrations" }}">{{ t "Integrations" }}</a>
</li>
<li> <li>
<a href="{{ route "users" }}">{{ t "Users" }}</a> <a href="{{ route "users" }}">{{ t "Users" }}</a>
</li> </li>
@ -806,6 +854,9 @@ var templateViewsMap = map[string]string{
<section class="page-header"> <section class="page-header">
<h1>{{ t "Settings" }}</h1> <h1>{{ t "Settings" }}</h1>
<ul> <ul>
<li>
<a href="{{ route "integrations" }}">{{ t "Integrations" }}</a>
</li>
<li> <li>
<a href="{{ route "sessions" }}">{{ t "Sessions" }}</a> <a href="{{ route "sessions" }}">{{ t "Sessions" }}</a>
</li> </li>
@ -920,6 +971,9 @@ var templateViewsMap = map[string]string{
<li> <li>
<a href="{{ route "settings" }}">{{ t "Settings" }}</a> <a href="{{ route "settings" }}">{{ t "Settings" }}</a>
</li> </li>
<li>
<a href="{{ route "integrations" }}">{{ t "Integrations" }}</a>
</li>
<li> <li>
<a href="{{ route "sessions" }}">{{ t "Sessions" }}</a> <a href="{{ route "sessions" }}">{{ t "Sessions" }}</a>
</li> </li>
@ -972,24 +1026,25 @@ var templateViewsMap = map[string]string{
} }
var templateViewsMapChecksums = map[string]string{ var templateViewsMapChecksums = map[string]string{
"about": "56f1d45d8b9944306c66be0712320527e739a0ce4fccbd97a4c414c8f9cfab04", "about": "ad2fb778fc73c39b733b3f81b13e5c7d689b041fadd24ee2d4577f545aa788ad",
"add_subscription": "098ea9e492e18242bd414b22c4d8638006d113f728e5ae78c9186663f60ae3f1", "add_subscription": "098ea9e492e18242bd414b22c4d8638006d113f728e5ae78c9186663f60ae3f1",
"categories": "ca1280cd157bb527d4fc907da67b05a8347378f6dce965b9389d4bcdf3600a11", "categories": "ca1280cd157bb527d4fc907da67b05a8347378f6dce965b9389d4bcdf3600a11",
"category_entries": "0bdcf28ef29b976b78d1add431896a8c56791476abd7a4240998d52c3efe1f35", "category_entries": "0bdcf28ef29b976b78d1add431896a8c56791476abd7a4240998d52c3efe1f35",
"choose_subscription": "d37682743d8bbd84738a964e238103db2651f95fa340c6e285ffe2e12548d673", "choose_subscription": "d37682743d8bbd84738a964e238103db2651f95fa340c6e285ffe2e12548d673",
"create_category": "2b82af5d2dcd67898dc5daa57a6461e6ff8121a6089b2a2a1be909f35e4a2275", "create_category": "2b82af5d2dcd67898dc5daa57a6461e6ff8121a6089b2a2a1be909f35e4a2275",
"create_user": "966b31d0414e0d0a547ef9ada428cbd24a91100bfed491f780c0461892a2489b", "create_user": "815dd31faaa6e9ba81a2a6664e5707aaf4153c392accd2b1f77cf1937035a881",
"edit_category": "cee720faadcec58289b707ad30af623d2ee66c1ce23a732965463250d7ff41c5", "edit_category": "cee720faadcec58289b707ad30af623d2ee66c1ce23a732965463250d7ff41c5",
"edit_feed": "c5bc4c22bf7e8348d880395250545595d21fb8c8e723fc5d7cca68e25d250884", "edit_feed": "c5bc4c22bf7e8348d880395250545595d21fb8c8e723fc5d7cca68e25d250884",
"edit_user": "f0f79704983de3ca7858bd8cda7a372c3999f5e4e0cf951fba5fa2c1752f9111", "edit_user": "c835d78f7cf36c11533db9cef253457a9003987d704070d59446cb2b0e84dcb9",
"entry": "32e605edd6d43773ac31329d247ebd81d38d974cd43689d91de79fffec7fe04b", "entry": "32e605edd6d43773ac31329d247ebd81d38d974cd43689d91de79fffec7fe04b",
"feed_entries": "9aff923b6c7452dec1514feada7e0d2bbc1ec21c6f5e9f48b2de41d1b731ffe4", "feed_entries": "9aff923b6c7452dec1514feada7e0d2bbc1ec21c6f5e9f48b2de41d1b731ffe4",
"feeds": "94e43404a4044490c065c888a49bebd3ff51b588b9fb47d03c2598003aa40dca", "feeds": "94e43404a4044490c065c888a49bebd3ff51b588b9fb47d03c2598003aa40dca",
"history": "947603cbde888516e62925f5d08fb0b13d930623d3ee4c690dbc22612fdda75e", "history": "947603cbde888516e62925f5d08fb0b13d930623d3ee4c690dbc22612fdda75e",
"import": "73b5112e20bfd232bf73334544186ea419505936bc237d481517a8622901878f", "import": "73b5112e20bfd232bf73334544186ea419505936bc237d481517a8622901878f",
"integrations": "c485d6d9ed996635e55e73320610e6bcb01a41c1153e8e739ae2294b0b14b243",
"login": "568f2f69f248048f3e55e9bbc719077a74ae23fe18f237aa40e3de37e97b7a41", "login": "568f2f69f248048f3e55e9bbc719077a74ae23fe18f237aa40e3de37e97b7a41",
"sessions": "5ac3793f0ee74d0807bab6a173a1aa6508e98add5c022fa54c8fdf5c6b4a0e75", "sessions": "878dbe8f8ea783b44130c495814179519fa5c3aa2666ac87508f94d58dd008bf",
"settings": "9c89bfd70ff288b4256e5205be78a7645450b364db1df51d10fee3cb915b2c6b", "settings": "a972fb5767fd32522648149880e40607ed8bbed7a389038bbab6b08539ac2893",
"unread": "b6f9be1a72188947c75a6fdcac6ff7878db7745f9efa46318e0433102892a722", "unread": "b6f9be1a72188947c75a6fdcac6ff7878db7745f9efa46318e0433102892a722",
"users": "c6b1fa81cf229dde88e69c353114b542c67794a2cd513eddaf600828ab14aa18", "users": "44677e28bb5347799ed0020c90ec785aadec4b1454446d92411cfdaf6e32110b",
} }

View file

@ -0,0 +1,20 @@
// Copyright 2017 Frédéric Guillot. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package controller
import "github.com/miniflux/miniflux2/server/core"
// ShowIntegrations renders the page with all external integrations.
func (c *Controller) ShowIntegrations(ctx *core.Context, request *core.Request, response *core.Response) {
args, err := c.getCommonTemplateArgs(ctx)
if err != nil {
response.HTML().ServerError(err)
return
}
response.HTML().Render("integrations", args.Merge(tplParams{
"menu": "settings",
}))
}

View file

@ -5,13 +5,30 @@
package controller package controller
import ( import (
"log"
"github.com/miniflux/miniflux2/model" "github.com/miniflux/miniflux2/model"
"github.com/miniflux/miniflux2/reader/subscription" "github.com/miniflux/miniflux2/reader/subscription"
"github.com/miniflux/miniflux2/server/core" "github.com/miniflux/miniflux2/server/core"
"github.com/miniflux/miniflux2/server/ui/form" "github.com/miniflux/miniflux2/server/ui/form"
"log"
) )
// Bookmarklet prefill the form to add a subscription from the URL provided by the bookmarklet.
func (c *Controller) Bookmarklet(ctx *core.Context, request *core.Request, response *core.Response) {
user := ctx.LoggedUser()
args, err := c.getSubscriptionFormTemplateArgs(ctx, user)
if err != nil {
response.HTML().ServerError(err)
return
}
bookmarkletURL := request.QueryStringParam("uri", "")
response.HTML().Render("add_subscription", args.Merge(tplParams{
"form": &form.SubscriptionForm{URL: bookmarkletURL},
}))
}
// AddSubscription shows the form to add a new feed.
func (c *Controller) AddSubscription(ctx *core.Context, request *core.Request, response *core.Response) { func (c *Controller) AddSubscription(ctx *core.Context, request *core.Request, response *core.Response) {
user := ctx.LoggedUser() user := ctx.LoggedUser()
@ -24,6 +41,7 @@ func (c *Controller) AddSubscription(ctx *core.Context, request *core.Request, r
response.HTML().Render("add_subscription", args) response.HTML().Render("add_subscription", args)
} }
// SubmitSubscription try to find a feed from the URL provided by the user.
func (c *Controller) SubmitSubscription(ctx *core.Context, request *core.Request, response *core.Response) { func (c *Controller) SubmitSubscription(ctx *core.Context, request *core.Request, response *core.Response) {
user := ctx.LoggedUser() user := ctx.LoggedUser()
@ -80,6 +98,7 @@ func (c *Controller) SubmitSubscription(ctx *core.Context, request *core.Request
} }
} }
// ChooseSubscription shows a page to choose a subscription.
func (c *Controller) ChooseSubscription(ctx *core.Context, request *core.Request, response *core.Response) { func (c *Controller) ChooseSubscription(ctx *core.Context, request *core.Request, response *core.Response) {
user := ctx.LoggedUser() user := ctx.LoggedUser()

View file

@ -1,5 +1,5 @@
// Code generated by go generate; DO NOT EDIT. // Code generated by go generate; DO NOT EDIT.
// 2017-11-21 15:41:59.457985225 -0800 PST m=+0.004220883 // 2017-11-21 19:31:59.625242989 -0800 PST m=+0.004241837
package sql package sql