1
0
Fork 0
mirror of https://github.com/miniflux/v2.git synced 2025-08-01 17:38:37 +00:00

Add Shaarli integration

This commit is contained in:
Frédéric Guillot 2023-08-13 14:30:57 -07:00
parent 28df0b119e
commit 9f465fd70d
26 changed files with 256 additions and 21 deletions

View file

@ -743,4 +743,13 @@ var migrations = []func(tx *sql.Tx) error{
_, err = tx.Exec(sql) _, err = tx.Exec(sql)
return err return err
}, },
func(tx *sql.Tx) (err error) {
sql := `
ALTER TABLE integrations ADD COLUMN shaarli_enabled bool default 'f';
ALTER TABLE integrations ADD COLUMN shaarli_url text default '';
ALTER TABLE integrations ADD COLUMN shaarli_api_secret text default '';
`
_, err = tx.Exec(sql)
return err
},
} }

View file

@ -15,6 +15,7 @@ import (
"miniflux.app/v2/internal/integration/pinboard" "miniflux.app/v2/internal/integration/pinboard"
"miniflux.app/v2/internal/integration/pocket" "miniflux.app/v2/internal/integration/pocket"
"miniflux.app/v2/internal/integration/readwise" "miniflux.app/v2/internal/integration/readwise"
"miniflux.app/v2/internal/integration/shaarli"
"miniflux.app/v2/internal/integration/shiori" "miniflux.app/v2/internal/integration/shiori"
"miniflux.app/v2/internal/integration/telegrambot" "miniflux.app/v2/internal/integration/telegrambot"
"miniflux.app/v2/internal/integration/wallabag" "miniflux.app/v2/internal/integration/wallabag"
@ -25,7 +26,7 @@ import (
// SendEntry sends the entry to third-party providers when the user click on "Save". // SendEntry sends the entry to third-party providers when the user click on "Save".
func SendEntry(entry *model.Entry, integration *model.Integration) { func SendEntry(entry *model.Entry, integration *model.Integration) {
if integration.PinboardEnabled { if integration.PinboardEnabled {
logger.Debug("[Integration] Sending Entry #%d %q for User #%d to Pinboard", entry.ID, entry.URL, integration.UserID) logger.Debug("[Integration] Sending entry #%d %q for user #%d to Pinboard", entry.ID, entry.URL, integration.UserID)
client := pinboard.NewClient(integration.PinboardToken) client := pinboard.NewClient(integration.PinboardToken)
err := client.AddBookmark( err := client.AddBookmark(
@ -41,7 +42,7 @@ func SendEntry(entry *model.Entry, integration *model.Integration) {
} }
if integration.InstapaperEnabled { if integration.InstapaperEnabled {
logger.Debug("[Integration] Sending Entry #%d %q for User #%d to Instapaper", entry.ID, entry.URL, integration.UserID) logger.Debug("[Integration] Sending entry #%d %q for user #%d to Instapaper", entry.ID, entry.URL, integration.UserID)
client := instapaper.NewClient(integration.InstapaperUsername, integration.InstapaperPassword) client := instapaper.NewClient(integration.InstapaperUsername, integration.InstapaperPassword)
if err := client.AddURL(entry.URL, entry.Title); err != nil { if err := client.AddURL(entry.URL, entry.Title); err != nil {
@ -50,7 +51,7 @@ func SendEntry(entry *model.Entry, integration *model.Integration) {
} }
if integration.WallabagEnabled { if integration.WallabagEnabled {
logger.Debug("[Integration] Sending Entry #%d %q for User #%d to Wallabag", entry.ID, entry.URL, integration.UserID) logger.Debug("[Integration] Sending entry #%d %q for user #%d to Wallabag", entry.ID, entry.URL, integration.UserID)
client := wallabag.NewClient( client := wallabag.NewClient(
integration.WallabagURL, integration.WallabagURL,
@ -67,7 +68,7 @@ func SendEntry(entry *model.Entry, integration *model.Integration) {
} }
if integration.NotionEnabled { if integration.NotionEnabled {
logger.Debug("[Integration] Sending Entry #%d %q for User #%d to Notion", entry.ID, entry.URL, integration.UserID) logger.Debug("[Integration] Sending entry #%d %q for user #%d to Notion", entry.ID, entry.URL, integration.UserID)
client := notion.NewClient( client := notion.NewClient(
integration.NotionToken, integration.NotionToken,
@ -79,7 +80,7 @@ func SendEntry(entry *model.Entry, integration *model.Integration) {
} }
if integration.NunuxKeeperEnabled { if integration.NunuxKeeperEnabled {
logger.Debug("[Integration] Sending Entry #%d %q for User #%d to NunuxKeeper", entry.ID, entry.URL, integration.UserID) logger.Debug("[Integration] Sending entry #%d %q for user #%d to NunuxKeeper", entry.ID, entry.URL, integration.UserID)
client := nunuxkeeper.NewClient( client := nunuxkeeper.NewClient(
integration.NunuxKeeperURL, integration.NunuxKeeperURL,
@ -92,7 +93,7 @@ func SendEntry(entry *model.Entry, integration *model.Integration) {
} }
if integration.EspialEnabled { if integration.EspialEnabled {
logger.Debug("[Integration] Sending Entry #%d %q for User #%d to Espial", entry.ID, entry.URL, integration.UserID) logger.Debug("[Integration] Sending entry #%d %q for user #%d to Espial", entry.ID, entry.URL, integration.UserID)
client := espial.NewClient( client := espial.NewClient(
integration.EspialURL, integration.EspialURL,
@ -105,7 +106,7 @@ func SendEntry(entry *model.Entry, integration *model.Integration) {
} }
if integration.PocketEnabled { if integration.PocketEnabled {
logger.Debug("[Integration] Sending Entry #%d %q for User #%d to Pocket", entry.ID, entry.URL, integration.UserID) logger.Debug("[Integration] Sending entry #%d %q for user #%d to Pocket", entry.ID, entry.URL, integration.UserID)
client := pocket.NewClient(config.Opts.PocketConsumerKey(integration.PocketConsumerKey), integration.PocketAccessToken) client := pocket.NewClient(config.Opts.PocketConsumerKey(integration.PocketConsumerKey), integration.PocketAccessToken)
if err := client.AddURL(entry.URL, entry.Title); err != nil { if err := client.AddURL(entry.URL, entry.Title); err != nil {
@ -114,7 +115,7 @@ func SendEntry(entry *model.Entry, integration *model.Integration) {
} }
if integration.LinkdingEnabled { if integration.LinkdingEnabled {
logger.Debug("[Integration] Sending Entry #%d %q for User #%d to Linkding", entry.ID, entry.URL, integration.UserID) logger.Debug("[Integration] Sending entry #%d %q for user #%d to Linkding", entry.ID, entry.URL, integration.UserID)
client := linkding.NewClient( client := linkding.NewClient(
integration.LinkdingURL, integration.LinkdingURL,
@ -128,7 +129,7 @@ func SendEntry(entry *model.Entry, integration *model.Integration) {
} }
if integration.ReadwiseEnabled { if integration.ReadwiseEnabled {
logger.Debug("[Integration] Sending Entry #%d %q for User #%d to Readwise Reader", entry.ID, entry.URL, integration.UserID) logger.Debug("[Integration] Sending entry #%d %q for user #%d to Readwise Reader", entry.ID, entry.URL, integration.UserID)
client := readwise.NewClient( client := readwise.NewClient(
integration.ReadwiseAPIKey, integration.ReadwiseAPIKey,
@ -140,7 +141,7 @@ func SendEntry(entry *model.Entry, integration *model.Integration) {
} }
if integration.ShioriEnabled { if integration.ShioriEnabled {
logger.Debug("[Integration] Sending Entry #%d %q for User #%d to Shiori", entry.ID, entry.URL, integration.UserID) logger.Debug("[Integration] Sending entry #%d %q for user #%d to Shiori", entry.ID, entry.URL, integration.UserID)
client := shiori.NewClient( client := shiori.NewClient(
integration.ShioriURL, integration.ShioriURL,
@ -152,6 +153,19 @@ func SendEntry(entry *model.Entry, integration *model.Integration) {
logger.Error("[Integration] Unable to send entry #%d to Shiori for user #%d: %v", entry.ID, integration.UserID, err) logger.Error("[Integration] Unable to send entry #%d to Shiori for user #%d: %v", entry.ID, integration.UserID, err)
} }
} }
if integration.ShaarliEnabled {
logger.Debug("[Integration] Sending entry #%d %q for user #%d to Shaarli", entry.ID, entry.URL, integration.UserID)
client := shaarli.NewClient(
integration.ShaarliURL,
integration.ShaarliAPISecret,
)
if err := client.AddLink(entry.URL, entry.Title); err != nil {
logger.Error("[Integration] Unable to send entry #%d to Shaarli for user #%d: %v", entry.ID, integration.UserID, err)
}
}
} }
// PushEntries pushes an entry array to third-party providers during feed refreshes. // PushEntries pushes an entry array to third-party providers during feed refreshes.

View file

@ -0,0 +1,91 @@
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package shaarli // import "miniflux.app/v2/internal/integration/shaarli"
import (
"bytes"
"crypto/hmac"
"crypto/sha512"
"encoding/base64"
"encoding/json"
"fmt"
"net/http"
"strings"
"time"
"miniflux.app/v2/internal/url"
"miniflux.app/v2/internal/version"
)
const defaultClientTimeout = 10 * time.Second
type Client struct {
baseURL string
apiSecret string
}
func NewClient(baseURL, apiSecret string) *Client {
return &Client{baseURL: baseURL, apiSecret: apiSecret}
}
func (c *Client) AddLink(entryURL, entryTitle string) error {
if c.baseURL == "" || c.apiSecret == "" {
return fmt.Errorf("shaarli: missing base URL or API secret")
}
apiEndpoint, err := url.JoinBaseURLAndPath(c.baseURL, "/api/v1/links")
if err != nil {
return fmt.Errorf("shaarli: invalid API endpoint: %v", err)
}
requestBody, err := json.Marshal(&addLinkRequest{
URL: entryURL,
Title: entryTitle,
Private: true,
})
if err != nil {
return fmt.Errorf("shaarli: unable to encode request body: %v", err)
}
request, err := http.NewRequest("POST", apiEndpoint, bytes.NewReader(requestBody))
if err != nil {
return fmt.Errorf("shaarli: unable to create request: %v", err)
}
request.Header.Set("Content-Type", "application/json")
request.Header.Set("Accept", "application/json")
request.Header.Set("User-Agent", "Miniflux/"+version.Version)
request.Header.Set("Authorization", "Bearer "+c.generateBearerToken())
httpClient := &http.Client{Timeout: defaultClientTimeout}
response, err := httpClient.Do(request)
if err != nil {
return fmt.Errorf("shaarli: unable to send request: %v", err)
}
defer response.Body.Close()
if response.StatusCode != http.StatusCreated {
return fmt.Errorf("shaarli: unable to add link: url=%s status=%d", apiEndpoint, response.StatusCode)
}
return nil
}
func (c *Client) generateBearerToken() string {
header := strings.TrimRight(base64.URLEncoding.EncodeToString([]byte(`{"typ":"JWT", "alg":"HS256"}`)), "=")
payload := strings.TrimRight(base64.URLEncoding.EncodeToString([]byte(fmt.Sprintf(`{"iat": %d}`, time.Now().Unix()))), "=")
mac := hmac.New(sha512.New, []byte(c.apiSecret))
mac.Write([]byte(header + "." + payload))
signature := strings.TrimRight(base64.URLEncoding.EncodeToString(mac.Sum(nil)), "=")
return header + "." + payload + "." + signature
}
type addLinkRequest struct {
URL string `json:"url"`
Title string `json:"title"`
Private bool `json:"private"`
}

View file

@ -385,6 +385,9 @@
"form.integration.shiori_endpoint": "Shiori API-Endpunkt", "form.integration.shiori_endpoint": "Shiori API-Endpunkt",
"form.integration.shiori_username": "Shiori Benutzername", "form.integration.shiori_username": "Shiori Benutzername",
"form.integration.shiori_password": "Shiori Passwort", "form.integration.shiori_password": "Shiori Passwort",
"form.integration.shaarli_activate": "Save articles to Shaarli",
"form.integration.shaarli_endpoint": "Shaarli URL",
"form.integration.shaarli_api_secret": "Shaarli API Secret",
"form.api_key.label.description": "API-Schlüsselbezeichnung", "form.api_key.label.description": "API-Schlüsselbezeichnung",
"form.submit.loading": "Lade...", "form.submit.loading": "Lade...",
"form.submit.saving": "Speichern...", "form.submit.saving": "Speichern...",

View file

@ -385,6 +385,9 @@
"form.integration.shiori_endpoint": "Τελικό σημείο Shiori", "form.integration.shiori_endpoint": "Τελικό σημείο Shiori",
"form.integration.shiori_username": "Όνομα Χρήστη Shiori", "form.integration.shiori_username": "Όνομα Χρήστη Shiori",
"form.integration.shiori_password": "Κωδικός Πρόσβασης Shiori", "form.integration.shiori_password": "Κωδικός Πρόσβασης Shiori",
"form.integration.shaarli_activate": "Save articles to Shaarli",
"form.integration.shaarli_endpoint": "Shaarli URL",
"form.integration.shaarli_api_secret": "Shaarli API Secret",
"form.api_key.label.description": "Ετικέτα κλειδιού API", "form.api_key.label.description": "Ετικέτα κλειδιού API",
"form.submit.loading": "Φόρτωση...", "form.submit.loading": "Φόρτωση...",
"form.submit.saving": "Αποθήκευση...", "form.submit.saving": "Αποθήκευση...",

View file

@ -385,6 +385,9 @@
"form.integration.shiori_endpoint": "Shiori API Endpoint", "form.integration.shiori_endpoint": "Shiori API Endpoint",
"form.integration.shiori_username": "Shiori Username", "form.integration.shiori_username": "Shiori Username",
"form.integration.shiori_password": "Shiori Password", "form.integration.shiori_password": "Shiori Password",
"form.integration.shaarli_activate": "Save articles to Shaarli",
"form.integration.shaarli_endpoint": "Shaarli URL",
"form.integration.shaarli_api_secret": "Shaarli API Secret",
"form.api_key.label.description": "API Key Label", "form.api_key.label.description": "API Key Label",
"form.submit.loading": "Loading…", "form.submit.loading": "Loading…",
"form.submit.saving": "Saving…", "form.submit.saving": "Saving…",

View file

@ -385,6 +385,9 @@
"form.integration.shiori_endpoint": "Extremo de API de Shiori", "form.integration.shiori_endpoint": "Extremo de API de Shiori",
"form.integration.shiori_username": "Nombre de usuario de Shiori", "form.integration.shiori_username": "Nombre de usuario de Shiori",
"form.integration.shiori_password": "Contraseña de Shiori", "form.integration.shiori_password": "Contraseña de Shiori",
"form.integration.shaarli_activate": "Save articles to Shaarli",
"form.integration.shaarli_endpoint": "Shaarli URL",
"form.integration.shaarli_api_secret": "Shaarli API Secret",
"form.api_key.label.description": "Etiqueta de clave API", "form.api_key.label.description": "Etiqueta de clave API",
"form.submit.loading": "Cargando...", "form.submit.loading": "Cargando...",
"form.submit.saving": "Guardando...", "form.submit.saving": "Guardando...",

View file

@ -385,6 +385,9 @@
"form.integration.shiori_endpoint": "Shiori API Endpoint", "form.integration.shiori_endpoint": "Shiori API Endpoint",
"form.integration.shiori_username": "Shiori Username", "form.integration.shiori_username": "Shiori Username",
"form.integration.shiori_password": "Shiori Password", "form.integration.shiori_password": "Shiori Password",
"form.integration.shaarli_activate": "Save articles to Shaarli",
"form.integration.shaarli_endpoint": "Shaarli URL",
"form.integration.shaarli_api_secret": "Shaarli API Secret",
"form.api_key.label.description": "API Key Label", "form.api_key.label.description": "API Key Label",
"form.submit.loading": "Ladataan...", "form.submit.loading": "Ladataan...",
"form.submit.saving": "Tallennetaan...", "form.submit.saving": "Tallennetaan...",

View file

@ -385,6 +385,9 @@
"form.integration.shiori_endpoint": "URL de l'API de Shiori", "form.integration.shiori_endpoint": "URL de l'API de Shiori",
"form.integration.shiori_username": "Nom d'utilisateur de Shiori", "form.integration.shiori_username": "Nom d'utilisateur de Shiori",
"form.integration.shiori_password": "Mot de passe de Shiori", "form.integration.shiori_password": "Mot de passe de Shiori",
"form.integration.shaarli_activate": "Sauvegarder les articles vers Shaarli",
"form.integration.shaarli_endpoint": "URL de l'API de Shaarli",
"form.integration.shaarli_api_secret": "Clé d'API de Shaarli API",
"form.api_key.label.description": "Libellé de la clé d'API", "form.api_key.label.description": "Libellé de la clé d'API",
"form.submit.loading": "Chargement...", "form.submit.loading": "Chargement...",
"form.submit.saving": "Sauvegarde en cours...", "form.submit.saving": "Sauvegarde en cours...",

View file

@ -385,6 +385,9 @@
"form.integration.shiori_endpoint": "Shiori API Endpoint", "form.integration.shiori_endpoint": "Shiori API Endpoint",
"form.integration.shiori_username": "Shiori Username", "form.integration.shiori_username": "Shiori Username",
"form.integration.shiori_password": "Shiori Password", "form.integration.shiori_password": "Shiori Password",
"form.integration.shaarli_activate": "Save articles to Shaarli",
"form.integration.shaarli_endpoint": "Shaarli URL",
"form.integration.shaarli_api_secret": "Shaarli API Secret",
"form.api_key.label.description": "एपीआई कुंजी लेबल", "form.api_key.label.description": "एपीआई कुंजी लेबल",
"form.submit.loading": "लोड हो रहा है...", "form.submit.loading": "लोड हो रहा है...",
"form.submit.saving": "सहेजा जा रहा है...", "form.submit.saving": "सहेजा जा रहा है...",

View file

@ -382,6 +382,9 @@
"form.integration.shiori_endpoint": "Shiori API Endpoint", "form.integration.shiori_endpoint": "Shiori API Endpoint",
"form.integration.shiori_username": "Shiori Username", "form.integration.shiori_username": "Shiori Username",
"form.integration.shiori_password": "Shiori Password", "form.integration.shiori_password": "Shiori Password",
"form.integration.shaarli_activate": "Save articles to Shaarli",
"form.integration.shaarli_endpoint": "Shaarli URL",
"form.integration.shaarli_api_secret": "Shaarli API Secret",
"form.api_key.label.description": "Label Kunci API", "form.api_key.label.description": "Label Kunci API",
"form.submit.loading": "Memuat...", "form.submit.loading": "Memuat...",
"form.submit.saving": "Menyimpan...", "form.submit.saving": "Menyimpan...",

View file

@ -385,6 +385,9 @@
"form.integration.shiori_endpoint": "Endpoint dell'API di Shiori", "form.integration.shiori_endpoint": "Endpoint dell'API di Shiori",
"form.integration.shiori_username": "Nome utente dell'account Shiori", "form.integration.shiori_username": "Nome utente dell'account Shiori",
"form.integration.shiori_password": "Password dell'account Shiori", "form.integration.shiori_password": "Password dell'account Shiori",
"form.integration.shaarli_activate": "Save articles to Shaarli",
"form.integration.shaarli_endpoint": "Shaarli URL",
"form.integration.shaarli_api_secret": "Shaarli API Secret",
"form.api_key.label.description": "Etichetta chiave API", "form.api_key.label.description": "Etichetta chiave API",
"form.submit.loading": "Caricamento in corso...", "form.submit.loading": "Caricamento in corso...",
"form.submit.saving": "Salvataggio in corso...", "form.submit.saving": "Salvataggio in corso...",

View file

@ -385,6 +385,9 @@
"form.integration.shiori_endpoint": "Shiori の API Endpoint", "form.integration.shiori_endpoint": "Shiori の API Endpoint",
"form.integration.shiori_username": "Shiori の ユーザー名", "form.integration.shiori_username": "Shiori の ユーザー名",
"form.integration.shiori_password": "Shiori の パスワード", "form.integration.shiori_password": "Shiori の パスワード",
"form.integration.shaarli_activate": "Save articles to Shaarli",
"form.integration.shaarli_endpoint": "Shaarli URL",
"form.integration.shaarli_api_secret": "Shaarli API Secret",
"form.api_key.label.description": "API キーラベル", "form.api_key.label.description": "API キーラベル",
"form.submit.loading": "読み込み中…", "form.submit.loading": "読み込み中…",
"form.submit.saving": "保存中…", "form.submit.saving": "保存中…",

View file

@ -385,6 +385,9 @@
"form.integration.shiori_endpoint": "Shiori URL", "form.integration.shiori_endpoint": "Shiori URL",
"form.integration.shiori_username": "Shiori gebruikersnaam", "form.integration.shiori_username": "Shiori gebruikersnaam",
"form.integration.shiori_password": "Shiori wachtwoord", "form.integration.shiori_password": "Shiori wachtwoord",
"form.integration.shaarli_activate": "Save articles to Shaarli",
"form.integration.shaarli_endpoint": "Shaarli URL",
"form.integration.shaarli_api_secret": "Shaarli API Secret",
"form.api_key.label.description": "API-sleutellabel", "form.api_key.label.description": "API-sleutellabel",
"form.submit.loading": "Laden...", "form.submit.loading": "Laden...",
"form.submit.saving": "Opslaag...", "form.submit.saving": "Opslaag...",

View file

@ -387,6 +387,9 @@
"form.integration.shiori_endpoint": "Shiori URL", "form.integration.shiori_endpoint": "Shiori URL",
"form.integration.shiori_username": "Login do Shiori", "form.integration.shiori_username": "Login do Shiori",
"form.integration.shiori_password": "Hasło do Shiori", "form.integration.shiori_password": "Hasło do Shiori",
"form.integration.shaarli_activate": "Save articles to Shaarli",
"form.integration.shaarli_endpoint": "Shaarli URL",
"form.integration.shaarli_api_secret": "Shaarli API Secret",
"form.api_key.label.description": "Etykieta klucza API", "form.api_key.label.description": "Etykieta klucza API",
"form.submit.loading": "Ładowanie...", "form.submit.loading": "Ładowanie...",
"form.submit.saving": "Zapisywanie...", "form.submit.saving": "Zapisywanie...",

View file

@ -385,6 +385,9 @@
"form.integration.shiori_endpoint": "Endpoint da API do Shiori", "form.integration.shiori_endpoint": "Endpoint da API do Shiori",
"form.integration.shiori_username": "Nome de usuário do Shiori", "form.integration.shiori_username": "Nome de usuário do Shiori",
"form.integration.shiori_password": "Senha do Shiori", "form.integration.shiori_password": "Senha do Shiori",
"form.integration.shaarli_activate": "Save articles to Shaarli",
"form.integration.shaarli_endpoint": "Shaarli URL",
"form.integration.shaarli_api_secret": "Shaarli API Secret",
"form.api_key.label.description": "Etiqueta da chave de API", "form.api_key.label.description": "Etiqueta da chave de API",
"form.submit.loading": "Carregando...", "form.submit.loading": "Carregando...",
"form.submit.saving": "Salvando...", "form.submit.saving": "Salvando...",

View file

@ -387,6 +387,9 @@
"form.integration.shiori_endpoint": "Конечная точка Shiori API", "form.integration.shiori_endpoint": "Конечная точка Shiori API",
"form.integration.shiori_username": "Имя пользователя Shiori", "form.integration.shiori_username": "Имя пользователя Shiori",
"form.integration.shiori_password": "Пароль Shiori", "form.integration.shiori_password": "Пароль Shiori",
"form.integration.shaarli_activate": "Save articles to Shaarli",
"form.integration.shaarli_endpoint": "Shaarli URL",
"form.integration.shaarli_api_secret": "Shaarli API Secret",
"form.api_key.label.description": "Описание API-ключа", "form.api_key.label.description": "Описание API-ключа",
"form.submit.loading": "Загрузка…", "form.submit.loading": "Загрузка…",
"form.submit.saving": "Сохранение…", "form.submit.saving": "Сохранение…",

View file

@ -385,6 +385,9 @@
"form.integration.shiori_endpoint": "Shiori API Uç Noktası", "form.integration.shiori_endpoint": "Shiori API Uç Noktası",
"form.integration.shiori_username": "Shiori Kullanıcı Adı", "form.integration.shiori_username": "Shiori Kullanıcı Adı",
"form.integration.shiori_password": "Shiori Parolası", "form.integration.shiori_password": "Shiori Parolası",
"form.integration.shaarli_activate": "Save articles to Shaarli",
"form.integration.shaarli_endpoint": "Shaarli URL",
"form.integration.shaarli_api_secret": "Shaarli API Secret",
"form.api_key.label.description": "API Anahtar Etiketi", "form.api_key.label.description": "API Anahtar Etiketi",
"form.submit.loading": "Yükleniyor...", "form.submit.loading": "Yükleniyor...",
"form.submit.saving": "Kaydediliyor...", "form.submit.saving": "Kaydediliyor...",

View file

@ -105,7 +105,11 @@
"page.feeds.last_check": "Остання перевірка:", "page.feeds.last_check": "Остання перевірка:",
"page.feeds.unread_counter": "Кількість непрочитаних записів", "page.feeds.unread_counter": "Кількість непрочитаних записів",
"page.feeds.read_counter": "Кількість прочитаних записів", "page.feeds.read_counter": "Кількість прочитаних записів",
"page.feeds.error_count": ["%d помилка", "%d помилки", "%d помилок"], "page.feeds.error_count": [
"%d помилка",
"%d помилки",
"%d помилок"
],
"page.history.title": "Історія", "page.history.title": "Історія",
"page.import.title": "Імпорт", "page.import.title": "Імпорт",
"page.search.title": "Результати пошуку", "page.search.title": "Результати пошуку",
@ -384,6 +388,9 @@
"form.integration.shiori_endpoint": "Shiori API Endpoint", "form.integration.shiori_endpoint": "Shiori API Endpoint",
"form.integration.shiori_username": "Shiori Username", "form.integration.shiori_username": "Shiori Username",
"form.integration.shiori_password": "Shiori Password", "form.integration.shiori_password": "Shiori Password",
"form.integration.shaarli_activate": "Save articles to Shaarli",
"form.integration.shaarli_endpoint": "Shaarli URL",
"form.integration.shaarli_api_secret": "Shaarli API Secret",
"form.api_key.label.description": "Назва ключа API", "form.api_key.label.description": "Назва ключа API",
"form.submit.loading": "Завантаження...", "form.submit.loading": "Завантаження...",
"form.submit.saving": "Зберігаю...", "form.submit.saving": "Зберігаю...",
@ -395,13 +402,29 @@
"%d хвилини тому", "%d хвилини тому",
"%d хвилин тому" "%d хвилин тому"
], ],
"time_elapsed.hours": ["%d годину тому", "%d години тому", "%d годин тому"], "time_elapsed.hours": [
"time_elapsed.days": ["%d день тому", "%d дні тому", "%d днів тому"], "%d годину тому",
"time_elapsed.weeks": ["%d тиждень тому", "%d тижня тому", "%d тижнів тому"], "%d години тому",
"%d годин тому"
],
"time_elapsed.days": [
"%d день тому",
"%d дні тому",
"%d днів тому"
],
"time_elapsed.weeks": [
"%d тиждень тому",
"%d тижня тому",
"%d тижнів тому"
],
"time_elapsed.months": [ "time_elapsed.months": [
"%d місяць тому", "%d місяць тому",
"%d місяця тому", "%d місяця тому",
"%d місяців тому" "%d місяців тому"
], ],
"time_elapsed.years": ["%d рік тому", "%d роки тому", "%d років тому"] "time_elapsed.years": [
"%d рік тому",
"%d роки тому",
"%d років тому"
]
} }

View file

@ -383,6 +383,9 @@
"form.integration.shiori_endpoint": "Shiori API Endpoint", "form.integration.shiori_endpoint": "Shiori API Endpoint",
"form.integration.shiori_username": "Shiori 用户名", "form.integration.shiori_username": "Shiori 用户名",
"form.integration.shiori_password": "Shiori 密码", "form.integration.shiori_password": "Shiori 密码",
"form.integration.shaarli_activate": "Save articles to Shaarli",
"form.integration.shaarli_endpoint": "Shaarli URL",
"form.integration.shaarli_api_secret": "Shaarli API Secret",
"form.api_key.label.description": "API密钥标签", "form.api_key.label.description": "API密钥标签",
"form.submit.loading": "载入中…", "form.submit.loading": "载入中…",
"form.submit.saving": "保存中…", "form.submit.saving": "保存中…",

View file

@ -385,6 +385,9 @@
"form.integration.shiori_endpoint": "Shiori API Endpoint", "form.integration.shiori_endpoint": "Shiori API Endpoint",
"form.integration.shiori_username": "Shiori Username", "form.integration.shiori_username": "Shiori Username",
"form.integration.shiori_password": "Shiori Password", "form.integration.shiori_password": "Shiori Password",
"form.integration.shaarli_activate": "Save articles to Shaarli",
"form.integration.shaarli_endpoint": "Shaarli URL",
"form.integration.shaarli_api_secret": "Shaarli API Secret",
"form.api_key.label.description": "API金鑰標籤", "form.api_key.label.description": "API金鑰標籤",
"form.submit.loading": "載入中…", "form.submit.loading": "載入中…",
"form.submit.saving": "儲存中…", "form.submit.saving": "儲存中…",

View file

@ -61,4 +61,7 @@ type Integration struct {
ShioriURL string ShioriURL string
ShioriUsername string ShioriUsername string
ShioriPassword string ShioriPassword string
ShaarliEnabled bool
ShaarliURL string
ShaarliAPISecret string
} }

View file

@ -164,7 +164,10 @@ func (s *Storage) Integration(userID int64) (*model.Integration, error) {
shiori_enabled, shiori_enabled,
shiori_url, shiori_url,
shiori_username, shiori_username,
shiori_password shiori_password,
shaarli_enabled,
shaarli_url,
shaarli_api_secret
FROM FROM
integrations integrations
WHERE WHERE
@ -228,6 +231,9 @@ func (s *Storage) Integration(userID int64) (*model.Integration, error) {
&integration.ShioriURL, &integration.ShioriURL,
&integration.ShioriUsername, &integration.ShioriUsername,
&integration.ShioriPassword, &integration.ShioriPassword,
&integration.ShaarliEnabled,
&integration.ShaarliURL,
&integration.ShaarliAPISecret,
) )
switch { switch {
case err == sql.ErrNoRows: case err == sql.ErrNoRows:
@ -299,9 +305,12 @@ func (s *Storage) UpdateIntegration(integration *model.Integration) error {
shiori_enabled=$52, shiori_enabled=$52,
shiori_url=$53, shiori_url=$53,
shiori_username=$54, shiori_username=$54,
shiori_password=$55 shiori_password=$55,
shaarli_enabled=$56,
shaarli_url=$57,
shaarli_api_secret=$58
WHERE WHERE
user_id=$56 user_id=$59
` `
_, err := s.db.Exec( _, err := s.db.Exec(
query, query,
@ -360,6 +369,9 @@ func (s *Storage) UpdateIntegration(integration *model.Integration) error {
integration.ShioriURL, integration.ShioriURL,
integration.ShioriUsername, integration.ShioriUsername,
integration.ShioriPassword, integration.ShioriPassword,
integration.ShaarliEnabled,
integration.ShaarliURL,
integration.ShaarliAPISecret,
integration.UserID, integration.UserID,
) )
@ -391,7 +403,8 @@ func (s *Storage) HasSaveEntry(userID int64) (result bool) {
pocket_enabled='t' OR pocket_enabled='t' OR
linkding_enabled='t' OR linkding_enabled='t' OR
apprise_enabled='t' OR apprise_enabled='t' OR
shiori_enabled='t' shiori_enabled='t' OR
shaarli_enabled='t'
) )
` `
if err := s.db.QueryRow(query, userID).Scan(&result); err != nil { if err := s.db.QueryRow(query, userID).Scan(&result); err != nil {

View file

@ -325,6 +325,25 @@
</div> </div>
</details> </details>
<details {{ if .form.ShaarliEnabled }}open{{ end }}>
<summary>Shaarli</summary>
<div class="form-section">
<label>
<input type="checkbox" name="shaarli_enabled" value="1" {{ if .form.ShaarliEnabled }}checked{{ end }}> {{ t "form.integration.shaarli_activate" }}
</label>
<label for="form-shaarli-url">{{ t "form.integration.shaarli_endpoint" }}</label>
<input type="url" name="shaarli_url" id="form-shaarli-url" value="{{ .form.ShaarliURL }}" placeholder="https://shaarli.example.org" spellcheck="false">
<label for="form-shaarli-api-secret">{{ t "form.integration.shaarli_api_secret" }}</label>
<input type="password" name="shaarli_api_secret" id="form-shaarli-api-secret" value="{{ .form.ShaarliAPISecret }}" autocomplete="new-password">
<div class="buttons">
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button>
</div>
</div>
</details>
<details {{ if .form.ShioriEnabled }}open{{ end }}> <details {{ if .form.ShioriEnabled }}open{{ end }}>
<summary>Shiori</summary> <summary>Shiori</summary>
<div class="form-section"> <div class="form-section">
@ -339,7 +358,7 @@
<input type="text" name="shiori_username" id="form-shiori-username" value="{{ .form.ShioriUsername }}" spellcheck="false"> <input type="text" name="shiori_username" id="form-shiori-username" value="{{ .form.ShioriUsername }}" spellcheck="false">
<label for="form-shiori-password">{{ t "form.integration.shiori_password" }}</label> <label for="form-shiori-password">{{ t "form.integration.shiori_password" }}</label>
<input type="password" name="shiori_password" id="form-shiori-password" value="{{ .form.ShioriPassword }}" spellcheck="false"> <input type="password" name="shiori_password" id="form-shiori-password" value="{{ .form.ShioriPassword }}" autocomplete="new-password">
<div class="buttons"> <div class="buttons">
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button> <button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button>

View file

@ -66,6 +66,9 @@ type IntegrationForm struct {
ShioriURL string ShioriURL string
ShioriUsername string ShioriUsername string
ShioriPassword string ShioriPassword string
ShaarliEnabled bool
ShaarliURL string
ShaarliAPISecret string
} }
// Merge copy form values to the model. // Merge copy form values to the model.
@ -123,6 +126,9 @@ func (i IntegrationForm) Merge(integration *model.Integration) {
integration.ShioriURL = i.ShioriURL integration.ShioriURL = i.ShioriURL
integration.ShioriUsername = i.ShioriUsername integration.ShioriUsername = i.ShioriUsername
integration.ShioriPassword = i.ShioriPassword integration.ShioriPassword = i.ShioriPassword
integration.ShaarliEnabled = i.ShaarliEnabled
integration.ShaarliURL = i.ShaarliURL
integration.ShaarliAPISecret = i.ShaarliAPISecret
} }
// NewIntegrationForm returns a new IntegrationForm. // NewIntegrationForm returns a new IntegrationForm.
@ -183,5 +189,8 @@ func NewIntegrationForm(r *http.Request) *IntegrationForm {
ShioriURL: r.FormValue("shiori_url"), ShioriURL: r.FormValue("shiori_url"),
ShioriUsername: r.FormValue("shiori_username"), ShioriUsername: r.FormValue("shiori_username"),
ShioriPassword: r.FormValue("shiori_password"), ShioriPassword: r.FormValue("shiori_password"),
ShaarliEnabled: r.FormValue("shaarli_enabled") == "1",
ShaarliURL: r.FormValue("shaarli_url"),
ShaarliAPISecret: r.FormValue("shaarli_api_secret"),
} }
} }

View file

@ -81,6 +81,9 @@ func (h *handler) showIntegrationPage(w http.ResponseWriter, r *http.Request) {
ShioriURL: integration.ShioriURL, ShioriURL: integration.ShioriURL,
ShioriUsername: integration.ShioriUsername, ShioriUsername: integration.ShioriUsername,
ShioriPassword: integration.ShioriPassword, ShioriPassword: integration.ShioriPassword,
ShaarliEnabled: integration.ShaarliEnabled,
ShaarliURL: integration.ShaarliURL,
ShaarliAPISecret: integration.ShaarliAPISecret,
} }
sess := session.New(h.store, request.SessionID(r)) sess := session.New(h.store, request.SessionID(r))