1
0
Fork 0
mirror of https://github.com/miniflux/v2.git synced 2025-09-15 18:57:04 +00:00

feat(integration): add support for Wallabag tags

This commit is contained in:
Kevin Sicong Jiang 2025-09-10 09:47:51 +09:00 committed by GitHub
parent f1143151c5
commit 8129500296
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 471 additions and 105 deletions

View file

@ -1151,4 +1151,11 @@ 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 wallabag_tags text default '';
`
_, err = tx.Exec(sql)
return err
},
} }

View file

@ -108,6 +108,7 @@ func SendEntry(entry *model.Entry, userIntegrations *model.Integration) {
if userIntegrations.WallabagEnabled { if userIntegrations.WallabagEnabled {
slog.Debug("Sending entry to Wallabag", slog.Debug("Sending entry to Wallabag",
slog.Int64("user_id", userIntegrations.UserID), slog.Int64("user_id", userIntegrations.UserID),
slog.String("user_tags", userIntegrations.WallabagTags),
slog.Int64("entry_id", entry.ID), slog.Int64("entry_id", entry.ID),
slog.String("entry_url", entry.URL), slog.String("entry_url", entry.URL),
) )
@ -118,12 +119,14 @@ func SendEntry(entry *model.Entry, userIntegrations *model.Integration) {
userIntegrations.WallabagClientSecret, userIntegrations.WallabagClientSecret,
userIntegrations.WallabagUsername, userIntegrations.WallabagUsername,
userIntegrations.WallabagPassword, userIntegrations.WallabagPassword,
userIntegrations.WallabagTags,
userIntegrations.WallabagOnlyURL, userIntegrations.WallabagOnlyURL,
) )
if err := client.CreateEntry(entry.URL, entry.Title, entry.Content); err != nil { if err := client.CreateEntry(entry.URL, entry.Title, entry.Content); err != nil {
slog.Error("Unable to send entry to Wallabag", slog.Error("Unable to send entry to Wallabag",
slog.Int64("user_id", userIntegrations.UserID), slog.Int64("user_id", userIntegrations.UserID),
slog.String("user_tags", userIntegrations.WallabagTags),
slog.Int64("entry_id", entry.ID), slog.Int64("entry_id", entry.ID),
slog.String("entry_url", entry.URL), slog.String("entry_url", entry.URL),
slog.Any("error", err), slog.Any("error", err),

View file

@ -24,11 +24,12 @@ type Client struct {
clientSecret string clientSecret string
username string username string
password string password string
tags string
onlyURL bool onlyURL bool
} }
func NewClient(baseURL, clientID, clientSecret, username, password string, onlyURL bool) *Client { func NewClient(baseURL, clientID, clientSecret, username, password, tags string, onlyURL bool) *Client {
return &Client{baseURL, clientID, clientSecret, username, password, onlyURL} return &Client{baseURL, clientID, clientSecret, username, password, tags, onlyURL}
} }
func (c *Client) CreateEntry(entryURL, entryTitle, entryContent string) error { func (c *Client) CreateEntry(entryURL, entryTitle, entryContent string) error {
@ -41,10 +42,10 @@ func (c *Client) CreateEntry(entryURL, entryTitle, entryContent string) error {
return err return err
} }
return c.createEntry(accessToken, entryURL, entryTitle, entryContent) return c.createEntry(accessToken, entryURL, entryTitle, entryContent, c.tags)
} }
func (c *Client) createEntry(accessToken, entryURL, entryTitle, entryContent string) error { func (c *Client) createEntry(accessToken, entryURL, entryTitle, entryContent, tags string) error {
apiEndpoint, err := urllib.JoinBaseURLAndPath(c.baseURL, "/api/entries.json") apiEndpoint, err := urllib.JoinBaseURLAndPath(c.baseURL, "/api/entries.json")
if err != nil { if err != nil {
return fmt.Errorf("wallbag: unable to generate entries endpoint: %v", err) return fmt.Errorf("wallbag: unable to generate entries endpoint: %v", err)
@ -58,6 +59,7 @@ func (c *Client) createEntry(accessToken, entryURL, entryTitle, entryContent str
URL: entryURL, URL: entryURL,
Title: entryTitle, Title: entryTitle,
Content: entryContent, Content: entryContent,
Tags: tags,
}) })
if err != nil { if err != nil {
return fmt.Errorf("wallbag: unable to encode request body: %v", err) return fmt.Errorf("wallbag: unable to encode request body: %v", err)
@ -81,7 +83,7 @@ func (c *Client) createEntry(accessToken, entryURL, entryTitle, entryContent str
defer response.Body.Close() defer response.Body.Close()
if response.StatusCode >= 400 { if response.StatusCode >= 400 {
return fmt.Errorf("wallabag: unable to get access token: url=%s status=%d", apiEndpoint, response.StatusCode) return fmt.Errorf("wallabag: unable to get save entry: url=%s status=%d", apiEndpoint, response.StatusCode)
} }
return nil return nil
@ -140,4 +142,5 @@ type createEntryRequest struct {
URL string `json:"url"` URL string `json:"url"`
Title string `json:"title"` Title string `json:"title"`
Content string `json:"content,omitempty"` Content string `json:"content,omitempty"`
Tags string `json:"tags,omitempty"`
} }

View file

@ -0,0 +1,321 @@
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package wallabag
import (
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestCreateEntry(t *testing.T) {
entryURL := "https://example.com"
entryTitle := "title"
entryContent := "content"
tags := "tag1,tag2,tag3"
tests := []struct {
name string
username string
password string
clientID string
clientSecret string
tags string
onlyURL bool
entryURL string
entryTitle string
entryContent string
serverResponse func(w http.ResponseWriter, r *http.Request)
wantErr bool
errContains string
}{
{
name: "successful entry creation with url only",
wantErr: false,
onlyURL: true,
username: "username",
password: "password",
clientID: "clientId",
clientSecret: "clientSecret",
tags: tags,
entryURL: entryURL,
entryTitle: entryTitle,
entryContent: entryContent,
serverResponse: func(w http.ResponseWriter, r *http.Request) {
if strings.Contains(r.URL.Path, "/oauth/v2/token") {
// Return success response
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]any{
"access_token": "test-token",
"expires_in": 3600,
"refresh_token": "token",
"scope": "scope",
"token_type": "token_type",
})
return
}
// Verify authorization header
auth := r.Header.Get("Authorization")
if auth != "Bearer test-token" {
t.Errorf("Expected Authorization header 'Bearer test-token', got %s", auth)
}
// Verify content type
contentType := r.Header.Get("Content-Type")
if contentType != "application/json" {
t.Errorf("Expected Content-Type 'application/json', got %s", contentType)
}
// Parse and verify request
body, _ := io.ReadAll(r.Body)
var req map[string]any
if err := json.Unmarshal(body, &req); err != nil {
t.Errorf("Failed to parse request body: %v", err)
}
if requstEntryURL := req["url"]; requstEntryURL != entryURL {
t.Errorf("Expected entryURL %s, got %s", entryURL, requstEntryURL)
}
if requestEntryTitle := req["title"]; requestEntryTitle != entryTitle {
t.Errorf("Expected entryTitle %s, got %s", entryTitle, requestEntryTitle)
}
if _, ok := req["content"]; ok {
t.Errorf("Expected entryContent to be empty, got value")
}
if requestTags := req["tags"]; requestTags != tags {
t.Errorf("Expected tags %s, got %s", tags, requestTags)
} // Return success response
w.WriteHeader(http.StatusOK)
},
errContains: "",
},
{
name: "successful entry creation with content",
wantErr: false,
onlyURL: false,
username: "username",
password: "password",
clientID: "clientId",
clientSecret: "clientSecret",
tags: tags,
entryURL: entryURL,
entryTitle: entryTitle,
entryContent: entryContent,
serverResponse: func(w http.ResponseWriter, r *http.Request) {
if strings.Contains(r.URL.Path, "/oauth/v2/token") {
// Return success response
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]any{
"access_token": "test-token",
"expires_in": 3600,
"refresh_token": "token",
"scope": "scope",
"token_type": "token_type",
})
return
}
// Verify authorization header
auth := r.Header.Get("Authorization")
if auth != "Bearer test-token" {
t.Errorf("Expected Authorization header 'Bearer test-token', got %s", auth)
}
// Verify content type
contentType := r.Header.Get("Content-Type")
if contentType != "application/json" {
t.Errorf("Expected Content-Type 'application/json', got %s", contentType)
}
// Parse and verify request
body, _ := io.ReadAll(r.Body)
var req map[string]any
if err := json.Unmarshal(body, &req); err != nil {
t.Errorf("Failed to parse request body: %v", err)
}
if requstEntryURL := req["url"]; requstEntryURL != entryURL {
t.Errorf("Expected entryURL %s, got %s", entryURL, requstEntryURL)
}
if requestEntryTitle := req["title"]; requestEntryTitle != entryTitle {
t.Errorf("Expected entryTitle %s, got %s", entryTitle, requestEntryTitle)
}
if requestEntryContent := req["content"]; requestEntryContent != entryContent {
t.Errorf("Expected entryContent %s, got %s", entryContent, requestEntryContent)
}
if requestTags := req["tags"]; requestTags != tags {
t.Errorf("Expected tags %s, got %s", tags, requestTags)
} // Return success response
w.WriteHeader(http.StatusOK)
},
errContains: "",
},
{
name: "failed when unable to decode accessToken response",
wantErr: true,
onlyURL: true,
username: "username",
password: "password",
clientID: "clientId",
clientSecret: "clientSecret",
tags: tags,
entryURL: entryURL,
entryTitle: entryTitle,
entryContent: entryContent,
serverResponse: func(w http.ResponseWriter, r *http.Request) {
if strings.Contains(r.URL.Path, "/oauth/v2/token") {
// Return success response
w.WriteHeader(http.StatusOK)
w.Write([]byte("invalid json"))
return
}
t.Error("Server should not be called when failed to get accessToken")
},
errContains: "unable to decode token response",
},
{
name: "failed when saving entry",
wantErr: true,
onlyURL: true,
username: "username",
password: "password",
clientID: "clientId",
clientSecret: "clientSecret",
tags: tags,
entryURL: entryURL,
entryTitle: entryTitle,
entryContent: entryContent,
serverResponse: func(w http.ResponseWriter, r *http.Request) {
if strings.Contains(r.URL.Path, "/oauth/v2/token") {
// Return success response
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]any{
"access_token": "test-token",
"expires_in": 3600,
"refresh_token": "token",
"scope": "scope",
"token_type": "token_type",
})
return
}
w.WriteHeader(http.StatusUnauthorized)
},
errContains: "unable to get save entry",
},
{
name: "failure due to no accessToken",
wantErr: true,
onlyURL: false,
username: "username",
password: "password",
clientID: "clientId",
clientSecret: "clientSecret",
tags: tags,
entryURL: entryURL,
entryTitle: entryTitle,
entryContent: entryContent,
serverResponse: func(w http.ResponseWriter, r *http.Request) {
if strings.Contains(r.URL.Path, "/oauth/v2/token") {
// Return error response
w.WriteHeader(http.StatusUnauthorized)
return
}
t.Error("Server should not be called when failed to get accessToken")
},
errContains: "unable to get access token",
},
{
name: "failure due to missing client parameters",
wantErr: true,
onlyURL: false,
tags: tags,
entryURL: entryURL,
entryTitle: entryTitle,
entryContent: entryContent,
serverResponse: func(w http.ResponseWriter, r *http.Request) {
t.Error("Server should not be called when failed to get accessToken")
},
errContains: "wallabag: missing base URL, client ID, client secret, username or password",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Create test server if we have a server response function
var serverURL string
if tt.serverResponse != nil {
server := httptest.NewServer(http.HandlerFunc(tt.serverResponse))
defer server.Close()
serverURL = server.URL
}
// Create client with test server URL
client := NewClient(serverURL, tt.clientID, tt.clientSecret, tt.username, tt.password, tt.tags, tt.onlyURL)
// Call CreateBookmark
err := client.CreateEntry(tt.entryURL, tt.entryTitle, tt.entryContent)
// Check error expectations
if tt.wantErr {
if err == nil {
t.Errorf("Expected error but got none")
} else if tt.errContains != "" && !strings.Contains(err.Error(), tt.errContains) {
t.Errorf("Expected error containing '%s', got '%s'", tt.errContains, err.Error())
}
} else {
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
}
})
}
}
func TestNewClient(t *testing.T) {
tests := []struct {
name string
baseURL string
clientID string
clientSecret string
username string
password string
tags string
onlyURL bool
}{
{
name: "with all parameters",
baseURL: "https://wallabag.example.com",
clientID: "clientID",
clientSecret: "clientSecret",
username: "wallabag",
password: "wallabag",
tags: "",
onlyURL: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
client := NewClient(tt.baseURL, tt.clientID, tt.clientSecret, tt.username, tt.password, tt.tags, tt.onlyURL)
if client.baseURL != tt.baseURL {
t.Errorf("Expected.baseURL %s, got %s", tt.baseURL, client.baseURL)
}
if client.username != tt.username {
t.Errorf("Expected username %s, got %s", tt.username, client.username)
}
if client.password != tt.password {
t.Errorf("Expected password %s, got %s", tt.password, client.password)
}
if client.clientID != tt.clientID {
t.Errorf("Expected clientID %s, got %s", tt.clientID, client.clientID)
}
if client.clientSecret != tt.clientSecret {
t.Errorf("Expected clientSecret %s, got %s", tt.clientSecret, client.clientSecret)
}
if client.tags != tt.tags {
t.Errorf("Expected tags %s, got %s", tt.tags, client.tags)
}
if client.onlyURL != tt.onlyURL {
t.Errorf("Expected onlyURL %v, got %v", tt.onlyURL, client.onlyURL)
}
})
}
}

View file

@ -333,6 +333,7 @@
"form.integration.wallabag_only_url": "Nur URL senden (anstelle des vollständigen Inhalts)", "form.integration.wallabag_only_url": "Nur URL senden (anstelle des vollständigen Inhalts)",
"form.integration.wallabag_password": "Wallabag-Passwort", "form.integration.wallabag_password": "Wallabag-Passwort",
"form.integration.wallabag_username": "Wallabag-Benutzername", "form.integration.wallabag_username": "Wallabag-Benutzername",
"form.integration.wallabag_tags": "Wallabag Tags",
"form.integration.webhook_activate": "Webhooks aktivieren", "form.integration.webhook_activate": "Webhooks aktivieren",
"form.integration.webhook_secret": "Webhook-Geheimnis", "form.integration.webhook_secret": "Webhook-Geheimnis",
"form.integration.webhook_url": "Standard-Webhook-URL", "form.integration.webhook_url": "Standard-Webhook-URL",

View file

@ -333,6 +333,7 @@
"form.integration.wallabag_only_url": "Αποστολή μόνο URL (αντί για πλήρες περιεχόμενο)", "form.integration.wallabag_only_url": "Αποστολή μόνο URL (αντί για πλήρες περιεχόμενο)",
"form.integration.wallabag_password": "Wallabag Κωδικός Πρόσβασης", "form.integration.wallabag_password": "Wallabag Κωδικός Πρόσβασης",
"form.integration.wallabag_username": "Όνομα Χρήστη Wallabag", "form.integration.wallabag_username": "Όνομα Χρήστη Wallabag",
"form.integration.wallabag_tags": "Wallabag Tags",
"form.integration.webhook_activate": "Ενεργοποίηση Webhooks", "form.integration.webhook_activate": "Ενεργοποίηση Webhooks",
"form.integration.webhook_secret": "Μυστικό Webhooks", "form.integration.webhook_secret": "Μυστικό Webhooks",
"form.integration.webhook_url": "Προεπιλεγμένη διεύθυνση URL Webhook", "form.integration.webhook_url": "Προεπιλεγμένη διεύθυνση URL Webhook",

View file

@ -333,6 +333,7 @@
"form.integration.wallabag_only_url": "Send only URL (instead of full content)", "form.integration.wallabag_only_url": "Send only URL (instead of full content)",
"form.integration.wallabag_password": "Wallabag Password", "form.integration.wallabag_password": "Wallabag Password",
"form.integration.wallabag_username": "Wallabag Username", "form.integration.wallabag_username": "Wallabag Username",
"form.integration.wallabag_tags": "Wallabag Tags",
"form.integration.webhook_activate": "Enable Webhooks", "form.integration.webhook_activate": "Enable Webhooks",
"form.integration.webhook_secret": "Webhooks Secret", "form.integration.webhook_secret": "Webhooks Secret",
"form.integration.webhook_url": "Default Webhook URL", "form.integration.webhook_url": "Default Webhook URL",

View file

@ -333,6 +333,7 @@
"form.integration.wallabag_only_url": "Enviar solo URL (en lugar de contenido completo)", "form.integration.wallabag_only_url": "Enviar solo URL (en lugar de contenido completo)",
"form.integration.wallabag_password": "Contraseña de Wallabag", "form.integration.wallabag_password": "Contraseña de Wallabag",
"form.integration.wallabag_username": "Nombre de usuario de Wallabag", "form.integration.wallabag_username": "Nombre de usuario de Wallabag",
"form.integration.wallabag_tags": "Wallabag Tags",
"form.integration.webhook_activate": "Habilitar Webhooks", "form.integration.webhook_activate": "Habilitar Webhooks",
"form.integration.webhook_secret": "Secreto de Webhooks", "form.integration.webhook_secret": "Secreto de Webhooks",
"form.integration.webhook_url": "Defecto URL de Webhook", "form.integration.webhook_url": "Defecto URL de Webhook",

View file

@ -333,6 +333,7 @@
"form.integration.wallabag_only_url": "Lähetä vain URL-osoite (koko sisällön sijaan)", "form.integration.wallabag_only_url": "Lähetä vain URL-osoite (koko sisällön sijaan)",
"form.integration.wallabag_password": "Wallabag-salasana", "form.integration.wallabag_password": "Wallabag-salasana",
"form.integration.wallabag_username": "Wallabag-käyttäjätunnus", "form.integration.wallabag_username": "Wallabag-käyttäjätunnus",
"form.integration.wallabag_tags": "Wallabag Tags",
"form.integration.webhook_activate": "Enable Webhooks", "form.integration.webhook_activate": "Enable Webhooks",
"form.integration.webhook_secret": "Webhooks Secret", "form.integration.webhook_secret": "Webhooks Secret",
"form.integration.webhook_url": "Default Webhook URL", "form.integration.webhook_url": "Default Webhook URL",

View file

@ -333,6 +333,7 @@
"form.integration.wallabag_only_url": "Envoyer uniquement l'URL (au lieu du contenu complet)", "form.integration.wallabag_only_url": "Envoyer uniquement l'URL (au lieu du contenu complet)",
"form.integration.wallabag_password": "Mot de passe de Wallabag", "form.integration.wallabag_password": "Mot de passe de Wallabag",
"form.integration.wallabag_username": "Nom d'utilisateur de Wallabag", "form.integration.wallabag_username": "Nom d'utilisateur de Wallabag",
"form.integration.wallabag_tags": "Wallabag Tags",
"form.integration.webhook_activate": "Activer le webhook", "form.integration.webhook_activate": "Activer le webhook",
"form.integration.webhook_secret": "Secret du webhook", "form.integration.webhook_secret": "Secret du webhook",
"form.integration.webhook_url": "URL du webhook", "form.integration.webhook_url": "URL du webhook",

View file

@ -333,6 +333,7 @@
"form.integration.wallabag_only_url": "केवल URL भेजें (पूर्ण सामग्री के बजाय)", "form.integration.wallabag_only_url": "केवल URL भेजें (पूर्ण सामग्री के बजाय)",
"form.integration.wallabag_password": "वालाबैग पासवर्ड", "form.integration.wallabag_password": "वालाबैग पासवर्ड",
"form.integration.wallabag_username": "वालाबैग उपयोगकर्ता नाम", "form.integration.wallabag_username": "वालाबैग उपयोगकर्ता नाम",
"form.integration.wallabag_tags": "Wallabag Tags",
"form.integration.webhook_activate": "Enable Webhooks", "form.integration.webhook_activate": "Enable Webhooks",
"form.integration.webhook_secret": "Webhooks Secret", "form.integration.webhook_secret": "Webhooks Secret",
"form.integration.webhook_url": "Default Webhook URL", "form.integration.webhook_url": "Default Webhook URL",

View file

@ -330,6 +330,7 @@
"form.integration.wallabag_only_url": "Kirim hanya URL (alih-alih konten penuh)", "form.integration.wallabag_only_url": "Kirim hanya URL (alih-alih konten penuh)",
"form.integration.wallabag_password": "Kata Sandi Wallabag", "form.integration.wallabag_password": "Kata Sandi Wallabag",
"form.integration.wallabag_username": "Nama Pengguna Wallabag", "form.integration.wallabag_username": "Nama Pengguna Wallabag",
"form.integration.wallabag_tags": "Wallabag Tags",
"form.integration.webhook_activate": "Aktifkan Webhook", "form.integration.webhook_activate": "Aktifkan Webhook",
"form.integration.webhook_secret": "Rahasia Webhook", "form.integration.webhook_secret": "Rahasia Webhook",
"form.integration.webhook_url": "URL Webhook baku", "form.integration.webhook_url": "URL Webhook baku",

View file

@ -333,6 +333,7 @@
"form.integration.wallabag_only_url": "Invia solo URL (invece del contenuto completo)", "form.integration.wallabag_only_url": "Invia solo URL (invece del contenuto completo)",
"form.integration.wallabag_password": "Password dell'account Wallabag", "form.integration.wallabag_password": "Password dell'account Wallabag",
"form.integration.wallabag_username": "Nome utente dell'account Wallabag", "form.integration.wallabag_username": "Nome utente dell'account Wallabag",
"form.integration.wallabag_tags": "Wallabag Tags",
"form.integration.webhook_activate": "Enable Webhooks", "form.integration.webhook_activate": "Enable Webhooks",
"form.integration.webhook_secret": "Webhooks Secret", "form.integration.webhook_secret": "Webhooks Secret",
"form.integration.webhook_url": "Default Webhook URL", "form.integration.webhook_url": "Default Webhook URL",

View file

@ -330,6 +330,7 @@
"form.integration.wallabag_only_url": "URL のみを送信 (完全なコンテンツではなく)", "form.integration.wallabag_only_url": "URL のみを送信 (完全なコンテンツではなく)",
"form.integration.wallabag_password": "Wallabag のパスワード", "form.integration.wallabag_password": "Wallabag のパスワード",
"form.integration.wallabag_username": "Wallabag のユーザー名", "form.integration.wallabag_username": "Wallabag のユーザー名",
"form.integration.wallabag_tags": "Wallabag Tags",
"form.integration.webhook_activate": "Enable Webhooks", "form.integration.webhook_activate": "Enable Webhooks",
"form.integration.webhook_secret": "Webhooks Secret", "form.integration.webhook_secret": "Webhooks Secret",
"form.integration.webhook_url": "Default Webhook URL", "form.integration.webhook_url": "Default Webhook URL",

View file

@ -330,6 +330,7 @@
"form.integration.wallabag_only_url": "Kan-na thoân bāng-chí (m̄ sī oân-chéng ê lōe-iông)", "form.integration.wallabag_only_url": "Kan-na thoân bāng-chí (m̄ sī oân-chéng ê lōe-iông)",
"form.integration.wallabag_password": "Wallabag bi̍t-bé", "form.integration.wallabag_password": "Wallabag bi̍t-bé",
"form.integration.wallabag_username": "Wallabag kháu-chō miâ", "form.integration.wallabag_username": "Wallabag kháu-chō miâ",
"form.integration.wallabag_tags": "Wallabag Tags",
"form.integration.webhook_activate": "Khai-sí iōng Webhooks", "form.integration.webhook_activate": "Khai-sí iōng Webhooks",
"form.integration.webhook_secret": "Webhooks Secret", "form.integration.webhook_secret": "Webhooks Secret",
"form.integration.webhook_url": "Default Webhook bāng-chí", "form.integration.webhook_url": "Default Webhook bāng-chí",

View file

@ -333,6 +333,7 @@
"form.integration.wallabag_only_url": "Alleen URL verzenden (in plaats van volledige inhoud)", "form.integration.wallabag_only_url": "Alleen URL verzenden (in plaats van volledige inhoud)",
"form.integration.wallabag_password": "Wallabag wachtwoord", "form.integration.wallabag_password": "Wallabag wachtwoord",
"form.integration.wallabag_username": "Wallabag gebruikersnaam", "form.integration.wallabag_username": "Wallabag gebruikersnaam",
"form.integration.wallabag_tags": "Wallabag Tags",
"form.integration.webhook_activate": "Webhooks activeren", "form.integration.webhook_activate": "Webhooks activeren",
"form.integration.webhook_secret": "Webhooks Secret", "form.integration.webhook_secret": "Webhooks Secret",
"form.integration.webhook_url": "Standard Webhook URL", "form.integration.webhook_url": "Standard Webhook URL",

View file

@ -333,6 +333,7 @@
"form.integration.wallabag_client_id": "Identyfikator klienta Wallabag", "form.integration.wallabag_client_id": "Identyfikator klienta Wallabag",
"form.integration.wallabag_client_secret": "Tajny klucz klienta Wallabag", "form.integration.wallabag_client_secret": "Tajny klucz klienta Wallabag",
"form.integration.wallabag_endpoint": "Podstawowy adres URL Wallabag", "form.integration.wallabag_endpoint": "Podstawowy adres URL Wallabag",
"form.integration.wallabag_tags": "Wallabag Tags",
"form.integration.wallabag_only_url": "Przesyłaj tylko adres URL (zamiast pełnej treści)", "form.integration.wallabag_only_url": "Przesyłaj tylko adres URL (zamiast pełnej treści)",
"form.integration.wallabag_password": "Hasło do Wallabag", "form.integration.wallabag_password": "Hasło do Wallabag",
"form.integration.wallabag_username": "Login do Wallabag", "form.integration.wallabag_username": "Login do Wallabag",

View file

@ -333,6 +333,7 @@
"form.integration.wallabag_only_url": "Enviar apenas URL (em vez de conteúdo completo)", "form.integration.wallabag_only_url": "Enviar apenas URL (em vez de conteúdo completo)",
"form.integration.wallabag_password": "Senha do Wallabag", "form.integration.wallabag_password": "Senha do Wallabag",
"form.integration.wallabag_username": "Nome de usuário do Wallabag", "form.integration.wallabag_username": "Nome de usuário do Wallabag",
"form.integration.wallabag_tags": "Wallabag Tags",
"form.integration.webhook_activate": "Enable Webhooks", "form.integration.webhook_activate": "Enable Webhooks",
"form.integration.webhook_secret": "Webhooks Secret", "form.integration.webhook_secret": "Webhooks Secret",
"form.integration.webhook_url": "Default Webhook URL", "form.integration.webhook_url": "Default Webhook URL",

View file

@ -333,6 +333,7 @@
"form.integration.wallabag_client_id": "ID Client Wallabag", "form.integration.wallabag_client_id": "ID Client Wallabag",
"form.integration.wallabag_client_secret": "Secret Client Wallabag", "form.integration.wallabag_client_secret": "Secret Client Wallabag",
"form.integration.wallabag_endpoint": "URL Wallabag", "form.integration.wallabag_endpoint": "URL Wallabag",
"form.integration.wallabag_tags": "Wallabag Tags",
"form.integration.wallabag_only_url": "Trimite numai URL-ul (fără conținut complet)", "form.integration.wallabag_only_url": "Trimite numai URL-ul (fără conținut complet)",
"form.integration.wallabag_password": "Parolă Wallabag", "form.integration.wallabag_password": "Parolă Wallabag",
"form.integration.wallabag_username": "Utilizator Wallabag", "form.integration.wallabag_username": "Utilizator Wallabag",

View file

@ -333,6 +333,7 @@
"form.integration.wallabag_client_id": "Номер клиента Wallabag", "form.integration.wallabag_client_id": "Номер клиента Wallabag",
"form.integration.wallabag_client_secret": "Секретный код клиента Wallabag", "form.integration.wallabag_client_secret": "Секретный код клиента Wallabag",
"form.integration.wallabag_endpoint": "URL-адрес базы Валлабаг", "form.integration.wallabag_endpoint": "URL-адрес базы Валлабаг",
"form.integration.wallabag_tags": "Wallabag Tags",
"form.integration.wallabag_only_url": "Отправлять только ссылку (без содержимого)", "form.integration.wallabag_only_url": "Отправлять только ссылку (без содержимого)",
"form.integration.wallabag_password": "Пароль Wallabag", "form.integration.wallabag_password": "Пароль Wallabag",
"form.integration.wallabag_username": "Имя пользователя Wallabag", "form.integration.wallabag_username": "Имя пользователя Wallabag",

View file

@ -333,6 +333,7 @@
"form.integration.wallabag_only_url": "Yalnızca URL gönder (tam makale yerine)", "form.integration.wallabag_only_url": "Yalnızca URL gönder (tam makale yerine)",
"form.integration.wallabag_password": "Wallabag Parolası", "form.integration.wallabag_password": "Wallabag Parolası",
"form.integration.wallabag_username": "Wallabag Kullanıcı Adı", "form.integration.wallabag_username": "Wallabag Kullanıcı Adı",
"form.integration.wallabag_tags": "Wallabag Tags",
"form.integration.webhook_activate": "Webhook'u etkinleştir", "form.integration.webhook_activate": "Webhook'u etkinleştir",
"form.integration.webhook_secret": "Webhooks Secret", "form.integration.webhook_secret": "Webhooks Secret",
"form.integration.webhook_url": "Default Webhook URL", "form.integration.webhook_url": "Default Webhook URL",

View file

@ -333,6 +333,7 @@
"form.integration.wallabag_client_id": "Wallabag Client ID", "form.integration.wallabag_client_id": "Wallabag Client ID",
"form.integration.wallabag_client_secret": "Wallabag Client Secret", "form.integration.wallabag_client_secret": "Wallabag Client Secret",
"form.integration.wallabag_endpoint": "Базова URL-адреса Wallabag", "form.integration.wallabag_endpoint": "Базова URL-адреса Wallabag",
"form.integration.wallabag_tags": "Wallabag Tags",
"form.integration.wallabag_only_url": "Надіслати лише URL (замість повного вмісту)", "form.integration.wallabag_only_url": "Надіслати лише URL (замість повного вмісту)",
"form.integration.wallabag_password": "Пароль Wallabag", "form.integration.wallabag_password": "Пароль Wallabag",
"form.integration.wallabag_username": "Ім’я користувача Wallabag", "form.integration.wallabag_username": "Ім’я користувача Wallabag",

View file

@ -330,6 +330,7 @@
"form.integration.wallabag_only_url": "仅发送 URL而非完整内容", "form.integration.wallabag_only_url": "仅发送 URL而非完整内容",
"form.integration.wallabag_password": "Wallabag 密码", "form.integration.wallabag_password": "Wallabag 密码",
"form.integration.wallabag_username": "Wallabag 用户名", "form.integration.wallabag_username": "Wallabag 用户名",
"form.integration.wallabag_tags": "Wallabag Tags",
"form.integration.webhook_activate": "启用 Webhooks", "form.integration.webhook_activate": "启用 Webhooks",
"form.integration.webhook_secret": "Webhooks 密钥", "form.integration.webhook_secret": "Webhooks 密钥",
"form.integration.webhook_url": "默认 Webhook URL", "form.integration.webhook_url": "默认 Webhook URL",

View file

@ -330,6 +330,7 @@
"form.integration.wallabag_only_url": "僅傳送網址(而不是完整內容)", "form.integration.wallabag_only_url": "僅傳送網址(而不是完整內容)",
"form.integration.wallabag_password": "Wallabag 密碼", "form.integration.wallabag_password": "Wallabag 密碼",
"form.integration.wallabag_username": "Wallabag 使用者名稱", "form.integration.wallabag_username": "Wallabag 使用者名稱",
"form.integration.wallabag_tags": "Wallabag Tags",
"form.integration.webhook_activate": "啟用 Webhooks", "form.integration.webhook_activate": "啟用 Webhooks",
"form.integration.webhook_secret": "Webhooks Secret", "form.integration.webhook_secret": "Webhooks Secret",
"form.integration.webhook_url": "Default Webhook 網址", "form.integration.webhook_url": "Default Webhook 網址",

View file

@ -29,6 +29,7 @@ type Integration struct {
WallabagClientSecret string WallabagClientSecret string
WallabagUsername string WallabagUsername string
WallabagPassword string WallabagPassword string
WallabagTags string
NunuxKeeperEnabled bool NunuxKeeperEnabled bool
NunuxKeeperURL string NunuxKeeperURL string
NunuxKeeperAPIKey string NunuxKeeperAPIKey string

View file

@ -130,6 +130,7 @@ func (s *Storage) Integration(userID int64) (*model.Integration, error) {
wallabag_client_secret, wallabag_client_secret,
wallabag_username, wallabag_username,
wallabag_password, wallabag_password,
wallabag_tags,
notion_enabled, notion_enabled,
notion_token, notion_token,
notion_page_id, notion_page_id,
@ -254,6 +255,7 @@ func (s *Storage) Integration(userID int64) (*model.Integration, error) {
&integration.WallabagClientSecret, &integration.WallabagClientSecret,
&integration.WallabagUsername, &integration.WallabagUsername,
&integration.WallabagPassword, &integration.WallabagPassword,
&integration.WallabagTags,
&integration.NotionEnabled, &integration.NotionEnabled,
&integration.NotionToken, &integration.NotionToken,
&integration.NotionPageID, &integration.NotionPageID,
@ -384,107 +386,108 @@ func (s *Storage) UpdateIntegration(integration *model.Integration) error {
wallabag_client_secret=$15, wallabag_client_secret=$15,
wallabag_username=$16, wallabag_username=$16,
wallabag_password=$17, wallabag_password=$17,
nunux_keeper_enabled=$18, wallabag_tags=$18,
nunux_keeper_url=$19, nunux_keeper_enabled=$19,
nunux_keeper_api_key=$20, nunux_keeper_url=$20,
googlereader_enabled=$21, nunux_keeper_api_key=$21,
googlereader_username=$22, googlereader_enabled=$22,
googlereader_password=$23, googlereader_username=$23,
telegram_bot_enabled=$24, googlereader_password=$24,
telegram_bot_token=$25, telegram_bot_enabled=$25,
telegram_bot_chat_id=$26, telegram_bot_token=$26,
telegram_bot_topic_id=$27, telegram_bot_chat_id=$27,
telegram_bot_disable_web_page_preview=$28, telegram_bot_topic_id=$28,
telegram_bot_disable_notification=$29, telegram_bot_disable_web_page_preview=$29,
telegram_bot_disable_buttons=$30, telegram_bot_disable_notification=$30,
espial_enabled=$31, telegram_bot_disable_buttons=$31,
espial_url=$32, espial_enabled=$32,
espial_api_key=$33, espial_url=$33,
espial_tags=$34, espial_api_key=$34,
linkace_enabled=$35, espial_tags=$35,
linkace_url=$36, linkace_enabled=$36,
linkace_api_key=$37, linkace_url=$37,
linkace_tags=$38, linkace_api_key=$38,
linkace_is_private=$39, linkace_tags=$39,
linkace_check_disabled=$40, linkace_is_private=$40,
linkding_enabled=$41, linkace_check_disabled=$41,
linkding_url=$42, linkding_enabled=$42,
linkding_api_key=$43, linkding_url=$43,
linkding_tags=$44, linkding_api_key=$44,
linkding_mark_as_unread=$45, linkding_tags=$45,
matrix_bot_enabled=$46, linkding_mark_as_unread=$46,
matrix_bot_user=$47, matrix_bot_enabled=$47,
matrix_bot_password=$48, matrix_bot_user=$48,
matrix_bot_url=$49, matrix_bot_password=$49,
matrix_bot_chat_id=$50, matrix_bot_url=$50,
notion_enabled=$51, matrix_bot_chat_id=$51,
notion_token=$52, notion_enabled=$52,
notion_page_id=$53, notion_token=$53,
readwise_enabled=$54, notion_page_id=$54,
readwise_api_key=$55, readwise_enabled=$55,
apprise_enabled=$56, readwise_api_key=$56,
apprise_url=$57, apprise_enabled=$57,
apprise_services_url=$58, apprise_url=$58,
readeck_enabled=$59, apprise_services_url=$59,
readeck_url=$60, readeck_enabled=$60,
readeck_api_key=$61, readeck_url=$61,
readeck_labels=$62, readeck_api_key=$62,
readeck_only_url=$63, readeck_labels=$63,
shiori_enabled=$64, readeck_only_url=$64,
shiori_url=$65, shiori_enabled=$65,
shiori_username=$66, shiori_url=$66,
shiori_password=$67, shiori_username=$67,
shaarli_enabled=$68, shiori_password=$68,
shaarli_url=$69, shaarli_enabled=$69,
shaarli_api_secret=$70, shaarli_url=$70,
webhook_enabled=$71, shaarli_api_secret=$71,
webhook_url=$72, webhook_enabled=$72,
webhook_secret=$73, webhook_url=$73,
rssbridge_enabled=$74, webhook_secret=$74,
rssbridge_url=$75, rssbridge_enabled=$75,
omnivore_enabled=$76, rssbridge_url=$76,
omnivore_api_key=$77, omnivore_enabled=$77,
omnivore_url=$78, omnivore_api_key=$78,
linkwarden_enabled=$79, omnivore_url=$79,
linkwarden_url=$80, linkwarden_enabled=$80,
linkwarden_api_key=$81, linkwarden_url=$81,
raindrop_enabled=$82, linkwarden_api_key=$82,
raindrop_token=$83, raindrop_enabled=$83,
raindrop_collection_id=$84, raindrop_token=$84,
raindrop_tags=$85, raindrop_collection_id=$85,
betula_enabled=$86, raindrop_tags=$86,
betula_url=$87, betula_enabled=$87,
betula_token=$88, betula_url=$88,
ntfy_enabled=$89, betula_token=$89,
ntfy_topic=$90, ntfy_enabled=$90,
ntfy_url=$91, ntfy_topic=$91,
ntfy_api_token=$92, ntfy_url=$92,
ntfy_username=$93, ntfy_api_token=$93,
ntfy_password=$94, ntfy_username=$94,
ntfy_icon_url=$95, ntfy_password=$95,
ntfy_internal_links=$96, ntfy_icon_url=$96,
cubox_enabled=$97, ntfy_internal_links=$97,
cubox_api_link=$98, cubox_enabled=$98,
discord_enabled=$99, cubox_api_link=$99,
discord_webhook_link=$100, discord_enabled=$100,
slack_enabled=$101, discord_webhook_link=$101,
slack_webhook_link=$102, slack_enabled=$102,
pushover_enabled=$103, slack_webhook_link=$103,
pushover_user=$104, pushover_enabled=$104,
pushover_token=$105, pushover_user=$105,
pushover_device=$106, pushover_token=$106,
pushover_prefix=$107, pushover_device=$107,
rssbridge_token=$108, pushover_prefix=$108,
karakeep_enabled=$109, rssbridge_token=$109,
karakeep_api_key=$110, karakeep_enabled=$110,
karakeep_url=$111, karakeep_api_key=$111,
linktaco_enabled=$112, karakeep_url=$112,
linktaco_api_token=$113, linktaco_enabled=$113,
linktaco_org_slug=$114, linktaco_api_token=$114,
linktaco_tags=$115, linktaco_org_slug=$115,
linktaco_visibility=$116 linktaco_tags=$116,
linktaco_visibility=$117
WHERE WHERE
user_id=$117 user_id=$118
` `
_, err := s.db.Exec( _, err := s.db.Exec(
query, query,
@ -505,6 +508,7 @@ func (s *Storage) UpdateIntegration(integration *model.Integration) error {
integration.WallabagClientSecret, integration.WallabagClientSecret,
integration.WallabagUsername, integration.WallabagUsername,
integration.WallabagPassword, integration.WallabagPassword,
integration.WallabagTags,
integration.NunuxKeeperEnabled, integration.NunuxKeeperEnabled,
integration.NunuxKeeperURL, integration.NunuxKeeperURL,
integration.NunuxKeeperAPIKey, integration.NunuxKeeperAPIKey,

View file

@ -664,6 +664,9 @@
<label for="form-wallabag-password">{{ t "form.integration.wallabag_password" }}</label> <label for="form-wallabag-password">{{ t "form.integration.wallabag_password" }}</label>
<input type="password" name="wallabag_password" id="form-wallabag-password" value="{{ .form.WallabagPassword }}" autocomplete="new-password"> <input type="password" name="wallabag_password" id="form-wallabag-password" value="{{ .form.WallabagPassword }}" autocomplete="new-password">
<label for="form-wallabag-tags">{{ t "form.integration.wallabag_tags" }}</label>
<input type="text" name="wallabag_tags" id="form-wallabag-tags" value="{{ .form.WallabagTags }}" spellcheck="false">
<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>
</div> </div>

View file

@ -32,6 +32,7 @@ type IntegrationForm struct {
WallabagClientSecret string WallabagClientSecret string
WallabagUsername string WallabagUsername string
WallabagPassword string WallabagPassword string
WallabagTags string
NotionEnabled bool NotionEnabled bool
NotionPageID string NotionPageID string
NotionToken string NotionToken string
@ -150,6 +151,7 @@ func (i IntegrationForm) Merge(integration *model.Integration) {
integration.WallabagClientSecret = i.WallabagClientSecret integration.WallabagClientSecret = i.WallabagClientSecret
integration.WallabagUsername = i.WallabagUsername integration.WallabagUsername = i.WallabagUsername
integration.WallabagPassword = i.WallabagPassword integration.WallabagPassword = i.WallabagPassword
integration.WallabagTags = i.WallabagTags
integration.NotionEnabled = i.NotionEnabled integration.NotionEnabled = i.NotionEnabled
integration.NotionPageID = i.NotionPageID integration.NotionPageID = i.NotionPageID
integration.NotionToken = i.NotionToken integration.NotionToken = i.NotionToken
@ -270,6 +272,7 @@ func NewIntegrationForm(r *http.Request) *IntegrationForm {
WallabagClientSecret: r.FormValue("wallabag_client_secret"), WallabagClientSecret: r.FormValue("wallabag_client_secret"),
WallabagUsername: r.FormValue("wallabag_username"), WallabagUsername: r.FormValue("wallabag_username"),
WallabagPassword: r.FormValue("wallabag_password"), WallabagPassword: r.FormValue("wallabag_password"),
WallabagTags: r.FormValue("wallabag_tags"),
NotionEnabled: r.FormValue("notion_enabled") == "1", NotionEnabled: r.FormValue("notion_enabled") == "1",
NotionPageID: r.FormValue("notion_page_id"), NotionPageID: r.FormValue("notion_page_id"),
NotionToken: r.FormValue("notion_token"), NotionToken: r.FormValue("notion_token"),

View file

@ -45,6 +45,7 @@ func (h *handler) showIntegrationPage(w http.ResponseWriter, r *http.Request) {
WallabagClientSecret: integration.WallabagClientSecret, WallabagClientSecret: integration.WallabagClientSecret,
WallabagUsername: integration.WallabagUsername, WallabagUsername: integration.WallabagUsername,
WallabagPassword: integration.WallabagPassword, WallabagPassword: integration.WallabagPassword,
WallabagTags: integration.WallabagTags,
NotionEnabled: integration.NotionEnabled, NotionEnabled: integration.NotionEnabled,
NotionPageID: integration.NotionPageID, NotionPageID: integration.NotionPageID,
NotionToken: integration.NotionToken, NotionToken: integration.NotionToken,