diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 7eb03e2c..c665d247 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -5,8 +5,36 @@ on: push: tags: - '*.*.*' + pull_request: + branches: [ main ] jobs: - docker-images: + test-docker-images: + if: github.event.pull_request + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Build Alpine image + uses: docker/build-push-action@v4 + with: + context: . + file: ./packaging/docker/alpine/Dockerfile + push: false + tags: ${{ github.repository_owner }}/miniflux:alpine-dev + - name: Test Alpine Docker image + run: docker run --rm ${{ github.repository_owner }}/miniflux:alpine-dev miniflux -i + - name: Build Distroless image + uses: docker/build-push-action@v4 + with: + context: . + file: ./packaging/docker/distroless/Dockerfile + push: false + tags: ${{ github.repository_owner }}/miniflux:distroless-dev + - name: Test Distroless Docker image + run: docker run --rm ${{ github.repository_owner }}/miniflux:distroless-dev miniflux -i + + publish-docker-images: + if: ${{ ! github.event.pull_request }} permissions: packages: write runs-on: ubuntu-latest diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index 1eeb4283..a8ac2c5e 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -28,4 +28,4 @@ jobs: go-version: "1.20" - uses: golangci/golangci-lint-action@v3 with: - args: --skip-dirs tests --disable errcheck --enable sqlclosecheck --enable misspell --enable gofmt --enable goimports --enable whitespace + args: --timeout 10m --skip-dirs tests --disable errcheck --enable sqlclosecheck --enable misspell --enable gofmt --enable goimports --enable whitespace diff --git a/.gitignore b/.gitignore index 5e30ffea..f53dc253 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ miniflux-* miniflux *.rpm *.deb -.idea \ No newline at end of file +.idea +.vscode \ No newline at end of file diff --git a/ChangeLog b/ChangeLog index 98e81908..d53d4c13 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,21 +1,61 @@ +Version 2.0.45 (June 21, 2023) +------------------------------ + +* Add media player to listen to audio and video podcasts with the possiblity to resume to last playback position +* Add default tag names for Linkding integration +* Mark only globally visible entries when marking all entries from UI +* Use image included in feed as feed icon when available +* Order history by `changed_at` and `published_at` +* Remove title attribute from entry title links +* Fix reading time that is not aligned correctly with the latest version of Safari +* Use glyphs of the same size on keyboard shortcuts page +* Add maskable versions of the PWA icon +* Replace copyright header with SPDX identifier +* Remove the "í" letter from the Portuguese "lido" word +* Increase golangci-lint timeout value +* Bump `github.com/tdewolff/minify/v2`, `github.com/prometheus/client_golang`, `golang.org/x/*` dependencies + +Version 2.0.44 (May 6, 2023) +---------------------------- + +* Add link to the URL rewrite rules documentation +* Update scraping rules for `ilpost.it` +* Update rewrite rules for `theverge.com` +* Add a rewrite rule to remove clickbait titles +* Make sure `PROXY_IMAGES` option is backward compatible with `PROXY_OPTION` and `PROXY_MEDIA_TYPES` +* Add new rule to remove tables +* Add support for searching well-known URLs in subdirectory +* Add CSS `word-wrap` rule to break very long entry title into multiple lines +* Add swipe as option for gesture navigation between entries. There are now 3 possible choices: `none`, `double-tap`, and `swipe`. +* Prefer typographic punctuation in English translation +* Process older entries first: + - Feed entries are usually ordered from most to least recent. + - Processing older entries first ensures that their creation timestamp + is lower than that of newer entries. + - This is useful when we order by creation, because then we get a + consistent timeline. +* Fix Grafana dashboard +* Push Docker images to `Quay.io` (RedHat) +* Bump `golang.org/x/*`, `github.com/lib/pq`, `mvdan.cc/xurls/v2` and `github.com/prometheus/client_golang` dependencies + Version 2.0.43 (March 16, 2023) ------------------------------- * Avoid XSS when opening a broken image due to unescaped ServerError in proxy handler (CVE-2023-27592) - + Creating an RSS feed item with the inline description containing an `` tag with a `srcset` attribute pointing to an invalid URL like `http:a`, we can coerce the proxy handler into an error condition where the invalid URL is returned unescaped and in full. - + This results in JavaScript execution on the Miniflux instance as soon as the user is convinced to open the broken image. * Use `r.RemoteAddr` to check `/metrics` endpoint network access (CVE-2023-27591) - + HTTP headers like `X-Forwarded-For` or `X-Real-Ip` can be easily spoofed. As such, it cannot be used to test if the client IP is allowed. - + The recommendation is to use HTTP Basic authentication to protect the metrics endpoint, or run Miniflux behind a trusted reverse-proxy. diff --git a/api/api.go b/api/api.go index ff2126d2..03e6c1fb 100644 --- a/api/api.go +++ b/api/api.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package api // import "miniflux.app/api" diff --git a/api/category.go b/api/category.go index 64cc036b..f35b0295 100644 --- a/api/category.go +++ b/api/category.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package api // import "miniflux.app/api" @@ -98,12 +97,20 @@ func (h *handler) markCategoryAsRead(w http.ResponseWriter, r *http.Request) { } func (h *handler) getCategories(w http.ResponseWriter, r *http.Request) { - categories, err := h.store.Categories(request.UserID(r)) + var categories model.Categories + var err error + includeCounts := request.QueryStringParam(r, "counts", "false") + + if includeCounts == "true" { + categories, err = h.store.CategoriesWithFeedCount(request.UserID(r)) + } else { + categories, err = h.store.Categories(request.UserID(r)) + } + if err != nil { json.ServerError(w, r, err) return } - json.OK(w, r, categories) } diff --git a/api/entry.go b/api/entry.go index e64e867f..26c306c6 100644 --- a/api/entry.go +++ b/api/entry.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package api // import "miniflux.app/api" @@ -143,8 +142,7 @@ func (h *handler) findEntries(w http.ResponseWriter, r *http.Request, feedID int builder.WithFeedID(feedID) builder.WithCategoryID(categoryID) builder.WithStatuses(statuses) - builder.WithOrder(order) - builder.WithDirection(direction) + builder.WithSorting(order, direction) builder.WithOffset(offset) builder.WithLimit(limit) builder.WithTags(tags) diff --git a/api/feed.go b/api/feed.go index 725ed801..339557c5 100644 --- a/api/feed.go +++ b/api/feed.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package api // import "miniflux.app/api" diff --git a/api/icon.go b/api/icon.go index 4720cddc..6dc19dff 100644 --- a/api/icon.go +++ b/api/icon.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package api // import "miniflux.app/api" diff --git a/api/middleware.go b/api/middleware.go index d7cf7483..da7b4652 100644 --- a/api/middleware.go +++ b/api/middleware.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package api // import "miniflux.app/api" diff --git a/api/opml.go b/api/opml.go index e5af7c9d..bc269286 100644 --- a/api/opml.go +++ b/api/opml.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package api // import "miniflux.app/api" diff --git a/api/payload.go b/api/payload.go index 16eca293..1bd8505d 100644 --- a/api/payload.go +++ b/api/payload.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package api // import "miniflux.app/api" diff --git a/api/subscription.go b/api/subscription.go index f391dc9d..5f7be916 100644 --- a/api/subscription.go +++ b/api/subscription.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package api // import "miniflux.app/api" diff --git a/api/user.go b/api/user.go index cd40bc68..b985d1e9 100644 --- a/api/user.go +++ b/api/user.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package api // import "miniflux.app/api" diff --git a/cli/ask_credentials.go b/cli/ask_credentials.go index 37341325..d8aa0de5 100644 --- a/cli/ask_credentials.go +++ b/cli/ask_credentials.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package cli // import "miniflux.app/cli" diff --git a/cli/cleanup_tasks.go b/cli/cleanup_tasks.go new file mode 100644 index 00000000..fba722b9 --- /dev/null +++ b/cli/cleanup_tasks.go @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package cli // import "miniflux.app/cli" + +import ( + "time" + + "miniflux.app/config" + "miniflux.app/logger" + "miniflux.app/metric" + "miniflux.app/model" + "miniflux.app/storage" +) + +func runCleanupTasks(store *storage.Storage) { + nbSessions := store.CleanOldSessions(config.Opts.CleanupRemoveSessionsDays()) + nbUserSessions := store.CleanOldUserSessions(config.Opts.CleanupRemoveSessionsDays()) + logger.Info("[Sessions] Removed %d application sessions and %d user sessions", nbSessions, nbUserSessions) + + startTime := time.Now() + if rowsAffected, err := store.ArchiveEntries(model.EntryStatusRead, config.Opts.CleanupArchiveReadDays(), config.Opts.CleanupArchiveBatchSize()); err != nil { + logger.Error("[ArchiveReadEntries] %v", err) + } else { + logger.Info("[ArchiveReadEntries] %d entries changed", rowsAffected) + + if config.Opts.HasMetricsCollector() { + metric.ArchiveEntriesDuration.WithLabelValues(model.EntryStatusRead).Observe(time.Since(startTime).Seconds()) + } + } + + startTime = time.Now() + if rowsAffected, err := store.ArchiveEntries(model.EntryStatusUnread, config.Opts.CleanupArchiveUnreadDays(), config.Opts.CleanupArchiveBatchSize()); err != nil { + logger.Error("[ArchiveUnreadEntries] %v", err) + } else { + logger.Info("[ArchiveUnreadEntries] %d entries changed", rowsAffected) + + if config.Opts.HasMetricsCollector() { + metric.ArchiveEntriesDuration.WithLabelValues(model.EntryStatusUnread).Observe(time.Since(startTime).Seconds()) + } + } +} diff --git a/cli/cli.go b/cli/cli.go index 5f17a85d..c7be42ab 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package cli // import "miniflux.app/cli" @@ -18,7 +17,7 @@ import ( ) const ( - flagInfoHelp = "Show application information" + flagInfoHelp = "Show build information" flagVersionHelp = "Show application version" flagMigrateHelp = "Run SQL migrations" flagFlushSessionsHelp = "Flush all sessions (disconnect users)" @@ -29,6 +28,8 @@ const ( flagConfigFileHelp = "Load configuration file" flagConfigDumpHelp = "Print parsed configuration values" flagHealthCheckHelp = `Perform a health check on the given endpoint (the value "auto" try to guess the health check endpoint).` + flagRefreshFeedsHelp = "Refresh a batch of feeds and exit" + flagRunCleanupTasksHelp = "Run cleanup tasks (delete old sessions and archives old entries)" ) // Parse parses command line arguments. @@ -46,6 +47,8 @@ func Parse() { flagConfigFile string flagConfigDump bool flagHealthCheck string + flagRefreshFeeds bool + flagRunCleanupTasks bool ) flag.BoolVar(&flagInfo, "info", false, flagInfoHelp) @@ -62,6 +65,8 @@ func Parse() { flag.StringVar(&flagConfigFile, "c", "", flagConfigFileHelp) flag.BoolVar(&flagConfigDump, "config-dump", false, flagConfigDumpHelp) flag.StringVar(&flagHealthCheck, "healthcheck", "", flagHealthCheckHelp) + flag.BoolVar(&flagRefreshFeeds, "refresh-feeds", false, flagRefreshFeedsHelp) + flag.BoolVar(&flagRunCleanupTasks, "run-cleanup-tasks", false, flagRunCleanupTasksHelp) flag.Parse() cfg := config.NewParser() @@ -188,5 +193,15 @@ func Parse() { createAdmin(store) } + if flagRefreshFeeds { + refreshFeeds(store) + return + } + + if flagRunCleanupTasks { + runCleanupTasks(store) + return + } + startDaemon(store) } diff --git a/cli/create_admin.go b/cli/create_admin.go index 9eefc370..8070e646 100644 --- a/cli/create_admin.go +++ b/cli/create_admin.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package cli // import "miniflux.app/cli" diff --git a/cli/daemon.go b/cli/daemon.go index 5953bb95..dcea6e56 100644 --- a/cli/daemon.go +++ b/cli/daemon.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package cli // import "miniflux.app/cli" @@ -13,17 +12,16 @@ import ( "time" "miniflux.app/config" + httpd "miniflux.app/http/server" "miniflux.app/logger" "miniflux.app/metric" - "miniflux.app/service/httpd" - "miniflux.app/service/scheduler" "miniflux.app/storage" "miniflux.app/systemd" "miniflux.app/worker" ) func startDaemon(store *storage.Storage) { - logger.Info("Starting Miniflux...") + logger.Info("Starting daemon...") stop := make(chan os.Signal, 1) signal.Notify(stop, os.Interrupt) @@ -32,12 +30,12 @@ func startDaemon(store *storage.Storage) { pool := worker.NewPool(store, config.Opts.WorkerPoolSize()) if config.Opts.HasSchedulerService() && !config.Opts.HasMaintenanceMode() { - scheduler.Serve(store, pool) + runScheduler(store, pool) } var httpServer *http.Server if config.Opts.HasHTTPService() { - httpServer = httpd.Serve(store, pool) + httpServer = httpd.StartWebServer(store, pool) } if config.Opts.HasMetricsCollector() { diff --git a/cli/doc.go b/cli/doc.go deleted file mode 100644 index a7bab431..00000000 --- a/cli/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 cli implements command line arguments for Miniflux application. -*/ -package cli // import "miniflux.app/cli" diff --git a/cli/flush_sessions.go b/cli/flush_sessions.go index 43e6e930..0d02d1bc 100644 --- a/cli/flush_sessions.go +++ b/cli/flush_sessions.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package cli // import "miniflux.app/cli" diff --git a/cli/health_check.go b/cli/health_check.go index 8e6a4e85..b04135b1 100644 --- a/cli/health_check.go +++ b/cli/health_check.go @@ -1,6 +1,5 @@ -// Copyright 2021 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package cli // import "miniflux.app/cli" diff --git a/cli/info.go b/cli/info.go index 8cbc028b..182d2587 100644 --- a/cli/info.go +++ b/cli/info.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package cli // import "miniflux.app/cli" diff --git a/cli/refresh_feeds.go b/cli/refresh_feeds.go new file mode 100644 index 00000000..355c8287 --- /dev/null +++ b/cli/refresh_feeds.go @@ -0,0 +1,51 @@ +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package cli // import "miniflux.app/cli" + +import ( + "sync" + "time" + + "miniflux.app/config" + "miniflux.app/logger" + "miniflux.app/model" + feedHandler "miniflux.app/reader/handler" + "miniflux.app/storage" +) + +func refreshFeeds(store *storage.Storage) { + var wg sync.WaitGroup + + startTime := time.Now() + jobs, err := store.NewBatch(config.Opts.BatchSize()) + if err != nil { + logger.Error("[Cronjob] %v", err) + } + + nbJobs := len(jobs) + logger.Info("[Cronjob]] Created %d jobs from a batch size of %d", nbJobs, config.Opts.BatchSize()) + var jobQueue = make(chan model.Job, nbJobs) + + logger.Info("[Cronjob] Starting a pool of %d workers", config.Opts.WorkerPoolSize()) + for i := 0; i < config.Opts.WorkerPoolSize(); i++ { + wg.Add(1) + go func(workerID int) { + defer wg.Done() + for job := range jobQueue { + logger.Info("[Cronjob] Refreshing feed #%d for user #%d in worker #%d", job.FeedID, job.UserID, workerID) + if err := feedHandler.RefreshFeed(store, job.UserID, job.FeedID); err != nil { + logger.Error("[Cronjob] Refreshing the feed #%d returned this error: %v", job.FeedID, err) + } + } + }(i) + } + + for _, job := range jobs { + jobQueue <- job + } + close(jobQueue) + + wg.Wait() + logger.Info("[Cronjob] Refreshed %d feed(s) in %s", nbJobs, time.Since(startTime)) +} diff --git a/cli/reset_password.go b/cli/reset_password.go index 93556576..3fb634cf 100644 --- a/cli/reset_password.go +++ b/cli/reset_password.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package cli // import "miniflux.app/cli" diff --git a/cli/scheduler.go b/cli/scheduler.go new file mode 100644 index 00000000..839d7f54 --- /dev/null +++ b/cli/scheduler.go @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package cli // import "miniflux.app/cli" + +import ( + "time" + + "miniflux.app/config" + "miniflux.app/logger" + "miniflux.app/storage" + "miniflux.app/worker" +) + +func runScheduler(store *storage.Storage, pool *worker.Pool) { + logger.Info(`Starting background scheduler...`) + + go feedScheduler( + store, + pool, + config.Opts.PollingFrequency(), + config.Opts.BatchSize(), + ) + + go cleanupScheduler( + store, + config.Opts.CleanupFrequencyHours(), + ) +} + +func feedScheduler(store *storage.Storage, pool *worker.Pool, frequency, batchSize int) { + for range time.Tick(time.Duration(frequency) * time.Minute) { + jobs, err := store.NewBatch(batchSize) + logger.Info("[Scheduler:Feed] Pushing %d jobs to the queue", len(jobs)) + if err != nil { + logger.Error("[Scheduler:Feed] %v", err) + } else { + pool.Push(jobs) + } + } +} + +func cleanupScheduler(store *storage.Storage, frequency int) { + for range time.Tick(time.Duration(frequency) * time.Hour) { + runCleanupTasks(store) + } +} diff --git a/client/client.go b/client/client.go index e1276d06..67d0fbd0 100644 --- a/client/client.go +++ b/client/client.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package client // import "miniflux.app/client" diff --git a/client/doc.go b/client/doc.go index aadfe992..b44495a1 100644 --- a/client/doc.go +++ b/client/doc.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 /* Package client implements a client library for the Miniflux REST API. diff --git a/client/model.go b/client/model.go index 0e18c29a..6d1209ba 100644 --- a/client/model.go +++ b/client/model.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package client // import "miniflux.app/client" @@ -41,6 +40,7 @@ type User struct { CJKReadingSpeed int `json:"cjk_reading_speed"` DefaultHomePage string `json:"default_home_page"` CategoriesSortingOrder string `json:"categories_sorting_order"` + MarkReadOnView bool `json:"mark_read_on_view"` } func (u User) String() string { @@ -79,6 +79,7 @@ type UserModificationRequest struct { CJKReadingSpeed *int `json:"cjk_reading_speed"` DefaultHomePage *string `json:"default_home_page"` CategoriesSortingOrder *string `json:"categories_sorting_order"` + MarkReadOnView *bool `json:"mark_read_on_view"` } // Users represents a list of users. diff --git a/client/request.go b/client/request.go index 913c5dc7..f409f979 100644 --- a/client/request.go +++ b/client/request.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package client // import "miniflux.app/client" diff --git a/config/config.go b/config/config.go index 60909748..fa999b7d 100644 --- a/config/config.go +++ b/config/config.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package config // import "miniflux.app/config" diff --git a/config/config_test.go b/config/config_test.go index de502d7a..8eb0e88d 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -1,6 +1,5 @@ -// Copyright 2019 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package config // import "miniflux.app/config" diff --git a/config/doc.go b/config/doc.go deleted file mode 100644 index acaa32f0..00000000 --- a/config/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2019 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 config handles configuration management for the application. -*/ -package config // import "miniflux.app/config" diff --git a/config/options.go b/config/options.go index d92645fc..f7448578 100644 --- a/config/options.go +++ b/config/options.go @@ -1,6 +1,5 @@ -// Copyright 2019 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package config // import "miniflux.app/config" diff --git a/config/parser.go b/config/parser.go index 770870d0..d8c3c7db 100644 --- a/config/parser.go +++ b/config/parser.go @@ -1,6 +1,5 @@ -// Copyright 2019 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package config // import "miniflux.app/config" diff --git a/config/parser_test.go b/config/parser_test.go index 8f896c12..8b080cb1 100644 --- a/config/parser_test.go +++ b/config/parser_test.go @@ -1,6 +1,5 @@ -// Copyright 2019 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package config // import "miniflux.app/config" diff --git a/crypto/crypto.go b/crypto/crypto.go index c06cd307..5e0b688f 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package crypto // import "miniflux.app/crypto" diff --git a/crypto/doc.go b/crypto/doc.go deleted file mode 100644 index 060c48b2..00000000 --- a/crypto/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 crypto implements helpers related to cryptography. -*/ -package crypto // import "miniflux.app/crypto" diff --git a/database/database.go b/database/database.go index d310e072..274f128c 100644 --- a/database/database.go +++ b/database/database.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package database // import "miniflux.app/database" diff --git a/database/migrations.go b/database/migrations.go index 141917dc..d4ebb55d 100644 --- a/database/migrations.go +++ b/database/migrations.go @@ -1,6 +1,5 @@ -// Copyright 2020 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package database // import "miniflux.app/database" @@ -653,4 +652,59 @@ var migrations = []func(tx *sql.Tx) error{ _, err = tx.Exec(sql) return err }, + func(tx *sql.Tx) (err error) { + sql := ` + ALTER TABLE integrations ADD COLUMN linkding_tags text default ''; + ` + _, err = tx.Exec(sql) + return err + }, + func(tx *sql.Tx) (err error) { + sql := ` + ALTER TABLE feeds ADD COLUMN no_media_player boolean default 'f'; + ALTER TABLE enclosures ADD COLUMN media_progression int default 0; + ` + _, err = tx.Exec(sql) + return err + }, + func(tx *sql.Tx) (err error) { + sql := ` + ALTER TABLE integrations ADD COLUMN linkding_mark_as_unread bool default 'f'; + ` + _, err = tx.Exec(sql) + return err + }, + func(tx *sql.Tx) (err error) { + // Delete duplicated rows + sql := ` + DELETE FROM enclosures a USING enclosures b + WHERE a.id < b.id + AND a.user_id = b.user_id + AND a.entry_id = b.entry_id + AND a.url = b.url; + ` + _, err = tx.Exec(sql) + if err != nil { + return err + } + + // Remove previous index + _, err = tx.Exec(`DROP INDEX enclosures_user_entry_url_idx`) + if err != nil { + return err + } + + // Create unique index + _, err = tx.Exec(`CREATE UNIQUE INDEX enclosures_user_entry_url_unique_idx ON enclosures(user_id, entry_id, md5(url))`) + if err != nil { + return err + } + + return nil + }, + func(tx *sql.Tx) (err error) { + sql := `ALTER TABLE users ADD COLUMN mark_read_on_view boolean default 't'` + _, err = tx.Exec(sql) + return err + }, } diff --git a/doc.go b/doc.go deleted file mode 100644 index 7d315a57..00000000 --- a/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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. - -/* -Miniflux is a feed reader application. -*/ -package main // import "miniflux.app" diff --git a/errors/doc.go b/errors/doc.go deleted file mode 100644 index cc509ff2..00000000 --- a/errors/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 errors handles localized errors. -*/ -package errors // import "miniflux.app/errors" diff --git a/errors/errors.go b/errors/errors.go index e6a979a4..7abdae73 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package errors // import "miniflux.app/errors" diff --git a/fever/doc.go b/fever/doc.go deleted file mode 100644 index 87d2ade5..00000000 --- a/fever/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 fever implements Fever API endpoints. -*/ -package fever // import "miniflux.app/fever" diff --git a/fever/handler.go b/fever/handler.go index ce5919c0..c7c576b3 100644 --- a/fever/handler.go +++ b/fever/handler.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package fever // import "miniflux.app/fever" @@ -242,8 +241,7 @@ func (h *handler) handleItems(w http.ResponseWriter, r *http.Request) { builder := h.store.NewEntryQueryBuilder(userID) builder.WithoutStatus(model.EntryStatusRemoved) builder.WithLimit(50) - builder.WithOrder("id") - builder.WithDirection(model.DefaultSortingDirection) + builder.WithSorting("id", model.DefaultSortingDirection) switch { case request.HasQueryParam(r, "since_id"): @@ -256,11 +254,11 @@ func (h *handler) handleItems(w http.ResponseWriter, r *http.Request) { maxID := request.QueryInt64Param(r, "max_id", 0) if maxID == 0 { logger.Debug("[Fever] Fetching most recent items for user #%d", userID) - builder.WithDirection("desc") + builder.WithSorting("id", "DESC") } else if maxID > 0 { logger.Debug("[Fever] Fetching items before #%d for user #%d", maxID, userID) builder.BeforeEntryID(maxID) - builder.WithDirection("desc") + builder.WithSorting("id", "DESC") } case request.HasQueryParam(r, "with_ids"): csvItemIDs := request.QueryStringParam(r, "with_ids", "") diff --git a/fever/middleware.go b/fever/middleware.go index e4bbf871..0f8f8482 100644 --- a/fever/middleware.go +++ b/fever/middleware.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package fever // import "miniflux.app/fever" diff --git a/fever/response.go b/fever/response.go index 0c68646a..6c8346d7 100644 --- a/fever/response.go +++ b/fever/response.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package fever // import "miniflux.app/fever" diff --git a/go.mod b/go.mod index 7c92dcab..68070e4f 100644 --- a/go.mod +++ b/go.mod @@ -4,40 +4,39 @@ module miniflux.app require ( github.com/PuerkitoBio/goquery v1.8.1 - github.com/coreos/go-oidc v2.2.1+incompatible + github.com/coreos/go-oidc/v3 v3.6.0 github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible github.com/gorilla/mux v1.8.0 - github.com/lib/pq v1.10.7 + github.com/lib/pq v1.10.9 github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530 - github.com/prometheus/client_golang v1.14.0 + github.com/mccutchen/go-httpbin/v2 v2.9.2 + github.com/prometheus/client_golang v1.16.0 github.com/rylans/getlang v0.0.0-20201227074721-9e7f44ff8aa0 - github.com/tdewolff/minify/v2 v2.12.5 + github.com/tdewolff/minify/v2 v2.12.7 github.com/yuin/goldmark v1.5.4 - golang.org/x/crypto v0.8.0 - golang.org/x/net v0.9.0 - golang.org/x/oauth2 v0.7.0 - golang.org/x/term v0.7.0 - mvdan.cc/xurls/v2 v2.4.0 + golang.org/x/crypto v0.11.0 + golang.org/x/net v0.12.0 + golang.org/x/oauth2 v0.10.0 + golang.org/x/term v0.10.0 + mvdan.cc/xurls/v2 v2.5.0 ) require ( github.com/andybalholm/cascadia v1.3.1 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/go-jose/go-jose/v3 v3.0.0 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/pquerna/cachecontrol v0.1.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect - github.com/stretchr/testify v1.7.0 // indirect - github.com/tdewolff/parse/v2 v2.6.5 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/tdewolff/parse/v2 v2.6.6 // indirect github.com/technoweenie/multipartstreamer v1.0.1 // indirect - golang.org/x/sys v0.7.0 // indirect - golang.org/x/text v0.9.0 // indirect + golang.org/x/sys v0.10.0 // indirect + golang.org/x/text v0.11.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.28.1 // indirect - gopkg.in/square/go-jose.v2 v2.6.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect ) go 1.19 diff --git a/go.sum b/go.sum index 9428404a..09bcfcb6 100644 --- a/go.sum +++ b/go.sum @@ -1,553 +1,129 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM= github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk= -github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-oidc/v3 v3.6.0 h1:AKVxfYw1Gmkn/w96z0DbT/B/xFnzTd3MkZvWLjF4n/o= +github.com/coreos/go-oidc/v3 v3.6.0/go.mod h1:ZpHUsHBucTUj6WOkrP4E20UPynbLZzhTQ1XKCXkxyPc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+XJPdBE= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo= +github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible h1:2cauKuaELYAEARXRkq2LrJ0yDDv1rW7+wrTEdVL3uaU= github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= -github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530 h1:kHKxCOLcHH8r4Fzarl4+Y3K5hjothkVW5z7T1dUM11U= github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s= github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/mccutchen/go-httpbin/v2 v2.9.2 h1:bte68xUmv/MXyIlDv+hBUinoxcPHxZ1BKxyQlsToJD0= +github.com/mccutchen/go-httpbin/v2 v2.9.2/go.mod h1:+DBHcmg6EOeoizuiOI8iL12VIHXx+9YQNlz+gjB9uxk= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pquerna/cachecontrol v0.1.0 h1:yJMy84ti9h/+OEWa752kBTKv4XC30OtVVHYv/8cTqKc= -github.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= github.com/rylans/getlang v0.0.0-20201227074721-9e7f44ff8aa0 h1:qSaU9YAEIxk/ozcmY1hiauktAYTpbwYIrPdQ0L2E8UM= github.com/rylans/getlang v0.0.0-20201227074721-9e7f44ff8aa0/go.mod h1:3vfmZI6aJd5Rb9W2TQ0Nmupl+qem21R05+hmCscI0Bk= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/tdewolff/minify/v2 v2.12.5 h1:s2KDBt/D/3ayE3gcqQF8VIgTmYgkx+btuLvVAeePzZM= -github.com/tdewolff/minify/v2 v2.12.5/go.mod h1:i8QXtVyL7Ddwc4I5gqzvgBqKlTMgMNTbiXaPO4Iqg+A= -github.com/tdewolff/parse/v2 v2.6.5 h1:lYvWBk55GkqKl0JJenGpmrgu/cPHQQ6/Mm1hBGswoGQ= -github.com/tdewolff/parse/v2 v2.6.5/go.mod h1:woz0cgbLwFdtbjJu8PIKxhW05KplTFQkOdX78o+Jgrs= -github.com/tdewolff/test v1.0.7 h1:8Vs0142DmPFW/bQeHRP3MV19m1gvndjUb1sn8yy74LM= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/tdewolff/minify/v2 v2.12.7 h1:pBzz2tAfz5VghOXiQIsSta6srhmTeinQPjRDHWoumCA= +github.com/tdewolff/minify/v2 v2.12.7/go.mod h1:ZRKTheiOGyLSK8hOZWWv+YoJAECzDivNgAlVYDHp/Ws= +github.com/tdewolff/parse/v2 v2.6.6 h1:Yld+0CrKUJaCV78DL1G2nk3C9lKrxyRTux5aaK/AkDo= +github.com/tdewolff/parse/v2 v2.6.6/go.mod h1:woz0cgbLwFdtbjJu8PIKxhW05KplTFQkOdX78o+Jgrs= github.com/tdewolff/test v1.0.7/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= +github.com/tdewolff/test v1.0.9 h1:SswqJCmeN4B+9gEAi/5uqT0qpi1y2/2O47V/1hhGZT0= +github.com/tdewolff/test v1.0.9/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM= github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.5.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU= github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= -golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g= -golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= +golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= +golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= -golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= +golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= -gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -mvdan.cc/xurls/v2 v2.4.0 h1:tzxjVAj+wSBmDcF6zBB7/myTy3gX9xvi8Tyr28AuQgc= -mvdan.cc/xurls/v2 v2.4.0/go.mod h1:+GEjq9uNjqs8LQfM9nVnM8rff0OQ5Iash5rzX+N1CSg= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +mvdan.cc/xurls/v2 v2.5.0 h1:lyBNOm8Wo71UknhUs4QTFUNNMyxy2JEIaKKo0RWOh+8= +mvdan.cc/xurls/v2 v2.5.0/go.mod h1:yQgaGQ1rFtJUzkmKiHYSSfuQxqfYmd//X6PxvholpeE= diff --git a/googlereader/doc.go b/googlereader/doc.go deleted file mode 100644 index 10b3d564..00000000 --- a/googlereader/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2022 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 googlereader implements Google Reader API endpoints. -*/ -package googlereader // import "miniflux.app/googlereader" diff --git a/googlereader/handler.go b/googlereader/handler.go index 4e5272ac..49815012 100644 --- a/googlereader/handler.go +++ b/googlereader/handler.go @@ -1,6 +1,5 @@ -// Copyright 2022 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package googlereader // import "miniflux.app/googlereader" @@ -790,8 +789,7 @@ func (h *handler) streamItemContents(w http.ResponseWriter, r *http.Request) { builder := h.store.NewEntryQueryBuilder(userID) builder.WithoutStatus(model.EntryStatusRemoved) builder.WithEntryIDs(itemIDs) - builder.WithOrder(model.DefaultSortingOrder) - builder.WithDirection(requestModifiers.SortDirection) + builder.WithSorting(model.DefaultSortingOrder, requestModifiers.SortDirection) entries, err := builder.GetEntries() if err != nil { @@ -1144,8 +1142,7 @@ func (h *handler) handleReadingListStream(w http.ResponseWriter, r *http.Request builder.WithoutStatus(model.EntryStatusRemoved) builder.WithLimit(rm.Count) builder.WithOffset(rm.Offset) - builder.WithOrder(model.DefaultSortingOrder) - builder.WithDirection(rm.SortDirection) + builder.WithSorting(model.DefaultSortingOrder, rm.SortDirection) if rm.StartTime > 0 { builder.AfterDate(time.Unix(rm.StartTime, 0)) } @@ -1187,8 +1184,7 @@ func (h *handler) handleStarredStream(w http.ResponseWriter, r *http.Request, rm builder.WithStarred(true) builder.WithLimit(rm.Count) builder.WithOffset(rm.Offset) - builder.WithOrder(model.DefaultSortingOrder) - builder.WithDirection(rm.SortDirection) + builder.WithSorting(model.DefaultSortingOrder, rm.SortDirection) if rm.StartTime > 0 { builder.AfterDate(time.Unix(rm.StartTime, 0)) } @@ -1230,8 +1226,7 @@ func (h *handler) handleReadStream(w http.ResponseWriter, r *http.Request, rm Re builder.WithStatus(model.EntryStatusRead) builder.WithLimit(rm.Count) builder.WithOffset(rm.Offset) - builder.WithOrder(model.DefaultSortingOrder) - builder.WithDirection(rm.SortDirection) + builder.WithSorting(model.DefaultSortingOrder, rm.SortDirection) if rm.StartTime > 0 { builder.AfterDate(time.Unix(rm.StartTime, 0)) } @@ -1279,8 +1274,7 @@ func (h *handler) handleFeedStream(w http.ResponseWriter, r *http.Request, rm Re builder.WithFeedID(feedID) builder.WithLimit(rm.Count) builder.WithOffset(rm.Offset) - builder.WithOrder(model.DefaultSortingOrder) - builder.WithDirection(rm.SortDirection) + builder.WithSorting(model.DefaultSortingOrder, rm.SortDirection) if rm.StartTime > 0 { builder.AfterDate(time.Unix(rm.StartTime, 0)) } diff --git a/googlereader/middleware.go b/googlereader/middleware.go index c7d542e6..d999582e 100644 --- a/googlereader/middleware.go +++ b/googlereader/middleware.go @@ -1,6 +1,5 @@ -// Copyright 2022 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package googlereader // import "miniflux.app/googlereader" diff --git a/googlereader/response.go b/googlereader/response.go index 58501256..6ea96a58 100644 --- a/googlereader/response.go +++ b/googlereader/response.go @@ -1,6 +1,5 @@ -// Copyright 2022 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package googlereader // import "miniflux.app/googlereader" diff --git a/http/client/client.go b/http/client/client.go index cb966944..6e7c2282 100644 --- a/http/client/client.go +++ b/http/client/client.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package client // import "miniflux.app/http/client" diff --git a/http/client/client_test.go b/http/client/client_test.go index 618345cf..2a5a4db4 100644 --- a/http/client/client_test.go +++ b/http/client/client_test.go @@ -1,15 +1,32 @@ -// Copyright 2020 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package client // import "miniflux.app/http/client" import ( + "fmt" + "net/http/httptest" + "os" "testing" + + "github.com/mccutchen/go-httpbin/v2/httpbin" ) +var srv *httptest.Server + +func TestMain(m *testing.M) { + srv = httptest.NewServer(httpbin.New()) + exitCode := m.Run() + srv.Close() + os.Exit(exitCode) +} + +func MakeClient(path string) *Client { + return New(fmt.Sprintf("%s%s", srv.URL, path)) +} + func TestClientWithDelay(t *testing.T) { - clt := New("http://httpbin.org/delay/5") + clt := MakeClient("/delay/5") clt.ClientTimeout = 1 _, err := clt.Get() if err == nil { @@ -18,7 +35,7 @@ func TestClientWithDelay(t *testing.T) { } func TestClientWithError(t *testing.T) { - clt := New("http://httpbin.org/status/502") + clt := MakeClient("/status/502") clt.ClientTimeout = 5 response, err := clt.Get() if err != nil { @@ -35,7 +52,7 @@ func TestClientWithError(t *testing.T) { } func TestClientWithResponseTooLarge(t *testing.T) { - clt := New("http://httpbin.org/bytes/100") + clt := MakeClient("/bytes/100") clt.ClientMaxBodySize = 10 _, err := clt.Get() if err == nil { @@ -44,7 +61,7 @@ func TestClientWithResponseTooLarge(t *testing.T) { } func TestClientWithBasicAuth(t *testing.T) { - clt := New("http://httpbin.org/basic-auth/testuser/testpassword") + clt := MakeClient("/basic-auth/testuser/testpassword") clt.WithCredentials("testuser", "testpassword") _, err := clt.Get() if err != nil { diff --git a/http/client/doc.go b/http/client/doc.go deleted file mode 100644 index 03cb6cf0..00000000 --- a/http/client/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 client provides an HTTP client builder. -*/ -package client // import "miniflux.app/http/client" diff --git a/http/client/response.go b/http/client/response.go index e61bf562..5f25eaf1 100644 --- a/http/client/response.go +++ b/http/client/response.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package client // import "miniflux.app/http/client" diff --git a/http/client/response_test.go b/http/client/response_test.go index dfcf17e5..42aa2ec0 100644 --- a/http/client/response_test.go +++ b/http/client/response_test.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package client // import "miniflux.app/http/client" diff --git a/http/cookie/cookie.go b/http/cookie/cookie.go index 8874c412..37688b55 100644 --- a/http/cookie/cookie.go +++ b/http/cookie/cookie.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package cookie // import "miniflux.app/http/cookie" diff --git a/http/cookie/doc.go b/http/cookie/doc.go deleted file mode 100644 index f80831bb..00000000 --- a/http/cookie/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 cookie provides functions to build cookies. -*/ -package cookie // import "miniflux.app/http/cookie" diff --git a/http/request/client_ip.go b/http/request/client_ip.go index 83d3a577..32952b67 100644 --- a/http/request/client_ip.go +++ b/http/request/client_ip.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package request // import "miniflux.app/http/request" diff --git a/http/request/client_ip_test.go b/http/request/client_ip_test.go index 714b0dcd..5a76fd6b 100644 --- a/http/request/client_ip_test.go +++ b/http/request/client_ip_test.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package request // import "miniflux.app/http/request" diff --git a/http/request/context.go b/http/request/context.go index 542c3d49..3fdaf798 100644 --- a/http/request/context.go +++ b/http/request/context.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package request // import "miniflux.app/http/request" diff --git a/http/request/context_test.go b/http/request/context_test.go index f33e2d87..6f3858fb 100644 --- a/http/request/context_test.go +++ b/http/request/context_test.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package request // import "miniflux.app/http/request" diff --git a/http/request/cookie.go b/http/request/cookie.go index 88cc626b..dddf1aed 100644 --- a/http/request/cookie.go +++ b/http/request/cookie.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package request // import "miniflux.app/http/request" diff --git a/http/request/cookie_test.go b/http/request/cookie_test.go index 9c3b54db..ec8f1a8a 100644 --- a/http/request/cookie_test.go +++ b/http/request/cookie_test.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package request // import "miniflux.app/http/request" diff --git a/http/request/doc.go b/http/request/doc.go deleted file mode 100644 index a35fcae2..00000000 --- a/http/request/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 request contains helper functions to work with the HTTP request. -*/ -package request // import "miniflux.app/http/request" diff --git a/http/request/params.go b/http/request/params.go index 3c5849ca..dc7512b6 100644 --- a/http/request/params.go +++ b/http/request/params.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package request // import "miniflux.app/http/request" diff --git a/http/request/params_test.go b/http/request/params_test.go index 7f1f8802..852959af 100644 --- a/http/request/params_test.go +++ b/http/request/params_test.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package request // import "miniflux.app/http/request" diff --git a/http/response/builder.go b/http/response/builder.go index 99197293..33c144b5 100644 --- a/http/response/builder.go +++ b/http/response/builder.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package response // import "miniflux.app/http/response" diff --git a/http/response/builder_test.go b/http/response/builder_test.go index c154dfe3..069eba78 100644 --- a/http/response/builder_test.go +++ b/http/response/builder_test.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package response // import "miniflux.app/http/response" diff --git a/http/response/doc.go b/http/response/doc.go deleted file mode 100644 index 6987ae26..00000000 --- a/http/response/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 response contains everything related to HTTP responses. -*/ -package response // import "miniflux.app/http/response" diff --git a/http/response/html/doc.go b/http/response/html/doc.go deleted file mode 100644 index 9051ea4f..00000000 --- a/http/response/html/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 html contains HTML response functions. -*/ -package html // import "miniflux.app/http/response/html" diff --git a/http/response/html/html.go b/http/response/html/html.go index 3bba07f3..29d00592 100644 --- a/http/response/html/html.go +++ b/http/response/html/html.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package html // import "miniflux.app/http/response/html" diff --git a/http/response/html/html_test.go b/http/response/html/html_test.go index 62c9bb80..433e1691 100644 --- a/http/response/html/html_test.go +++ b/http/response/html/html_test.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package html // import "miniflux.app/http/response/html" diff --git a/http/response/json/doc.go b/http/response/json/doc.go deleted file mode 100644 index 7c10e5e6..00000000 --- a/http/response/json/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 json contains JSON response functions. -*/ -package json // import "miniflux.app/http/response/json" diff --git a/http/response/json/json.go b/http/response/json/json.go index dfd49784..3033045d 100644 --- a/http/response/json/json.go +++ b/http/response/json/json.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package json // import "miniflux.app/http/response/json" diff --git a/http/response/json/json_test.go b/http/response/json/json_test.go index ee5580bf..1f3e6f19 100644 --- a/http/response/json/json_test.go +++ b/http/response/json/json_test.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package json // import "miniflux.app/http/response/json" diff --git a/http/response/xml/doc.go b/http/response/xml/doc.go deleted file mode 100644 index 8ca4655e..00000000 --- a/http/response/xml/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 xml contains XML response functions. -*/ -package xml // import "miniflux.app/http/response/xml" diff --git a/http/response/xml/xml.go b/http/response/xml/xml.go index 771a85ef..de26ad66 100644 --- a/http/response/xml/xml.go +++ b/http/response/xml/xml.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package xml // import "miniflux.app/http/response/xml" diff --git a/http/response/xml/xml_test.go b/http/response/xml/xml_test.go index ada6bd4f..638e14d4 100644 --- a/http/response/xml/xml_test.go +++ b/http/response/xml/xml_test.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package xml // import "miniflux.app/http/response/xml" diff --git a/http/route/doc.go b/http/route/doc.go deleted file mode 100644 index 3633d709..00000000 --- a/http/route/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 route contains helper functions to work with defined routes. -*/ -package route // import "miniflux.app/http/route" diff --git a/http/route/route.go b/http/route/route.go index 0a0ec3b6..4cee659e 100644 --- a/http/route/route.go +++ b/http/route/route.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package route // import "miniflux.app/http/route" diff --git a/service/httpd/httpd.go b/http/server/httpd.go similarity index 95% rename from service/httpd/httpd.go rename to http/server/httpd.go index eac45b46..ace2eb0d 100644 --- a/service/httpd/httpd.go +++ b/http/server/httpd.go @@ -1,8 +1,7 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 -package httpd // import "miniflux.app/service/httpd" +package httpd // import "miniflux.app/http/server" import ( "crypto/tls" @@ -30,8 +29,7 @@ import ( "golang.org/x/crypto/acme/autocert" ) -// Serve starts a new HTTP server. -func Serve(store *storage.Storage, pool *worker.Pool) *http.Server { +func StartWebServer(store *storage.Storage, pool *worker.Pool) *http.Server { certFile := config.Opts.CertFile() keyFile := config.Opts.CertKeyFile() certDomain := config.Opts.CertDomain() diff --git a/service/httpd/middleware.go b/http/server/middleware.go similarity index 77% rename from service/httpd/middleware.go rename to http/server/middleware.go index c169e08f..3c4a7c5c 100644 --- a/service/httpd/middleware.go +++ b/http/server/middleware.go @@ -1,8 +1,7 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 -package httpd // import "miniflux.app/service/httpd" +package httpd // import "miniflux.app/http/server" import ( "context" diff --git a/integration/espial/espial.go b/integration/espial/espial.go index 0ae8f0d5..c276774f 100644 --- a/integration/espial/espial.go +++ b/integration/espial/espial.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + package espial // import "miniflux.app/integration/espial" import ( diff --git a/integration/instapaper/instapaper.go b/integration/instapaper/instapaper.go index e333a177..2f24a813 100644 --- a/integration/instapaper/instapaper.go +++ b/integration/instapaper/instapaper.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package instapaper // import "miniflux.app/integration/instapaper" diff --git a/integration/integration.go b/integration/integration.go index c86ebae5..52a4b8af 100644 --- a/integration/integration.go +++ b/integration/integration.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package integration // import "miniflux.app/integration" @@ -104,6 +103,8 @@ func SendEntry(entry *model.Entry, integration *model.Integration) { client := linkding.NewClient( integration.LinkdingURL, integration.LinkdingAPIKey, + integration.LinkdingTags, + integration.LinkdingMarkAsUnread, ) if err := client.AddEntry(entry.Title, entry.URL); err != nil { logger.Error("[Integration] UserID #%d: %v", integration.UserID, err) diff --git a/integration/linkding/linkding.go b/integration/linkding/linkding.go index 4674c6a1..8c506bdb 100644 --- a/integration/linkding/linkding.go +++ b/integration/linkding/linkding.go @@ -1,31 +1,35 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package linkding // import "miniflux.app/integration/linkding" import ( "fmt" "net/url" + "strings" "miniflux.app/http/client" ) // Document structure of a Linkding document type Document struct { - Url string `json:"url,omitempty"` - Title string `json:"title,omitempty"` + Url string `json:"url,omitempty"` + Title string `json:"title,omitempty"` + TagNames []string `json:"tag_names,omitempty"` + Unread bool `json:"unread,omitempty"` } // Client represents an Linkding client. type Client struct { baseURL string apiKey string + tags string + unread bool } // NewClient returns a new Linkding client. -func NewClient(baseURL, apiKey string) *Client { - return &Client{baseURL: baseURL, apiKey: apiKey} +func NewClient(baseURL, apiKey, tags string, unread bool) *Client { + return &Client{baseURL: baseURL, apiKey: apiKey, tags: tags, unread: unread} } // AddEntry sends an entry to Linkding. @@ -34,9 +38,15 @@ func (c *Client) AddEntry(title, url string) error { return fmt.Errorf("linkding: missing credentials") } + tagsSplitFn := func(c rune) bool { + return c == ',' || c == ' ' + } + doc := &Document{ - Url: url, - Title: title, + Url: url, + Title: title, + TagNames: strings.FieldsFunc(c.tags, tagsSplitFn), + Unread: c.unread, } apiURL, err := getAPIEndpoint(c.baseURL, "/api/bookmarks/") diff --git a/integration/matrixbot/matrixbot.go b/integration/matrixbot/matrixbot.go index 35ec1763..b9aab38c 100644 --- a/integration/matrixbot/matrixbot.go +++ b/integration/matrixbot/matrixbot.go @@ -1,6 +1,5 @@ -// Copyright 2021 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package matrixbot // import "miniflux.app/integration/matrixbot" diff --git a/integration/nunuxkeeper/nunuxkeeper.go b/integration/nunuxkeeper/nunuxkeeper.go index 31e51d86..217cb371 100644 --- a/integration/nunuxkeeper/nunuxkeeper.go +++ b/integration/nunuxkeeper/nunuxkeeper.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package nunuxkeeper // import "miniflux.app/integration/nunuxkeeper" diff --git a/integration/pinboard/pinboard.go b/integration/pinboard/pinboard.go index 12bddf1b..5a56ef20 100644 --- a/integration/pinboard/pinboard.go +++ b/integration/pinboard/pinboard.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package pinboard // import "miniflux.app/integration/pinboard" diff --git a/integration/pocket/connector.go b/integration/pocket/connector.go index ab6796ab..3df818d4 100644 --- a/integration/pocket/connector.go +++ b/integration/pocket/connector.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package pocket // import "miniflux.app/integration/pocket" diff --git a/integration/pocket/pocket.go b/integration/pocket/pocket.go index 52214e24..6d46ccfc 100644 --- a/integration/pocket/pocket.go +++ b/integration/pocket/pocket.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package pocket // import "miniflux.app/integration/pocket" diff --git a/integration/telegrambot/telegrambot.go b/integration/telegrambot/telegrambot.go index b24974bc..b7dbbcdc 100644 --- a/integration/telegrambot/telegrambot.go +++ b/integration/telegrambot/telegrambot.go @@ -1,6 +1,5 @@ -// Copyright 2021 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package telegrambot // import "miniflux.app/integration/telegrambot" diff --git a/integration/wallabag/wallabag.go b/integration/wallabag/wallabag.go index 4f04f586..84a5fe6c 100644 --- a/integration/wallabag/wallabag.go +++ b/integration/wallabag/wallabag.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package wallabag // import "miniflux.app/integration/wallabag" @@ -44,7 +43,7 @@ func (c *Client) AddEntry(link, title, content string) error { } func (c *Client) createEntry(accessToken, link, title, content string) error { - endpoint, err := getAPIEndpoint(c.baseURL, "/api/entries.json") + endpoint, err := url.JoinPath(c.baseURL, "/api/entries.json") if err != nil { return fmt.Errorf("wallbag: unable to get entries endpoint: %v", err) } @@ -76,7 +75,7 @@ func (c *Client) getAccessToken() (string, error) { values.Add("username", c.username) values.Add("password", c.password) - endpoint, err := getAPIEndpoint(c.baseURL, "/oauth/v2/token") + endpoint, err := url.JoinPath(c.baseURL, "/oauth/v2/token") if err != nil { return "", fmt.Errorf("wallbag: unable to get token endpoint: %v", err) } @@ -99,15 +98,6 @@ func (c *Client) getAccessToken() (string, error) { return token.AccessToken, nil } -func getAPIEndpoint(baseURL, path string) (string, error) { - u, err := url.Parse(baseURL) - if err != nil { - return "", fmt.Errorf("wallabag: invalid API endpoint: %v", err) - } - u.Path = path - return u.String(), nil -} - type tokenResponse struct { AccessToken string `json:"access_token"` Expires int `json:"expires_in"` diff --git a/locale/catalog.go b/locale/catalog.go index 60a47dcd..71053c41 100644 --- a/locale/catalog.go +++ b/locale/catalog.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package locale // import "miniflux.app/locale" diff --git a/locale/catalog_test.go b/locale/catalog_test.go index 35232fcd..203a9f0c 100644 --- a/locale/catalog_test.go +++ b/locale/catalog_test.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package locale // import "miniflux.app/locale" diff --git a/locale/doc.go b/locale/doc.go deleted file mode 100644 index 2f6849e7..00000000 --- a/locale/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 locale handles the internationalization of the application. -*/ -package locale // import "miniflux.app/locale" diff --git a/locale/locale.go b/locale/locale.go index bdf22255..8c6e4915 100644 --- a/locale/locale.go +++ b/locale/locale.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package locale // import "miniflux.app/locale" diff --git a/locale/locale_test.go b/locale/locale_test.go index 9f49b601..c69157fe 100644 --- a/locale/locale_test.go +++ b/locale/locale_test.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package locale // import "miniflux.app/locale" diff --git a/locale/plural.go b/locale/plural.go index 9fe21af1..37ef1580 100644 --- a/locale/plural.go +++ b/locale/plural.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package locale // import "miniflux.app/locale" diff --git a/locale/plural_test.go b/locale/plural_test.go index 4cd9accc..5d1d3b61 100644 --- a/locale/plural_test.go +++ b/locale/plural_test.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package locale // import "miniflux.app/locale" diff --git a/locale/printer.go b/locale/printer.go index ef04e050..1a2812f2 100644 --- a/locale/printer.go +++ b/locale/printer.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package locale // import "miniflux.app/locale" diff --git a/locale/printer_test.go b/locale/printer_test.go index 1d8f58d6..54510a6a 100644 --- a/locale/printer_test.go +++ b/locale/printer_test.go @@ -1,6 +1,6 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + package locale // import "miniflux.app/locale" import "testing" diff --git a/locale/translations/de_DE.json b/locale/translations/de_DE.json index 17184438..5a001d4e 100644 --- a/locale/translations/de_DE.json +++ b/locale/translations/de_DE.json @@ -80,7 +80,8 @@ "entry.estimated_reading_time": [ "%d Minute zu lesen", "%d Minuten zu lesen" - ], + ], + "entry.tags.label": "Stichworte:", "page.shared_entries.title": "Geteilte Artikel", "page.unread.title": "Ungelesen", "page.starred.title": "Lesezeichen", @@ -284,6 +285,7 @@ "form.feed.label.allow_self_signed_certificates": "Erlaube selbstsignierte oder ungültige Zertifikate", "form.feed.label.fetch_via_proxy": "Über Proxy abrufen", "form.feed.label.disabled": "Dieses Abonnement nicht aktualisieren", + "form.feed.label.no_media_player": "No media player (audio/video)", "form.feed.label.hide_globally": "Einträge in der globalen Ungelesen-Liste ausblenden", "form.category.label.title": "Titel", "form.category.hide_globally": "Einträge in der globalen Ungelesen-Liste ausblenden", @@ -320,6 +322,7 @@ "form.prefs.label.entry_order": "Eintrag Sortierspalte", "form.prefs.label.default_home_page": "Standard Startseite", "form.prefs.label.categories_sorting_order": "Kategorien sortieren", + "form.prefs.label.mark_read_on_view": "Einträge automatisch als gelesen markieren, wenn sie angezeigt werden", "form.import.label.file": "OPML Datei", "form.import.label.url": "URL", "form.integration.fever_activate": "Fever API aktivieren", @@ -361,6 +364,8 @@ "form.integration.linkding_activate": "Artikel in Linkding speichern", "form.integration.linkding_endpoint": "Linkding API-Endpunkt", "form.integration.linkding_api_key": "Linkding API-Schlüssel", + "form.integration.linkding_tags": "Linkding Tags", + "form.integration.linkding_bookmark": "Lesezeichen als ungelesen markieren", "form.integration.matrix_bot_activate": "Neue Artikel in die Matrix übertragen", "form.integration.matrix_bot_user": "Benutzername für Matrix", "form.integration.matrix_bot_password": "Passwort für Matrix-Benutzer", diff --git a/locale/translations/el_EL.json b/locale/translations/el_EL.json index c8b8340a..acfca611 100644 --- a/locale/translations/el_EL.json +++ b/locale/translations/el_EL.json @@ -81,6 +81,7 @@ "%d λεπτό ανάγνωση", "%d λεπτά ανάγνωση" ], + "entry.tags.label": "Ετικέτες:", "page.shared_entries.title": "Κοινόχρηστες Καταχωρήσεις", "page.unread.title": "Μη αναγνωσμένα", "page.starred.title": "Αγαπημένo", @@ -284,6 +285,7 @@ "form.feed.label.allow_self_signed_certificates": "Να επιτρέπονται αυτο-υπογεγραμμένα ή μη έγκυρα πιστοποιητικά", "form.feed.label.fetch_via_proxy": "Λήψη μέσω διακομιστή μεσολάβησης", "form.feed.label.disabled": "Μη ανανέωση αυτής της ροής", + "form.feed.label.no_media_player": "No media player (audio/video)", "form.feed.label.hide_globally": "Απόκρυψη καταχωρήσεων σε γενική λίστα μη αναγνωσμένων", "form.category.label.title": "Τίτλος", "form.category.hide_globally": "Απόκρυψη καταχωρήσεων σε γενική λίστα μη αναγνωσμένων", @@ -320,6 +322,7 @@ "form.prefs.label.entry_order": "Στήλη ταξινόμησης εισόδου", "form.prefs.label.default_home_page": "Προεπιλεγμένη αρχική σελίδα", "form.prefs.label.categories_sorting_order": "Ταξινόμηση κατηγοριών", + "form.prefs.label.mark_read_on_view": "Αυτόματη επισήμανση καταχωρήσεων ως αναγνωσμένων κατά την προβολή", "form.import.label.file": "Αρχείο OPML", "form.import.label.url": "URL", "form.integration.fever_activate": "Ενεργοποιήστε το Fever API", @@ -361,6 +364,8 @@ "form.integration.linkding_activate": "Αποθήκευση άρθρων στο Linkding", "form.integration.linkding_endpoint": "Τελικό σημείο Linkding API", "form.integration.linkding_api_key": "Κλειδί API Linkding", + "form.integration.linkding_tags": "Linkding Tags", + "form.integration.linkding_bookmark": "Σημείωση του σελιδοδείκτη ως μη αναγνωσμένου", "form.integration.matrix_bot_activate": "Μεταφορά νέων άρθρων στο Matrix", "form.integration.matrix_bot_user": "Όνομα χρήστη για το Matrix", "form.integration.matrix_bot_password": "Κωδικός πρόσβασης για τον χρήστη Matrix", diff --git a/locale/translations/en_US.json b/locale/translations/en_US.json index a62ea3a5..0d5e0350 100644 --- a/locale/translations/en_US.json +++ b/locale/translations/en_US.json @@ -81,6 +81,7 @@ "%d minute read", "%d minutes read" ], + "entry.tags.label": "Tags:", "page.shared_entries.title": "Shared entries", "page.unread.title": "Unread", "page.starred.title": "Starred", @@ -284,6 +285,7 @@ "form.feed.label.allow_self_signed_certificates": "Allow self-signed or invalid certificates", "form.feed.label.fetch_via_proxy": "Fetch via proxy", "form.feed.label.disabled": "Do not refresh this feed", + "form.feed.label.no_media_player": "No media player (audio/video)", "form.feed.label.hide_globally": "Hide entries in global unread list", "form.category.label.title": "Title", "form.category.hide_globally": "Hide entries in global unread list", @@ -320,6 +322,7 @@ "form.prefs.label.entry_order": "Entry sorting column", "form.prefs.label.default_home_page": "Default home page", "form.prefs.label.categories_sorting_order": "Categories sorting", + "form.prefs.label.mark_read_on_view": "Automatically mark entries as read when viewed", "form.import.label.file": "OPML file", "form.import.label.url": "URL", "form.integration.fever_activate": "Activate Fever API", @@ -361,6 +364,8 @@ "form.integration.linkding_activate": "Save entries to Linkding", "form.integration.linkding_endpoint": "Linkding API Endpoint", "form.integration.linkding_api_key": "Linkding API key", + "form.integration.linkding_tags": "Linkding Tags", + "form.integration.linkding_bookmark": "Mark bookmark as unread", "form.integration.matrix_bot_activate": "Push new entries to Matrix", "form.integration.matrix_bot_user": "Username for Matrix", "form.integration.matrix_bot_password": "Password for Matrix user", diff --git a/locale/translations/es_ES.json b/locale/translations/es_ES.json index 8ae726ff..d1b9fcc5 100644 --- a/locale/translations/es_ES.json +++ b/locale/translations/es_ES.json @@ -80,7 +80,8 @@ "entry.estimated_reading_time": [ "%d minuto de lectura", "%d minutos de lectura" - ], + ], + "entry.tags.label": "Etiquetas:", "page.shared_entries.title": "Artículos compartidos", "page.unread.title": "No leídos", "page.starred.title": "Marcadores", @@ -284,6 +285,7 @@ "form.feed.label.allow_self_signed_certificates": "Permitir certificados autofirmados o no válidos", "form.feed.label.fetch_via_proxy": "Buscar a través de proxy", "form.feed.label.disabled": "No actualice este feed", + "form.feed.label.no_media_player": "No media player (audio/video)", "form.feed.label.hide_globally": "Ocultar artículos en la lista global de no leídos", "form.category.label.title": "Título", "form.category.hide_globally": "Ocultar artículos en la lista global de no leídos", @@ -320,6 +322,7 @@ "form.prefs.label.entry_order": "Columna de clasificación de artículos", "form.prefs.label.default_home_page": "Página de inicio por defecto", "form.prefs.label.categories_sorting_order": "Clasificación por categorías", + "form.prefs.label.mark_read_on_view": "Marcar automáticamente las entradas como leídas cuando se vean", "form.import.label.file": "Archivo OPML", "form.import.label.url": "URL", "form.integration.fever_activate": "Activar API de Fever", @@ -361,6 +364,8 @@ "form.integration.linkding_activate": "Enviar artículos a Linkding", "form.integration.linkding_endpoint": "Acceso API de Linkding", "form.integration.linkding_api_key": "Clave de API de Linkding", + "form.integration.linkding_tags": "Linkding Tags", + "form.integration.linkding_bookmark": "Marcar marcador como no leído", "form.integration.matrix_bot_activate": "Transferir nuevos artículos a Matrix", "form.integration.matrix_bot_user": "Nombre de usuario para Matrix", "form.integration.matrix_bot_password": "Contraseña para el usuario de Matrix", diff --git a/locale/translations/fi_FI.json b/locale/translations/fi_FI.json index 9f49ce66..554fe014 100644 --- a/locale/translations/fi_FI.json +++ b/locale/translations/fi_FI.json @@ -81,6 +81,7 @@ "%d minuutin lukuaika", "%d minuutin lukuaika" ], + "entry.tags.label": "Tags:", "page.shared_entries.title": "Jaetut artikkelit", "page.unread.title": "Lukemattomat", "page.starred.title": "Suosikit", @@ -284,6 +285,7 @@ "form.feed.label.allow_self_signed_certificates": "Salli itseallekirjoitetut tai virheelliset varmenteet", "form.feed.label.fetch_via_proxy": "Nouda välityspalvelimen kautta", "form.feed.label.disabled": "Älä päivitä tätä syötettä", + "form.feed.label.no_media_player": "No media player (audio/video)", "form.feed.label.hide_globally": "Piilota artikkelit lukemattomien listassa", "form.category.label.title": "Otsikko", "form.category.hide_globally": "Piilota artikkelit lukemattomien listassa", @@ -320,6 +322,7 @@ "form.prefs.label.entry_order": "Lajittele sarakkeen mukaan", "form.prefs.label.default_home_page": "Oletusarvoinen etusivu", "form.prefs.label.categories_sorting_order": "Kategorioiden lajittelu", + "form.prefs.label.mark_read_on_view": "Merkitse kohdat automaattisesti luetuiksi, kun niitä tarkastellaan", "form.import.label.file": "OPML-tiedosto", "form.import.label.url": "URL", "form.integration.fever_activate": "Ota Fever API käyttöön", @@ -361,6 +364,8 @@ "form.integration.linkding_activate": "Tallenna artikkelit Linkkiin", "form.integration.linkding_endpoint": "Linkding API-päätepiste", "form.integration.linkding_api_key": "Linkding API-avain", + "form.integration.linkding_tags": "Linkding Tags", + "form.integration.linkding_bookmark": "Merkitse kirjanmerkki lukemattomaksi", "form.integration.matrix_bot_activate": "Siirrä uudet artikkelit Matrixiin", "form.integration.matrix_bot_user": "Matrixin käyttäjätunnus", "form.integration.matrix_bot_password": "Matrix-käyttäjän salasana", diff --git a/locale/translations/fr_FR.json b/locale/translations/fr_FR.json index 5ed90ddf..13b5481b 100644 --- a/locale/translations/fr_FR.json +++ b/locale/translations/fr_FR.json @@ -80,7 +80,8 @@ "entry.estimated_reading_time": [ "%d minute de lecture", "%d minutes de lecture" - ], + ], + "entry.tags.label": "Libellés :", "page.shared_entries.title": "Articles partagés", "page.unread.title": "Non lus", "page.starred.title": "Favoris", @@ -284,6 +285,7 @@ "form.feed.label.allow_self_signed_certificates": "Autoriser les certificats auto-signés ou non valides", "form.feed.label.fetch_via_proxy": "Récupérer via proxy", "form.feed.label.disabled": "Ne pas actualiser ce flux", + "form.feed.label.no_media_player": "Pas de lecteur multimedia (audio/vidéo)", "form.feed.label.hide_globally": "Masquer les entrées dans la liste globale non lue", "form.category.label.title": "Titre", "form.category.hide_globally": "Masquer les entrées dans la liste globale non lue", @@ -320,6 +322,7 @@ "form.prefs.label.entry_order": "Colonne de tri des entrées", "form.prefs.label.default_home_page": "Page d'accueil par défaut", "form.prefs.label.categories_sorting_order": "Colonne de tri des catégories", + "form.prefs.label.mark_read_on_view": "Marquer automatiquement les entrées comme lues lorsqu'elles sont consultées", "form.import.label.file": "Fichier OPML", "form.import.label.url": "URL", "form.integration.fever_activate": "Activer l'API de Fever", @@ -361,6 +364,8 @@ "form.integration.linkding_activate": "Sauvegarder les articles vers Linkding", "form.integration.linkding_endpoint": "URL de l'API de Linkding", "form.integration.linkding_api_key": "Clé d'API de Linkding", + "form.integration.linkding_tags": "Linkding Tags", + "form.integration.linkding_bookmark": "Marquer le lien comme non lu", "form.integration.matrix_bot_activate": "Envoyer les nouveaux articles vers Matrix", "form.integration.matrix_bot_user": "Nom de l'utilisateur Matrix", "form.integration.matrix_bot_password": "Mot de passe de l'utilisateur Matrix", diff --git a/locale/translations/hi_IN.json b/locale/translations/hi_IN.json index 179fe59a..dbe6107d 100644 --- a/locale/translations/hi_IN.json +++ b/locale/translations/hi_IN.json @@ -80,7 +80,8 @@ "entry.estimated_reading_time": [ "पढ़ने मे %d मिनट मागेगा", "पढ़ने मे %d मिनट मागेगा" - ], + ], + "entry.tags.label": "टैग:", "page.shared_entries.title": "साझा किया हुआ प्रविष्टि", "page.unread.title": "अपठित", "page.starred.title": "तारांकित", @@ -284,6 +285,7 @@ "form.feed.label.allow_self_signed_certificates": "स्व-हस्ताक्षरित या अमान्य प्रमाणपत्रों की अनुमति दें", "form.feed.label.fetch_via_proxy": "प्रॉक्सी के माध्यम से प्राप्त करें", "form.feed.label.disabled": "इस फ़ीड को रीफ़्रेश न करें", + "form.feed.label.no_media_player": "No media player (audio/video)", "form.feed.label.hide_globally": "वैश्विक अपठित सूची में प्रविष्टियां छिपाएं", "form.category.label.title": "शीर्षक", "form.category.hide_globally": "वैश्विक अपठित सूची में प्रविष्टियां छिपाएं", @@ -320,6 +322,7 @@ "form.prefs.label.entry_order": "प्रवेश छँटाई कॉलम", "form.prefs.label.default_home_page": "डिफ़ॉल्ट होमपेज़", "form.prefs.label.categories_sorting_order": "श्रेणियाँ छँटाई", + "form.prefs.label.mark_read_on_view": "देखे जाने पर स्वचालित रूप से प्रविष्टियों को पढ़ने के रूप में चिह्नित करें", "form.import.label.file": "ओपीएमएल फ़ाइल", "form.import.label.url": "यूआरएल", "form.integration.fever_activate": "फीवर एपीआई सक्रिय करें", @@ -361,6 +364,8 @@ "form.integration.linkding_activate": "लिंक्डिन में विषयवस्तु सहेजें", "form.integration.linkding_endpoint": "लिंकिंग एपीआई समापन बिंदु", "form.integration.linkding_api_key": "लिंकिंग एपीआई कुंजी", + "form.integration.linkding_tags": "Linkding Tags", + "form.integration.linkding_bookmark": "बुकमार्क को अपठित के रूप में चिह्नित करें", "form.integration.matrix_bot_activate": "नए लेखों को मैट्रिक्स में स्थानांतरित करें", "form.integration.matrix_bot_user": "मैट्रिक्स के लिए उपयोगकर्ता नाम", "form.integration.matrix_bot_password": "मैट्रिक्स उपयोगकर्ता के लिए पासवर्ड", diff --git a/locale/translations/id_ID.json b/locale/translations/id_ID.json index 6748ea41..3fa277fb 100644 --- a/locale/translations/id_ID.json +++ b/locale/translations/id_ID.json @@ -79,7 +79,8 @@ "entry.shared_entry.label": "Bagikan", "entry.estimated_reading_time": [ "%d menit untuk dibaca" - ], + ], + "entry.tags.label": "Tanda:", "page.shared_entries.title": "Entri yang Dibagikan", "page.unread.title": "Belum Dibaca", "page.starred.title": "Markah", @@ -281,6 +282,7 @@ "form.feed.label.allow_self_signed_certificates": "Perbolehkan sertifikat web tidak valid atau sertifikasi sendiri", "form.feed.label.fetch_via_proxy": "Ambil via Proksi", "form.feed.label.disabled": "Jangan perbarui umpan ini", + "form.feed.label.no_media_player": "No media player (audio/video)", "form.feed.label.hide_globally": "Sembunyikan entri di daftar belum dibaca global", "form.category.label.title": "Judul", "form.category.hide_globally": "Sembunyikan entri di daftar belum dibaca global", @@ -317,6 +319,7 @@ "form.prefs.label.entry_order": "Pengurutan Kolom Entri", "form.prefs.label.default_home_page": "Beranda Baku", "form.prefs.label.categories_sorting_order": "Pengurutan Kategori", + "form.prefs.label.mark_read_on_view": "Secara otomatis menandai entri sebagai telah dibaca saat dilihat", "form.import.label.file": "Berkas OPML", "form.import.label.url": "URL", "form.integration.fever_activate": "Aktifkan API Fever", @@ -358,6 +361,8 @@ "form.integration.linkding_activate": "Simpan artikel ke Linkding", "form.integration.linkding_endpoint": "Titik URL API Linkding", "form.integration.linkding_api_key": "Kunci API Linkding", + "form.integration.linkding_tags": "Linkding Tags", + "form.integration.linkding_bookmark": "Tandai markah sebagai belum dibaca", "form.integration.matrix_bot_activate": "Kirim entri baru ke Matrix", "form.integration.matrix_bot_user": "Nama Pengguna Matrix", "form.integration.matrix_bot_password": "Kata Sandi Matrix", diff --git a/locale/translations/it_IT.json b/locale/translations/it_IT.json index d244c79a..31876b79 100644 --- a/locale/translations/it_IT.json +++ b/locale/translations/it_IT.json @@ -80,7 +80,8 @@ "entry.estimated_reading_time": [ "%d minuto di lettura", "%d minuti di lettura" - ], + ], + "entry.tags.label": "Tag:", "page.shared_entries.title": "Voci condivise", "page.unread.title": "Da leggere", "page.starred.title": "Preferiti", @@ -284,6 +285,7 @@ "form.feed.label.allow_self_signed_certificates": "Consenti certificati autofirmati o non validi", "form.feed.label.fetch_via_proxy": "Recuperare tramite proxy", "form.feed.label.disabled": "Non aggiornare questo feed", + "form.feed.label.no_media_player": "No media player (audio/video)", "form.feed.label.hide_globally": "Nascondere le voci nella lista globale dei non letti", "form.category.label.title": "Titolo", "form.category.hide_globally": "Nascondere le voci nella lista globale dei non letti", @@ -320,6 +322,7 @@ "form.prefs.label.entry_order": "Colonna di ordinamento delle voci", "form.prefs.label.default_home_page": "Pagina iniziale predefinita", "form.prefs.label.categories_sorting_order": "Ordinamento delle categorie", + "form.prefs.label.mark_read_on_view": "Contrassegna automaticamente le voci come lette quando visualizzate", "form.import.label.file": "File OPML", "form.import.label.url": "URL", "form.integration.fever_activate": "Abilita l'API di Fever", @@ -361,6 +364,8 @@ "form.integration.linkding_activate": "Salva gli articoli su Linkding", "form.integration.linkding_endpoint": "Endpoint dell'API di Linkding", "form.integration.linkding_api_key": "API key dell'account Linkding", + "form.integration.linkding_tags": "Linkding Tags", + "form.integration.linkding_bookmark": "Segna i preferiti come non letti", "form.integration.matrix_bot_activate": "Trasferimento di nuovi articoli a Matrix", "form.integration.matrix_bot_user": "Nome utente per Matrix", "form.integration.matrix_bot_password": "Password per l'utente Matrix", diff --git a/locale/translations/ja_JP.json b/locale/translations/ja_JP.json index cd394f3d..4d4ba130 100644 --- a/locale/translations/ja_JP.json +++ b/locale/translations/ja_JP.json @@ -81,6 +81,7 @@ "%d 分で読めます", "%d 分で読めます" ], + "entry.tags.label": "タグ:", "page.shared_entries.title": "共有エントリ", "page.unread.title": "未読", "page.starred.title": "星付き", @@ -284,6 +285,7 @@ "form.feed.label.allow_self_signed_certificates": "自己署名証明書または無効な証明書を許可する", "form.feed.label.fetch_via_proxy": "プロキシ経由で取得", "form.feed.label.disabled": "このフィードを更新しない", + "form.feed.label.no_media_player": "No media player (audio/video)", "form.feed.label.hide_globally": "未読一覧に記事を表示しない", "form.category.label.title": "タイトル", "form.category.hide_globally": "未読一覧に記事を表示しない", @@ -320,6 +322,7 @@ "form.prefs.label.entry_order": "記事の表示順の基準", "form.prefs.label.default_home_page": "デフォルトのトップページ", "form.prefs.label.categories_sorting_order": "カテゴリの表示順", + "form.prefs.label.mark_read_on_view": "表示時にエントリを自動的に既読としてマークします", "form.import.label.file": "OPML ファイル", "form.import.label.url": "URL", "form.integration.fever_activate": "Fever API を有効にする", @@ -361,6 +364,8 @@ "form.integration.linkding_activate": "Linkding に記事を保存する", "form.integration.linkding_endpoint": "Linkding の API Endpoint", "form.integration.linkding_api_key": "Linkding の API key", + "form.integration.linkding_tags": "Linkding Tags", + "form.integration.linkding_bookmark": "ブックマークを未読にする", "form.integration.matrix_bot_activate": "新しい記事をMatrixに転送する", "form.integration.matrix_bot_user": "Matrixのユーザー名", "form.integration.matrix_bot_password": "Matrixユーザ用パスワード", diff --git a/locale/translations/nl_NL.json b/locale/translations/nl_NL.json index a21e2454..102d1359 100644 --- a/locale/translations/nl_NL.json +++ b/locale/translations/nl_NL.json @@ -81,6 +81,7 @@ "%d minuut leestijd", "%d minuten leestijd" ], + "entry.tags.label": "Labels:", "page.shared_entries.title": "Gedeelde vermeldingen", "page.unread.title": "Ongelezen", "page.starred.title": "Favorieten", @@ -284,6 +285,7 @@ "form.feed.label.allow_self_signed_certificates": "Sta zelfondertekende of ongeldige certificaten toe", "form.feed.label.fetch_via_proxy": "Ophalen via proxy", "form.feed.label.disabled": "Vernieuw deze feed niet", + "form.feed.label.no_media_player": "No media player (audio/video)", "form.feed.label.hide_globally": "Verberg items in de globale ongelezen lijst", "form.category.label.title": "Naam", "form.category.hide_globally": "Verberg items in de globale ongelezen lijst", @@ -320,6 +322,7 @@ "form.prefs.label.entry_order": "Ingang Sorteerkolom", "form.prefs.label.default_home_page": "Standaard startpagina", "form.prefs.label.categories_sorting_order": "Categorieën sorteren", + "form.prefs.label.mark_read_on_view": "Items automatisch markeren als gelezen wanneer ze worden bekeken", "form.import.label.file": "OPML-bestand", "form.import.label.url": "URL", "form.integration.fever_activate": "Activeer Fever API", @@ -361,6 +364,8 @@ "form.integration.linkding_activate": "Opslaan naar Linkding", "form.integration.linkding_endpoint": "Linkding URL", "form.integration.linkding_api_key": "Linkding API-sleutel", + "form.integration.linkding_tags": "Linkding Tags", + "form.integration.linkding_bookmark": "Markeer bookmark als gelezen", "form.integration.matrix_bot_activate": "Nieuwe artikelen overbrengen naar Matrix", "form.integration.matrix_bot_user": "Gebruikersnaam voor Matrix", "form.integration.matrix_bot_password": "Wachtwoord voor Matrix-gebruiker", diff --git a/locale/translations/pl_PL.json b/locale/translations/pl_PL.json index ea1ef740..d22775bf 100644 --- a/locale/translations/pl_PL.json +++ b/locale/translations/pl_PL.json @@ -81,6 +81,7 @@ "%d minuta czytania", "%d minut czytania" ], + "entry.tags.label": "Tagi:", "page.shared_entries.title": "Udostępnione wpisy", "page.unread.title": "Nieprzeczytane", "page.starred.title": "Oznaczone gwiazdką", @@ -286,6 +287,7 @@ "form.feed.label.allow_self_signed_certificates": "Zezwalaj na certyfikaty z podpisem własnym lub nieprawidłowe certyfikaty", "form.feed.label.fetch_via_proxy": "Pobierz przez proxy", "form.feed.label.disabled": "Nie odświeżaj tego kanału", + "form.feed.label.no_media_player": "No media player (audio/video)", "form.feed.label.hide_globally": "Ukryj wpisy na globalnej liście nieprzeczytanych", "form.category.label.title": "Tytuł", "form.category.hide_globally": "Ukryj wpisy na globalnej liście nieprzeczytanych", @@ -322,6 +324,7 @@ "form.prefs.label.entry_order": "Kolumna sortowania wpisów", "form.prefs.label.default_home_page": "Domyślna strona główna", "form.prefs.label.categories_sorting_order": "Sortowanie kategorii", + "form.prefs.label.mark_read_on_view": "Automatycznie oznaczaj wpisy jako przeczytane podczas przeglądania", "form.import.label.file": "Plik OPML", "form.import.label.url": "URL", "form.integration.fever_activate": "Aktywuj Fever API", @@ -363,6 +366,8 @@ "form.integration.linkding_activate": "Zapisz artykuły do Linkding", "form.integration.linkding_endpoint": "Linkding URL", "form.integration.linkding_api_key": "Linkding API key", + "form.integration.linkding_tags": "Linkding Tags", + "form.integration.linkding_bookmark": "Zaznacz zakładkę jako nieprzeczytaną", "form.integration.matrix_bot_activate": "Przenieś nowe artykuły do Matrix", "form.integration.matrix_bot_user": "Nazwa użytkownika dla Matrix", "form.integration.matrix_bot_password": "Hasło dla użytkownika Matrix", diff --git a/locale/translations/pt_BR.json b/locale/translations/pt_BR.json index 01df91f4..ac8b2059 100644 --- a/locale/translations/pt_BR.json +++ b/locale/translations/pt_BR.json @@ -32,7 +32,7 @@ "menu.export": "Exportar", "menu.import": "Importar", "menu.create_category": "Criar uma categoria", - "menu.mark_page_as_read": "Marcar essa página como lída", + "menu.mark_page_as_read": "Marcar essa página como lida", "menu.mark_all_as_read": "Marcar todos como lido", "menu.show_all_entries": "Mostrar todas os itens", "menu.show_only_unread_entries": "Mostrar apenas itens não lidos", @@ -81,8 +81,9 @@ "Leitura de %d minuto", "Leitura de %d minutos" ], + "entry.tags.label": "Etiquetas:", "page.shared_entries.title": "Itens compartilhados", - "page.unread.title": "Não lídos", + "page.unread.title": "Não lidos", "page.starred.title": "Favoritos", "page.categories.title": "Categorias", "page.categories.no_feed": "Sem fonte.", @@ -283,6 +284,7 @@ "form.feed.label.ignore_http_cache": "Ignorar cache HTTP", "form.feed.label.allow_self_signed_certificates": "Permitir certificados autoassinados ou inválidos", "form.feed.label.disabled": "Não atualizar esta fonte", + "form.feed.label.no_media_player": "No media player (audio/video)", "form.feed.label.fetch_via_proxy": "Buscar via proxy", "form.feed.label.hide_globally": "Ocultar entradas na lista global não lida", "form.category.label.title": "Título", @@ -320,6 +322,7 @@ "form.prefs.label.entry_order": "Coluna de Ordenação de Entrada", "form.prefs.label.default_home_page": "Página inicial predefinida", "form.prefs.label.categories_sorting_order": "Classificação das categorias", + "form.prefs.label.mark_read_on_view": "Marcar automaticamente as entradas como lidas quando visualizadas", "form.import.label.file": "Arquivo OPML", "form.import.label.url": "URL", "form.integration.fever_activate": "Ativar API do Fever", @@ -333,7 +336,7 @@ "form.integration.pinboard_activate": "Salvar itens no Pinboard", "form.integration.pinboard_token": "Token de API do Pinboard", "form.integration.pinboard_tags": "Etiquetas (tags) do Pinboard", - "form.integration.pinboard_bookmark": "Salvar marcador como não lído", + "form.integration.pinboard_bookmark": "Salvar marcador como não lido", "form.integration.instapaper_activate": "Salvar itens no Instapaper", "form.integration.instapaper_username": "Nome do usuário do Instapaper", "form.integration.instapaper_password": "Senha do Instapaper", @@ -361,6 +364,8 @@ "form.integration.linkding_activate": "Salvar itens no Linkding", "form.integration.linkding_endpoint": "Endpoint de API do Linkding", "form.integration.linkding_api_key": "Chave de API do Linkding", + "form.integration.linkding_tags": "Linkding Tags", + "form.integration.linkding_bookmark": "Salvar marcador como não lido", "form.integration.matrix_bot_activate": "Transferir novos artigos para o Matrix", "form.integration.matrix_bot_user": "Nome de utilizador para Matrix", "form.integration.matrix_bot_password": "Palavra-passe para utilizador da Matrix", diff --git a/locale/translations/ru_RU.json b/locale/translations/ru_RU.json index 9a73d61b..7df14c03 100644 --- a/locale/translations/ru_RU.json +++ b/locale/translations/ru_RU.json @@ -81,7 +81,8 @@ "%d минута чтения", "%d минут чтения" ], - "page.shared_entries.title": "Общедоступные записи", + "entry.tags.label": "Теги:", + "page.shared_entries.title": "Общедоступные статьи", "page.unread.title": "Непрочитанное", "page.starred.title": "Избранное", "page.categories.title": "Категории", @@ -93,15 +94,15 @@ "Есть %d подписки.", "Есть %d подписок." ], - "page.categories.unread_counter": "Количество непрочитанных записей", + "page.categories.unread_counter": "Количество непрочитанных статей", "page.new_category.title": "Новая категория", "page.new_user.title": "Новый пользователь", "page.edit_category.title": "Изменить категорию: %s", "page.edit_user.title": "Изменить пользователя: %s", "page.feeds.title": "Подписки", "page.feeds.last_check": "Последняя проверка:", - "page.feeds.unread_counter": "Количество непрочитанных записей", - "page.feeds.read_counter": "Количество прочитанных записей", + "page.feeds.unread_counter": "Количество непрочитанных статей", + "page.feeds.read_counter": "Количество прочитанных статей", "page.feeds.error_count": [ "%d ошибка", "%d ошибки", @@ -116,15 +117,15 @@ "page.about.build_date": "Дата сборки:", "page.about.author": "Автор:", "page.about.license": "Лицензия:", - "page.about.postgres_version": "Postgres версия:", - "page.about.go_version": "Go версия:", - "page.about.global_config_options": "глобальные параметры конфигурации", + "page.about.postgres_version": "Версия Postgres:", + "page.about.go_version": "Версия Go:", + "page.about.global_config_options": "Глобальные параметры конфигурации", "page.add_feed.title": "Новая подписка", "page.add_feed.no_category": "Категории отсутствуют. У вас должна быть хотя бы одна категория.", - "page.add_feed.label.url": "URL", + "page.add_feed.label.url": "Ссылка", "page.add_feed.submit": "Найти подписку", "page.add_feed.legend.advanced_options": "Расширенные настройки", - "page.add_feed.choose_feed": "Выбрать подписку", + "page.add_feed.choose_feed": "Выберите подписку", "page.edit_feed.title": "Изменить подписку: %s", "page.edit_feed.last_check": "Последняя проверка:", "page.edit_feed.last_modified_header": "Заголовок LastModified:", @@ -132,7 +133,7 @@ "page.edit_feed.no_header": "Отсутствует", "page.edit_feed.last_parsing_error": "Последняя ошибка парсинга", "page.entry.attachments": "Вложения", - "page.keyboard_shortcuts.title": "Сочетания клавиш", + "page.keyboard_shortcuts.title": "Горячие клавиши", "page.keyboard_shortcuts.subtitle.sections": "Навигация по секциям", "page.keyboard_shortcuts.subtitle.items": "Навигация по элементам", "page.keyboard_shortcuts.subtitle.pages": "Навигация по страницам", @@ -195,7 +196,7 @@ "page.sessions.title": "Сессии", "page.sessions.table.date": "Время", "page.sessions.table.ip": "IP адрес", - "page.sessions.table.user_agent": "User Agent", + "page.sessions.table.user_agent": "User-Agent", "page.sessions.table.actions": "Действия", "page.sessions.table.current_session": "Текущая сессия", "page.api_keys.title": "API-ключи", @@ -209,14 +210,14 @@ "page.offline.title": "Автономный режим", "page.offline.message": "Ты не в сети", "page.offline.refresh_page": "Попробуйте обновить страницу", - "alert.no_shared_entry": "Общедоступные записи отсутствуют.", + "alert.no_shared_entry": "Общедоступные статьи отсутствуют.", "alert.no_bookmark": "Избранное отсутствует.", "alert.no_category": "Категории отсутствуют.", "alert.no_category_entry": "В этой категории нет статей.", "alert.no_feed_entry": "В этой подписке отсутствуют статьи.", "alert.no_feed": "У вас нет ни одной подписки.", "alert.no_feed_in_category": "Для этой категории нет подписки.", - "alert.no_history": "Истории пока нет.", + "alert.no_history": "Истории пока что нет.", "alert.feed_error": "С этой подпиской есть проблема", "alert.no_search_result": "Нет результатов для данного поискового запроса.", "alert.no_unread_entry": "Нет непрочитанных статей.", @@ -229,16 +230,16 @@ "error.duplicate_linked_account": "Уже есть кто-то, кто ассоциирован с этим аккаунтом!", "error.duplicate_fever_username": "Уже есть кто-то с таким же именем пользователя Fever!", "error.duplicate_googlereader_username": "Уже есть кто-то с таким же именем пользователя Google Reader!", - "error.pocket_request_token": "Не удается извлечь request token из Pocket!", - "error.pocket_access_token": "Не удается извлечь access token из Pocket!", + "error.pocket_request_token": "Не удалось получить request token от Pocket!", + "error.pocket_access_token": "Не удалось получить ключ доступа от Pocket!", "error.category_already_exists": "Эта категория уже существует.", - "error.unable_to_create_category": "Не удается создать эту категорию.", - "error.unable_to_update_category": "Не удается обновить эту категорию.", + "error.unable_to_create_category": "Не удалось создать эту категорию.", + "error.unable_to_update_category": "Не удалось обновить эту категорию.", "error.user_already_exists": "Этот пользователь уже существует.", - "error.unable_to_create_user": "Не удается создать этого пользователя.", - "error.unable_to_update_user": "Не удается обновить этого пользователя.", - "error.unable_to_update_feed": "Не удается обновить эту подписку.", - "error.subscription_not_found": "Не удается найти подписки.", + "error.unable_to_create_user": "Не удалось создать этого пользователя.", + "error.unable_to_update_user": "Не удалось обновить этого пользователя.", + "error.unable_to_update_feed": "Не удалось обновить эту подписку.", + "error.subscription_not_found": "Не удалось найти подписки.", "error.empty_file": "Этот файл пуст.", "error.bad_credentials": "Неверное имя пользователя или пароль.", "error.fields_mandatory": "Все поля обязательны.", @@ -247,27 +248,27 @@ "error.password_min_length": "Вы должны использовать минимум 6 символов.", "error.settings_mandatory_fields": "Имя пользователя, тема, язык и часовой пояс обязательны.", "error.settings_reading_speed_is_positive": "Скорости считывания должны быть целыми положительными числами.", - "error.entries_per_page_invalid": "Количество записей на странице недействительно.", - "error.feed_mandatory_fields": "URL и категория обязательны.", - "error.feed_already_exists": "Этот фид уже существует.", - "error.invalid_feed_url": "Недействительный URL фида.", - "error.invalid_site_url": "Недействительный URL сайта.", - "error.feed_url_not_empty": "URL-адрес канала не может быть пустым.", - "error.site_url_not_empty": "URL сайта не может быть пустым.", - "error.feed_title_not_empty": "Заголовок фида не может быть пустым.", + "error.entries_per_page_invalid": "Недопустимое значение количества записей на странице.", + "error.feed_mandatory_fields": "Ссылка и категория обязательны.", + "error.feed_already_exists": "Эта подписка уже существует.", + "error.invalid_feed_url": "Недействительная ссылка подписки.", + "error.invalid_site_url": "Недействительный ссылка сайта.", + "error.feed_url_not_empty": "URL-адрес подписки не может быть пустым.", + "error.site_url_not_empty": "Ссылка на сайт не может быть пустой.", + "error.feed_title_not_empty": "Заголовок подписки не может быть пустым.", "error.feed_category_not_found": "Эта категория не существует или не принадлежит этому пользователю.", - "error.feed_invalid_blocklist_rule": "Правило черного списка недействительно.", - "error.feed_invalid_keeplist_rule": "Правило списка хранения недействительно.", + "error.feed_invalid_blocklist_rule": "Правило черного списка некорректно.", + "error.feed_invalid_keeplist_rule": "Правило белого списка некорректно.", "error.user_mandatory_fields": "Имя пользователя обязательно.", - "error.api_key_already_exists": "Этот ключ API уже существует.", - "error.unable_to_create_api_key": "Невозможно создать этот ключ API.", - "error.invalid_theme": "Неверная тема.", - "error.invalid_language": "Неверный язык.", - "error.invalid_timezone": "Неверный часовой пояс.", - "error.invalid_entry_direction": "Неверное направление входа.", + "error.api_key_already_exists": "Этот API-ключ уже существует.", + "error.unable_to_create_api_key": "Невозможно создать этот API-ключ.", + "error.invalid_theme": "Недопустимая тема.", + "error.invalid_language": "Недопустимый язык.", + "error.invalid_timezone": "Недопустымый часовой пояс.", + "error.invalid_entry_direction": "Недопустимая сортировка записей.", "error.invalid_display_mode": "Недопустимый режим отображения веб-приложения.", - "error.invalid_gesture_nav": "Неверная жестовая навигация.", - "error.invalid_default_home_page": "Неверная домашняя страница по умолчанию!", + "error.invalid_gesture_nav": "Недопустимая навигация жестами.", + "error.invalid_default_home_page": "Недопустимая домашняя страница по умолчанию!", "form.feed.label.title": "Название", "form.feed.label.site_url": "URL сайта", "form.feed.label.feed_url": "URL подписки", @@ -275,17 +276,18 @@ "form.feed.label.crawler": "Извлечь оригинальное содержимое", "form.feed.label.feed_username": "Имя пользователя подписки", "form.feed.label.feed_password": "Пароль подписки", - "form.feed.label.user_agent": "Переопределить User Agent по умолчанию", - "form.feed.label.cookie": "Установить Ку́ки", + "form.feed.label.user_agent": "Переопределить User-Agent по умолчанию", + "form.feed.label.cookie": "Установить куки", "form.feed.label.scraper_rules": "Правила Scraper", "form.feed.label.rewrite_rules": "Правила Rewrite", - "form.feed.label.blocklist_rules": "Правила блокировки", - "form.feed.label.keeplist_rules": "правила разрешений", + "form.feed.label.blocklist_rules": "Правила черного списка", + "form.feed.label.keeplist_rules": "Правила белого списка", "form.feed.label.urlrewrite_rules": "Правила перезаписи URL", - "form.feed.label.ignore_http_cache": "Игнорировать HTTP-кеш", + "form.feed.label.ignore_http_cache": "Игнорировать HTTP кеш", "form.feed.label.allow_self_signed_certificates": "Разрешить самоподписанные или недействительные сертификаты", - "form.feed.label.fetch_via_proxy": "Получить через прокси", - "form.feed.label.disabled": "Не обновлять этот канал", + "form.feed.label.fetch_via_proxy": "Использовать прокси", + "form.feed.label.disabled": "Не обновлять эту подписку", + "form.feed.label.no_media_player": "Отключить медиаплеер (аудио и видео)", "form.feed.label.hide_globally": "Скрыть записи в глобальном списке непрочитанных", "form.category.label.title": "Название", "form.category.hide_globally": "Скрыть записи в глобальном списке непрочитанных", @@ -296,34 +298,35 @@ "form.prefs.label.language": "Язык", "form.prefs.label.timezone": "Часовой пояс", "form.prefs.label.theme": "Тема", - "form.prefs.label.entry_sorting": "Сортировка записей", - "form.prefs.label.entries_per_page": "Записи на странице", + "form.prefs.label.entry_sorting": "Сортировка статей", + "form.prefs.label.entries_per_page": "Количество статей на страницу", "form.prefs.label.default_reading_speed": "Скорость чтения на других языках (слов в минуту)", "form.prefs.label.cjk_reading_speed": "Скорость чтения на китайском, корейском и японском языках (знаков в минуту)", "form.prefs.label.display_mode": "Режим отображения Progressive Web App (PWA)", "form.prefs.select.older_first": "Сначала старые записи", - "form.prefs.select.recent_first": "Сначала последние записи", + "form.prefs.select.recent_first": "Сначала новые записи", "form.prefs.select.fullscreen": "Полноэкранный", "form.prefs.select.standalone": "Автономный", "form.prefs.select.minimal_ui": "Минимальный", "form.prefs.select.browser": "Браузер", - "form.prefs.select.publish_time": "Время публикации заявки", - "form.prefs.select.created_time": "Время создания записи", - "form.prefs.select.alphabetical": "По алфавиту", + "form.prefs.select.publish_time": "Время публикации статьи", + "form.prefs.select.created_time": "Время создания статьи", + "form.prefs.select.alphabetical": "В алфавитном порядке", "form.prefs.select.unread_count": "Количество непрочитанных", - "form.prefs.select.none": "Никто", + "form.prefs.select.none": "Отключить", "form.prefs.select.tap": "Двойное нажатие", - "form.prefs.select.swipe": "Проведите", - "form.prefs.label.keyboard_shortcuts": "Включить сочетания клавиш", - "form.prefs.label.entry_swipe": "Включить пролистывание ввода на сенсорных экранах", - "form.prefs.label.gesture_nav": "Жест для перехода между записями", + "form.prefs.select.swipe": "Свайп", + "form.prefs.label.keyboard_shortcuts": "Включить горячие клавиши", + "form.prefs.label.entry_swipe": "Включить пролистывание свайпом на сенсорных экранах", + "form.prefs.label.gesture_nav": "Жест для перехода между статьями", "form.prefs.label.show_reading_time": "Показать примерное время чтения статей", - "form.prefs.label.custom_css": "Пользовательские CSS", - "form.prefs.label.entry_order": "Колонка сортировки ввода", + "form.prefs.label.custom_css": "Пользовательский CSS", + "form.prefs.label.entry_order": "Столбец сортировки статей", "form.prefs.label.default_home_page": "Домашняя страница по умолчанию", "form.prefs.label.categories_sorting_order": "Сортировка категорий", + "form.prefs.label.mark_read_on_view": "Автоматически отмечать записи как прочитанные при просмотре", "form.import.label.file": "OPML файл", - "form.import.label.url": "URL", + "form.import.label.url": "Ссылка", "form.integration.fever_activate": "Активировать Fever API", "form.integration.fever_username": "Имя пользователя Fever", "form.integration.fever_password": "Пароль Fever", @@ -333,41 +336,43 @@ "form.integration.googlereader_password": "Пароль Google Reader", "form.integration.googlereader_endpoint": "Конечная точка Google Reader API:", "form.integration.pinboard_activate": "Сохранять статьи в Pinboard", - "form.integration.pinboard_token": "Pinboard API Token", + "form.integration.pinboard_token": "Токен Pinboard API", "form.integration.pinboard_tags": "Теги Pinboard", "form.integration.pinboard_bookmark": "Помечать закладки как непрочитанное", "form.integration.instapaper_activate": "Сохранять статьи в Instapaper", "form.integration.instapaper_username": "Имя пользователя Instapaper", "form.integration.instapaper_password": "Пароль Instapaper", "form.integration.pocket_activate": "Сохранять статьи в Pocket", - "form.integration.pocket_consumer_key": "Pocket Consumer Key", - "form.integration.pocket_access_token": "Pocket Access Token", + "form.integration.pocket_consumer_key": "Ключ пользователя Pocket", + "form.integration.pocket_access_token": "Ключ доспупа к Pocket", "form.integration.pocket_connect_link": "Подключить аккаунт Pocket", - "form.integration.wallabag_only_url": "Отправлять только URL (вместо всего содержимого)", + "form.integration.wallabag_only_url": "Отправлять только ссылку (без содержимого)", "form.integration.wallabag_activate": "Сохранять статьи в Wallabag", "form.integration.wallabag_endpoint": "Конечная точка Wallabag API", - "form.integration.wallabag_client_id": "Wallabag Client ID", + "form.integration.wallabag_client_id": "Номер клиента Wallabag", "form.integration.wallabag_client_secret": "Wallabag Client Secret", "form.integration.wallabag_username": "Имя пользователя Wallabag", "form.integration.wallabag_password": "Пароль Wallabag", "form.integration.nunux_keeper_activate": "Сохранять статьи в Nunux Keeper", "form.integration.nunux_keeper_endpoint": "Конечная точка Nunux Keeper API", - "form.integration.nunux_keeper_api_key": "Nunux Keeper API Key", + "form.integration.nunux_keeper_api_key": "API-ключ Nunux Keeper", "form.integration.espial_activate": "Сохранять статьи в Espial", "form.integration.espial_endpoint": "Конечная точка Espial API", - "form.integration.espial_api_key": "Espial API key", + "form.integration.espial_api_key": "API-ключ Espial", "form.integration.espial_tags": "Теги Espial", - "form.integration.telegram_bot_activate": "Публикуйте новые статьи в Telegram-чате", + "form.integration.telegram_bot_activate": "Репостить новые статьи в Telegram-чат", "form.integration.telegram_bot_token": "Токен бота", "form.integration.telegram_chat_id": "ID чата", "form.integration.linkding_activate": "Сохранять статьи в Linkding", "form.integration.linkding_endpoint": "Конечная точка Linkding API", - "form.integration.linkding_api_key": "Linkding API key", - "form.integration.matrix_bot_activate": "Перенос новых статей в Матрицу", - "form.integration.matrix_bot_user": "Имя пользователя для Matrix", - "form.integration.matrix_bot_password": "Пароль для пользователя Matrix", - "form.integration.matrix_bot_url": "URL сервера Матрицы", - "form.integration.matrix_bot_chat_id": "ID комнаты Матрицы", + "form.integration.linkding_api_key": "API-ключ Linkding", + "form.integration.linkding_tags": "Теги Linkding", + "form.integration.linkding_bookmark": "Помечать закладки как непрочитанное", + "form.integration.matrix_bot_activate": "Репостить новые статьи в Matrix", + "form.integration.matrix_bot_user": "Имя пользователя Matrix", + "form.integration.matrix_bot_password": "Пароль пользователя Matrix", + "form.integration.matrix_bot_url": "Ссылка на сервер Matrix", + "form.integration.matrix_bot_chat_id": "ID комнаты Matrix", "form.api_key.label.description": "Описание API-ключа", "form.submit.loading": "Загрузка…", "form.submit.saving": "Сохранение…", diff --git a/locale/translations/tr_TR.json b/locale/translations/tr_TR.json index a495c9d6..78cbdbe9 100644 --- a/locale/translations/tr_TR.json +++ b/locale/translations/tr_TR.json @@ -81,6 +81,7 @@ "%d dakikalık okuma", "%d dakikalık okuma" ], + "entry.tags.label": "Etiketleri:", "page.shared_entries.title": "Paylaşılan iletiler", "page.unread.title": "Okunmadı", "page.starred.title": "Yıldızlı", @@ -284,6 +285,7 @@ "form.feed.label.allow_self_signed_certificates": "Kendinden imzalı veya geçersiz sertifikalara izin ver", "form.feed.label.fetch_via_proxy": "Proxy ile çek", "form.feed.label.disabled": "Bu beslemeyi yenileme", + "form.feed.label.no_media_player": "No media player (audio/video)", "form.feed.label.hide_globally": "Genel okunmamış listesindeki girişleri gizle", "form.category.label.title": "Başlık", "form.category.hide_globally": "Genel okunmamış listesindeki girişleri gizle", @@ -320,6 +322,7 @@ "form.prefs.label.entry_order": "Giriş Sıralama Sütunu", "form.prefs.label.default_home_page": "Varsayılan ana sayfa", "form.prefs.label.categories_sorting_order": "Kategoriler sıralama", + "form.prefs.label.mark_read_on_view": "Girişleri görüntülendiğinde otomatik olarak okundu olarak işaretle", "form.import.label.file": "OPML dosyası", "form.import.label.url": "URL", "form.integration.fever_activate": "Fever API'yi Etkinleştir", @@ -361,6 +364,8 @@ "form.integration.linkding_activate": "Makaleleri Linkding'e kaydet", "form.integration.linkding_endpoint": "Linkding API Uç Noktası", "form.integration.linkding_api_key": "Linkding API Anahtarı", + "form.integration.linkding_tags": "Linkding Tags", + "form.integration.linkding_bookmark": "Yer imini okunmadı olarak işaretle", "form.integration.matrix_bot_activate": "Yeni makaleleri Matrix'e aktarın", "form.integration.matrix_bot_user": "Matrix için Kullanıcı Adı", "form.integration.matrix_bot_password": "Matrix kullanıcısı için şifre", diff --git a/locale/translations/uk_UA.json b/locale/translations/uk_UA.json index abc47b35..4e06b6e5 100644 --- a/locale/translations/uk_UA.json +++ b/locale/translations/uk_UA.json @@ -82,6 +82,7 @@ "читати %d хвилини", "читати %d хвилин" ], + "entry.tags.label": "Теги:", "page.shared_entries.title": "Спильні записи", "page.unread.title": "Непрочитане", "page.starred.title": "З зірочкою", @@ -283,6 +284,7 @@ "form.feed.label.allow_self_signed_certificates": "Дозволити сертифікати з власним підписом або недійсні", "form.feed.label.fetch_via_proxy": "Використати проксі-сервер", "form.feed.label.disabled": "Не оновлювати цю стрічку", + "form.feed.label.no_media_player": "No media player (audio/video)", "form.feed.label.hide_globally": "Приховати записи в глобальному списку непрочитаного", "form.category.label.title": "Назва", "form.category.hide_globally": "Приховати записи в глобальному списку непрочитаного", @@ -319,6 +321,7 @@ "form.prefs.label.entry_order": "Стовпець сортування записів", "form.prefs.label.default_home_page": "Домашня сторінка за умовчанням", "form.prefs.label.categories_sorting_order": "Сортування за категоріями", + "form.prefs.label.mark_read_on_view": "Автоматично позначати записи як прочитані під час перегляду", "form.import.label.file": "Файл OPML", "form.import.label.url": "URL-адреса", "form.integration.fever_activate": "Увімкнути API Fever", @@ -360,6 +363,8 @@ "form.integration.linkding_activate": "Зберігати статті до Linkding", "form.integration.linkding_endpoint": "Linkding API Endpoint", "form.integration.linkding_api_key": "Ключ API Linkding", + "form.integration.linkding_tags": "Linkding Tags", + "form.integration.linkding_bookmark": "Відмічати закладку як непрочитану", "form.integration.matrix_bot_activate": "Перенесення нових статей в Матрицю", "form.integration.matrix_bot_user": "Ім'я користувача для Matrix", "form.integration.matrix_bot_password": "Пароль для користувача Matrix", diff --git a/locale/translations/zh_CN.json b/locale/translations/zh_CN.json index b867cb3d..e333885e 100644 --- a/locale/translations/zh_CN.json +++ b/locale/translations/zh_CN.json @@ -81,6 +81,7 @@ "需要 %d 分钟阅读", "需要 %d 分钟阅读" ], + "entry.tags.label": "标签:", "page.shared_entries.title": "分享文章", "page.unread.title": "未读", "page.starred.title": "收藏", @@ -282,6 +283,7 @@ "form.feed.label.allow_self_signed_certificates": "允许自签名证书或无效证书", "form.feed.label.fetch_via_proxy": "通过代理获取", "form.feed.label.disabled": "请勿刷新此源", + "form.feed.label.no_media_player": "No media player (audio/video)", "form.feed.label.hide_globally": "隐藏全局未读列表中的文章", "form.category.label.title": "标题", "form.category.hide_globally": "隐藏全局未读列表中的文章", @@ -318,6 +320,7 @@ "form.prefs.label.entry_order": "文章排序依据", "form.prefs.label.default_home_page": "默认主页", "form.prefs.label.categories_sorting_order": "分类排序", + "form.prefs.label.mark_read_on_view": "查看时自动将条目标记为已读", "form.import.label.file": "OPML 文件", "form.import.label.url": "URL", "form.integration.fever_activate": "启用 Fever API", @@ -359,6 +362,8 @@ "form.integration.linkding_activate": "保存文章到 Linkding", "form.integration.linkding_endpoint": "Linkding API 端点", "form.integration.linkding_api_key": "Linkding API 密钥", + "form.integration.linkding_tags": "Linkding 默认标签", + "form.integration.linkding_bookmark": "标记为未读", "form.integration.matrix_bot_activate": "将新文章转移到 Matrix", "form.integration.matrix_bot_user": "矩阵的用户名", "form.integration.matrix_bot_password": "矩阵用户密码", diff --git a/locale/translations/zh_TW.json b/locale/translations/zh_TW.json index 3e8f85d8..1ef1e2b1 100644 --- a/locale/translations/zh_TW.json +++ b/locale/translations/zh_TW.json @@ -81,6 +81,7 @@ "需要 %d 分鐘閱讀", "需要 %d 分鐘閱讀" ], + "entry.tags.label": "標籤:", "page.shared_entries.title": "分享文章", "page.unread.title": "未讀", "page.starred.title": "收藏", @@ -284,6 +285,7 @@ "form.feed.label.allow_self_signed_certificates": "允許自簽章憑證或無效憑證", "form.feed.label.fetch_via_proxy": "透過代理獲取", "form.feed.label.disabled": "請勿重新整理此Feed", + "form.feed.label.no_media_player": "No media player (audio/video)", "form.feed.label.hide_globally": "隱藏全域性未讀列表中的文章", "form.category.label.title": "標題", "form.category.hide_globally": "隱藏全域性未讀列表中的文章", @@ -320,6 +322,7 @@ "form.prefs.label.entry_order": "文章排序依據", "form.prefs.label.default_home_page": "默認主頁", "form.prefs.label.categories_sorting_order": "分類排序", + "form.prefs.label.mark_read_on_view": "查看時自動將條目標記為已讀", "form.import.label.file": "OPML 檔案", "form.import.label.url": "URL", "form.integration.fever_activate": "啟用 Fever API", @@ -361,6 +364,8 @@ "form.integration.linkding_activate": "儲存文章到 Linkding", "form.integration.linkding_endpoint": "Linkding API 端點", "form.integration.linkding_api_key": "Linkding API 金鑰", + "form.integration.linkding_tags": "Linkding Tags", + "form.integration.linkding_bookmark": "標記為未讀", "form.integration.matrix_bot_activate": "將新文章轉移到 Matrix", "form.integration.matrix_bot_user": "矩陣的用戶名", "form.integration.matrix_bot_password": "矩陣用戶密碼", diff --git a/logger/doc.go b/logger/doc.go deleted file mode 100644 index 82852a44..00000000 --- a/logger/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 logger handles application log messages with different levels. -*/ -package logger // import "miniflux.app/logger" diff --git a/logger/logger.go b/logger/logger.go index f329b2c6..67e8ce3d 100644 --- a/logger/logger.go +++ b/logger/logger.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package logger // import "miniflux.app/logger" diff --git a/main.go b/main.go index aaed0c53..a3b9c6d0 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package main // import "miniflux.app" diff --git a/metric/metric.go b/metric/metric.go index 924f44ac..2ec51978 100644 --- a/metric/metric.go +++ b/metric/metric.go @@ -1,6 +1,5 @@ -// Copyright 2020 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package metric // import "miniflux.app/metric" diff --git a/miniflux.1 b/miniflux.1 index 29f8e149..d4d2d8d5 100644 --- a/miniflux.1 +++ b/miniflux.1 @@ -1,33 +1,34 @@ .\" Manpage for miniflux. -.TH "MINIFLUX" "1" "August 29, 2022" "\ \&" "\ \&" +.TH "MINIFLUX" "1" "June 25, 2023" "\ \&" "\ \&" .SH NAME miniflux \- Minimalist and opinionated feed reader .SH SYNOPSIS -\fBminiflux\fR [-vic] [-create-admin] [-debug] [-flush-sessions] [-info] [-migrate] - [-reset-feed-errors] [-reset-password] [-version] [-config-file] [-config-dump] +\fBminiflux\fR [-vic] [-config-dump] [-config-file] [-create-admin] [-debug] [-flush-sessions] + [-healthcheck] [-info] [-migrate] [-refresh-feeds] [-reset-feed-errors] [-reset-password] + [-run-cleanup-tasks] [-version] .SH DESCRIPTION \fBminiflux\fR is a minimalist and opinionated feed reader. .SH OPTIONS .PP -.B \-c -.RS 4 -Load configuration file\&. -.RE -.PP -.B \-config-file -.RS 4 -Load configuration file\&. -.RE -.PP .B \-config-dump .RS 4 Print parsed configuration values. This will include sensitive information like passwords\&. .RE .PP +.B \-c /path/to/miniflux.conf +.RS 4 +Load configuration file\&. +.RE +.PP +.B \-config-file /path/to/miniflux.conf +.RS 4 +Load configuration file\&. +.RE +.PP .B \-create-admin .RS 4 Create admin user\&. @@ -43,7 +44,7 @@ Show debug logs\&. Flush all sessions (disconnect users)\&. .RE .PP -.B \-healthcheck +.B \-healthcheck .RS 4 Perform a health check on the given endpoint\&. .br @@ -52,12 +53,12 @@ The value "auto" try to guess the health check endpoint\&. .PP .B \-i .RS 4 -Show application information\&. +Show build information\&. .RE .PP .B \-info .RS 4 -Show application information\&. +Show build information\&. .RE .PP .B \-migrate @@ -65,6 +66,11 @@ Show application information\&. Run SQL migrations\&. .RE .PP +.B \-refresh-feeds +.RS 4 +Refresh a batch of feeds and exit\&. +.RE +.PP .B \-reset-feed-errors .RS 4 Clear all feed errors for all users\&. @@ -75,6 +81,11 @@ Clear all feed errors for all users\&. Reset user password\&. .RE .PP +.B \-run-cleanup-tasks +.RS 4 +Run cleanup tasks (delete old sessions and archives old entries)\&. +.RE +.PP .B \-v .RS 4 Show application version\&. diff --git a/model/api_key.go b/model/api_key.go index 479c429e..7f105a68 100644 --- a/model/api_key.go +++ b/model/api_key.go @@ -1,6 +1,5 @@ -// Copyright 2020 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package model // import "miniflux.app/model" diff --git a/model/app_session.go b/model/app_session.go index ad4db3bb..ad4fb5dc 100644 --- a/model/app_session.go +++ b/model/app_session.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package model // import "miniflux.app/model" diff --git a/model/categories_sort_options.go b/model/categories_sort_options.go index fa77f1cc..0ec086bf 100644 --- a/model/categories_sort_options.go +++ b/model/categories_sort_options.go @@ -1,6 +1,5 @@ -// Copyright 2022 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package model // import "miniflux.app/model" diff --git a/model/category.go b/model/category.go index 6bb415d4..ed223286 100644 --- a/model/category.go +++ b/model/category.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package model // import "miniflux.app/model" @@ -12,8 +11,8 @@ type Category struct { Title string `json:"title"` UserID int64 `json:"user_id"` HideGlobally bool `json:"hide_globally"` - FeedCount int `json:"-"` - TotalUnread int `json:"-"` + FeedCount *int `json:"feed_count,omitempty"` + TotalUnread *int `json:"total_unread,omitempty"` } func (c *Category) String() string { diff --git a/model/doc.go b/model/doc.go deleted file mode 100644 index c5536ff1..00000000 --- a/model/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 model contains all data structures used by the application. -*/ -package model // import "miniflux.app/model" diff --git a/model/enclosure.go b/model/enclosure.go index 52388794..b25657e5 100644 --- a/model/enclosure.go +++ b/model/enclosure.go @@ -1,17 +1,33 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package model // import "miniflux.app/model" +import "strings" // Enclosure represents an attachment. type Enclosure struct { - ID int64 `json:"id"` - UserID int64 `json:"user_id"` - EntryID int64 `json:"entry_id"` - URL string `json:"url"` - MimeType string `json:"mime_type"` - Size int64 `json:"size"` + ID int64 `json:"id"` + UserID int64 `json:"user_id"` + EntryID int64 `json:"entry_id"` + URL string `json:"url"` + MimeType string `json:"mime_type"` + Size int64 `json:"size"` + MediaProgression int64 `json:"media_progression"` +} + +// Html5MimeType will modify the actual MimeType to allow direct playback from HTML5 player for some kind of MimeType +func (e Enclosure) Html5MimeType() string { + if strings.HasPrefix(e.MimeType, "video") { + switch e.MimeType { + // Solution from this stackoverflow discussion: + // https://stackoverflow.com/questions/15277147/m4v-mimetype-video-mp4-or-video-m4v/66945470#66945470 + // tested at the time of this commit (06/2023) on latest Firefox & Vivaldi on this feed + // https://www.florenceporcel.com/podcast/lfhdu.xml + case "video/m4v": + return "video/x-m4v" + } + } + return e.MimeType } // EnclosureList represents a list of attachments. diff --git a/model/enclosure_test.go b/model/enclosure_test.go new file mode 100644 index 00000000..37b19b4d --- /dev/null +++ b/model/enclosure_test.go @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package model + +import ( + "testing" +) + +func TestEnclosure_Html5MimeTypeGivesOriginalMimeType(t *testing.T) { + enclosure := Enclosure{MimeType: "thing/thisMimeTypeIsNotExpectedToBeReplaced"} + if enclosure.Html5MimeType() != enclosure.MimeType { + t.Fatalf( + "HTML5 MimeType must provide original MimeType if not explicitly Replaced. Got %s ,expected '%s' ", + enclosure.Html5MimeType(), + enclosure.MimeType, + ) + } +} + +func TestEnclosure_Html5MimeTypeReplaceStandardM4vByAppleSpecificMimeType(t *testing.T) { + enclosure := Enclosure{MimeType: "video/m4v"} + if enclosure.Html5MimeType() != "video/x-m4v" { + // Solution from this stackoverflow discussion: + // https://stackoverflow.com/questions/15277147/m4v-mimetype-video-mp4-or-video-m4v/66945470#66945470 + // tested at the time of this commit (06/2023) on latest Firefox & Vivaldi on this feed + // https://www.florenceporcel.com/podcast/lfhdu.xml + t.Fatalf( + "HTML5 MimeType must be replaced by 'video/x-m4v' when originally video/m4v to ensure playbacks in brownser. Got '%s'", + enclosure.Html5MimeType(), + ) + } +} diff --git a/model/entry.go b/model/entry.go index 3290d273..3bb396b8 100644 --- a/model/entry.go +++ b/model/entry.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package model // import "miniflux.app/model" diff --git a/model/feed.go b/model/feed.go index 7068404f..5abaa9eb 100644 --- a/model/feed.go +++ b/model/feed.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package model // import "miniflux.app/model" @@ -46,11 +45,13 @@ type Feed struct { Username string `json:"username"` Password string `json:"password"` Disabled bool `json:"disabled"` + NoMediaPlayer bool `json:"no_media_player"` IgnoreHTTPCache bool `json:"ignore_http_cache"` AllowSelfSignedCertificates bool `json:"allow_self_signed_certificates"` FetchViaProxy bool `json:"fetch_via_proxy"` Category *Category `json:"category,omitempty"` Entries Entries `json:"entries,omitempty"` + IconURL string `json:"icon_url"` Icon *FeedIcon `json:"icon"` HideGlobally bool `json:"hide_globally"` UnreadCount int `json:"-"` @@ -134,6 +135,7 @@ type FeedCreationRequest struct { Password string `json:"password"` Crawler bool `json:"crawler"` Disabled bool `json:"disabled"` + NoMediaPlayer bool `json:"no_media_player"` IgnoreHTTPCache bool `json:"ignore_http_cache"` AllowSelfSignedCertificates bool `json:"allow_self_signed_certificates"` FetchViaProxy bool `json:"fetch_via_proxy"` @@ -162,6 +164,7 @@ type FeedModificationRequest struct { Password *string `json:"password"` CategoryID *int64 `json:"category_id"` Disabled *bool `json:"disabled"` + NoMediaPlayer *bool `json:"no_media_player"` IgnoreHTTPCache *bool `json:"ignore_http_cache"` AllowSelfSignedCertificates *bool `json:"allow_self_signed_certificates"` FetchViaProxy *bool `json:"fetch_via_proxy"` @@ -230,6 +233,10 @@ func (f *FeedModificationRequest) Patch(feed *Feed) { feed.Disabled = *f.Disabled } + if f.NoMediaPlayer != nil { + feed.NoMediaPlayer = *f.NoMediaPlayer + } + if f.IgnoreHTTPCache != nil { feed.IgnoreHTTPCache = *f.IgnoreHTTPCache } diff --git a/model/feed_test.go b/model/feed_test.go index f24ce497..0083c2f4 100644 --- a/model/feed_test.go +++ b/model/feed_test.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package model // import "miniflux.app/model" diff --git a/model/home_page.go b/model/home_page.go index f5195550..4f0737ef 100644 --- a/model/home_page.go +++ b/model/home_page.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package model // import "miniflux.app/model" diff --git a/model/icon.go b/model/icon.go index e47fc4a5..f039798b 100644 --- a/model/icon.go +++ b/model/icon.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package model // import "miniflux.app/model" diff --git a/model/integration.go b/model/integration.go index 8124d667..138af747 100644 --- a/model/integration.go +++ b/model/integration.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package model // import "miniflux.app/model" @@ -43,6 +42,8 @@ type Integration struct { LinkdingEnabled bool LinkdingURL string LinkdingAPIKey string + LinkdingTags string + LinkdingMarkAsUnread bool MatrixBotEnabled bool MatrixBotUser string MatrixBotPassword string diff --git a/model/job.go b/model/job.go index 7a42437a..af303d17 100644 --- a/model/job.go +++ b/model/job.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package model // import "miniflux.app/model" diff --git a/model/model.go b/model/model.go index 74b72b34..fa990c98 100644 --- a/model/model.go +++ b/model/model.go @@ -1,6 +1,5 @@ -// Copyright 2021 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package model // import "miniflux.app/model" diff --git a/model/subscription.go b/model/subscription.go index e1157cb5..281ec30d 100644 --- a/model/subscription.go +++ b/model/subscription.go @@ -1,6 +1,5 @@ -// Copyright 2020 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package model // import "miniflux.app/model" diff --git a/model/theme.go b/model/theme.go index 12016ce7..64cc3e37 100644 --- a/model/theme.go +++ b/model/theme.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package model // import "miniflux.app/model" diff --git a/model/user.go b/model/user.go index 62a0594d..235827f4 100644 --- a/model/user.go +++ b/model/user.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package model // import "miniflux.app/model" @@ -35,6 +34,7 @@ type User struct { CJKReadingSpeed int `json:"cjk_reading_speed"` DefaultHomePage string `json:"default_home_page"` CategoriesSortingOrder string `json:"categories_sorting_order"` + MarkReadOnView bool `json:"mark_read_on_view"` } // UserCreationRequest represents the request to create a user. @@ -69,6 +69,7 @@ type UserModificationRequest struct { CJKReadingSpeed *int `json:"cjk_reading_speed"` DefaultHomePage *string `json:"default_home_page"` CategoriesSortingOrder *string `json:"categories_sorting_order"` + MarkReadOnView *bool `json:"mark_read_on_view"` } // Patch updates the User object with the modification request. @@ -156,6 +157,10 @@ func (u *UserModificationRequest) Patch(user *User) { if u.CategoriesSortingOrder != nil { user.CategoriesSortingOrder = *u.CategoriesSortingOrder } + + if u.MarkReadOnView != nil { + user.MarkReadOnView = *u.MarkReadOnView + } } // UseTimezone converts last login date to the given timezone. diff --git a/model/user_session.go b/model/user_session.go index 55c2d67c..6db92b0c 100644 --- a/model/user_session.go +++ b/model/user_session.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package model // import "miniflux.app/model" diff --git a/oauth2/doc.go b/oauth2/doc.go deleted file mode 100644 index bfc0ff68..00000000 --- a/oauth2/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 oauth2 abstracts different OAuth2 providers. -*/ -package oauth2 // import "miniflux.app/oauth2" diff --git a/oauth2/google.go b/oauth2/google.go index d0685f0b..ba5f33d6 100644 --- a/oauth2/google.go +++ b/oauth2/google.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package oauth2 // import "miniflux.app/oauth2" diff --git a/oauth2/manager.go b/oauth2/manager.go index 52b2fc8b..dd8abf9f 100644 --- a/oauth2/manager.go +++ b/oauth2/manager.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package oauth2 // import "miniflux.app/oauth2" diff --git a/oauth2/oidc.go b/oauth2/oidc.go index 26d73433..c2cbb4ef 100644 --- a/oauth2/oidc.go +++ b/oauth2/oidc.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package oauth2 // import "miniflux.app/oauth2" @@ -9,7 +8,7 @@ import ( "miniflux.app/model" - "github.com/coreos/go-oidc" + "github.com/coreos/go-oidc/v3/oidc" "golang.org/x/oauth2" ) diff --git a/oauth2/profile.go b/oauth2/profile.go index c3e013e0..4a9e6135 100644 --- a/oauth2/profile.go +++ b/oauth2/profile.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package oauth2 // import "miniflux.app/oauth2" diff --git a/oauth2/provider.go b/oauth2/provider.go index a1fbe94c..41de8916 100644 --- a/oauth2/provider.go +++ b/oauth2/provider.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package oauth2 // import "miniflux.app/oauth2" diff --git a/packaging/docker/alpine/Dockerfile b/packaging/docker/alpine/Dockerfile index fdb9c9c6..a8727624 100644 --- a/packaging/docker/alpine/Dockerfile +++ b/packaging/docker/alpine/Dockerfile @@ -1,4 +1,5 @@ FROM golang:alpine AS build +ENV CGO_ENABLED=0 RUN apk add --no-cache --update git ADD . /go/src/app WORKDIR /go/src/app diff --git a/packaging/docker/distroless/Dockerfile b/packaging/docker/distroless/Dockerfile index 9e28b0df..c6c0a0ef 100644 --- a/packaging/docker/distroless/Dockerfile +++ b/packaging/docker/distroless/Dockerfile @@ -1,4 +1,5 @@ FROM golang:latest AS build +ENV CGO_ENABLED=0 ADD . /go/src/app WORKDIR /go/src/app RUN go build \ diff --git a/packaging/rpm/Dockerfile b/packaging/rpm/Dockerfile index b946e6b1..2e5203a4 100644 --- a/packaging/rpm/Dockerfile +++ b/packaging/rpm/Dockerfile @@ -1,11 +1,10 @@ FROM golang:1 AS build +ENV CGO_ENABLED=0 ADD . /go/src/app WORKDIR /go/src/app RUN make miniflux -FROM centos:8 -RUN dnf --disablerepo '*' --enablerepo=extras swap centos-linux-repos centos-stream-repos -y -RUN dnf distro-sync -y +FROM rockylinux:8 RUN dnf install -y rpm-build RUN mkdir -p /root/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS} RUN echo "%_topdir /root/rpmbuild" >> .rpmmacros diff --git a/proxy/media_proxy.go b/proxy/media_proxy.go index 965ce993..1e433f2a 100644 --- a/proxy/media_proxy.go +++ b/proxy/media_proxy.go @@ -1,6 +1,5 @@ -// Copyright 2020 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package proxy // import "miniflux.app/proxy" diff --git a/proxy/media_proxy_test.go b/proxy/media_proxy_test.go index 45469f0b..2afd27f1 100644 --- a/proxy/media_proxy_test.go +++ b/proxy/media_proxy_test.go @@ -1,6 +1,5 @@ -// Copyright 2020 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package proxy // import "miniflux.app/proxy" diff --git a/proxy/proxy.go b/proxy/proxy.go index 1fe9eceb..a701ecfb 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -1,6 +1,5 @@ -// Copyright 2020 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package proxy // import "miniflux.app/proxy" diff --git a/reader/atom/atom_03.go b/reader/atom/atom_03.go index 3e8dc6d0..1b9ff48f 100644 --- a/reader/atom/atom_03.go +++ b/reader/atom/atom_03.go @@ -1,6 +1,5 @@ -// Copyright 2019 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package atom // import "miniflux.app/reader/atom" diff --git a/reader/atom/atom_03_test.go b/reader/atom/atom_03_test.go index f88424c3..b9cb6f49 100644 --- a/reader/atom/atom_03_test.go +++ b/reader/atom/atom_03_test.go @@ -1,6 +1,5 @@ -// Copyright 2019 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package atom // import "miniflux.app/reader/atom" diff --git a/reader/atom/atom_10.go b/reader/atom/atom_10.go index 344b0aba..1779d25a 100644 --- a/reader/atom/atom_10.go +++ b/reader/atom/atom_10.go @@ -1,6 +1,5 @@ -// Copyright 2019 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package atom // import "miniflux.app/reader/atom" @@ -28,6 +27,7 @@ type atom10Feed struct { ID string `xml:"id"` Title atom10Text `xml:"title"` Authors atomAuthors `xml:"author"` + Icon string `xml:"icon"` Links atomLinks `xml:"link"` Entries []atom10Entry `xml:"entry"` } @@ -54,6 +54,8 @@ func (a *atom10Feed) Transform(baseURL string) *model.Feed { feed.Title = feed.SiteURL } + feed.IconURL = strings.TrimSpace(a.Icon) + for _, entry := range a.Entries { item := entry.Transform() entryURL, err := url.AbsoluteURL(feed.SiteURL, item.URL) diff --git a/reader/atom/atom_10_test.go b/reader/atom/atom_10_test.go index bf45a7a9..632fde9e 100644 --- a/reader/atom/atom_10_test.go +++ b/reader/atom/atom_10_test.go @@ -1,6 +1,5 @@ -// Copyright 2019 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package atom // import "miniflux.app/reader/atom" @@ -49,6 +48,10 @@ func TestParseAtomSample(t *testing.T) { t.Errorf("Incorrect site URL, got: %s", feed.SiteURL) } + if feed.IconURL != "" { + t.Errorf("Incorrect icon URL, got: %s", feed.IconURL) + } + if len(feed.Entries) != 1 { t.Errorf("Incorrect number of entries, got: %d", len(feed.Entries)) } @@ -1649,3 +1652,21 @@ func TestParseFeedWithCategories(t *testing.T) { t.Errorf("Incorrect entry category, got %q instead of %q", result, expected) } } + +func TestParseFeedWithIconURL(t *testing.T) { + data := ` + + Example Feed + + http://example.org/icon.png + ` + + feed, err := Parse("https://example.org/", bytes.NewBufferString(data)) + if err != nil { + t.Fatal(err) + } + + if feed.IconURL != "http://example.org/icon.png" { + t.Errorf("Incorrect icon URL, got: %s", feed.IconURL) + } +} diff --git a/reader/atom/atom_common.go b/reader/atom/atom_common.go index 4a01fa94..4b965d97 100644 --- a/reader/atom/atom_common.go +++ b/reader/atom/atom_common.go @@ -1,6 +1,5 @@ -// Copyright 2019 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package atom // import "miniflux.app/reader/atom" diff --git a/reader/atom/doc.go b/reader/atom/doc.go deleted file mode 100644 index 5cbecc2b..00000000 --- a/reader/atom/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 atom provides an Atom feed parser. -*/ -package atom // import "miniflux.app/reader/atom" diff --git a/reader/atom/parser.go b/reader/atom/parser.go index e4e66819..76eaf605 100644 --- a/reader/atom/parser.go +++ b/reader/atom/parser.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package atom // import "miniflux.app/reader/atom" diff --git a/reader/atom/parser_test.go b/reader/atom/parser_test.go index fac14ef6..ad3ff2ab 100644 --- a/reader/atom/parser_test.go +++ b/reader/atom/parser_test.go @@ -1,6 +1,5 @@ -// Copyright 2019 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package atom // import "miniflux.app/reader/atom" diff --git a/reader/browser/browser.go b/reader/browser/browser.go index a3c3ec47..c539bc09 100644 --- a/reader/browser/browser.go +++ b/reader/browser/browser.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package browser // import "miniflux.app/reader/browser" diff --git a/reader/browser/doc.go b/reader/browser/doc.go deleted file mode 100644 index bcb8d99e..00000000 --- a/reader/browser/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 browser handles website crawling. -*/ -package browser // import "miniflux.app/reader/browser" diff --git a/reader/date/doc.go b/reader/date/doc.go deleted file mode 100644 index fe79d070..00000000 --- a/reader/date/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 date provides a feed date parser. -*/ -package date // import "miniflux.app/reader/date" diff --git a/reader/date/parser.go b/reader/date/parser.go index 04826daa..f00b28eb 100644 --- a/reader/date/parser.go +++ b/reader/date/parser.go @@ -1,12 +1,12 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package date // import "miniflux.app/reader/date" import ( "errors" "fmt" + "math" "strconv" "strings" "time" @@ -211,6 +211,14 @@ var dateFormats = []string{ "Jan. 2, 2006, 03:04 p.m.", "2006-01-02 15:04:05 -07:00", "2 January, 2006", + "2 Jan 2006 MST", + "Mon, January 2, 2006 at 03:04 PM MST", + "Jan 2, 2006 15:04 MST", + "01/02/2006 3:04 pm MST", + "Mon, 2th Jan 2006 15:04:05 MST", + "Mon, 2rd Jan 2006 15:04:05 MST", + "Mon, 2nd Jan 2006 15:04:05 MST", + "Mon, 2st Jan 2006 15:04:05 MST", } var invalidTimezoneReplacer = strings.NewReplacer( @@ -316,11 +324,13 @@ func Parse(rawInput string) (t time.Time, err error) { switch layout { case time.RFC822, time.RFC850, time.RFC1123: if t, err = parseLocalTimeDates(layout, processedInput); err == nil { + t = checkTimezoneRange(t) return } } if t, err = time.Parse(layout, processedInput); err == nil { + t = checkTimezoneRange(t) return } } @@ -348,3 +358,14 @@ func parseLocalTimeDates(layout, ds string) (t time.Time, err error) { return time.ParseInLocation(layout, ds, loc) } + +// https://en.wikipedia.org/wiki/List_of_UTC_offsets +// Offset range: westernmost (−12:00) to the easternmost (+14:00) +// Avoid "pq: time zone displacement out of range" errors +func checkTimezoneRange(t time.Time) time.Time { + _, offset := t.Zone() + if math.Abs(float64(offset)) > 14*60*60 { + t = t.UTC() + } + return t +} diff --git a/reader/date/parser_test.go b/reader/date/parser_test.go index f1d68179..2c4a02c5 100644 --- a/reader/date/parser_test.go +++ b/reader/date/parser_test.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package date // import "miniflux.app/reader/date" @@ -180,6 +179,12 @@ func TestParseWeirdDateFormat(t *testing.T) { "mar., 01 déc. 2020 16:11:02 +0000", "Thurs, 15 Oct 2020 00:00:39 +0000", "Thur, 19 Nov 2020 00:00:39 +0000", + "26 Sep 2022 GMT", + "Thu, June 22, 2023 at 01:11 PM EDT", + "Apr 16, 2023 08:01 GMT", + "Jun 23, 2023 19:00 GMT", + "09/15/2014 4:20 pm PST", + "Fri, 23rd Jun 2023 09:32:20 GMT", } for _, date := range dates { @@ -188,3 +193,16 @@ func TestParseWeirdDateFormat(t *testing.T) { } } } + +func TestParseDateWithTimezoneOutOfRange(t *testing.T) { + date, err := Parse("2023-05-29 00:00:00-23:00") + + if err != nil { + t.Errorf(`Unable to parse date: %v`, err) + } + + _, offset := date.Zone() + if offset != 0 { + t.Errorf(`The offset should be reinitialized to 0 instead of %v because it's out of range`, offset) + } +} diff --git a/reader/doc.go b/reader/doc.go deleted file mode 100644 index a7844e6c..00000000 --- a/reader/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 reader implements everything related to feed parsing. -*/ -package reader // import "miniflux.app/reader" diff --git a/reader/encoding/doc.go b/reader/encoding/doc.go deleted file mode 100644 index 5ab63799..00000000 --- a/reader/encoding/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 encoding handles workarounds to deal with encoding edge cases found into feeds. -*/ -package encoding // import "miniflux.app/reader/encoding" diff --git a/reader/encoding/encoding.go b/reader/encoding/encoding.go index 6bfb9128..a69ccf44 100644 --- a/reader/encoding/encoding.go +++ b/reader/encoding/encoding.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package encoding // import "miniflux.app/reader/encoding" diff --git a/reader/handler/handler.go b/reader/handler/handler.go index be9d6a63..b727ef90 100644 --- a/reader/handler/handler.go +++ b/reader/handler/handler.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package handler // import "miniflux.app/reader/handler" @@ -96,6 +95,7 @@ func CreateFeed(store *storage.Storage, userID int64, feedCreationRequest *model store, subscription.ID, subscription.SiteURL, + subscription.IconURL, feedCreationRequest.UserAgent, feedCreationRequest.FetchViaProxy, feedCreationRequest.AllowSelfSignedCertificates, @@ -189,6 +189,7 @@ func RefreshFeed(store *storage.Storage, userID, feedID int64) error { store, originalFeed.ID, originalFeed.SiteURL, + updatedFeed.IconURL, originalFeed.UserAgent, originalFeed.FetchViaProxy, originalFeed.AllowSelfSignedCertificates, @@ -208,9 +209,9 @@ func RefreshFeed(store *storage.Storage, userID, feedID int64) error { return nil } -func checkFeedIcon(store *storage.Storage, feedID int64, websiteURL, userAgent string, fetchViaProxy, allowSelfSignedCertificates bool) { +func checkFeedIcon(store *storage.Storage, feedID int64, websiteURL, iconURL, userAgent string, fetchViaProxy, allowSelfSignedCertificates bool) { if !store.HasIcon(feedID) { - icon, err := icon.FindIcon(websiteURL, userAgent, fetchViaProxy, allowSelfSignedCertificates) + icon, err := icon.FindIcon(websiteURL, iconURL, userAgent, fetchViaProxy, allowSelfSignedCertificates) if err != nil { logger.Debug(`[CheckFeedIcon] %v (feedID=%d websiteURL=%s)`, err, feedID, websiteURL) } else if icon == nil { diff --git a/reader/icon/doc.go b/reader/icon/doc.go deleted file mode 100644 index 6c72aed9..00000000 --- a/reader/icon/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 icon provides all the logic to download website icons. -*/ -package icon // import "miniflux.app/reader/icon" diff --git a/reader/icon/finder.go b/reader/icon/finder.go index a3985b3f..02d0fe61 100644 --- a/reader/icon/finder.go +++ b/reader/icon/finder.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package icon // import "miniflux.app/reader/icon" @@ -23,30 +22,32 @@ import ( ) // FindIcon try to find the website's icon. -func FindIcon(websiteURL, userAgent string, fetchViaProxy, allowSelfSignedCertificates bool) (*model.Icon, error) { - rootURL := url.RootURL(websiteURL) - logger.Debug("[FindIcon] Trying to find an icon: rootURL=%q websiteURL=%q userAgent=%q", rootURL, websiteURL, userAgent) +func FindIcon(websiteURL, iconURL, userAgent string, fetchViaProxy, allowSelfSignedCertificates bool) (*model.Icon, error) { + if iconURL == "" { + rootURL := url.RootURL(websiteURL) + logger.Debug("[FindIcon] Trying to find an icon: rootURL=%q websiteURL=%q userAgent=%q", rootURL, websiteURL, userAgent) - clt := client.NewClientWithConfig(rootURL, config.Opts) - clt.WithUserAgent(userAgent) - clt.AllowSelfSignedCertificates = allowSelfSignedCertificates + clt := client.NewClientWithConfig(rootURL, config.Opts) + clt.WithUserAgent(userAgent) + clt.AllowSelfSignedCertificates = allowSelfSignedCertificates - if fetchViaProxy { - clt.WithProxy() - } + if fetchViaProxy { + clt.WithProxy() + } - response, err := clt.Get() - if err != nil { - return nil, fmt.Errorf("icon: unable to download website index page: %v", err) - } + response, err := clt.Get() + if err != nil { + return nil, fmt.Errorf("icon: unable to download website index page: %v", err) + } - if response.HasServerFailure() { - return nil, fmt.Errorf("icon: unable to download website index page: status=%d", response.StatusCode) - } + if response.HasServerFailure() { + return nil, fmt.Errorf("icon: unable to download website index page: status=%d", response.StatusCode) + } - iconURL, err := parseDocument(rootURL, response.Body) - if err != nil { - return nil, err + iconURL, err = parseDocument(rootURL, response.Body) + if err != nil { + return nil, err + } } if strings.HasPrefix(iconURL, "data:") { diff --git a/reader/icon/finder_test.go b/reader/icon/finder_test.go index 3f32a566..321b2f7b 100644 --- a/reader/icon/finder_test.go +++ b/reader/icon/finder_test.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package icon // import "miniflux.app/reader/icon" diff --git a/reader/json/doc.go b/reader/json/doc.go deleted file mode 100644 index af67ce08..00000000 --- a/reader/json/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 json provides a JSON feed parser. -*/ -package json // import "miniflux.app/reader/json" diff --git a/reader/json/json.go b/reader/json/json.go index 759b10c7..0011dc23 100644 --- a/reader/json/json.go +++ b/reader/json/json.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package json // import "miniflux.app/reader/json" @@ -17,13 +16,15 @@ import ( ) type jsonFeed struct { - Version string `json:"version"` - Title string `json:"title"` - SiteURL string `json:"home_page_url"` - FeedURL string `json:"feed_url"` - Authors []jsonAuthor `json:"authors"` - Author jsonAuthor `json:"author"` - Items []jsonItem `json:"items"` + Version string `json:"version"` + Title string `json:"title"` + SiteURL string `json:"home_page_url"` + IconURL string `json:"icon"` + FaviconURL string `json:"favicon"` + FeedURL string `json:"feed_url"` + Authors []jsonAuthor `json:"authors"` + Author jsonAuthor `json:"author"` + Items []jsonItem `json:"items"` } type jsonAuthor struct { @@ -76,6 +77,12 @@ func (j *jsonFeed) Transform(baseURL string) *model.Feed { feed.SiteURL = j.SiteURL } + feed.IconURL = strings.TrimSpace(j.IconURL) + + if feed.IconURL == "" { + feed.IconURL = strings.TrimSpace(j.FaviconURL) + } + feed.Title = strings.TrimSpace(j.Title) if feed.Title == "" { feed.Title = feed.SiteURL diff --git a/reader/json/parser.go b/reader/json/parser.go index 56383177..f7ef8331 100644 --- a/reader/json/parser.go +++ b/reader/json/parser.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package json // import "miniflux.app/reader/json" diff --git a/reader/json/parser_test.go b/reader/json/parser_test.go index 186baca6..4640502c 100644 --- a/reader/json/parser_test.go +++ b/reader/json/parser_test.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package json // import "miniflux.app/reader/json" @@ -15,6 +14,8 @@ func TestParseJsonFeed(t *testing.T) { data := `{ "version": "https://jsonfeed.org/version/1", "title": "My Example Feed", + "icon": "https://micro.blog/jsonfeed/avatar.jpg", + "favicon": "https://micro.blog/jsonfeed/favicon.png", "home_page_url": "https://example.org/", "feed_url": "https://example.org/feed.json", "items": [ @@ -48,6 +49,10 @@ func TestParseJsonFeed(t *testing.T) { t.Errorf("Incorrect site URL, got: %s", feed.SiteURL) } + if feed.IconURL != "https://micro.blog/jsonfeed/avatar.jpg" { + t.Errorf("Incorrect icon URL, got: %s", feed.IconURL) + } + if len(feed.Entries) != 2 { t.Errorf("Incorrect number of entries, got: %d", len(feed.Entries)) } @@ -617,3 +622,33 @@ func TestParseTags(t *testing.T) { t.Errorf("Incorrect entry tag, got %q instead of %q", result, expected) } } + +func TestParseFavicon(t *testing.T) { + data := `{ + "version": "https://jsonfeed.org/version/1", + "title": "My Example Feed", + "favicon": "https://micro.blog/jsonfeed/favicon.png", + "home_page_url": "https://example.org/", + "feed_url": "https://example.org/feed.json", + "items": [ + { + "id": "2", + "content_text": "This is a second item.", + "url": "https://example.org/second-item" + }, + { + "id": "1", + "content_html": "

Hello, world!

", + "url": "https://example.org/initial-post" + } + ] + }` + + feed, err := Parse("https://example.org/feed.json", bytes.NewBufferString(data)) + if err != nil { + t.Fatal(err) + } + if feed.IconURL != "https://micro.blog/jsonfeed/favicon.png" { + t.Errorf("Incorrect icon URL, got: %s", feed.IconURL) + } +} diff --git a/reader/media/media.go b/reader/media/media.go index d414191d..8a507598 100644 --- a/reader/media/media.go +++ b/reader/media/media.go @@ -1,6 +1,5 @@ -// Copyright 2019 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package media // import "miniflux.app/reader/media" diff --git a/reader/media/media_test.go b/reader/media/media_test.go index b0d2842d..5a1cee15 100644 --- a/reader/media/media_test.go +++ b/reader/media/media_test.go @@ -1,6 +1,5 @@ -// Copyright 2019 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package media // import "miniflux.app/reader/media" diff --git a/reader/opml/doc.go b/reader/opml/doc.go deleted file mode 100644 index d4e79deb..00000000 --- a/reader/opml/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 opml provides an OPML parser and writer. -*/ -package opml // import "miniflux.app/reader/opml" diff --git a/reader/opml/handler.go b/reader/opml/handler.go index 7661cd4c..a5ad9d8e 100644 --- a/reader/opml/handler.go +++ b/reader/opml/handler.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package opml // import "miniflux.app/reader/opml" diff --git a/reader/opml/opml.go b/reader/opml/opml.go index 98a7b3f9..d65136fe 100644 --- a/reader/opml/opml.go +++ b/reader/opml/opml.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package opml // import "miniflux.app/reader/opml" diff --git a/reader/opml/parser.go b/reader/opml/parser.go index 2dcb809d..7fc4255f 100644 --- a/reader/opml/parser.go +++ b/reader/opml/parser.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package opml // import "miniflux.app/reader/opml" diff --git a/reader/opml/parser_test.go b/reader/opml/parser_test.go index 091d9952..04d2ff14 100644 --- a/reader/opml/parser_test.go +++ b/reader/opml/parser_test.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package opml // import "miniflux.app/reader/opml" diff --git a/reader/opml/serializer.go b/reader/opml/serializer.go index fd3acb33..54f72f40 100644 --- a/reader/opml/serializer.go +++ b/reader/opml/serializer.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package opml // import "miniflux.app/reader/opml" diff --git a/reader/opml/serializer_test.go b/reader/opml/serializer_test.go index ff26e195..f53fcc08 100644 --- a/reader/opml/serializer_test.go +++ b/reader/opml/serializer_test.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package opml // import "miniflux.app/reader/opml" diff --git a/reader/opml/subscription.go b/reader/opml/subscription.go index 415b0957..5fd9e9ed 100644 --- a/reader/opml/subscription.go +++ b/reader/opml/subscription.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package opml // import "miniflux.app/reader/opml" diff --git a/reader/parser/doc.go b/reader/parser/doc.go deleted file mode 100644 index 872ea0ef..00000000 --- a/reader/parser/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 parser provides a generic feed parser that abstract all different formats. -*/ -package parser // import "miniflux.app/reader/parser" diff --git a/reader/parser/format.go b/reader/parser/format.go index c85dd159..27cd8302 100644 --- a/reader/parser/format.go +++ b/reader/parser/format.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package parser // import "miniflux.app/reader/parser" diff --git a/reader/parser/format_test.go b/reader/parser/format_test.go index b2836b6c..7010abd3 100644 --- a/reader/parser/format_test.go +++ b/reader/parser/format_test.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package parser // import "miniflux.app/reader/parser" diff --git a/reader/parser/parser.go b/reader/parser/parser.go index 6214fdb8..ea8e7eaa 100644 --- a/reader/parser/parser.go +++ b/reader/parser/parser.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package parser // import "miniflux.app/reader/parser" diff --git a/reader/parser/parser_test.go b/reader/parser/parser_test.go index 19440b2c..03b8fb2c 100644 --- a/reader/parser/parser_test.go +++ b/reader/parser/parser_test.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package parser // import "miniflux.app/reader/parser" diff --git a/reader/processor/doc.go b/reader/processor/doc.go deleted file mode 100644 index c57660b8..00000000 --- a/reader/processor/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 processor applies rules and sanitize content for feed entries. -*/ -package processor // import "miniflux.app/reader/processor" diff --git a/reader/processor/processor.go b/reader/processor/processor.go index 67f50b28..03765e76 100644 --- a/reader/processor/processor.go +++ b/reader/processor/processor.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package processor @@ -85,7 +84,7 @@ func ProcessFeedEntries(store *storage.Storage, feed *model.Feed, user *model.Us } } - entry.Content = rewrite.Rewriter(url, entry.Content, feed.RewriteRules) + rewrite.Rewriter(url, entry, feed.RewriteRules) // The sanitizer should always run at the end of the process to make sure unsafe HTML is filtered. entry.Content = sanitizer.Sanitize(url, entry.Content) @@ -168,14 +167,14 @@ func ProcessEntryWebPage(feed *model.Feed, entry *model.Entry, user *model.User) return scraperErr } - content = rewrite.Rewriter(url, content, entry.Feed.RewriteRules) - content = sanitizer.Sanitize(url, content) - if content != "" { entry.Content = content entry.ReadingTime = calculateReadingTime(content, user) } + rewrite.Rewriter(url, entry, entry.Feed.RewriteRules) + entry.Content = sanitizer.Sanitize(url, entry.Content) + return nil } diff --git a/reader/processor/processor_test.go b/reader/processor/processor_test.go index e91055b5..acdd674c 100644 --- a/reader/processor/processor_test.go +++ b/reader/processor/processor_test.go @@ -1,6 +1,5 @@ -// Copyright 2020 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package processor // import "miniflux.app/reader/processor" diff --git a/reader/rdf/doc.go b/reader/rdf/doc.go deleted file mode 100644 index d8334ed3..00000000 --- a/reader/rdf/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 rdf provides a RDF feed parser. -*/ -package rdf // import "miniflux.app/reader/rdf" diff --git a/reader/rdf/dublincore.go b/reader/rdf/dublincore.go index f06456d7..fc0a2dfe 100644 --- a/reader/rdf/dublincore.go +++ b/reader/rdf/dublincore.go @@ -1,6 +1,5 @@ -// Copyright 2019 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package rdf // import "miniflux.app/reader/rdf" diff --git a/reader/rdf/parser.go b/reader/rdf/parser.go index 70a9a9e6..75ec39fe 100644 --- a/reader/rdf/parser.go +++ b/reader/rdf/parser.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package rdf // import "miniflux.app/reader/rdf" diff --git a/reader/rdf/parser_test.go b/reader/rdf/parser_test.go index c1bc67cd..280ddc80 100644 --- a/reader/rdf/parser_test.go +++ b/reader/rdf/parser_test.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package rdf // import "miniflux.app/reader/rdf" diff --git a/reader/rdf/rdf.go b/reader/rdf/rdf.go index 9a86bff7..0d0e4606 100644 --- a/reader/rdf/rdf.go +++ b/reader/rdf/rdf.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package rdf // import "miniflux.app/reader/rdf" diff --git a/reader/readability/doc.go b/reader/readability/doc.go deleted file mode 100644 index 22179e55..00000000 --- a/reader/readability/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 readability implements a web page scraper that returns only relevant content. -*/ -package readability // import "miniflux.app/reader/readability" diff --git a/reader/readability/readability.go b/reader/readability/readability.go index b9891a19..bf303e78 100644 --- a/reader/readability/readability.go +++ b/reader/readability/readability.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package readability // import "miniflux.app/reader/readability" diff --git a/reader/rewrite/doc.go b/reader/rewrite/doc.go deleted file mode 100644 index 4f0c172e..00000000 --- a/reader/rewrite/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 rewrite provides functions to manipulate feed contents. -*/ -package rewrite // import "miniflux.app/reader/rewrite" diff --git a/reader/rewrite/rewrite_functions.go b/reader/rewrite/rewrite_functions.go index 1d34a6ea..20ba28cf 100644 --- a/reader/rewrite/rewrite_functions.go +++ b/reader/rewrite/rewrite_functions.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package rewrite // import "miniflux.app/reader/rewrite" @@ -367,3 +366,17 @@ func removeTables(entryContent string) string { output, _ := doc.Find("body").First().Html() return output } + +func removeClickbait(entryTitle string) string { + titleWords := []string{} + for _, word := range strings.Fields(entryTitle) { + runes := []rune(word) + if len(runes) > 1 { + // keep first rune as is to keep the first capital letter + titleWords = append(titleWords, string([]rune{runes[0]})+strings.ToLower(string(runes[1:]))) + } else { + titleWords = append(titleWords, word) + } + } + return strings.Join(titleWords, " ") +} diff --git a/reader/rewrite/rewriter.go b/reader/rewrite/rewriter.go index 961a47ec..ae5397d1 100644 --- a/reader/rewrite/rewriter.go +++ b/reader/rewrite/rewriter.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package rewrite // import "miniflux.app/reader/rewrite" @@ -10,6 +9,7 @@ import ( "text/scanner" "miniflux.app/logger" + "miniflux.app/model" "miniflux.app/url" ) @@ -19,7 +19,7 @@ type rule struct { } // Rewriter modify item contents with a set of rewriting rules. -func Rewriter(entryURL, entryContent, customRewriteRules string) string { +func Rewriter(entryURL string, entry *model.Entry, customRewriteRules string) { rulesList := getPredefinedRewriteRules(entryURL) if customRewriteRules != "" { rulesList = customRewriteRules @@ -31,10 +31,8 @@ func Rewriter(entryURL, entryContent, customRewriteRules string) string { logger.Debug(`[Rewrite] Applying rules %v for %q`, rules, entryURL) for _, rule := range rules { - entryContent = applyRule(entryURL, entryContent, rule) + applyRule(entryURL, entry, rule) } - - return entryContent } func parseRules(rulesText string) (rules []rule) { @@ -60,61 +58,61 @@ func parseRules(rulesText string) (rules []rule) { } } -func applyRule(entryURL, entryContent string, rule rule) string { +func applyRule(entryURL string, entry *model.Entry, rule rule) { switch rule.name { case "add_image_title": - entryContent = addImageTitle(entryURL, entryContent) + entry.Content = addImageTitle(entryURL, entry.Content) case "add_mailto_subject": - entryContent = addMailtoSubject(entryURL, entryContent) + entry.Content = addMailtoSubject(entryURL, entry.Content) case "add_dynamic_image": - entryContent = addDynamicImage(entryURL, entryContent) + entry.Content = addDynamicImage(entryURL, entry.Content) case "add_youtube_video": - entryContent = addYoutubeVideo(entryURL, entryContent) + entry.Content = addYoutubeVideo(entryURL, entry.Content) case "add_invidious_video": - entryContent = addInvidiousVideo(entryURL, entryContent) + entry.Content = addInvidiousVideo(entryURL, entry.Content) case "add_youtube_video_using_invidious_player": - entryContent = addYoutubeVideoUsingInvidiousPlayer(entryURL, entryContent) + entry.Content = addYoutubeVideoUsingInvidiousPlayer(entryURL, entry.Content) case "add_youtube_video_from_id": - entryContent = addYoutubeVideoFromId(entryContent) + entry.Content = addYoutubeVideoFromId(entry.Content) case "add_pdf_download_link": - entryContent = addPDFLink(entryURL, entryContent) + entry.Content = addPDFLink(entryURL, entry.Content) case "nl2br": - entryContent = replaceLineFeeds(entryContent) + entry.Content = replaceLineFeeds(entry.Content) case "convert_text_link", "convert_text_links": - entryContent = replaceTextLinks(entryContent) + entry.Content = replaceTextLinks(entry.Content) case "fix_medium_images": - entryContent = fixMediumImages(entryURL, entryContent) + entry.Content = fixMediumImages(entryURL, entry.Content) case "use_noscript_figure_images": - entryContent = useNoScriptImages(entryURL, entryContent) + entry.Content = useNoScriptImages(entryURL, entry.Content) case "replace": // Format: replace("search-term"|"replace-term") if len(rule.args) >= 2 { - entryContent = replaceCustom(entryContent, rule.args[0], rule.args[1]) + entry.Content = replaceCustom(entry.Content, rule.args[0], rule.args[1]) } else { logger.Debug("[Rewrite] Cannot find search and replace terms for replace rule %s", rule) } case "remove": // Format: remove("#selector > .element, .another") if len(rule.args) >= 1 { - entryContent = removeCustom(entryContent, rule.args[0]) + entry.Content = removeCustom(entry.Content, rule.args[0]) } else { logger.Debug("[Rewrite] Cannot find selector for remove rule %s", rule) } case "add_castopod_episode": - entryContent = addCastopodEpisode(entryURL, entryContent) + entry.Content = addCastopodEpisode(entryURL, entry.Content) case "base64_decode": if len(rule.args) >= 1 { - entryContent = applyFuncOnTextContent(entryContent, rule.args[0], decodeBase64Content) + entry.Content = applyFuncOnTextContent(entry.Content, rule.args[0], decodeBase64Content) } else { - entryContent = applyFuncOnTextContent(entryContent, "body", decodeBase64Content) + entry.Content = applyFuncOnTextContent(entry.Content, "body", decodeBase64Content) } case "parse_markdown": - entryContent = parseMarkdown(entryContent) + entry.Content = parseMarkdown(entry.Content) case "remove_tables": - entryContent = removeTables(entryContent) + entry.Content = removeTables(entry.Content) + case "remove_clickbait": + entry.Title = removeClickbait(entry.Title) } - - return entryContent } func getPredefinedRewriteRules(entryURL string) string { diff --git a/reader/rewrite/rewriter_test.go b/reader/rewrite/rewriter_test.go index 7d3306b1..b7e04e90 100644 --- a/reader/rewrite/rewriter_test.go +++ b/reader/rewrite/rewriter_test.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package rewrite // import "miniflux.app/reader/rewrite" @@ -8,6 +7,8 @@ import ( "reflect" "strings" "testing" + + "miniflux.app/model" ) func TestParseRules(t *testing.T) { @@ -46,178 +47,301 @@ func TestReplaceTextLinks(t *testing.T) { } func TestRewriteWithNoMatchingRule(t *testing.T) { - output := Rewriter("https://example.org/article", `Some text.`, ``) - expected := `Some text.` + controlEntry := &model.Entry{ + Title: `A title`, + Content: `Some text.`, + } + testEntry := &model.Entry{ + Title: `A title`, + Content: `Some text.`, + } + Rewriter("https://example.org/article", testEntry, ``) - if expected != output { - t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected) + if !reflect.DeepEqual(testEntry, controlEntry) { + t.Errorf(`Not expected output: got "%+v" instead of "%+v"`, testEntry, controlEntry) } } func TestRewriteWithYoutubeLink(t *testing.T) { - output := Rewriter("https://www.youtube.com/watch?v=1234", "Video Description", ``) - expected := `
Video Description` + controlEntry := &model.Entry{ + Title: `A title`, + Content: `
Video Description`, + } + testEntry := &model.Entry{ + Title: `A title`, + Content: `Video Description`, + } + Rewriter("https://www.youtube.com/watch?v=1234", testEntry, ``) - if expected != output { - t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected) + if !reflect.DeepEqual(testEntry, controlEntry) { + t.Errorf(`Not expected output: got "%+v" instead of "%+v"`, testEntry, controlEntry) } } func TestRewriteWithInexistingCustomRule(t *testing.T) { - output := Rewriter("https://www.youtube.com/watch?v=1234", `Video Description`, `some rule`) - expected := `Video Description` - if expected != output { - t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected) + controlEntry := &model.Entry{ + Title: `A title`, + Content: `Video Description`, + } + testEntry := &model.Entry{ + Title: `A title`, + Content: `Video Description`, + } + Rewriter("https://www.youtube.com/watch?v=1234", testEntry, `some rule`) + + if !reflect.DeepEqual(testEntry, controlEntry) { + t.Errorf(`Not expected output: got "%+v" instead of "%+v"`, testEntry, controlEntry) } } func TestRewriteWithXkcdLink(t *testing.T) { - description := `Your problem is so terrible, I worry that, if I help you, I risk drawing the attention of whatever god of technology inflicted it on you.` - output := Rewriter("https://xkcd.com/1912/", description, ``) - expected := `
Your problem is so terrible, I worry that, if I help you, I risk drawing the attention of whatever god of technology inflicted it on you.

Your problem is so terrible, I worry that, if I help you, I risk drawing the attention of whatever god of technology inflicted it on you.

` - if expected != output { - t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected) + controlEntry := &model.Entry{ + Title: `A title`, + Content: `
Your problem is so terrible, I worry that, if I help you, I risk drawing the attention of whatever god of technology inflicted it on you.

Your problem is so terrible, I worry that, if I help you, I risk drawing the attention of whatever god of technology inflicted it on you.

`, + } + testEntry := &model.Entry{ + Title: `A title`, + Content: `Your problem is so terrible, I worry that, if I help you, I risk drawing the attention of whatever god of technology inflicted it on you.`, + } + Rewriter("https://xkcd.com/1912/", testEntry, ``) + + if !reflect.DeepEqual(testEntry, controlEntry) { + t.Errorf(`Not expected output: got "%+v" instead of "%+v"`, testEntry, controlEntry) } } func TestRewriteWithXkcdLinkHtmlInjection(t *testing.T) { - description := `<foo>` - output := Rewriter("https://xkcd.com/1912/", description, ``) - expected := `
<foo>

<foo>

` - if expected != output { - t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected) + controlEntry := &model.Entry{ + Title: `A title`, + Content: `
<foo>

<foo>

`, + } + testEntry := &model.Entry{ + Title: `A title`, + Content: `<foo>`, + } + Rewriter("https://xkcd.com/1912/", testEntry, ``) + + if !reflect.DeepEqual(testEntry, controlEntry) { + t.Errorf(`Not expected output: got "%+v" instead of "%+v"`, testEntry, controlEntry) } } func TestRewriteWithXkcdLinkAndImageNoTitle(t *testing.T) { - description := `Your problem is so terrible, I worry that, if I help you, I risk drawing the attention of whatever god of technology inflicted it on you.` - output := Rewriter("https://xkcd.com/1912/", description, ``) - expected := description - if expected != output { - t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected) + controlEntry := &model.Entry{ + Title: `A title`, + Content: `Your problem is so terrible, I worry that, if I help you, I risk drawing the attention of whatever god of technology inflicted it on you.`, + } + testEntry := &model.Entry{ + Title: `A title`, + Content: `Your problem is so terrible, I worry that, if I help you, I risk drawing the attention of whatever god of technology inflicted it on you.`, + } + Rewriter("https://xkcd.com/1912/", testEntry, ``) + + if !reflect.DeepEqual(testEntry, controlEntry) { + t.Errorf(`Not expected output: got "%+v" instead of "%+v"`, testEntry, controlEntry) } } func TestRewriteWithXkcdLinkAndNoImage(t *testing.T) { - description := "test" - output := Rewriter("https://xkcd.com/1912/", description, ``) - expected := description - if expected != output { - t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected) + controlEntry := &model.Entry{ + Title: `A title`, + Content: `test`, + } + testEntry := &model.Entry{ + Title: `A title`, + Content: `test`, + } + Rewriter("https://xkcd.com/1912/", testEntry, ``) + + if !reflect.DeepEqual(testEntry, controlEntry) { + t.Errorf(`Not expected output: got "%+v" instead of "%+v"`, testEntry, controlEntry) } } func TestRewriteWithXkcdAndNoImage(t *testing.T) { - description := "test" - output := Rewriter("https://xkcd.com/1912/", description, ``) - expected := description + controlEntry := &model.Entry{ + Title: `A title`, + Content: `test`, + } + testEntry := &model.Entry{ + Title: `A title`, + Content: `test`, + } + Rewriter("https://xkcd.com/1912/", testEntry, ``) - if expected != output { - t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected) + if !reflect.DeepEqual(testEntry, controlEntry) { + t.Errorf(`Not expected output: got "%+v" instead of "%+v"`, testEntry, controlEntry) } } func TestRewriteMailtoLink(t *testing.T) { - description := `contact` - output := Rewriter("https://www.qwantz.com/", description, ``) - expected := `contact [blah blah]` - if expected != output { - t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected) + controlEntry := &model.Entry{ + Title: `A title`, + Content: `contact [blah blah]`, + } + testEntry := &model.Entry{ + Title: `A title`, + Content: `contact`, + } + Rewriter("https://www.qwantz.com/", testEntry, ``) + + if !reflect.DeepEqual(testEntry, controlEntry) { + t.Errorf(`Not expected output: got "%+v" instead of "%+v"`, testEntry, controlEntry) } } func TestRewriteWithPDFLink(t *testing.T) { - description := "test" - output := Rewriter("https://example.org/document.pdf", description, ``) - expected := `PDF
test` + controlEntry := &model.Entry{ + Title: `A title`, + Content: `PDF
test`, + } + testEntry := &model.Entry{ + Title: `A title`, + Content: `test`, + } + Rewriter("https://example.org/document.pdf", testEntry, ``) - if expected != output { - t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected) + if !reflect.DeepEqual(testEntry, controlEntry) { + t.Errorf(`Not expected output: got "%+v" instead of "%+v"`, testEntry, controlEntry) } } func TestRewriteWithNoLazyImage(t *testing.T) { - description := `Image` - output := Rewriter("https://example.org/article", description, "add_dynamic_image") - expected := description + controlEntry := &model.Entry{ + Title: `A title`, + Content: `Image`, + } + testEntry := &model.Entry{ + Title: `A title`, + Content: `Image`, + } + Rewriter("https://example.org/article", testEntry, "add_dynamic_image") - if expected != output { - t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected) + if !reflect.DeepEqual(testEntry, controlEntry) { + t.Errorf(`Not expected output: got "%+v" instead of "%+v"`, testEntry, controlEntry) } } func TestRewriteWithLazyImage(t *testing.T) { - description := `Image` - output := Rewriter("https://example.org/article", description, "add_dynamic_image") - expected := `Image` + controlEntry := &model.Entry{ + Title: `A title`, + Content: `Image`, + } + testEntry := &model.Entry{ + Title: `A title`, + Content: `Image`, + } + Rewriter("https://example.org/article", testEntry, "add_dynamic_image") - if expected != output { - t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected) + if !reflect.DeepEqual(testEntry, controlEntry) { + t.Errorf(`Not expected output: got "%+v" instead of "%+v"`, testEntry, controlEntry) } } func TestRewriteWithLazyDivImage(t *testing.T) { - description := `
` - output := Rewriter("https://example.org/article", description, "add_dynamic_image") - expected := `Image` + controlEntry := &model.Entry{ + Title: `A title`, + Content: `Image`, + } + testEntry := &model.Entry{ + Title: `A title`, + Content: `
`, + } + Rewriter("https://example.org/article", testEntry, "add_dynamic_image") - if expected != output { - t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected) + if !reflect.DeepEqual(testEntry, controlEntry) { + t.Errorf(`Not expected output: got "%+v" instead of "%+v"`, testEntry, controlEntry) } } func TestRewriteWithUnknownLazyNoScriptImage(t *testing.T) { - description := `Image` - output := Rewriter("https://example.org/article", description, "add_dynamic_image") - expected := `ImageFallback` + controlEntry := &model.Entry{ + Title: `A title`, + Content: `ImageFallback`, + } + testEntry := &model.Entry{ + Title: `A title`, + Content: `Image`, + } + Rewriter("https://example.org/article", testEntry, "add_dynamic_image") - if expected != output { - t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected) + if !reflect.DeepEqual(testEntry, controlEntry) { + t.Errorf(`Not expected output: got "%+v" instead of "%+v"`, testEntry, controlEntry) } } func TestRewriteWithLazySrcset(t *testing.T) { - description := `Image` - output := Rewriter("https://example.org/article", description, "add_dynamic_image") - expected := `Image` + controlEntry := &model.Entry{ + Title: `A title`, + Content: `Image`, + } + testEntry := &model.Entry{ + Title: `A title`, + Content: `Image`, + } + Rewriter("https://example.org/article", testEntry, "add_dynamic_image") - if expected != output { - t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected) + if !reflect.DeepEqual(testEntry, controlEntry) { + t.Errorf(`Not expected output: got "%+v" instead of "%+v"`, testEntry, controlEntry) } } func TestRewriteWithImageAndLazySrcset(t *testing.T) { - description := `Image` - output := Rewriter("https://example.org/article", description, "add_dynamic_image") - expected := `Image` + controlEntry := &model.Entry{ + Title: `A title`, + Content: `Image`, + } + testEntry := &model.Entry{ + Title: `A title`, + Content: `Image`, + } + Rewriter("https://example.org/article", testEntry, "add_dynamic_image") - if expected != output { - t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected) + if !reflect.DeepEqual(testEntry, controlEntry) { + t.Errorf(`Not expected output: got "%+v" instead of "%+v"`, testEntry, controlEntry) } } func TestNewLineRewriteRule(t *testing.T) { - description := "A\nB\nC" - output := Rewriter("https://example.org/article", description, "nl2br") - expected := `A
B
C` + controlEntry := &model.Entry{ + Title: `A title`, + Content: `A
B
C`, + } + testEntry := &model.Entry{ + Title: `A title`, + Content: "A\nB\nC", + } + Rewriter("https://example.org/article", testEntry, "nl2br") - if expected != output { - t.Errorf(`Not expected output: got %q instead of %q`, output, expected) + if !reflect.DeepEqual(testEntry, controlEntry) { + t.Errorf(`Not expected output: got "%+v" instead of "%+v"`, testEntry, controlEntry) } } func TestConvertTextLinkRewriteRule(t *testing.T) { - description := "Test: http://example.org/a/b" - output := Rewriter("https://example.org/article", description, "convert_text_link") - expected := `Test: http://example.org/a/b` + controlEntry := &model.Entry{ + Title: `A title`, + Content: `Test: http://example.org/a/b`, + } + testEntry := &model.Entry{ + Title: `A title`, + Content: `Test: http://example.org/a/b`, + } + Rewriter("https://example.org/article", testEntry, "convert_text_link") - if expected != output { - t.Errorf(`Not expected output: got %q instead of %q`, output, expected) + if !reflect.DeepEqual(testEntry, controlEntry) { + t.Errorf(`Not expected output: got "%+v" instead of "%+v"`, testEntry, controlEntry) } } func TestMediumImage(t *testing.T) { - content := ` + controlEntry := &model.Entry{ + Title: `A title`, + Content: `Image for post`, + } + testEntry := &model.Entry{ + Title: `A title`, + Content: `
@@ -235,103 +359,174 @@ func TestMediumImage(t *testing.T) {
- ` - expected := `Image for post` - output := Rewriter("https://example.org/article", content, "fix_medium_images") - output = strings.TrimSpace(output) + `, + } + Rewriter("https://example.org/article", testEntry, "fix_medium_images") + testEntry.Content = strings.TrimSpace(testEntry.Content) - if expected != output { - t.Errorf(`Not expected output: %s`, output) + if !reflect.DeepEqual(testEntry, controlEntry) { + t.Errorf(`Not expected output: got "%+v" instead of "%+v"`, testEntry, controlEntry) } } func TestRewriteNoScriptImageWithoutNoScriptTag(t *testing.T) { - content := `
The beautiful MDN logo.
MDN Logo
` - expected := `
The beautiful MDN logo.
MDN Logo
` - output := Rewriter("https://example.org/article", content, "use_noscript_figure_images") - output = strings.TrimSpace(output) + controlEntry := &model.Entry{ + Title: `A title`, + Content: `
The beautiful MDN logo.
MDN Logo
`, + } + testEntry := &model.Entry{ + Title: `A title`, + Content: `
The beautiful MDN logo.
MDN Logo
`, + } + Rewriter("https://example.org/article", testEntry, "use_noscript_figure_images") + testEntry.Content = strings.TrimSpace(testEntry.Content) - if expected != output { - t.Errorf(`Not expected output: %s`, output) + if !reflect.DeepEqual(testEntry, controlEntry) { + t.Errorf(`Not expected output: got "%+v" instead of "%+v"`, testEntry, controlEntry) } } func TestRewriteNoScriptImageWithNoScriptTag(t *testing.T) { - content := `
The beautiful MDN logo.
MDN Logo
` - expected := `
MDN Logo
` - output := Rewriter("https://example.org/article", content, "use_noscript_figure_images") - output = strings.TrimSpace(output) + controlEntry := &model.Entry{ + Title: `A title`, + Content: `
MDN Logo
`, + } + testEntry := &model.Entry{ + Title: `A title`, + Content: `
The beautiful MDN logo.
MDN Logo
`, + } + Rewriter("https://example.org/article", testEntry, "use_noscript_figure_images") + testEntry.Content = strings.TrimSpace(testEntry.Content) - if expected != output { - t.Errorf(`Not expected output: %s`, output) + if !reflect.DeepEqual(testEntry, controlEntry) { + t.Errorf(`Not expected output: got "%+v" instead of "%+v"`, testEntry, controlEntry) } } func TestRewriteReplaceCustom(t *testing.T) { - content := `` - expected := `` - output := Rewriter("https://example.org/article", content, `replace("article/(.*).svg"|"article/$1.png")`) + controlEntry := &model.Entry{ + Title: `A title`, + Content: ``, + } + testEntry := &model.Entry{ + Title: `A title`, + Content: ``, + } + Rewriter("https://example.org/article", testEntry, `replace("article/(.*).svg"|"article/$1.png")`) - if expected != output { - t.Errorf(`Not expected output: %s`, output) + if !reflect.DeepEqual(testEntry, controlEntry) { + t.Errorf(`Not expected output: got "%+v" instead of "%+v"`, testEntry, controlEntry) } } func TestRewriteRemoveCustom(t *testing.T) { - content := `
Lorem Ipsum I dont want to see thisSuper important info
` - expected := `
Lorem Ipsum Super important info
` - output := Rewriter("https://example.org/article", content, `remove(".spam, .ads:not(.keep)")`) + controlEntry := &model.Entry{ + Title: `A title`, + Content: `
Lorem Ipsum Super important info
`, + } + testEntry := &model.Entry{ + Title: `A title`, + Content: `
Lorem Ipsum I dont want to see thisSuper important info
`, + } + Rewriter("https://example.org/article", testEntry, `remove(".spam, .ads:not(.keep)")`) - if expected != output { - t.Errorf(`Not expected output: %s`, output) + if !reflect.DeepEqual(testEntry, controlEntry) { + t.Errorf(`Not expected output: got "%+v" instead of "%+v"`, testEntry, controlEntry) } } func TestRewriteAddCastopodEpisode(t *testing.T) { - output := Rewriter("https://podcast.demo/@demo/episodes/test", "Episode Description", `add_castopod_episode`) - expected := `
Episode Description` + controlEntry := &model.Entry{ + Title: `A title`, + Content: `
Episode Description`, + } + testEntry := &model.Entry{ + Title: `A title`, + Content: `Episode Description`, + } + Rewriter("https://podcast.demo/@demo/episodes/test", testEntry, `add_castopod_episode`) - if expected != output { - t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected) + if !reflect.DeepEqual(testEntry, controlEntry) { + t.Errorf(`Not expected output: got "%+v" instead of "%+v"`, testEntry, controlEntry) } } func TestRewriteBase64Decode(t *testing.T) { - content := `VGhpcyBpcyBzb21lIGJhc2U2NCBlbmNvZGVkIGNvbnRlbnQ=` - expected := `This is some base64 encoded content` - output := Rewriter("https://example.org/article", content, `base64_decode`) + controlEntry := &model.Entry{ + Title: `A title`, + Content: `This is some base64 encoded content`, + } + testEntry := &model.Entry{ + Title: `A title`, + Content: `VGhpcyBpcyBzb21lIGJhc2U2NCBlbmNvZGVkIGNvbnRlbnQ=`, + } + Rewriter("https://example.org/article", testEntry, `base64_decode`) - if expected != output { - t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected) + if !reflect.DeepEqual(testEntry, controlEntry) { + t.Errorf(`Not expected output: got "%+v" instead of "%+v"`, testEntry, controlEntry) } } func TestRewriteBase64DecodeInHTML(t *testing.T) { - content := `
Lorem Ipsum not valid base64VGhpcyBpcyBzb21lIGJhc2U2NCBlbmNvZGVkIGNvbnRlbnQ=
` - expected := `
Lorem Ipsum not valid base64This is some base64 encoded content
` - output := Rewriter("https://example.org/article", content, `base64_decode`) + controlEntry := &model.Entry{ + Title: `A title`, + Content: `
Lorem Ipsum not valid base64This is some base64 encoded content
`, + } + testEntry := &model.Entry{ + Title: `A title`, + Content: `
Lorem Ipsum not valid base64VGhpcyBpcyBzb21lIGJhc2U2NCBlbmNvZGVkIGNvbnRlbnQ=
`, + } + Rewriter("https://example.org/article", testEntry, `base64_decode`) - if expected != output { - t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected) + if !reflect.DeepEqual(testEntry, controlEntry) { + t.Errorf(`Not expected output: got "%+v" instead of "%+v"`, testEntry, controlEntry) } } func TestRewriteBase64DecodeArgs(t *testing.T) { - content := `
Lorem IpsumVGhpcyBpcyBzb21lIGJhc2U2NCBlbmNvZGVkIGNvbnRlbnQ=
` - expected := `
Lorem IpsumThis is some base64 encoded content
` - output := Rewriter("https://example.org/article", content, `base64_decode(".base64")`) + controlEntry := &model.Entry{ + Title: `A title`, + Content: `
Lorem IpsumThis is some base64 encoded content
`, + } + testEntry := &model.Entry{ + Title: `A title`, + Content: `
Lorem IpsumVGhpcyBpcyBzb21lIGJhc2U2NCBlbmNvZGVkIGNvbnRlbnQ=
`, + } + Rewriter("https://example.org/article", testEntry, `base64_decode(".base64")`) - if expected != output { - t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected) + if !reflect.DeepEqual(testEntry, controlEntry) { + t.Errorf(`Not expected output: got "%+v" instead of "%+v"`, testEntry, controlEntry) } } func TestRewriteRemoveTables(t *testing.T) { - content := `

Test

Hello World!

Test

` - expected := `

Test

Hello World!

Test

` - output := Rewriter("https://example.org/article", content, `remove_tables`) + controlEntry := &model.Entry{ + Title: `A title`, + Content: `

Test

Hello World!

Test

`, + } + testEntry := &model.Entry{ + Title: `A title`, + Content: `

Test

Hello World!

Test

`, + } + Rewriter("https://example.org/article", testEntry, `remove_tables`) - if expected != output { - t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected) + if !reflect.DeepEqual(testEntry, controlEntry) { + t.Errorf(`Not expected output: got "%+v" instead of "%+v"`, testEntry, controlEntry) + } +} + +func TestRemoveClickbait(t *testing.T) { + controlEntry := &model.Entry{ + Title: `This Is Amazing`, + Content: `Some description`, + } + testEntry := &model.Entry{ + Title: `THIS IS AMAZING`, + Content: `Some description`, + } + Rewriter("https://example.org/article", testEntry, `remove_clickbait`) + + if !reflect.DeepEqual(testEntry, controlEntry) { + t.Errorf(`Not expected output: got "%+v" instead of "%+v"`, testEntry, controlEntry) } } diff --git a/reader/rewrite/rules.go b/reader/rewrite/rules.go index ec6963b8..c2262644 100644 --- a/reader/rewrite/rules.go +++ b/reader/rewrite/rules.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package rewrite // import "miniflux.app/reader/rewrite" @@ -17,7 +16,7 @@ var predefinedRules = map[string]string{ "exocomics.com": "add_image_title", "framatube.org": "nl2br,convert_text_link", "happletea.com": "add_image_title", - "ilpost.it": `remove(".art_tag, #audioPlayerArticle, .author-container, .caption, .ilpostShare, .lastRecents, #mc_embed_signup, .outbrain_inread, p:has(.leggi-anche)")`, + "ilpost.it": `remove(".art_tag, #audioPlayerArticle, .author-container, .caption, .ilpostShare, .lastRecents, #mc_embed_signup, .outbrain_inread, p:has(.leggi-anche), .youtube-overlay")`, "imogenquest.net": "add_image_title", "lukesurl.com": "add_image_title", "medium.com": "fix_medium_images", diff --git a/reader/rss/doc.go b/reader/rss/doc.go deleted file mode 100644 index f5a92fca..00000000 --- a/reader/rss/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 rss provides a RSS feed parser. -*/ -package rss // import "miniflux.app/reader/rss" diff --git a/reader/rss/dublincore.go b/reader/rss/dublincore.go index c461ece8..320d5c61 100644 --- a/reader/rss/dublincore.go +++ b/reader/rss/dublincore.go @@ -1,6 +1,5 @@ -// Copyright 2019 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package rss // import "miniflux.app/reader/rss" diff --git a/reader/rss/feedburner.go b/reader/rss/feedburner.go index baa4e19e..87c9f875 100644 --- a/reader/rss/feedburner.go +++ b/reader/rss/feedburner.go @@ -1,6 +1,5 @@ -// Copyright 2019 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package rss // import "miniflux.app/reader/rss" diff --git a/reader/rss/parser.go b/reader/rss/parser.go index 6c9af4ff..13f8e4b4 100644 --- a/reader/rss/parser.go +++ b/reader/rss/parser.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package rss // import "miniflux.app/reader/rss" diff --git a/reader/rss/parser_test.go b/reader/rss/parser_test.go index d0662c60..e3d106da 100644 --- a/reader/rss/parser_test.go +++ b/reader/rss/parser_test.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package rss // import "miniflux.app/reader/rss" @@ -18,6 +17,11 @@ func TestParseRss2Sample(t *testing.T) { Liftoff News http://liftoff.msfc.nasa.gov/ Liftoff to Space Exploration. + + http://liftoff.msfc.nasa.gov/HomePageXtra/MeatBall.gif + NASA + http://liftoff.msfc.nasa.gov/ + en-us Tue, 10 Jun 2003 04:00:00 GMT Tue, 10 Jun 2003 09:41:01 GMT @@ -71,6 +75,10 @@ func TestParseRss2Sample(t *testing.T) { t.Errorf("Incorrect site URL, got: %s", feed.SiteURL) } + if feed.IconURL != "http://liftoff.msfc.nasa.gov/HomePageXtra/MeatBall.gif" { + t.Errorf("Incorrect image URL, got: %s", feed.IconURL) + } + if len(feed.Entries) != 4 { t.Errorf("Incorrect number of entries, got: %d", len(feed.Entries)) } diff --git a/reader/rss/podcast.go b/reader/rss/podcast.go index 61501ee9..a7d9ab53 100644 --- a/reader/rss/podcast.go +++ b/reader/rss/podcast.go @@ -1,6 +1,5 @@ -// Copyright 2019 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package rss // import "miniflux.app/reader/rss" diff --git a/reader/rss/rss.go b/reader/rss/rss.go index 2969f6bc..48c436af 100644 --- a/reader/rss/rss.go +++ b/reader/rss/rss.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package rss // import "miniflux.app/reader/rss" @@ -27,6 +26,7 @@ type rssFeed struct { Version string `xml:"version,attr"` Title string `xml:"channel>title"` Links []rssLink `xml:"channel>link"` + ImageURL string `xml:"channel>image>url"` Language string `xml:"channel>language"` Description string `xml:"channel>description"` PubDate string `xml:"channel>pubDate"` @@ -58,6 +58,8 @@ func (r *rssFeed) Transform(baseURL string) *model.Feed { feed.Title = feed.SiteURL } + feed.IconURL = strings.TrimSpace(r.ImageURL) + for _, item := range r.Items { entry := item.Transform() if entry.Author == "" { diff --git a/reader/sanitizer/doc.go b/reader/sanitizer/doc.go deleted file mode 100644 index 8e7a4d13..00000000 --- a/reader/sanitizer/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 sanitizer implements a HTML sanitizer that removes unsafe elements. -*/ -package sanitizer // import "miniflux.app/reader/sanitizer" diff --git a/reader/sanitizer/sanitizer.go b/reader/sanitizer/sanitizer.go index 29b5d3fa..9d1154b9 100644 --- a/reader/sanitizer/sanitizer.go +++ b/reader/sanitizer/sanitizer.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package sanitizer // import "miniflux.app/reader/sanitizer" diff --git a/reader/sanitizer/sanitizer_test.go b/reader/sanitizer/sanitizer_test.go index 65961875..4b2a5cb7 100644 --- a/reader/sanitizer/sanitizer_test.go +++ b/reader/sanitizer/sanitizer_test.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package sanitizer // import "miniflux.app/reader/sanitizer" diff --git a/reader/sanitizer/srcset.go b/reader/sanitizer/srcset.go index 758d8d5c..0db17b43 100644 --- a/reader/sanitizer/srcset.go +++ b/reader/sanitizer/srcset.go @@ -1,6 +1,5 @@ -// Copyright 2022 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package sanitizer diff --git a/reader/sanitizer/srcset_test.go b/reader/sanitizer/srcset_test.go index 4f19f9c3..e1501373 100644 --- a/reader/sanitizer/srcset_test.go +++ b/reader/sanitizer/srcset_test.go @@ -1,6 +1,5 @@ -// Copyright 2022 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package sanitizer diff --git a/reader/sanitizer/strip_tags.go b/reader/sanitizer/strip_tags.go index 8dc944fc..263ef91f 100644 --- a/reader/sanitizer/strip_tags.go +++ b/reader/sanitizer/strip_tags.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package sanitizer // import "miniflux.app/reader/sanitizer" diff --git a/reader/sanitizer/strip_tags_test.go b/reader/sanitizer/strip_tags_test.go index 18a0ce82..33a07016 100644 --- a/reader/sanitizer/strip_tags_test.go +++ b/reader/sanitizer/strip_tags_test.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package sanitizer // import "miniflux.app/reader/sanitizer" diff --git a/reader/sanitizer/truncate.go b/reader/sanitizer/truncate.go index 04acc1d6..bac2b453 100644 --- a/reader/sanitizer/truncate.go +++ b/reader/sanitizer/truncate.go @@ -1,6 +1,5 @@ -// Copyright 2022 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package sanitizer diff --git a/reader/sanitizer/truncate_test.go b/reader/sanitizer/truncate_test.go index 2c7e87b6..bb50f039 100644 --- a/reader/sanitizer/truncate_test.go +++ b/reader/sanitizer/truncate_test.go @@ -1,6 +1,5 @@ -// Copyright 2022 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package sanitizer diff --git a/reader/scraper/doc.go b/reader/scraper/doc.go deleted file mode 100644 index 5419c257..00000000 --- a/reader/scraper/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 scraper implements a web page crawler. -*/ -package scraper // import "miniflux.app/reader/scraper" diff --git a/reader/scraper/rules.go b/reader/scraper/rules.go index 5ee66b49..8e0fef74 100644 --- a/reader/scraper/rules.go +++ b/reader/scraper/rules.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package scraper // import "miniflux.app/reader/scraper" diff --git a/reader/scraper/scraper.go b/reader/scraper/scraper.go index 81ebd8c8..d2fef40a 100644 --- a/reader/scraper/scraper.go +++ b/reader/scraper/scraper.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package scraper // import "miniflux.app/reader/scraper" diff --git a/reader/scraper/scraper_test.go b/reader/scraper/scraper_test.go index 53bf37ee..9d586c78 100644 --- a/reader/scraper/scraper_test.go +++ b/reader/scraper/scraper_test.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package scraper // import "miniflux.app/reader/scraper" diff --git a/reader/subscription/doc.go b/reader/subscription/doc.go deleted file mode 100644 index b8b4d1a0..00000000 --- a/reader/subscription/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 subscription implements the logic to find subscriptions on a website. -*/ -package subscription // import "miniflux.app/reader/subscription" diff --git a/reader/subscription/finder.go b/reader/subscription/finder.go index 8d941e8d..df3d275a 100644 --- a/reader/subscription/finder.go +++ b/reader/subscription/finder.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package subscription // import "miniflux.app/reader/subscription" diff --git a/reader/subscription/finder_test.go b/reader/subscription/finder_test.go index 6ea0606e..9124bdeb 100644 --- a/reader/subscription/finder_test.go +++ b/reader/subscription/finder_test.go @@ -1,6 +1,5 @@ -// Copyright 2020 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package subscription diff --git a/reader/subscription/subscription.go b/reader/subscription/subscription.go index 604dce56..2a7ff55b 100644 --- a/reader/subscription/subscription.go +++ b/reader/subscription/subscription.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package subscription // import "miniflux.app/reader/subscription" diff --git a/reader/xml/decoder.go b/reader/xml/decoder.go index 7e5cd755..edf58f47 100644 --- a/reader/xml/decoder.go +++ b/reader/xml/decoder.go @@ -1,6 +1,5 @@ -// Copyright 2019 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package xml // import "miniflux.app/reader/xml" diff --git a/reader/xml/decoder_test.go b/reader/xml/decoder_test.go index 1208ef86..a350955a 100644 --- a/reader/xml/decoder_test.go +++ b/reader/xml/decoder_test.go @@ -1,6 +1,5 @@ -// Copyright 2019 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package xml // import "miniflux.app/reader/xml" diff --git a/service/httpd/doc.go b/service/httpd/doc.go deleted file mode 100644 index 05f6e7c8..00000000 --- a/service/httpd/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 httpd implements the HTTP service. -*/ -package httpd // import "miniflux.app/service/httpd" diff --git a/service/scheduler/doc.go b/service/scheduler/doc.go deleted file mode 100644 index acb1425d..00000000 --- a/service/scheduler/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 scheduler implements the scheduler service. -*/ -package scheduler // import "miniflux.app/service/scheduler" diff --git a/service/scheduler/scheduler.go b/service/scheduler/scheduler.go deleted file mode 100644 index c8dd1322..00000000 --- a/service/scheduler/scheduler.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2018 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 scheduler // import "miniflux.app/service/scheduler" - -import ( - "time" - - "miniflux.app/config" - "miniflux.app/logger" - "miniflux.app/metric" - "miniflux.app/model" - "miniflux.app/storage" - "miniflux.app/worker" -) - -// Serve starts the internal scheduler. -func Serve(store *storage.Storage, pool *worker.Pool) { - logger.Info(`Starting scheduler...`) - - go feedScheduler( - store, - pool, - config.Opts.PollingFrequency(), - config.Opts.BatchSize(), - ) - - go cleanupScheduler( - store, - config.Opts.CleanupFrequencyHours(), - config.Opts.CleanupArchiveReadDays(), - config.Opts.CleanupArchiveUnreadDays(), - config.Opts.CleanupArchiveBatchSize(), - config.Opts.CleanupRemoveSessionsDays(), - ) -} - -func feedScheduler(store *storage.Storage, pool *worker.Pool, frequency, batchSize int) { - for range time.Tick(time.Duration(frequency) * time.Minute) { - jobs, err := store.NewBatch(batchSize) - if err != nil { - logger.Error("[Scheduler:Feed] %v", err) - } else { - logger.Debug("[Scheduler:Feed] Pushing %d jobs", len(jobs)) - pool.Push(jobs) - } - } -} - -func cleanupScheduler(store *storage.Storage, frequency, archiveReadDays, archiveUnreadDays, archiveBatchSize, sessionsDays int) { - for range time.Tick(time.Duration(frequency) * time.Hour) { - nbSessions := store.CleanOldSessions(sessionsDays) - nbUserSessions := store.CleanOldUserSessions(sessionsDays) - logger.Info("[Scheduler:Cleanup] Cleaned %d sessions and %d user sessions", nbSessions, nbUserSessions) - - startTime := time.Now() - if rowsAffected, err := store.ArchiveEntries(model.EntryStatusRead, archiveReadDays, archiveBatchSize); err != nil { - logger.Error("[Scheduler:ArchiveReadEntries] %v", err) - } else { - logger.Info("[Scheduler:ArchiveReadEntries] %d entries changed", rowsAffected) - - if config.Opts.HasMetricsCollector() { - metric.ArchiveEntriesDuration.WithLabelValues(model.EntryStatusRead).Observe(time.Since(startTime).Seconds()) - } - } - - startTime = time.Now() - if rowsAffected, err := store.ArchiveEntries(model.EntryStatusUnread, archiveUnreadDays, archiveBatchSize); err != nil { - logger.Error("[Scheduler:ArchiveUnreadEntries] %v", err) - } else { - logger.Info("[Scheduler:ArchiveUnreadEntries] %d entries changed", rowsAffected) - - if config.Opts.HasMetricsCollector() { - metric.ArchiveEntriesDuration.WithLabelValues(model.EntryStatusUnread).Observe(time.Since(startTime).Seconds()) - } - } - } -} diff --git a/storage/api_key.go b/storage/api_key.go index 00db91df..ea25e484 100644 --- a/storage/api_key.go +++ b/storage/api_key.go @@ -1,6 +1,5 @@ -// Copyright 2020 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package storage // import "miniflux.app/storage" diff --git a/storage/category.go b/storage/category.go index 14e19a53..c5e5f74d 100644 --- a/storage/category.go +++ b/storage/category.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package storage // import "miniflux.app/storage" diff --git a/storage/certificate_cache.go b/storage/certificate_cache.go index 9025df13..0e0c26e0 100644 --- a/storage/certificate_cache.go +++ b/storage/certificate_cache.go @@ -1,6 +1,5 @@ -// Copyright 2020 Dave Marquard. All rights reserved. -// Use of this source code is governed by the Apache 2.0 -// license that can be found in the LICENSE file. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package storage // import "miniflux.app/storage" diff --git a/storage/doc.go b/storage/doc.go deleted file mode 100644 index f5af3109..00000000 --- a/storage/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 storage implements a set of functions to interact with the database. -*/ -package storage // import "miniflux.app/storage" diff --git a/storage/enclosure.go b/storage/enclosure.go index aa9f64ef..3573609b 100644 --- a/storage/enclosure.go +++ b/storage/enclosure.go @@ -1,12 +1,12 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package storage // import "miniflux.app/storage" import ( "database/sql" "fmt" + "strings" "miniflux.app/model" ) @@ -20,7 +20,8 @@ func (s *Storage) GetEnclosures(entryID int64) (model.EnclosureList, error) { entry_id, url, size, - mime_type + mime_type, + media_progression FROM enclosures WHERE @@ -44,6 +45,7 @@ func (s *Storage) GetEnclosures(entryID int64) (model.EnclosureList, error) { &enclosure.URL, &enclosure.Size, &enclosure.MimeType, + &enclosure.MediaProgression, ) if err != nil { @@ -56,46 +58,135 @@ func (s *Storage) GetEnclosures(entryID int64) (model.EnclosureList, error) { return enclosures, nil } +func (s *Storage) GetEnclosure(enclosureID int64) (*model.Enclosure, error) { + query := ` + SELECT + id, + user_id, + entry_id, + url, + size, + mime_type, + media_progression + FROM + enclosures + WHERE + id = $1 + ORDER BY id ASC + ` + + row := s.db.QueryRow(query, enclosureID) + + var enclosure model.Enclosure + err := row.Scan( + &enclosure.ID, + &enclosure.UserID, + &enclosure.EntryID, + &enclosure.URL, + &enclosure.Size, + &enclosure.MimeType, + &enclosure.MediaProgression, + ) + + if err != nil { + return nil, fmt.Errorf(`store: unable to fetch enclosure row: %v`, err) + } + + return &enclosure, nil +} + func (s *Storage) createEnclosure(tx *sql.Tx, enclosure *model.Enclosure) error { - if enclosure.URL == "" { + enclosureURL := strings.TrimSpace(enclosure.URL) + if enclosureURL == "" { return nil } query := ` INSERT INTO enclosures - (url, size, mime_type, entry_id, user_id) + (url, size, mime_type, entry_id, user_id, media_progression) VALUES - ($1, $2, $3, $4, $5) - RETURNING - id + ($1, $2, $3, $4, $5, $6) + ON CONFLICT (user_id, entry_id, md5(url)) DO NOTHING ` - err := tx.QueryRow( + _, err := tx.Exec( query, - enclosure.URL, + enclosureURL, enclosure.Size, enclosure.MimeType, enclosure.EntryID, enclosure.UserID, - ).Scan(&enclosure.ID) + enclosure.MediaProgression, + ) if err != nil { - return fmt.Errorf(`store: unable to create enclosure %q: %v`, enclosure.URL, err) + return fmt.Errorf(`store: unable to create enclosure: %v`, err) } return nil } func (s *Storage) updateEnclosures(tx *sql.Tx, userID, entryID int64, enclosures model.EnclosureList) error { - // We delete all attachments in the transaction to keep only the ones visible in the feeds. - if _, err := tx.Exec(`DELETE FROM enclosures WHERE user_id=$1 AND entry_id=$2`, userID, entryID); err != nil { - return err + if len(enclosures) == 0 { + return nil } + sqlValues := []any{userID, entryID} + sqlPlaceholders := []string{} + for _, enclosure := range enclosures { + sqlPlaceholders = append(sqlPlaceholders, fmt.Sprintf(`$%d`, len(sqlValues)+1)) + sqlValues = append(sqlValues, strings.TrimSpace(enclosure.URL)) + if err := s.createEnclosure(tx, enclosure); err != nil { return err } } + query := ` + DELETE FROM enclosures + WHERE + user_id=$1 AND + entry_id=$2 AND + url NOT IN (%s) + ` + + query = fmt.Sprintf(query, strings.Join(sqlPlaceholders, `,`)) + + _, err := tx.Exec(query, sqlValues...) + if err != nil { + return fmt.Errorf(`store: unable to delete old enclosures: %v`, err) + } + + return nil +} + +func (s *Storage) UpdateEnclosure(enclosure *model.Enclosure) error { + query := ` + UPDATE + enclosures + SET + url=$1, + size=$2, + mime_type=$3, + entry_id=$4, + user_id=$5, + media_progression=$6 + WHERE + id=$7 + ` + _, err := s.db.Exec(query, + enclosure.URL, + enclosure.Size, + enclosure.MimeType, + enclosure.EntryID, + enclosure.UserID, + enclosure.MediaProgression, + enclosure.ID, + ) + + if err != nil { + return fmt.Errorf(`store: unable to update enclosure #%d : %v`, enclosure.ID, err) + } + return nil } diff --git a/storage/entry.go b/storage/entry.go index f99d9ccc..5dbb9c04 100644 --- a/storage/entry.go +++ b/storage/entry.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package storage // import "miniflux.app/storage" @@ -220,15 +219,17 @@ func (s *Storage) updateEntry(tx *sql.Tx, entry *model.Entry) error { } // entryExists checks if an entry already exists based on its hash when refreshing a feed. -func (s *Storage) entryExists(tx *sql.Tx, entry *model.Entry) bool { +func (s *Storage) entryExists(tx *sql.Tx, entry *model.Entry) (bool, error) { var result bool - tx.QueryRow( - `SELECT true FROM entries WHERE user_id=$1 AND feed_id=$2 AND hash=$3`, - entry.UserID, - entry.FeedID, - entry.Hash, - ).Scan(&result) - return result + + // Note: This query uses entries_feed_id_hash_key index (filtering on user_id is not necessary). + err := tx.QueryRow(`SELECT true FROM entries WHERE feed_id=$1 AND hash=$2`, entry.FeedID, entry.Hash).Scan(&result) + + if err != nil && err != sql.ErrNoRows { + return result, fmt.Errorf(`store: unable to check if entry exists: %v`, err) + } + + return result, nil } // GetReadTime fetches the read time of an entry based on its hash, and the feed id and user id from the feed. @@ -275,7 +276,15 @@ func (s *Storage) RefreshFeedEntries(userID, feedID int64, entries model.Entries return fmt.Errorf(`store: unable to start transaction: %v`, err) } - if s.entryExists(tx, entry) { + entryExists, err := s.entryExists(tx, entry) + if err != nil { + if rollbackErr := tx.Rollback(); rollbackErr != nil { + return fmt.Errorf(`store: unable to rollback transaction: %v (rolled back due to: %v)`, rollbackErr, err) + } + return err + } + + if entryExists { if updateExistingEntries { err = s.updateEntry(tx, entry) } @@ -284,7 +293,9 @@ func (s *Storage) RefreshFeedEntries(userID, feedID int64, entries model.Entries } if err != nil { - tx.Rollback() + if rollbackErr := tx.Rollback(); rollbackErr != nil { + return fmt.Errorf(`store: unable to rollback transaction: %v (rolled back due to: %v)`, rollbackErr, err) + } return err } @@ -449,6 +460,33 @@ func (s *Storage) MarkAllAsRead(userID int64) error { return nil } +// MarkGloballyVisibleFeedsAsRead updates all user entries to the read status. +func (s *Storage) MarkGloballyVisibleFeedsAsRead(userID int64) error { + query := ` + UPDATE + entries + SET + status=$1, + changed_at=now() + FROM + feeds + WHERE + entries.feed_id = feeds.id + AND entries.user_id=$2 + AND entries.status=$3 + AND feeds.hide_globally=$4 + ` + result, err := s.db.Exec(query, model.EntryStatusRead, userID, model.EntryStatusUnread, false) + if err != nil { + return fmt.Errorf(`store: unable to mark globally visible feeds as read: %v`, err) + } + + count, _ := result.RowsAffected() + logger.Debug("[Storage:MarkGloballyVisibleFeedsAsRead] %d items marked as read", count) + + return nil +} + // MarkFeedAsRead updates all feed entries to the read status. func (s *Storage) MarkFeedAsRead(userID, feedID int64, before time.Time) error { query := ` diff --git a/storage/entry_pagination_builder.go b/storage/entry_pagination_builder.go index 5b7d5ec1..13987c84 100644 --- a/storage/entry_pagination_builder.go +++ b/storage/entry_pagination_builder.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package storage // import "miniflux.app/storage" diff --git a/storage/entry_query_builder.go b/storage/entry_query_builder.go index dae7bc1f..ce50e342 100644 --- a/storage/entry_query_builder.go +++ b/storage/entry_query_builder.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package storage // import "miniflux.app/storage" @@ -18,13 +17,12 @@ import ( // EntryQueryBuilder builds a SQL query to fetch entries. type EntryQueryBuilder struct { - store *Storage - args []interface{} - conditions []string - order string - direction string - limit int - offset int + store *Storage + args []interface{} + conditions []string + sortExpressions []string + limit int + offset int } // WithSearchQuery adds full-text search query to the condition. @@ -35,8 +33,10 @@ func (e *EntryQueryBuilder) WithSearchQuery(query string) *EntryQueryBuilder { e.args = append(e.args, query) // 0.0000001 = 0.1 / (seconds_in_a_day) - e.WithOrder(fmt.Sprintf("ts_rank(document_vectors, plainto_tsquery($%d)) - extract (epoch from now() - published_at)::float * 0.0000001", nArgs)) - e.WithDirection("DESC") + e.WithSorting( + fmt.Sprintf("ts_rank(document_vectors, plainto_tsquery($%d)) - extract (epoch from now() - published_at)::float * 0.0000001", nArgs), + "DESC", + ) } return e } @@ -168,15 +168,9 @@ func (e *EntryQueryBuilder) WithShareCodeNotEmpty() *EntryQueryBuilder { return e } -// WithOrder set the sorting order. -func (e *EntryQueryBuilder) WithOrder(order string) *EntryQueryBuilder { - e.order = order - return e -} - -// WithDirection set the sorting direction. -func (e *EntryQueryBuilder) WithDirection(direction string) *EntryQueryBuilder { - e.direction = direction +// WithSorting add a sort expression. +func (e *EntryQueryBuilder) WithSorting(column, direction string) *EntryQueryBuilder { + e.sortExpressions = append(e.sortExpressions, fmt.Sprintf("%s %s", column, direction)) return e } @@ -272,6 +266,7 @@ func (e *EntryQueryBuilder) GetEntries() (model.Entries, error) { f.crawler, f.user_agent, f.cookie, + f.no_media_player, fi.icon_id, u.timezone FROM @@ -336,6 +331,7 @@ func (e *EntryQueryBuilder) GetEntries() (model.Entries, error) { &entry.Feed.Crawler, &entry.Feed.UserAgent, &entry.Feed.Cookie, + &entry.Feed.NoMediaPlayer, &iconID, &tz, ) @@ -401,12 +397,8 @@ func (e *EntryQueryBuilder) buildCondition() string { func (e *EntryQueryBuilder) buildSorting() string { var parts []string - if e.order != "" { - parts = append(parts, fmt.Sprintf(`ORDER BY %s`, e.order)) - } - - if e.direction != "" { - parts = append(parts, e.direction) + if len(e.sortExpressions) > 0 { + parts = append(parts, fmt.Sprintf(`ORDER BY %s`, strings.Join(e.sortExpressions, ", "))) } if e.limit > 0 { diff --git a/storage/feed.go b/storage/feed.go index 03d0ba1c..df36bdec 100644 --- a/storage/feed.go +++ b/storage/feed.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package storage // import "miniflux.app/storage" @@ -135,8 +134,7 @@ func (s *Storage) CountAllFeedsWithErrors() int { // Feeds returns all feeds that belongs to the given user. func (s *Storage) Feeds(userID int64) (model.Feeds, error) { builder := NewFeedQueryBuilder(s, userID) - builder.WithOrder(model.DefaultFeedSorting) - builder.WithDirection(model.DefaultFeedSortingDirection) + builder.WithSorting(model.DefaultFeedSorting, model.DefaultFeedSortingDirection) return builder.GetFeeds() } @@ -153,8 +151,7 @@ func getFeedsSorted(builder *FeedQueryBuilder) (model.Feeds, error) { func (s *Storage) FeedsWithCounters(userID int64) (model.Feeds, error) { builder := NewFeedQueryBuilder(s, userID) builder.WithCounters() - builder.WithOrder(model.DefaultFeedSorting) - builder.WithDirection(model.DefaultFeedSortingDirection) + builder.WithSorting(model.DefaultFeedSorting, model.DefaultFeedSortingDirection) return getFeedsSorted(builder) } @@ -171,8 +168,7 @@ func (s *Storage) FeedsByCategoryWithCounters(userID, categoryID int64) (model.F builder := NewFeedQueryBuilder(s, userID) builder.WithCategoryID(categoryID) builder.WithCounters() - builder.WithOrder(model.DefaultFeedSorting) - builder.WithDirection(model.DefaultFeedSortingDirection) + builder.WithSorting(model.DefaultFeedSorting, model.DefaultFeedSortingDirection) return getFeedsSorted(builder) } @@ -243,10 +239,11 @@ func (s *Storage) CreateFeed(feed *model.Feed) error { allow_self_signed_certificates, fetch_via_proxy, hide_globally, - url_rewrite_rules + url_rewrite_rules, + no_media_player ) VALUES - ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22) + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23) RETURNING id ` @@ -274,6 +271,7 @@ func (s *Storage) CreateFeed(feed *model.Feed) error { feed.FetchViaProxy, feed.HideGlobally, feed.UrlRewriteRules, + feed.NoMediaPlayer, ).Scan(&feed.ID) if err != nil { return fmt.Errorf(`store: unable to create feed %q: %v`, feed.FeedURL, err) @@ -288,9 +286,19 @@ func (s *Storage) CreateFeed(feed *model.Feed) error { return fmt.Errorf(`store: unable to start transaction: %v`, err) } - if !s.entryExists(tx, feed.Entries[i]) { + entryExists, err := s.entryExists(tx, feed.Entries[i]) + if err != nil { + if rollbackErr := tx.Rollback(); rollbackErr != nil { + return fmt.Errorf(`store: unable to rollback transaction: %v (rolled back due to: %v)`, rollbackErr, err) + } + return err + } + + if !entryExists { if err := s.createEntry(tx, feed.Entries[i]); err != nil { - tx.Rollback() + if rollbackErr := tx.Rollback(); rollbackErr != nil { + return fmt.Errorf(`store: unable to rollback transaction: %v (rolled back due to: %v)`, rollbackErr, err) + } return err } } @@ -333,9 +341,10 @@ func (s *Storage) UpdateFeed(feed *model.Feed) (err error) { allow_self_signed_certificates=$22, fetch_via_proxy=$23, hide_globally=$24, - url_rewrite_rules=$25 + url_rewrite_rules=$25, + no_media_player=$26 WHERE - id=$26 AND user_id=$27 + id=$27 AND user_id=$28 ` _, err = s.db.Exec(query, feed.FeedURL, @@ -363,6 +372,7 @@ func (s *Storage) UpdateFeed(feed *model.Feed) (err error) { feed.FetchViaProxy, feed.HideGlobally, feed.UrlRewriteRules, + feed.NoMediaPlayer, feed.ID, feed.UserID, ) diff --git a/storage/feed_query_builder.go b/storage/feed_query_builder.go index 4f3194db..88862b55 100644 --- a/storage/feed_query_builder.go +++ b/storage/feed_query_builder.go @@ -1,6 +1,5 @@ -// Copyright 2021 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package storage // import "miniflux.app/storage" @@ -18,8 +17,7 @@ type FeedQueryBuilder struct { store *Storage args []interface{} conditions []string - order string - direction string + sortExpressions []string limit int offset int withCounters bool @@ -66,15 +64,9 @@ func (f *FeedQueryBuilder) WithCounters() *FeedQueryBuilder { return f } -// WithOrder set the sorting order. -func (f *FeedQueryBuilder) WithOrder(order string) *FeedQueryBuilder { - f.order = order - return f -} - -// WithDirection set the sorting direction. -func (f *FeedQueryBuilder) WithDirection(direction string) *FeedQueryBuilder { - f.direction = direction +// WithSorting add a sort expression. +func (f *FeedQueryBuilder) WithSorting(column, direction string) *FeedQueryBuilder { + f.sortExpressions = append(f.sortExpressions, fmt.Sprintf("%s %s", column, direction)) return f } @@ -101,12 +93,8 @@ func (f *FeedQueryBuilder) buildCounterCondition() string { func (f *FeedQueryBuilder) buildSorting() string { var parts []string - if f.order != "" { - parts = append(parts, fmt.Sprintf(`ORDER BY %s`, f.order)) - } - - if f.direction != "" { - parts = append(parts, f.direction) + if len(f.sortExpressions) > 0 { + parts = append(parts, fmt.Sprintf(`ORDER BY %s`, strings.Join(f.sortExpressions, ", "))) } if len(parts) > 0 { @@ -167,6 +155,7 @@ func (f *FeedQueryBuilder) GetFeeds() (model.Feeds, error) { f.allow_self_signed_certificates, f.fetch_via_proxy, f.disabled, + f.no_media_player, f.hide_globally, f.category_id, c.title as category_title, @@ -230,6 +219,7 @@ func (f *FeedQueryBuilder) GetFeeds() (model.Feeds, error) { &feed.AllowSelfSignedCertificates, &feed.FetchViaProxy, &feed.Disabled, + &feed.NoMediaPlayer, &feed.HideGlobally, &feed.Category.ID, &feed.Category.Title, diff --git a/storage/icon.go b/storage/icon.go index 57e6a6fb..ffae3ac2 100644 --- a/storage/icon.go +++ b/storage/icon.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package storage // import "miniflux.app/storage" diff --git a/storage/integration.go b/storage/integration.go index 71ed83c0..98dd271c 100644 --- a/storage/integration.go +++ b/storage/integration.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package storage // import "miniflux.app/storage" @@ -147,6 +146,8 @@ func (s *Storage) Integration(userID int64) (*model.Integration, error) { linkding_enabled, linkding_url, linkding_api_key, + linkding_tags, + linkding_mark_as_unread, matrix_bot_enabled, matrix_bot_user, matrix_bot_password, @@ -196,6 +197,8 @@ func (s *Storage) Integration(userID int64) (*model.Integration, error) { &integration.LinkdingEnabled, &integration.LinkdingURL, &integration.LinkdingAPIKey, + &integration.LinkdingTags, + &integration.LinkdingMarkAsUnread, &integration.MatrixBotEnabled, &integration.MatrixBotUser, &integration.MatrixBotPassword, @@ -260,13 +263,15 @@ func (s *Storage) UpdateIntegration(integration *model.Integration) error { linkding_enabled=$34, linkding_url=$35, linkding_api_key=$36, - matrix_bot_enabled=$37, - matrix_bot_user=$38, - matrix_bot_password=$39, - matrix_bot_url=$40, - matrix_bot_chat_id=$41 + linkding_tags=$37, + linkding_mark_as_unread=$38, + matrix_bot_enabled=$39, + matrix_bot_user=$40, + matrix_bot_password=$41, + matrix_bot_url=$42, + matrix_bot_chat_id=$43 WHERE - user_id=$42 + user_id=$44 ` _, err = s.db.Exec( query, @@ -306,6 +311,8 @@ func (s *Storage) UpdateIntegration(integration *model.Integration) error { integration.LinkdingEnabled, integration.LinkdingURL, integration.LinkdingAPIKey, + integration.LinkdingTags, + integration.LinkdingMarkAsUnread, integration.MatrixBotEnabled, integration.MatrixBotUser, integration.MatrixBotPassword, @@ -354,13 +361,15 @@ func (s *Storage) UpdateIntegration(integration *model.Integration) error { linkding_enabled=$34, linkding_url=$35, linkding_api_key=$36, - matrix_bot_enabled=$37, - matrix_bot_user=$38, - matrix_bot_password=$39, - matrix_bot_url=$40, - matrix_bot_chat_id=$41 + linkding_tags=$37, + linkding_mark_as_unread=$38, + matrix_bot_enabled=$39, + matrix_bot_user=$40, + matrix_bot_password=$41, + matrix_bot_url=$42, + matrix_bot_chat_id=$43 WHERE - user_id=$42 + user_id=$44 ` _, err = s.db.Exec( query, @@ -400,6 +409,8 @@ func (s *Storage) UpdateIntegration(integration *model.Integration) error { integration.LinkdingEnabled, integration.LinkdingURL, integration.LinkdingAPIKey, + integration.LinkdingTags, + integration.LinkdingMarkAsUnread, integration.MatrixBotEnabled, integration.MatrixBotUser, integration.MatrixBotPassword, diff --git a/storage/job.go b/storage/job.go index 57e289ed..e983b55c 100644 --- a/storage/job.go +++ b/storage/job.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package storage // import "miniflux.app/storage" diff --git a/storage/session.go b/storage/session.go index f57b68e8..71de8291 100644 --- a/storage/session.go +++ b/storage/session.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package storage // import "miniflux.app/storage" diff --git a/storage/storage.go b/storage/storage.go index ca3d2360..5b582b1f 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package storage // import "miniflux.app/storage" diff --git a/storage/timezone.go b/storage/timezone.go index d21ac262..14545093 100644 --- a/storage/timezone.go +++ b/storage/timezone.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package storage // import "miniflux.app/storage" diff --git a/storage/user.go b/storage/user.go index 4b0737f2..5e20cee4 100644 --- a/storage/user.go +++ b/storage/user.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package storage // import "miniflux.app/storage" @@ -87,10 +86,11 @@ func (s *Storage) CreateUser(userCreationRequest *model.UserCreationRequest) (*m openid_connect_id, display_mode, entry_order, - default_reading_speed, - cjk_reading_speed, - default_home_page, - categories_sorting_order + default_reading_speed, + cjk_reading_speed, + default_home_page, + categories_sorting_order, + mark_read_on_view ` tx, err := s.db.Begin() @@ -128,6 +128,7 @@ func (s *Storage) CreateUser(userCreationRequest *model.UserCreationRequest) (*m &user.CJKReadingSpeed, &user.DefaultHomePage, &user.CategoriesSortingOrder, + &user.MarkReadOnView, ) if err != nil { tx.Rollback() @@ -183,9 +184,10 @@ func (s *Storage) UpdateUser(user *model.User) error { default_reading_speed=$18, cjk_reading_speed=$19, default_home_page=$20, - categories_sorting_order=$21 + categories_sorting_order=$21, + mark_read_on_view=$22 WHERE - id=$22 + id=$23 ` _, err = s.db.Exec( @@ -211,6 +213,7 @@ func (s *Storage) UpdateUser(user *model.User) error { user.CJKReadingSpeed, user.DefaultHomePage, user.CategoriesSortingOrder, + user.MarkReadOnView, user.ID, ) if err != nil { @@ -238,9 +241,10 @@ func (s *Storage) UpdateUser(user *model.User) error { default_reading_speed=$17, cjk_reading_speed=$18, default_home_page=$19, - categories_sorting_order=$20 + categories_sorting_order=$20, + mark_read_on_view=$21 WHERE - id=$21 + id=$22 ` _, err := s.db.Exec( @@ -265,6 +269,7 @@ func (s *Storage) UpdateUser(user *model.User) error { user.CJKReadingSpeed, user.DefaultHomePage, user.CategoriesSortingOrder, + user.MarkReadOnView, user.ID, ) @@ -311,7 +316,8 @@ func (s *Storage) UserByID(userID int64) (*model.User, error) { default_reading_speed, cjk_reading_speed, default_home_page, - categories_sorting_order + categories_sorting_order, + mark_read_on_view FROM users WHERE @@ -345,7 +351,8 @@ func (s *Storage) UserByUsername(username string) (*model.User, error) { default_reading_speed, cjk_reading_speed, default_home_page, - categories_sorting_order + categories_sorting_order, + mark_read_on_view FROM users WHERE @@ -379,7 +386,8 @@ func (s *Storage) UserByField(field, value string) (*model.User, error) { default_reading_speed, cjk_reading_speed, default_home_page, - categories_sorting_order + categories_sorting_order, + mark_read_on_view FROM users WHERE @@ -420,7 +428,8 @@ func (s *Storage) UserByAPIKey(token string) (*model.User, error) { u.default_reading_speed, u.cjk_reading_speed, u.default_home_page, - u.categories_sorting_order + u.categories_sorting_order, + u.mark_read_on_view FROM users u LEFT JOIN @@ -456,6 +465,7 @@ func (s *Storage) fetchUser(query string, args ...interface{}) (*model.User, err &user.CJKReadingSpeed, &user.DefaultHomePage, &user.CategoriesSortingOrder, + &user.MarkReadOnView, ) if err == sql.ErrNoRows { @@ -552,7 +562,8 @@ func (s *Storage) Users() (model.Users, error) { default_reading_speed, cjk_reading_speed, default_home_page, - categories_sorting_order + categories_sorting_order, + mark_read_on_view FROM users ORDER BY username ASC @@ -589,6 +600,7 @@ func (s *Storage) Users() (model.Users, error) { &user.CJKReadingSpeed, &user.DefaultHomePage, &user.CategoriesSortingOrder, + &user.MarkReadOnView, ) if err != nil { diff --git a/storage/user_session.go b/storage/user_session.go index e891bbcc..edc3c2a4 100644 --- a/storage/user_session.go +++ b/storage/user_session.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package storage // import "miniflux.app/storage" diff --git a/systemd/systemd.go b/systemd/systemd.go index d4415a0c..5f7a149e 100644 --- a/systemd/systemd.go +++ b/systemd/systemd.go @@ -1,6 +1,5 @@ -// Copyright 2021 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package systemd // import "miniflux.app/systemd" diff --git a/template/engine.go b/template/engine.go index 3436de7a..2d3071c4 100644 --- a/template/engine.go +++ b/template/engine.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package template // import "miniflux.app/template" diff --git a/template/functions.go b/template/functions.go index fd2b44a7..0a3a5657 100644 --- a/template/functions.go +++ b/template/functions.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package template // import "miniflux.app/template" @@ -108,6 +107,7 @@ func (f *funcMap) Map() template.FuncMap { "nonce": func() string { return crypto.GenerateRandomStringHex(16) }, + "deRef": func(i *int) int { return *i }, // These functions are overrode at runtime after the parsing. "elapsed": func(timezone string, t time.Time) string { diff --git a/template/functions_test.go b/template/functions_test.go index 3df35bfb..5b6e4341 100644 --- a/template/functions_test.go +++ b/template/functions_test.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package template // import "miniflux.app/template" diff --git a/template/templates/common/feed_list.html b/template/templates/common/feed_list.html index 906385fe..b41bcfbe 100644 --- a/template/templates/common/feed_list.html +++ b/template/templates/common/feed_list.html @@ -20,7 +20,7 @@
- +
+ +   + + {{ icon "external-link" }} + +
diff --git a/template/templates/views/bookmark_entries.html b/template/templates/views/bookmark_entries.html index 939fd750..d10fd391 100644 --- a/template/templates/views/bookmark_entries.html +++ b/template/templates/views/bookmark_entries.html @@ -19,7 +19,7 @@ {{ if ne .Feed.Icon.IconID 0 }} {{ .Feed.Title }} {{ end }} - {{ .Title }} + {{ .Title }} {{ .Feed.Category.Title }} diff --git a/template/templates/views/categories.html b/template/templates/views/categories.html index 34655aa0..c1d30e3f 100644 --- a/template/templates/views/categories.html +++ b/template/templates/views/categories.html @@ -15,7 +15,7 @@ {{ else }}
{{ range .categories }} -
+
{{ .Title }} @@ -25,7 +25,7 @@
  • - {{ if eq .FeedCount 0 }}{{ t "page.categories.no_feed" }}{{ else }}{{ plural "page.categories.feed_count" .FeedCount .FeedCount }}{{ end }} + {{ if eq (deRef .FeedCount) 0 }}{{ t "page.categories.no_feed" }}{{ else }}{{ plural "page.categories.feed_count" (deRef .FeedCount) (deRef .FeedCount) }}{{ end }}
diff --git a/template/templates/views/edit_feed.html b/template/templates/views/edit_feed.html index 90d88d9c..af4a5b2a 100644 --- a/template/templates/views/edit_feed.html +++ b/template/templates/views/edit_feed.html @@ -111,7 +111,15 @@
- +
+ +   + + {{ icon "external-link" }} + +
@@ -122,6 +130,8 @@ {{ end }} + + {{ if not .form.CategoryHidden }} {{ end }} diff --git a/template/templates/views/entry.html b/template/templates/views/entry.html index a0fec9b7..ebcad3ea 100644 --- a/template/templates/views/entry.html +++ b/template/templates/views/entry.html @@ -72,7 +72,7 @@ target="_blank" rel="noopener noreferrer" referrerpolicy="no-referrer" - data-original-link="true">{{ icon "external-link" }}{{ t "entry.external_link.label" }} + data-original-link="{{ .user.MarkReadOnView }}">{{ icon "external-link" }}{{ t "entry.external_link.label" }}
  • {{ end }}
  • + {{ if .entry.Tags }} + + {{ end }}
    {{ if .user }} @@ -144,6 +150,39 @@ {{ end }} {{ end }}
    + {{ if (and .entry.Enclosures (not .entry.Feed.NoMediaPlayer)) }} + {{ range .entry.Enclosures }} + {{ if ne .URL "" }} + {{ if hasPrefix .MimeType "audio/" }} +
    + +
    + {{ else if hasPrefix .MimeType "video/" }} +
    + +
    + {{ end }} + {{ end }} + {{ end }} + {{end}} {{ if .user }} {{ noescape (proxyFilter .entry.Content) }} {{ else }} @@ -158,21 +197,27 @@
    {{ if hasPrefix .MimeType "audio/" }}
    -
    {{ else if hasPrefix .MimeType "video/" }}
    -
    diff --git a/template/templates/views/feed_entries.html b/template/templates/views/feed_entries.html index 8eabc11b..4615e044 100644 --- a/template/templates/views/feed_entries.html +++ b/template/templates/views/feed_entries.html @@ -3,7 +3,7 @@ {{ define "content"}}
    diff --git a/template/templates/views/history_entries.html b/template/templates/views/history_entries.html index b03138a8..66948748 100644 --- a/template/templates/views/history_entries.html +++ b/template/templates/views/history_entries.html @@ -35,7 +35,7 @@ {{ if ne .Feed.Icon.IconID 0 }} {{ .Feed.Title }} {{ end }} - {{ .Title }} + {{ .Title }} {{ .Feed.Category.Title }}
    diff --git a/template/templates/views/integrations.html b/template/templates/views/integrations.html index 8bb615d4..a89e16c0 100644 --- a/template/templates/views/integrations.html +++ b/template/templates/views/integrations.html @@ -192,6 +192,13 @@ + + + + +
    diff --git a/template/templates/views/search_entries.html b/template/templates/views/search_entries.html index 4c73ac5b..04db64a9 100644 --- a/template/templates/views/search_entries.html +++ b/template/templates/views/search_entries.html @@ -19,7 +19,7 @@ {{ if ne .Feed.Icon.IconID 0 }} {{ .Feed.Title }} {{ end }} - {{ .Title }} + {{ .Title }} {{ .Feed.Category.Title }} diff --git a/template/templates/views/settings.html b/template/templates/views/settings.html index a7d7e26b..ce3d0d7a 100644 --- a/template/templates/views/settings.html +++ b/template/templates/views/settings.html @@ -99,6 +99,8 @@ + + diff --git a/template/templates/views/shared_entries.html b/template/templates/views/shared_entries.html index a2e1962c..eb5bd93a 100644 --- a/template/templates/views/shared_entries.html +++ b/template/templates/views/shared_entries.html @@ -32,7 +32,7 @@ {{ if ne .Feed.Icon.IconID 0 }} {{ .Feed.Title }} {{ end }} - {{ .Title }} + {{ .Title }} {{ if .ShareCode }} {{ end }} - {{ .Title }} + {{ .Title }} {{ .Feed.Category.Title }} diff --git a/tests/category_test.go b/tests/category_test.go index e99cb98d..5ef4a0bf 100644 --- a/tests/category_test.go +++ b/tests/category_test.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 //go:build integration // +build integration diff --git a/tests/doc.go b/tests/doc.go deleted file mode 100644 index 7c9975e7..00000000 --- a/tests/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 tests contains API integration tests. -*/ -package tests // import "miniflux.app/tests" diff --git a/tests/endpoint_test.go b/tests/endpoint_test.go index 91871176..c864f9e1 100644 --- a/tests/endpoint_test.go +++ b/tests/endpoint_test.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 //go:build integration // +build integration diff --git a/tests/entry_test.go b/tests/entry_test.go index 97ce4cbe..8807c951 100644 --- a/tests/entry_test.go +++ b/tests/entry_test.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 //go:build integration // +build integration diff --git a/tests/feed_test.go b/tests/feed_test.go index a5ad4dad..7409a550 100644 --- a/tests/feed_test.go +++ b/tests/feed_test.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 //go:build integration // +build integration diff --git a/tests/import_export_test.go b/tests/import_export_test.go index 0233822f..b30e6acd 100644 --- a/tests/import_export_test.go +++ b/tests/import_export_test.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 //go:build integration // +build integration diff --git a/tests/subscription_test.go b/tests/subscription_test.go index b54f6ee7..01201097 100644 --- a/tests/subscription_test.go +++ b/tests/subscription_test.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 //go:build integration // +build integration diff --git a/tests/tests.go b/tests/tests.go index 27e7885d..56965ce6 100644 --- a/tests/tests.go +++ b/tests/tests.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 //go:build integration // +build integration diff --git a/tests/user_test.go b/tests/user_test.go index e8369f77..8b2fe8f3 100644 --- a/tests/user_test.go +++ b/tests/user_test.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 //go:build integration // +build integration diff --git a/timer/doc.go b/timer/doc.go deleted file mode 100644 index 216c08ca..00000000 --- a/timer/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 timer implements utility functions to measure the execution time of a block of code. -*/ -package timer // import "miniflux.app/timer" diff --git a/timer/timer.go b/timer/timer.go index 8f311c3d..92c14970 100644 --- a/timer/timer.go +++ b/timer/timer.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package timer // import "miniflux.app/timer" diff --git a/timezone/doc.go b/timezone/doc.go deleted file mode 100644 index d46da71c..00000000 --- a/timezone/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 timezone contains helper functions to work with timezones. -*/ -package timezone // import "miniflux.app/timezone" diff --git a/timezone/timezone.go b/timezone/timezone.go index aaef655b..c14ae25e 100644 --- a/timezone/timezone.go +++ b/timezone/timezone.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package timezone // import "miniflux.app/timezone" diff --git a/timezone/timezone_test.go b/timezone/timezone_test.go index ad62343d..0e8cfb94 100644 --- a/timezone/timezone_test.go +++ b/timezone/timezone_test.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package timezone // import "miniflux.app/timezone" diff --git a/ui/about.go b/ui/about.go index c429ca52..3bfa554b 100644 --- a/ui/about.go +++ b/ui/about.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/api_key_create.go b/ui/api_key_create.go index c33f8d0e..68535089 100644 --- a/ui/api_key_create.go +++ b/ui/api_key_create.go @@ -1,6 +1,5 @@ -// Copyright 2020 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/api_key_list.go b/ui/api_key_list.go index 0e6a8cd7..5fd1d0e3 100644 --- a/ui/api_key_list.go +++ b/ui/api_key_list.go @@ -1,6 +1,5 @@ -// Copyright 2020 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/api_key_remove.go b/ui/api_key_remove.go index 7c1ceca8..50345478 100644 --- a/ui/api_key_remove.go +++ b/ui/api_key_remove.go @@ -1,6 +1,5 @@ -// Copyright 2020 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/api_key_save.go b/ui/api_key_save.go index 4c7dfc59..84537385 100644 --- a/ui/api_key_save.go +++ b/ui/api_key_save.go @@ -1,6 +1,5 @@ -// Copyright 2020 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/bookmark_entries.go b/ui/bookmark_entries.go index 225131d0..38815a4c 100644 --- a/ui/bookmark_entries.go +++ b/ui/bookmark_entries.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" @@ -26,8 +25,7 @@ func (h *handler) showStarredPage(w http.ResponseWriter, r *http.Request) { builder := h.store.NewEntryQueryBuilder(user.ID) builder.WithoutStatus(model.EntryStatusRemoved) builder.WithStarred(true) - builder.WithOrder(user.EntryOrder) - builder.WithDirection(user.EntryDirection) + builder.WithSorting(user.EntryOrder, user.EntryDirection) builder.WithOffset(offset) builder.WithLimit(user.EntriesPerPage) diff --git a/ui/category_create.go b/ui/category_create.go index c61b2407..506dc331 100644 --- a/ui/category_create.go +++ b/ui/category_create.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/category_edit.go b/ui/category_edit.go index 41673cc3..71fcbf2d 100644 --- a/ui/category_edit.go +++ b/ui/category_edit.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/category_entries.go b/ui/category_entries.go index c0889452..f3ee0a2f 100644 --- a/ui/category_entries.go +++ b/ui/category_entries.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" @@ -37,8 +36,7 @@ func (h *handler) showCategoryEntriesPage(w http.ResponseWriter, r *http.Request offset := request.QueryIntParam(r, "offset", 0) builder := h.store.NewEntryQueryBuilder(user.ID) builder.WithCategoryID(category.ID) - builder.WithOrder(user.EntryOrder) - builder.WithDirection(user.EntryDirection) + builder.WithSorting(user.EntryOrder, user.EntryDirection) builder.WithStatus(model.EntryStatusUnread) builder.WithOffset(offset) builder.WithLimit(user.EntriesPerPage) diff --git a/ui/category_entries_all.go b/ui/category_entries_all.go index 84478251..fe860cf4 100644 --- a/ui/category_entries_all.go +++ b/ui/category_entries_all.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" @@ -37,8 +36,7 @@ func (h *handler) showCategoryEntriesAllPage(w http.ResponseWriter, r *http.Requ offset := request.QueryIntParam(r, "offset", 0) builder := h.store.NewEntryQueryBuilder(user.ID) builder.WithCategoryID(category.ID) - builder.WithOrder(user.EntryOrder) - builder.WithDirection(user.EntryDirection) + builder.WithSorting(user.EntryOrder, user.EntryDirection) builder.WithoutStatus(model.EntryStatusRemoved) builder.WithOffset(offset) builder.WithLimit(user.EntriesPerPage) diff --git a/ui/category_feeds.go b/ui/category_feeds.go index cbcce7bd..7bec8419 100644 --- a/ui/category_feeds.go +++ b/ui/category_feeds.go @@ -1,6 +1,5 @@ -// Copyright 2019 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/category_list.go b/ui/category_list.go index 1375888a..120d4dcd 100644 --- a/ui/category_list.go +++ b/ui/category_list.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/category_mark_as_read.go b/ui/category_mark_as_read.go index e7f12e37..aae83780 100644 --- a/ui/category_mark_as_read.go +++ b/ui/category_mark_as_read.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/category_refresh.go b/ui/category_refresh.go index 0b4689f9..9c871eea 100644 --- a/ui/category_refresh.go +++ b/ui/category_refresh.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/category_remove.go b/ui/category_remove.go index 8e616fc3..11080ece 100644 --- a/ui/category_remove.go +++ b/ui/category_remove.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/category_save.go b/ui/category_save.go index 6bf9d7df..c6f083ce 100644 --- a/ui/category_save.go +++ b/ui/category_save.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/category_update.go b/ui/category_update.go index 4475508b..f0e71274 100644 --- a/ui/category_update.go +++ b/ui/category_update.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/doc.go b/ui/doc.go deleted file mode 100644 index ea38fad6..00000000 --- a/ui/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 ui implements handlers to render to user interface. -*/ -package ui // import "miniflux.app/ui" diff --git a/ui/entry_bookmark.go b/ui/entry_bookmark.go index 388c3643..d67ef0e9 100644 --- a/ui/entry_bookmark.go +++ b/ui/entry_bookmark.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" @@ -39,7 +38,7 @@ func (h *handler) showStarredEntryPage(w http.ResponseWriter, r *http.Request) { return } - if entry.Status == model.EntryStatusUnread { + if user.MarkReadOnView && entry.Status == model.EntryStatusUnread { err = h.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead) if err != nil { html.ServerError(w, r, err) diff --git a/ui/entry_category.go b/ui/entry_category.go index f3df2224..a40ff09c 100644 --- a/ui/entry_category.go +++ b/ui/entry_category.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" @@ -42,7 +41,7 @@ func (h *handler) showCategoryEntryPage(w http.ResponseWriter, r *http.Request) return } - if entry.Status == model.EntryStatusUnread { + if user.MarkReadOnView && entry.Status == model.EntryStatusUnread { err = h.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead) if err != nil { html.ServerError(w, r, err) diff --git a/ui/entry_enclosure_save_position.go b/ui/entry_enclosure_save_position.go new file mode 100644 index 00000000..91fd0078 --- /dev/null +++ b/ui/entry_enclosure_save_position.go @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package ui // import "miniflux.app/ui" + +import ( + json2 "encoding/json" + "io" + "net/http" + + "miniflux.app/http/request" + "miniflux.app/http/response/json" +) + +func (h *handler) saveEnclosureProgression(w http.ResponseWriter, r *http.Request) { + enclosureID := request.RouteInt64Param(r, "enclosureID") + enclosure, err := h.store.GetEnclosure(enclosureID) + if err != nil { + json.ServerError(w, r, err) + return + } + + if enclosure == nil { + json.NotFound(w, r) + return + } + + type enclosurePositionSaveRequest struct { + Progression int64 `json:"progression"` + } + + var postData enclosurePositionSaveRequest + body, err := io.ReadAll(r.Body) + if err != nil { + json.ServerError(w, r, err) + return + } + + json2.Unmarshal(body, &postData) + if err != nil { + json.ServerError(w, r, err) + return + } + enclosure.MediaProgression = postData.Progression + + err = h.store.UpdateEnclosure(enclosure) + if err != nil { + json.ServerError(w, r, err) + return + } + + json.Created(w, r, map[string]string{"message": "saved"}) +} diff --git a/ui/entry_feed.go b/ui/entry_feed.go index a331e2db..c1ce09d3 100644 --- a/ui/entry_feed.go +++ b/ui/entry_feed.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" @@ -42,7 +41,7 @@ func (h *handler) showFeedEntryPage(w http.ResponseWriter, r *http.Request) { return } - if entry.Status == model.EntryStatusUnread { + if user.MarkReadOnView && entry.Status == model.EntryStatusUnread { err = h.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead) if err != nil { html.ServerError(w, r, err) diff --git a/ui/entry_read.go b/ui/entry_read.go index b61e64e7..3185b09d 100644 --- a/ui/entry_read.go +++ b/ui/entry_read.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/entry_save.go b/ui/entry_save.go index 43d25038..b467d68b 100644 --- a/ui/entry_save.go +++ b/ui/entry_save.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/entry_scraper.go b/ui/entry_scraper.go index 77755c74..ec9cfa9f 100644 --- a/ui/entry_scraper.go +++ b/ui/entry_scraper.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/entry_search.go b/ui/entry_search.go index 36a13e3b..de487259 100644 --- a/ui/entry_search.go +++ b/ui/entry_search.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" @@ -41,7 +40,7 @@ func (h *handler) showSearchEntryPage(w http.ResponseWriter, r *http.Request) { return } - if entry.Status == model.EntryStatusUnread { + if user.MarkReadOnView && entry.Status == model.EntryStatusUnread { err = h.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead) if err != nil { html.ServerError(w, r, err) diff --git a/ui/entry_toggle_bookmark.go b/ui/entry_toggle_bookmark.go index 3e638a11..54cc416b 100644 --- a/ui/entry_toggle_bookmark.go +++ b/ui/entry_toggle_bookmark.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/entry_unread.go b/ui/entry_unread.go index 99033310..ba75e1e9 100644 --- a/ui/entry_unread.go +++ b/ui/entry_unread.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" @@ -67,13 +66,18 @@ func (h *handler) showUnreadEntryPage(w http.ResponseWriter, r *http.Request) { prevEntryRoute = route.Path(h.router, "unreadEntry", "entryID", prevEntry.ID) } - // Always mark the entry as read after fetching the pagination. - err = h.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead) - if err != nil { - html.ServerError(w, r, err) - return + if user.MarkReadOnView { + entry.Status = model.EntryStatusRead + } + + // Restore entry read status if needed after fetching the pagination. + if entry.Status == model.EntryStatusRead { + err = h.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead) + if err != nil { + html.ServerError(w, r, err) + return + } } - entry.Status = model.EntryStatusRead sess := session.New(h.store, request.SessionID(r)) view := view.New(h.tpl, r, sess) diff --git a/ui/entry_update_status.go b/ui/entry_update_status.go index d01ef678..f1228310 100644 --- a/ui/entry_update_status.go +++ b/ui/entry_update_status.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/feed_edit.go b/ui/feed_edit.go index ed9e5378..b13abc4f 100644 --- a/ui/feed_edit.go +++ b/ui/feed_edit.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" @@ -59,6 +58,7 @@ func (h *handler) showEditFeedPage(w http.ResponseWriter, r *http.Request) { AllowSelfSignedCertificates: feed.AllowSelfSignedCertificates, FetchViaProxy: feed.FetchViaProxy, Disabled: feed.Disabled, + NoMediaPlayer: feed.NoMediaPlayer, HideGlobally: feed.HideGlobally, CategoryHidden: feed.Category.HideGlobally, } diff --git a/ui/feed_entries.go b/ui/feed_entries.go index fa975f78..7b93b8ff 100644 --- a/ui/feed_entries.go +++ b/ui/feed_entries.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" @@ -38,8 +37,7 @@ func (h *handler) showFeedEntriesPage(w http.ResponseWriter, r *http.Request) { builder := h.store.NewEntryQueryBuilder(user.ID) builder.WithFeedID(feed.ID) builder.WithStatus(model.EntryStatusUnread) - builder.WithOrder(user.EntryOrder) - builder.WithDirection(user.EntryDirection) + builder.WithSorting(user.EntryOrder, user.EntryDirection) builder.WithOffset(offset) builder.WithLimit(user.EntriesPerPage) diff --git a/ui/feed_entries_all.go b/ui/feed_entries_all.go index 0c42f8a0..6493d258 100644 --- a/ui/feed_entries_all.go +++ b/ui/feed_entries_all.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" @@ -38,8 +37,7 @@ func (h *handler) showFeedEntriesAllPage(w http.ResponseWriter, r *http.Request) builder := h.store.NewEntryQueryBuilder(user.ID) builder.WithFeedID(feed.ID) builder.WithoutStatus(model.EntryStatusRemoved) - builder.WithOrder(user.EntryOrder) - builder.WithDirection(user.EntryDirection) + builder.WithSorting(user.EntryOrder, user.EntryDirection) builder.WithOffset(offset) builder.WithLimit(user.EntriesPerPage) diff --git a/ui/feed_icon.go b/ui/feed_icon.go index 66c47075..093952d7 100644 --- a/ui/feed_icon.go +++ b/ui/feed_icon.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/feed_list.go b/ui/feed_list.go index 761c803e..1d218349 100644 --- a/ui/feed_list.go +++ b/ui/feed_list.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/feed_mark_as_read.go b/ui/feed_mark_as_read.go index fce5646a..65400a95 100644 --- a/ui/feed_mark_as_read.go +++ b/ui/feed_mark_as_read.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/feed_refresh.go b/ui/feed_refresh.go index 724e50cf..e8c5f353 100644 --- a/ui/feed_refresh.go +++ b/ui/feed_refresh.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/feed_remove.go b/ui/feed_remove.go index 15d997d2..3874d47a 100644 --- a/ui/feed_remove.go +++ b/ui/feed_remove.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/feed_update.go b/ui/feed_update.go index 183c30c6..e8080110 100644 --- a/ui/feed_update.go +++ b/ui/feed_update.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/form/api_key.go b/ui/form/api_key.go index a30dcb0b..b2a56991 100644 --- a/ui/form/api_key.go +++ b/ui/form/api_key.go @@ -1,6 +1,5 @@ -// Copyright 2020 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package form // import "miniflux.app/ui/form" diff --git a/ui/form/auth.go b/ui/form/auth.go index eed0ef5a..9d0ad884 100644 --- a/ui/form/auth.go +++ b/ui/form/auth.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package form // import "miniflux.app/ui/form" diff --git a/ui/form/category.go b/ui/form/category.go index 0c013f4b..e7488c9f 100644 --- a/ui/form/category.go +++ b/ui/form/category.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package form // import "miniflux.app/ui/form" diff --git a/ui/form/doc.go b/ui/form/doc.go deleted file mode 100644 index d81ce0fe..00000000 --- a/ui/form/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 form handles HTML form validation and serialization. -*/ -package form // import "miniflux.app/ui/form" diff --git a/ui/form/feed.go b/ui/form/feed.go index 53181a7f..3a673379 100644 --- a/ui/form/feed.go +++ b/ui/form/feed.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package form // import "miniflux.app/ui/form" @@ -31,6 +30,7 @@ type FeedForm struct { AllowSelfSignedCertificates bool FetchViaProxy bool Disabled bool + NoMediaPlayer bool HideGlobally bool CategoryHidden bool // Category has "hide_globally" } @@ -57,6 +57,7 @@ func (f FeedForm) Merge(feed *model.Feed) *model.Feed { feed.AllowSelfSignedCertificates = f.AllowSelfSignedCertificates feed.FetchViaProxy = f.FetchViaProxy feed.Disabled = f.Disabled + feed.NoMediaPlayer = f.NoMediaPlayer feed.HideGlobally = f.HideGlobally return feed } @@ -86,6 +87,7 @@ func NewFeedForm(r *http.Request) *FeedForm { AllowSelfSignedCertificates: r.FormValue("allow_self_signed_certificates") == "1", FetchViaProxy: r.FormValue("fetch_via_proxy") == "1", Disabled: r.FormValue("disabled") == "1", + NoMediaPlayer: r.FormValue("no_media_player") == "1", HideGlobally: r.FormValue("hide_globally") == "1", } } diff --git a/ui/form/integration.go b/ui/form/integration.go index 07ad856a..f17ff89b 100644 --- a/ui/form/integration.go +++ b/ui/form/integration.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package form // import "miniflux.app/ui/form" @@ -48,6 +47,8 @@ type IntegrationForm struct { LinkdingEnabled bool LinkdingURL string LinkdingAPIKey string + LinkdingTags string + LinkdingMarkAsUnread bool MatrixBotEnabled bool MatrixBotUser string MatrixBotPassword string @@ -91,6 +92,8 @@ func (i IntegrationForm) Merge(integration *model.Integration) { integration.LinkdingEnabled = i.LinkdingEnabled integration.LinkdingURL = i.LinkdingURL integration.LinkdingAPIKey = i.LinkdingAPIKey + integration.LinkdingTags = i.LinkdingTags + integration.LinkdingMarkAsUnread = i.LinkdingMarkAsUnread integration.MatrixBotEnabled = i.MatrixBotEnabled integration.MatrixBotUser = i.MatrixBotUser integration.MatrixBotPassword = i.MatrixBotPassword @@ -137,6 +140,8 @@ func NewIntegrationForm(r *http.Request) *IntegrationForm { LinkdingEnabled: r.FormValue("linkding_enabled") == "1", LinkdingURL: r.FormValue("linkding_url"), LinkdingAPIKey: r.FormValue("linkding_api_key"), + LinkdingTags: r.FormValue("linkding_tags"), + LinkdingMarkAsUnread: r.FormValue("linkding_mark_as_unread") == "1", MatrixBotEnabled: r.FormValue("matrix_bot_enabled") == "1", MatrixBotUser: r.FormValue("matrix_bot_user"), MatrixBotPassword: r.FormValue("matrix_bot_password"), diff --git a/ui/form/settings.go b/ui/form/settings.go index 4a6c56e3..f8773997 100644 --- a/ui/form/settings.go +++ b/ui/form/settings.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package form // import "miniflux.app/ui/form" @@ -33,6 +32,7 @@ type SettingsForm struct { CJKReadingSpeed int DefaultHomePage string CategoriesSortingOrder string + MarkReadOnView bool } // Merge updates the fields of the given user. @@ -54,6 +54,7 @@ func (s *SettingsForm) Merge(user *model.User) *model.User { user.DefaultReadingSpeed = s.DefaultReadingSpeed user.DefaultHomePage = s.DefaultHomePage user.CategoriesSortingOrder = s.CategoriesSortingOrder + user.MarkReadOnView = s.MarkReadOnView if s.Password != "" { user.Password = s.Password @@ -120,5 +121,6 @@ func NewSettingsForm(r *http.Request) *SettingsForm { CJKReadingSpeed: int(cjkReadingSpeed), DefaultHomePage: r.FormValue("default_home_page"), CategoriesSortingOrder: r.FormValue("categories_sorting_order"), + MarkReadOnView: r.FormValue("mark_read_on_view") == "1", } } diff --git a/ui/form/settings_test.go b/ui/form/settings_test.go index 655c9fe2..3a58c838 100644 --- a/ui/form/settings_test.go +++ b/ui/form/settings_test.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + package form // import "miniflux.app/ui/form" import ( diff --git a/ui/form/subscription.go b/ui/form/subscription.go index 642c9534..4a36f792 100644 --- a/ui/form/subscription.go +++ b/ui/form/subscription.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package form // import "miniflux.app/ui/form" diff --git a/ui/form/user.go b/ui/form/user.go index 7abaeca7..ed626f29 100644 --- a/ui/form/user.go +++ b/ui/form/user.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package form // import "miniflux.app/ui/form" diff --git a/ui/handler.go b/ui/handler.go index 1bfc3dae..78600ea0 100644 --- a/ui/handler.go +++ b/ui/handler.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/history_entries.go b/ui/history_entries.go index efeb1d57..c5275105 100644 --- a/ui/history_entries.go +++ b/ui/history_entries.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" @@ -25,8 +24,8 @@ func (h *handler) showHistoryPage(w http.ResponseWriter, r *http.Request) { offset := request.QueryIntParam(r, "offset", 0) builder := h.store.NewEntryQueryBuilder(user.ID) builder.WithStatus(model.EntryStatusRead) - builder.WithOrder("changed_at") - builder.WithDirection("desc") + builder.WithSorting("changed_at", "DESC") + builder.WithSorting("published_at", "DESC") builder.WithOffset(offset) builder.WithLimit(user.EntriesPerPage) diff --git a/ui/history_flush.go b/ui/history_flush.go index e74c5b97..8a8a1203 100644 --- a/ui/history_flush.go +++ b/ui/history_flush.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/integration_pocket.go b/ui/integration_pocket.go index 3e771878..7d2d476c 100644 --- a/ui/integration_pocket.go +++ b/ui/integration_pocket.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/integration_show.go b/ui/integration_show.go index afd0bfd5..c51d8965 100644 --- a/ui/integration_show.go +++ b/ui/integration_show.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" @@ -63,6 +62,8 @@ func (h *handler) showIntegrationPage(w http.ResponseWriter, r *http.Request) { LinkdingEnabled: integration.LinkdingEnabled, LinkdingURL: integration.LinkdingURL, LinkdingAPIKey: integration.LinkdingAPIKey, + LinkdingTags: integration.LinkdingTags, + LinkdingMarkAsUnread: integration.LinkdingMarkAsUnread, MatrixBotEnabled: integration.MatrixBotEnabled, MatrixBotUser: integration.MatrixBotUser, MatrixBotPassword: integration.MatrixBotPassword, diff --git a/ui/integration_update.go b/ui/integration_update.go index d5380c93..30f1824f 100644 --- a/ui/integration_update.go +++ b/ui/integration_update.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/login_check.go b/ui/login_check.go index fbafc58f..cdd7417b 100644 --- a/ui/login_check.go +++ b/ui/login_check.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + package ui // import "miniflux.app/ui" import ( diff --git a/ui/login_show.go b/ui/login_show.go index d6abd2f6..81be88cc 100644 --- a/ui/login_show.go +++ b/ui/login_show.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/logout.go b/ui/logout.go index 479426b5..34dfe000 100644 --- a/ui/logout.go +++ b/ui/logout.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/middleware.go b/ui/middleware.go index 31a912ab..72334088 100644 --- a/ui/middleware.go +++ b/ui/middleware.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/oauth2.go b/ui/oauth2.go index 045d9cbc..7cf5aa3d 100644 --- a/ui/oauth2.go +++ b/ui/oauth2.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/oauth2_callback.go b/ui/oauth2_callback.go index cd033f86..ac1e19e8 100644 --- a/ui/oauth2_callback.go +++ b/ui/oauth2_callback.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/oauth2_redirect.go b/ui/oauth2_redirect.go index b2fe9bc8..fb24df37 100644 --- a/ui/oauth2_redirect.go +++ b/ui/oauth2_redirect.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/oauth2_unlink.go b/ui/oauth2_unlink.go index 46cc0df6..fa2f51bd 100644 --- a/ui/oauth2_unlink.go +++ b/ui/oauth2_unlink.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/offline.go b/ui/offline.go index b6c4dff7..da04e910 100644 --- a/ui/offline.go +++ b/ui/offline.go @@ -1,6 +1,5 @@ -// Copyright 2021 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/opml_export.go b/ui/opml_export.go index 4eb6d2c9..a7b14161 100644 --- a/ui/opml_export.go +++ b/ui/opml_export.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/opml_import.go b/ui/opml_import.go index ea6b3626..0f7bbac0 100644 --- a/ui/opml_import.go +++ b/ui/opml_import.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/opml_upload.go b/ui/opml_upload.go index 08430737..bebab3e8 100644 --- a/ui/opml_upload.go +++ b/ui/opml_upload.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/pagination.go b/ui/pagination.go index 46e0a397..395e1714 100644 --- a/ui/pagination.go +++ b/ui/pagination.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/proxy.go b/ui/proxy.go index bffbc939..a21d75f5 100644 --- a/ui/proxy.go +++ b/ui/proxy.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/search_entries.go b/ui/search_entries.go index 5dfd6db7..28f648ac 100644 --- a/ui/search_entries.go +++ b/ui/search_entries.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/session/doc.go b/ui/session/doc.go deleted file mode 100644 index 617a69b8..00000000 --- a/ui/session/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 session provides helper functions to work with the user session. -*/ -package session // import "miniflux.app/ui/session" diff --git a/ui/session/session.go b/ui/session/session.go index 0874f927..b0756daf 100644 --- a/ui/session/session.go +++ b/ui/session/session.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package session // import "miniflux.app/ui/session" diff --git a/ui/session_list.go b/ui/session_list.go index 53051b2b..2db9e1cf 100644 --- a/ui/session_list.go +++ b/ui/session_list.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/session_remove.go b/ui/session_remove.go index 7e57b82f..024ebdf2 100644 --- a/ui/session_remove.go +++ b/ui/session_remove.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/settings_show.go b/ui/settings_show.go index 7b04d4e0..39700d17 100644 --- a/ui/settings_show.go +++ b/ui/settings_show.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" @@ -44,6 +43,7 @@ func (h *handler) showSettingsPage(w http.ResponseWriter, r *http.Request) { CJKReadingSpeed: user.CJKReadingSpeed, DefaultHomePage: user.DefaultHomePage, CategoriesSortingOrder: user.CategoriesSortingOrder, + MarkReadOnView: user.MarkReadOnView, } timezones, err := h.store.Timezones() diff --git a/ui/settings_update.go b/ui/settings_update.go index ce516eb6..5d0b0d1c 100644 --- a/ui/settings_update.go +++ b/ui/settings_update.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/share.go b/ui/share.go index 6d3ff6e4..0ee405da 100644 --- a/ui/share.go +++ b/ui/share.go @@ -1,6 +1,5 @@ -// Copyright 2020 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/shared_entries.go b/ui/shared_entries.go index da84eeb2..3a00cbe7 100644 --- a/ui/shared_entries.go +++ b/ui/shared_entries.go @@ -1,6 +1,5 @@ -// Copyright 2020 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" @@ -22,8 +21,7 @@ func (h *handler) sharedEntries(w http.ResponseWriter, r *http.Request) { builder := h.store.NewEntryQueryBuilder(user.ID) builder.WithShareCodeNotEmpty() - builder.WithOrder(user.EntryOrder) - builder.WithDirection(user.EntryDirection) + builder.WithSorting(user.EntryOrder, user.EntryDirection) entries, err := builder.GetEntries() if err != nil { diff --git a/ui/static/bin/maskable-icon-120.png b/ui/static/bin/maskable-icon-120.png new file mode 100644 index 00000000..7f30c5b3 Binary files /dev/null and b/ui/static/bin/maskable-icon-120.png differ diff --git a/ui/static/bin/maskable-icon-192.png b/ui/static/bin/maskable-icon-192.png new file mode 100644 index 00000000..975fc13e Binary files /dev/null and b/ui/static/bin/maskable-icon-192.png differ diff --git a/ui/static/bin/maskable-icon-512.png b/ui/static/bin/maskable-icon-512.png new file mode 100644 index 00000000..c8ee563e Binary files /dev/null and b/ui/static/bin/maskable-icon-512.png differ diff --git a/ui/static/css/common.css b/ui/static/css/common.css index cd61f912..cc213c9b 100644 --- a/ui/static/css/common.css +++ b/ui/static/css/common.css @@ -216,7 +216,7 @@ a:hover { 100% {visibility: hidden; opacity: 0; z-index: 0} } -@media (min-width: 600px) { +@media (min-width: 620px) { body { margin: auto; max-width: 750px; @@ -734,15 +734,11 @@ template { font-size: 0.85em; } -.item-meta-info li:after { +.item-meta-info li:not(:last-child):after { content: "|"; color: var(--item-meta-li-color); } -.item-meta-info li:last-child:after { - content: ""; -} - .item-meta-icons li { margin-right: 8px; margin-top: 4px; @@ -854,6 +850,15 @@ article.category-has-unread { overflow-wrap: break-word; } +.entry-tags { + margin-top: 20px; + margin-bottom: 20px; +} + +.entry-tags strong { + font-weight: 600; +} + .entry-website img { vertical-align: top; } @@ -1085,3 +1090,7 @@ details.entry-enclosures { .disabled { opacity: 20%; } + +audio,video { + width: 100%; +} diff --git a/ui/static/doc.go b/ui/static/doc.go deleted file mode 100644 index b1725468..00000000 --- a/ui/static/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 static contains assets for the user interface. -*/ -package static // import "miniflux.app/ui/static" diff --git a/ui/static/js/app.js b/ui/static/js/app.js index b8082fc8..308a22b7 100644 --- a/ui/static/js/app.js +++ b/ui/static/js/app.js @@ -618,3 +618,23 @@ function showToast(label, iconElement) { function goToAddSubscription() { window.location.href = document.body.dataset.addSubscriptionUrl; } + +/** + * save player position to allow to resume playback later + * @param {Element} playerElement + */ +function handlePlayerProgressionSave(playerElement) { + const currentPositionInSeconds = Math.floor(playerElement.currentTime); // we do not need a precise value + const lastKnownPositionInSeconds = parseInt(playerElement.dataset.lastPosition, 10); + const recordInterval = 10; + + // we limit the number of update to only one by interval. Otherwise, we would have multiple update per seconds + if (currentPositionInSeconds >= (lastKnownPositionInSeconds + recordInterval) || + currentPositionInSeconds <= (lastKnownPositionInSeconds - recordInterval) + ) { + playerElement.dataset.lastPosition = currentPositionInSeconds.toString(); + let request = new RequestBuilder(playerElement.dataset.saveUrl); + request.withBody({progression: currentPositionInSeconds}); + request.execute(); + } +} diff --git a/ui/static/js/bootstrap.js b/ui/static/js/bootstrap.js index b4c46017..be68225b 100644 --- a/ui/static/js/bootstrap.js +++ b/ui/static/js/bootstrap.js @@ -69,10 +69,10 @@ document.addEventListener("DOMContentLoaded", function () { request.execute(); })); - onClick("a[data-original-link]", (event) => { + onClick("a[data-original-link='true']", (event) => { handleEntryStatus("next", event.target, true); }, true); - onAuxClick("a[data-original-link]", (event) => { + onAuxClick("a[data-original-link='true']", (event) => { if (event.button == 1) { handleEntryStatus("next", event.target, true); } @@ -112,4 +112,12 @@ document.addEventListener("DOMContentLoaded", function () { } } }); + + // enclosure media player position save & resume + const elements = document.querySelectorAll("audio[data-last-position],video[data-last-position]"); + elements.forEach((element) => { + // we set the current time of media players + if (element.dataset.lastPosition){ element.currentTime = element.dataset.lastPosition; } + element.ontimeupdate = () => handlePlayerProgressionSave(element); + }); }); diff --git a/ui/static/static.go b/ui/static/static.go index 5f1bd003..0201cc98 100644 --- a/ui/static/static.go +++ b/ui/static/static.go @@ -1,6 +1,5 @@ -// Copyright 2021 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package static // import "miniflux.app/ui/static" diff --git a/ui/static_app_icon.go b/ui/static_app_icon.go index 46b16fe2..40f41011 100644 --- a/ui/static_app_icon.go +++ b/ui/static_app_icon.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/static_favicon.go b/ui/static_favicon.go index 1f158fd2..426fa52d 100644 --- a/ui/static_favicon.go +++ b/ui/static_favicon.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/static_javascript.go b/ui/static_javascript.go index 99255d84..a722c868 100644 --- a/ui/static_javascript.go +++ b/ui/static_javascript.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/static_manifest.go b/ui/static_manifest.go index b1a5284d..5f2424f9 100644 --- a/ui/static_manifest.go +++ b/ui/static_manifest.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" @@ -27,9 +26,10 @@ func (h *handler) showWebManifest(w http.ResponseWriter, r *http.Request) { } type webManifestIcon struct { - Source string `json:"src"` - Sizes string `json:"sizes"` - Type string `json:"type"` + Source string `json:"src"` + Sizes string `json:"sizes"` + Type string `json:"type"` + Purpose string `json:"purpose"` } type webManifest struct { @@ -63,9 +63,12 @@ func (h *handler) showWebManifest(w http.ResponseWriter, r *http.Request) { ThemeColor: themeColor, BackgroundColor: themeColor, Icons: []webManifestIcon{ - {Source: route.Path(h.router, "appIcon", "filename", "icon-120.png"), Sizes: "120x120", Type: "image/png"}, - {Source: route.Path(h.router, "appIcon", "filename", "icon-192.png"), Sizes: "192x192", Type: "image/png"}, - {Source: route.Path(h.router, "appIcon", "filename", "icon-512.png"), Sizes: "512x512", Type: "image/png"}, + {Source: route.Path(h.router, "appIcon", "filename", "icon-120.png"), Sizes: "120x120", Type: "image/png", Purpose: "any"}, + {Source: route.Path(h.router, "appIcon", "filename", "icon-192.png"), Sizes: "192x192", Type: "image/png", Purpose: "any"}, + {Source: route.Path(h.router, "appIcon", "filename", "icon-512.png"), Sizes: "512x512", Type: "image/png", Purpose: "any"}, + {Source: route.Path(h.router, "appIcon", "filename", "maskable-icon-120.png"), Sizes: "120x120", Type: "image/png", Purpose: "maskable"}, + {Source: route.Path(h.router, "appIcon", "filename", "maskable-icon-192.png"), Sizes: "192x192", Type: "image/png", Purpose: "maskable"}, + {Source: route.Path(h.router, "appIcon", "filename", "maskable-icon-512.png"), Sizes: "512x512", Type: "image/png", Purpose: "maskable"}, }, ShareTarget: webManifestShareTarget{ Action: route.Path(h.router, "bookmarklet"), diff --git a/ui/static_stylesheet.go b/ui/static_stylesheet.go index 8b67e306..f95e2ddf 100644 --- a/ui/static_stylesheet.go +++ b/ui/static_stylesheet.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/subscription_add.go b/ui/subscription_add.go index 5062c908..d42aabe6 100644 --- a/ui/subscription_add.go +++ b/ui/subscription_add.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/subscription_bookmarklet.go b/ui/subscription_bookmarklet.go index 01e4a8cc..38b09123 100644 --- a/ui/subscription_bookmarklet.go +++ b/ui/subscription_bookmarklet.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/subscription_choose.go b/ui/subscription_choose.go index 6d106330..d4b0e9a2 100644 --- a/ui/subscription_choose.go +++ b/ui/subscription_choose.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/subscription_submit.go b/ui/subscription_submit.go index 21b03f3f..ce88c0fc 100644 --- a/ui/subscription_submit.go +++ b/ui/subscription_submit.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/ui.go b/ui/ui.go index 15f5da78..969bf975 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" @@ -95,6 +94,7 @@ func Serve(router *mux.Router, store *storage.Storage, pool *worker.Pool) { // Entry pages. uiRouter.HandleFunc("/entry/status", handler.updateEntriesStatus).Name("updateEntriesStatus").Methods(http.MethodPost) uiRouter.HandleFunc("/entry/save/{entryID}", handler.saveEntry).Name("saveEntry").Methods(http.MethodPost) + uiRouter.HandleFunc("/entry/enclosure/{enclosureID}/save-progression", handler.saveEnclosureProgression).Name("saveEnclosureProgression").Methods(http.MethodPost) uiRouter.HandleFunc("/entry/download/{entryID}", handler.fetchContent).Name("fetchContent").Methods(http.MethodPost) uiRouter.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", handler.mediaProxy).Name("proxy").Methods(http.MethodGet) uiRouter.HandleFunc("/entry/bookmark/{entryID}", handler.toggleBookmark).Name("toggleBookmark").Methods(http.MethodPost) diff --git a/ui/unread_entries.go b/ui/unread_entries.go index ed22eae9..5fe1ab2b 100644 --- a/ui/unread_entries.go +++ b/ui/unread_entries.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" @@ -49,8 +48,7 @@ func (h *handler) showUnreadPage(w http.ResponseWriter, r *http.Request) { beginSqlFetchUnreadEntries := time.Now() builder = h.store.NewEntryQueryBuilder(user.ID) builder.WithStatus(model.EntryStatusUnread) - builder.WithOrder(user.EntryOrder) - builder.WithDirection(user.EntryDirection) + builder.WithSorting(user.EntryOrder, user.EntryDirection) builder.WithOffset(offset) builder.WithLimit(user.EntriesPerPage) builder.WithGloballyVisible() diff --git a/ui/unread_mark_all_read.go b/ui/unread_mark_all_read.go index 3952063b..54effa16 100644 --- a/ui/unread_mark_all_read.go +++ b/ui/unread_mark_all_read.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" @@ -12,7 +11,7 @@ import ( ) func (h *handler) markAllAsRead(w http.ResponseWriter, r *http.Request) { - if err := h.store.MarkAllAsRead(request.UserID(r)); err != nil { + if err := h.store.MarkGloballyVisibleFeedsAsRead(request.UserID(r)); err != nil { json.ServerError(w, r, err) return } diff --git a/ui/user_create.go b/ui/user_create.go index a30f3346..e8d3d408 100644 --- a/ui/user_create.go +++ b/ui/user_create.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/user_edit.go b/ui/user_edit.go index 23f577db..f675f4b7 100644 --- a/ui/user_edit.go +++ b/ui/user_edit.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/user_list.go b/ui/user_list.go index 6c19de41..9e9cce31 100644 --- a/ui/user_list.go +++ b/ui/user_list.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/user_remove.go b/ui/user_remove.go index 70536fe9..e6cc9847 100644 --- a/ui/user_remove.go +++ b/ui/user_remove.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/user_save.go b/ui/user_save.go index 45b9e84a..49827def 100644 --- a/ui/user_save.go +++ b/ui/user_save.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/user_update.go b/ui/user_update.go index 8d6be865..abdbe925 100644 --- a/ui/user_update.go +++ b/ui/user_update.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package ui // import "miniflux.app/ui" diff --git a/ui/view/doc.go b/ui/view/doc.go deleted file mode 100644 index ed32f41f..00000000 --- a/ui/view/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 view handles template parameters. -*/ -package view // import "miniflux.app/ui/view" diff --git a/ui/view/view.go b/ui/view/view.go index fd7df2c4..d63deefa 100644 --- a/ui/view/view.go +++ b/ui/view/view.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package view // import "miniflux.app/ui/view" diff --git a/url/doc.go b/url/doc.go deleted file mode 100644 index 2204b955..00000000 --- a/url/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 url implements a set of utility functions to parse URL. -*/ -package url // import "miniflux.app/url" diff --git a/url/url.go b/url/url.go index 859afc87..2806572a 100644 --- a/url/url.go +++ b/url/url.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package url // import "miniflux.app/url" diff --git a/url/url_test.go b/url/url_test.go index 7aa1cf98..5d3d2ccc 100644 --- a/url/url_test.go +++ b/url/url_test.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package url // import "miniflux.app/url" diff --git a/validator/category.go b/validator/category.go index 9a62b4e0..b4255176 100644 --- a/validator/category.go +++ b/validator/category.go @@ -1,6 +1,5 @@ -// Copyright 2021 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package validator // import "miniflux.app/validator" diff --git a/validator/entry.go b/validator/entry.go index c7a96acb..2d4c7eb2 100644 --- a/validator/entry.go +++ b/validator/entry.go @@ -1,6 +1,5 @@ -// Copyright 2021 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package validator // import "miniflux.app/validator" diff --git a/validator/entry_test.go b/validator/entry_test.go index 3d580454..2022ad71 100644 --- a/validator/entry_test.go +++ b/validator/entry_test.go @@ -1,6 +1,5 @@ -// Copyright 2021 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package validator // import "miniflux.app/validator" diff --git a/validator/feed.go b/validator/feed.go index 614b688e..89f086ca 100644 --- a/validator/feed.go +++ b/validator/feed.go @@ -1,6 +1,5 @@ -// Copyright 2021 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package validator // import "miniflux.app/validator" diff --git a/validator/subscription.go b/validator/subscription.go index a907f4fe..adfda342 100644 --- a/validator/subscription.go +++ b/validator/subscription.go @@ -1,6 +1,5 @@ -// Copyright 2021 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package validator // import "miniflux.app/validator" diff --git a/validator/user.go b/validator/user.go index f7f271c0..7a552ee7 100644 --- a/validator/user.go +++ b/validator/user.go @@ -1,6 +1,5 @@ -// Copyright 2021 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package validator // import "miniflux.app/validator" diff --git a/validator/validator.go b/validator/validator.go index 5ebaef88..4ef0a586 100644 --- a/validator/validator.go +++ b/validator/validator.go @@ -1,6 +1,5 @@ -// Copyright 2021 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package validator // import "miniflux.app/validator" diff --git a/validator/validator_test.go b/validator/validator_test.go index 6f26f660..fd72b4f9 100644 --- a/validator/validator_test.go +++ b/validator/validator_test.go @@ -1,6 +1,5 @@ -// Copyright 2021 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package validator // import "miniflux.app/validator" diff --git a/version/doc.go b/version/doc.go deleted file mode 100644 index 58d1bc0b..00000000 --- a/version/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 version contains application and build information. -*/ -package version // import "miniflux.app/version" diff --git a/version/version.go b/version/version.go index 0b1a4b2b..c4494048 100644 --- a/version/version.go +++ b/version/version.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package version // import "miniflux.app/version" diff --git a/worker/doc.go b/worker/doc.go deleted file mode 100644 index 909b34d8..00000000 --- a/worker/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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 worker implements the background workers. -*/ -package worker // import "miniflux.app/worker" diff --git a/worker/pool.go b/worker/pool.go index bcd075fd..b914cc20 100644 --- a/worker/pool.go +++ b/worker/pool.go @@ -1,6 +1,5 @@ -// Copyright 2018 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package worker // import "miniflux.app/worker" diff --git a/worker/worker.go b/worker/worker.go index 192cae50..2b3b473e 100644 --- a/worker/worker.go +++ b/worker/worker.go @@ -1,6 +1,5 @@ -// 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. +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 package worker // import "miniflux.app/worker"