Jelajahi Sumber

Add bookmarklet

Frédéric Guillot 8 tahun lalu
induk
melakukan
6690f6a70e

+ 1 - 1
README.md

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

+ 4 - 0
config/config.go

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

+ 8 - 3
locale/translations.go

@@ -1,5 +1,5 @@
 // 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
 
@@ -139,12 +139,17 @@ var Translations = map[string]string{
     "Work in progress...": "Travail en cours...",
     "This user already exists.": "Cet utilisateur 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{
 	"en_US": "6fe95384260941e8a5a3c695a655a932e0a8a6a572c1e45cb2b1ae8baa01b897",
-	"fr_FR": "5c8c2c5e35a17a7dd3c30596b73342f70950a3bbce00786d43ccba01b96ea672",
+	"fr_FR": "f1ddbcfb8ffd837a2df69e8506d023e4254ead2f0b94e518ab595df97d32c87a",
 }

+ 6 - 1
locale/translations/fr_FR.json

@@ -123,5 +123,10 @@
     "Work in progress...": "Travail en cours...",
     "This user already exists.": "Cet utilisateur 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"
 }

+ 6 - 2
server/core/handler.go

@@ -18,16 +18,19 @@ import (
 	"github.com/gorilla/mux"
 )
 
+// HandlerFunc is an application HTTP handler.
 type HandlerFunc func(ctx *Context, request *Request, response *Response)
 
+// Handler manages HTTP handlers and middlewares.
 type Handler struct {
 	store      *storage.Storage
 	translator *locale.Translator
-	template   *template.TemplateEngine
+	template   *template.Engine
 	router     *mux.Router
 	middleware *middleware.MiddlewareChain
 }
 
+// Use is a wrapper around an HTTP handler.
 func (h *Handler) Use(f HandlerFunc) http.Handler {
 	return h.middleware.WrapFunc(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		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{
 		store:      store,
 		translator: translator,

+ 1 - 1
server/core/html_response.go

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

+ 2 - 2
server/core/response.go

@@ -15,7 +15,7 @@ import (
 type Response struct {
 	writer   http.ResponseWriter
 	request  *http.Request
-	template *template.TemplateEngine
+	template *template.Engine
 }
 
 // SetCookie send a cookie to the client.
@@ -67,6 +67,6 @@ func (r *Response) commonHeaders() {
 }
 
 // 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}
 }

+ 6 - 2
server/routes.go

@@ -7,6 +7,7 @@ package server
 import (
 	"net/http"
 
+	"github.com/miniflux/miniflux2/config"
 	"github.com/miniflux/miniflux2/locale"
 	"github.com/miniflux/miniflux2/reader/feed"
 	"github.com/miniflux/miniflux2/reader/opml"
@@ -20,10 +21,10 @@ import (
 	"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()
 	translator := locale.Load()
-	templateEngine := template.NewTemplateEngine(router, translator)
+	templateEngine := template.NewEngine(cfg, router, translator)
 
 	apiController := api_controller.NewController(store, feedHandler)
 	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.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/{sessionID}/remove", uiHandler.Use(uiController.RemoveSession)).Name("removeSession").Methods("POST")
 

+ 6 - 4
server/server.go

@@ -5,21 +5,23 @@
 package server
 
 import (
-	"github.com/miniflux/miniflux2/config"
-	"github.com/miniflux/miniflux2/reader/feed"
-	"github.com/miniflux/miniflux2/storage"
 	"log"
 	"net/http"
 	"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 {
 	server := &http.Server{
 		ReadTimeout:  5 * time.Second,
 		WriteTimeout: 10 * time.Second,
 		IdleTimeout:  60 * time.Second,
 		Addr:         cfg.Get("LISTEN_ADDR", "127.0.0.1:8080"),
-		Handler:      getRoutes(store, feedHandler),
+		Handler:      getRoutes(cfg, store, feedHandler),
 	}
 
 	go func() {

+ 1 - 1
server/static/bin.go

@@ -1,5 +1,5 @@
 // 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
 

File diff ditekan karena terlalu besar
+ 1 - 1
server/static/css.go


+ 15 - 0
server/static/css/common.css

@@ -659,3 +659,18 @@ a.button {
 .loading {
     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;
+}

+ 1 - 1
server/static/js.go

@@ -1,5 +1,5 @@
 // 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
 

+ 1 - 1
server/template/common.go

@@ -1,5 +1,5 @@
 // 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
 

+ 3 - 0
server/template/html/about.html

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

+ 3 - 0
server/template/html/create_user.html

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

+ 3 - 0
server/template/html/edit_user.html

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

+ 35 - 0
server/template/html/integrations.html

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

+ 3 - 0
server/template/html/sessions.html

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

+ 3 - 0
server/template/html/settings.html

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

+ 3 - 0
server/template/html/users.html

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

+ 27 - 17
server/template/template.go

@@ -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
 // license that can be found in the LICENSE file.
 
@@ -13,6 +13,7 @@ import (
 	"strings"
 	"time"
 
+	"github.com/miniflux/miniflux2/config"
 	"github.com/miniflux/miniflux2/errors"
 	"github.com/miniflux/miniflux2/locale"
 	"github.com/miniflux/miniflux2/server/route"
@@ -22,23 +23,28 @@ import (
 	"github.com/gorilla/mux"
 )
 
-type TemplateEngine struct {
+// Engine handles the templating system.
+type Engine struct {
 	templates     map[string]*template.Template
 	router        *mux.Router
 	translator    *locale.Translator
 	currentLocale *locale.Language
+	cfg           *config.Config
 }
 
-func (t *TemplateEngine) ParseAll() {
+func (e *Engine) parseAll() {
 	funcMap := template.FuncMap{
+		"baseURL": func() string {
+			return e.cfg.Get("BASE_URL", config.DefaultBaseURL)
+		},
 		"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 {
 			return template.HTML(str)
 		},
 		"proxyFilter": func(data string) string {
-			return filter.ImageProxyFilter(t.router, data)
+			return filter.ImageProxyFilter(e.router, data)
 		},
 		"domain": func(websiteURL string) string {
 			parsedURL, err := url.Parse(websiteURL)
@@ -58,15 +64,15 @@ func (t *TemplateEngine) ParseAll() {
 			return ts.Format("2006-01-02 15:04:05")
 		},
 		"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 {
 			switch key.(type) {
 			case string:
-				return t.currentLocale.Get(key.(string), args...)
+				return e.currentLocale.Get(key.(string), args...)
 			case errors.LocalizedError:
 				err := key.(errors.LocalizedError)
-				return err.Localize(t.currentLocale)
+				return err.Localize(e.currentLocale)
 			case error:
 				return key.(error).Error()
 			default:
@@ -74,7 +80,7 @@ func (t *TemplateEngine) ParseAll() {
 			}
 		},
 		"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 {
 		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) {
-	t.currentLocale = t.translator.GetLanguage(language)
+// SetLanguage change the language for template processing.
+func (e *Engine) SetLanguage(language string) {
+	e.currentLocale = e.translator.GetLanguage(language)
 }
 
-func (t *TemplateEngine) Execute(w io.Writer, name string, data interface{}) {
-	tpl, ok := t.templates[name]
+// Execute process a template.
+func (e *Engine) Execute(w io.Writer, name string, data interface{}) {
+	tpl, ok := e.templates[name]
 	if !ok {
 		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)
 }
 
-func NewTemplateEngine(router *mux.Router, translator *locale.Translator) *TemplateEngine {
-	tpl := &TemplateEngine{
+// NewEngine returns a new template Engine.
+func NewEngine(cfg *config.Config, router *mux.Router, translator *locale.Translator) *Engine {
+	tpl := &Engine{
 		templates:  make(map[string]*template.Template),
 		router:     router,
 		translator: translator,
+		cfg:        cfg,
 	}
 
-	tpl.ParseAll()
+	tpl.parseAll()
 	return tpl
 }

+ 62 - 7
server/template/views.go

@@ -1,5 +1,5 @@
 // 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
 
@@ -13,6 +13,9 @@ var templateViewsMap = map[string]string{
         <li>
             <a href="{{ route "settings" }}">{{ t "Settings" }}</a>
         </li>
+        <li>
+            <a href="{{ route "integrations" }}">{{ t "Integrations" }}</a>
+        </li>
         <li>
             <a href="{{ route "sessions" }}">{{ t "Sessions" }}</a>
         </li>
@@ -267,6 +270,9 @@ var templateViewsMap = map[string]string{
         <li>
             <a href="{{ route "settings" }}">{{ t "Settings" }}</a>
         </li>
+        <li>
+            <a href="{{ route "integrations" }}">{{ t "Integrations" }}</a>
+        </li>
         <li>
             <a href="{{ route "sessions" }}">{{ t "Sessions" }}</a>
         </li>
@@ -401,6 +407,9 @@ var templateViewsMap = map[string]string{
         <li>
             <a href="{{ route "settings" }}">{{ t "Settings" }}</a>
         </li>
+        <li>
+            <a href="{{ route "integrations" }}">{{ t "Integrations" }}</a>
+        </li>
         <li>
             <a href="{{ route "sessions" }}">{{ t "Sessions" }}</a>
         </li>
@@ -725,6 +734,42 @@ var templateViewsMap = map[string]string{
     </div>
 </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 }}
 `,
 	"login": `{{ define "title"}}{{ t "Sign In" }}{{ end }}
@@ -760,6 +805,9 @@ var templateViewsMap = map[string]string{
         <li>
             <a href="{{ route "settings" }}">{{ t "Settings" }}</a>
         </li>
+        <li>
+            <a href="{{ route "integrations" }}">{{ t "Integrations" }}</a>
+        </li>
         <li>
             <a href="{{ route "users" }}">{{ t "Users" }}</a>
         </li>
@@ -806,6 +854,9 @@ var templateViewsMap = map[string]string{
 <section class="page-header">
     <h1>{{ t "Settings" }}</h1>
     <ul>
+        <li>
+            <a href="{{ route "integrations" }}">{{ t "Integrations" }}</a>
+        </li>
         <li>
             <a href="{{ route "sessions" }}">{{ t "Sessions" }}</a>
         </li>
@@ -920,6 +971,9 @@ var templateViewsMap = map[string]string{
         <li>
             <a href="{{ route "settings" }}">{{ t "Settings" }}</a>
         </li>
+        <li>
+            <a href="{{ route "integrations" }}">{{ t "Integrations" }}</a>
+        </li>
         <li>
             <a href="{{ route "sessions" }}">{{ t "Sessions" }}</a>
         </li>
@@ -972,24 +1026,25 @@ var templateViewsMap = map[string]string{
 }
 
 var templateViewsMapChecksums = map[string]string{
-	"about":               "56f1d45d8b9944306c66be0712320527e739a0ce4fccbd97a4c414c8f9cfab04",
+	"about":               "ad2fb778fc73c39b733b3f81b13e5c7d689b041fadd24ee2d4577f545aa788ad",
 	"add_subscription":    "098ea9e492e18242bd414b22c4d8638006d113f728e5ae78c9186663f60ae3f1",
 	"categories":          "ca1280cd157bb527d4fc907da67b05a8347378f6dce965b9389d4bcdf3600a11",
 	"category_entries":    "0bdcf28ef29b976b78d1add431896a8c56791476abd7a4240998d52c3efe1f35",
 	"choose_subscription": "d37682743d8bbd84738a964e238103db2651f95fa340c6e285ffe2e12548d673",
 	"create_category":     "2b82af5d2dcd67898dc5daa57a6461e6ff8121a6089b2a2a1be909f35e4a2275",
-	"create_user":         "966b31d0414e0d0a547ef9ada428cbd24a91100bfed491f780c0461892a2489b",
+	"create_user":         "815dd31faaa6e9ba81a2a6664e5707aaf4153c392accd2b1f77cf1937035a881",
 	"edit_category":       "cee720faadcec58289b707ad30af623d2ee66c1ce23a732965463250d7ff41c5",
 	"edit_feed":           "c5bc4c22bf7e8348d880395250545595d21fb8c8e723fc5d7cca68e25d250884",
-	"edit_user":           "f0f79704983de3ca7858bd8cda7a372c3999f5e4e0cf951fba5fa2c1752f9111",
+	"edit_user":           "c835d78f7cf36c11533db9cef253457a9003987d704070d59446cb2b0e84dcb9",
 	"entry":               "32e605edd6d43773ac31329d247ebd81d38d974cd43689d91de79fffec7fe04b",
 	"feed_entries":        "9aff923b6c7452dec1514feada7e0d2bbc1ec21c6f5e9f48b2de41d1b731ffe4",
 	"feeds":               "94e43404a4044490c065c888a49bebd3ff51b588b9fb47d03c2598003aa40dca",
 	"history":             "947603cbde888516e62925f5d08fb0b13d930623d3ee4c690dbc22612fdda75e",
 	"import":              "73b5112e20bfd232bf73334544186ea419505936bc237d481517a8622901878f",
+	"integrations":        "c485d6d9ed996635e55e73320610e6bcb01a41c1153e8e739ae2294b0b14b243",
 	"login":               "568f2f69f248048f3e55e9bbc719077a74ae23fe18f237aa40e3de37e97b7a41",
-	"sessions":            "5ac3793f0ee74d0807bab6a173a1aa6508e98add5c022fa54c8fdf5c6b4a0e75",
-	"settings":            "9c89bfd70ff288b4256e5205be78a7645450b364db1df51d10fee3cb915b2c6b",
+	"sessions":            "878dbe8f8ea783b44130c495814179519fa5c3aa2666ac87508f94d58dd008bf",
+	"settings":            "a972fb5767fd32522648149880e40607ed8bbed7a389038bbab6b08539ac2893",
 	"unread":              "b6f9be1a72188947c75a6fdcac6ff7878db7745f9efa46318e0433102892a722",
-	"users":               "c6b1fa81cf229dde88e69c353114b542c67794a2cd513eddaf600828ab14aa18",
+	"users":               "44677e28bb5347799ed0020c90ec785aadec4b1454446d92411cfdaf6e32110b",
 }

+ 20 - 0
server/ui/controller/integrations.go

@@ -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",
+	}))
+}

+ 20 - 1
server/ui/controller/subscription.go

@@ -5,13 +5,30 @@
 package controller
 
 import (
+	"log"
+
 	"github.com/miniflux/miniflux2/model"
 	"github.com/miniflux/miniflux2/reader/subscription"
 	"github.com/miniflux/miniflux2/server/core"
 	"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) {
 	user := ctx.LoggedUser()
 
@@ -24,6 +41,7 @@ func (c *Controller) AddSubscription(ctx *core.Context, request *core.Request, r
 	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) {
 	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) {
 	user := ctx.LoggedUser()
 

+ 1 - 1
sql/sql.go

@@ -1,5 +1,5 @@
 // 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
 

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini