From 9871e4f5d084a2678b2a5b5ce051151a98158777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Guillot?= Date: Sun, 22 Mar 2020 18:48:14 -0700 Subject: [PATCH] Add page to list and remove shared entries --- database/migration.go | 2 +- locale/translations.go | 80 ++++++++++++++++++++--- locale/translations/de_DE.json | 6 ++ locale/translations/en_US.json | 6 ++ locale/translations/es_ES.json | 6 ++ locale/translations/fr_FR.json | 6 ++ locale/translations/it_IT.json | 6 ++ locale/translations/ja_JP.json | 6 ++ locale/translations/nl_NL.json | 6 ++ locale/translations/pl_PL.json | 6 ++ locale/translations/ru_RU.json | 6 ++ locale/translations/zh_CN.json | 6 ++ storage/entry.go | 50 +++++++++------ storage/entry_query_builder.go | 6 ++ template/common.go | 21 +++++- template/html/common/icons.html | 10 +++ template/html/common/item_meta.html | 7 ++ template/html/entry.html | 13 ++-- template/html/history_entries.html | 9 +++ template/html/shared_entries.html | 71 +++++++++++++++++++++ template/views.go | 99 +++++++++++++++++++++++++++-- ui/middleware.go | 2 +- ui/share.go | 20 ++++-- ui/shared_entries.go | 52 +++++++++++++++ ui/ui.go | 6 +- 25 files changed, 457 insertions(+), 51 deletions(-) create mode 100644 template/html/shared_entries.html create mode 100644 ui/shared_entries.go diff --git a/database/migration.go b/database/migration.go index 7bff6e21..ea0f1f85 100644 --- a/database/migration.go +++ b/database/migration.go @@ -59,7 +59,7 @@ func IsSchemaUpToDate(db *sql.DB) error { var currentVersion int db.QueryRow(`SELECT version FROM schema_version`).Scan(¤tVersion) if currentVersion != schemaVersion { - return fmt.Errorf(`database schema is not up to date: current=v%d expected=v%d`, currentVersion, schemaVersion) + return fmt.Errorf(`the database schema is not up to date: current=v%d expected=v%d`, currentVersion, schemaVersion) } return nil } diff --git a/locale/translations.go b/locale/translations.go index c01779a2..a86d0513 100644 --- a/locale/translations.go +++ b/locale/translations.go @@ -51,6 +51,7 @@ var translations = map[string]string{ "menu.feed_entries": "Artikel", "menu.api_keys": "API-Schlüssel", "menu.create_api_key": "Erstellen Sie einen neuen API-Schlüssel", + "menu.shared_entries": "Geteilte Einträge", "search.label": "Suche", "search.placeholder": "Suche...", "pagination.next": "Nächste", @@ -78,6 +79,10 @@ var translations = map[string]string{ "entry.comments.title": "Kommentare anzeigen", "entry.share.label": "Teilen", "entry.share.title": "Diesen Artikel teilen", + "entry.unshare.label": "Unshare", + "entry.shared_entry.title": "Öffnen Sie den öffentlichen Link", + "entry.shared_entry.label": "Teilen", + "page.shared_entries.title": "Geteilte Einträge", "page.unread.title": "Ungelesen", "page.starred.title": "Lesezeichen", "page.categories.title": "Kategorien", @@ -191,6 +196,7 @@ var translations = map[string]string{ "page.api_keys.table.actions": "Aktionen", "page.api_keys.never_used": "Nie benutzt", "page.new_api_key.title": "Neuer API-Schlüssel", + "alert.no_shared_entry": "Es gibt keinen gemeinsamen Eintrag.", "alert.no_bookmark": "Es existiert derzeit kein Lesezeichen.", "alert.no_category": "Es ist keine Kategorie vorhanden.", "alert.no_category_entry": "Es befindet sich kein Artikel in dieser Kategorie.", @@ -379,6 +385,7 @@ var translations = map[string]string{ "menu.feed_entries": "Entries", "menu.api_keys": "API Keys", "menu.create_api_key": "Create a new API key", + "menu.shared_entries": "Shared entries", "search.label": "Search", "search.placeholder": "Search...", "pagination.next": "Next", @@ -406,6 +413,10 @@ var translations = map[string]string{ "entry.comments.title": "View Comments", "entry.share.label": "Share", "entry.share.title": "Share this article", + "entry.unshare.label": "Unshare", + "entry.shared_entry.title": "Open the public link", + "entry.shared_entry.label": "Share", + "page.shared_entries.title": "Shared Entries", "page.unread.title": "Unread", "page.starred.title": "Starred", "page.categories.title": "Categories", @@ -519,6 +530,7 @@ var translations = map[string]string{ "page.api_keys.table.actions": "Actions", "page.api_keys.never_used": "Never Used", "page.new_api_key.title": "New API Key", + "alert.no_shared_entry": "There is no shared entry.", "alert.no_bookmark": "There is no bookmark at the moment.", "alert.no_category": "There is no category.", "alert.no_category_entry": "There are no articles in this category.", @@ -687,6 +699,7 @@ var translations = map[string]string{ "menu.feed_entries": "Artículos", "menu.api_keys": "Claves API", "menu.create_api_key": "Crear una nueva clave API", + "menu.shared_entries": "Entradas compartidas", "search.label": "Buscar", "search.placeholder": "Búsqueda...", "pagination.next": "Siguiente", @@ -714,6 +727,10 @@ var translations = map[string]string{ "entry.comments.title": "Ver comentarios", "entry.share.label": "Comparta", "entry.share.title": "Comparta este articulo", + "entry.unshare.label": "No compartir", + "entry.shared_entry.title": "Abrir el enlace público", + "entry.shared_entry.label": "Compartir", + "page.shared_entries.title": "Entradas compartidas", "page.unread.title": "No leídos", "page.starred.title": "Marcadores", "page.categories.title": "Categorias", @@ -827,6 +844,7 @@ var translations = map[string]string{ "page.api_keys.table.actions": "Acciones", "page.api_keys.never_used": "Nunca usado", "page.new_api_key.title": "Nueva clave API", + "alert.no_shared_entry": "No hay entrada compartida.", "alert.no_bookmark": "No hay marcador en este momento.", "alert.no_category": "No hay categoría.", "alert.no_category_entry": "No hay artículos en esta categoria.", @@ -995,6 +1013,7 @@ var translations = map[string]string{ "menu.feed_entries": "Articles", "menu.api_keys": "Clés d'API", "menu.create_api_key": "Créer une nouvelle clé d'API", + "menu.shared_entries": "Articles partagés", "search.label": "Recherche", "search.placeholder": "Recherche...", "pagination.next": "Suivant", @@ -1022,6 +1041,10 @@ var translations = map[string]string{ "entry.comments.title": "Voir les commentaires", "entry.share.label": "Partager", "entry.share.title": "Partager cet article", + "entry.unshare.label": "Enlever le partage", + "entry.shared_entry.title": "Ouvrir le lien public", + "entry.shared_entry.label": "Partage", + "page.shared_entries.title": "Articles partagés", "page.unread.title": "Non lus", "page.starred.title": "Favoris", "page.categories.title": "Catégories", @@ -1135,6 +1158,7 @@ var translations = map[string]string{ "page.api_keys.table.actions": "Actions", "page.api_keys.never_used": "Jamais utilisé", "page.new_api_key.title": "Nouvelle clé d'API", + "alert.no_shared_entry": "Il n'y a pas d'article partagé.", "alert.no_bookmark": "Il n'y a aucun favoris pour le moment.", "alert.no_category": "Il n'y a aucune catégorie.", "alert.no_category_entry": "Il n'y a aucun article dans cette catégorie.", @@ -1323,6 +1347,7 @@ var translations = map[string]string{ "menu.feed_entries": "Articoli", "menu.api_keys": "Chiavi API", "menu.create_api_key": "Crea una nuova chiave API", + "menu.shared_entries": "Voci condivise", "search.label": "Cerca", "search.placeholder": "Cerca...", "pagination.next": "Successivo", @@ -1350,6 +1375,10 @@ var translations = map[string]string{ "entry.comments.title": "Mostra i commenti", "entry.share.label": "Condividi", "entry.share.title": "Condividi questo articolo", + "entry.unshare.label": "Unshare", + "entry.shared_entry.title": "Apri il link pubblico", + "entry.shared_entry.label": "Condivisione", + "page.shared_entries.title": "Voci condivise", "page.unread.title": "Da leggere", "page.starred.title": "Preferiti", "page.categories.title": "Categorie", @@ -1463,6 +1492,7 @@ var translations = map[string]string{ "page.api_keys.table.actions": "Azioni", "page.api_keys.never_used": "Mai usato", "page.new_api_key.title": "Nuova chiave API", + "alert.no_shared_entry": "Non ci sono voci condivise.", "alert.no_bookmark": "Nessun preferito disponibile.", "alert.no_category": "Nessuna categoria disponibile.", "alert.no_category_entry": "Questa categoria non contiene alcun articolo.", @@ -1631,6 +1661,7 @@ var translations = map[string]string{ "menu.feed_entries": "記事一覧", "menu.api_keys": "APIキー", "menu.create_api_key": "新しいAPIキーを作成する", + "menu.shared_entries": "共有エントリ", "search.label": "検索", "search.placeholder": "…を検索", "pagination.next": "次", @@ -1658,6 +1689,10 @@ var translations = map[string]string{ "entry.comments.title": "コメントを見る", "entry.share.label": "共有", "entry.share.title": "この記事を共有する", + "entry.unshare.label": "共有解除", + "entry.shared_entry.title": "公開リンクを開く", + "entry.shared_entry.label": "共有する", + "page.shared_entries.title": "共有エントリ", "page.unread.title": "未読", "page.starred.title": "星付き", "page.categories.title": "カテゴリ", @@ -1771,6 +1806,7 @@ var translations = map[string]string{ "page.api_keys.table.actions": "アクション", "page.api_keys.never_used": "使われたことがない", "page.new_api_key.title": "新しいAPIキー", + "alert.no_shared_entry": "共有エントリはありません。", "alert.no_bookmark": "現在星付きはありません。", "alert.no_category": "カテゴリが存在しません。", "alert.no_category_entry": "このカテゴリには記事がありません。", @@ -1939,6 +1975,7 @@ var translations = map[string]string{ "menu.feed_entries": "Lidwoord", "menu.api_keys": "API-sleutels", "menu.create_api_key": "Maak een nieuwe API-sleutel", + "menu.shared_entries": "Gedeelde vermeldingen", "search.label": "Zoeken", "search.placeholder": "Zoeken...", "pagination.next": "Volgende", @@ -1966,6 +2003,10 @@ var translations = map[string]string{ "entry.comments.title": "Bekijk de reacties", "entry.share.label": "Deel", "entry.share.title": "Deel dit artikel", + "entry.unshare.label": "Delen ongedaan maken", + "entry.shared_entry.title": "Open de openbare link", + "entry.shared_entry.label": "Delen", + "page.shared_entries.title": "Gedeelde vermeldingen", "page.unread.title": "Ongelezen", "page.starred.title": "Favorieten", "page.categories.title": "Categorieën", @@ -2079,6 +2120,7 @@ var translations = map[string]string{ "page.api_keys.table.actions": "Acties", "page.api_keys.never_used": "Nooit gebruikt", "page.new_api_key.title": "Nieuwe API-sleutel", + "alert.no_shared_entry": "Er is geen gedeelde toegang.", "alert.no_bookmark": "Er zijn op dit moment geen favorieten.", "alert.no_category": "Er zijn geen categorieën.", "alert.no_category_entry": "Deze categorie bevat geen feeds.", @@ -2265,6 +2307,7 @@ var translations = map[string]string{ "menu.feed_entries": "Artykuły", "menu.api_keys": "Klucze API", "menu.create_api_key": "Utwórz nowy klucz API", + "menu.shared_entries": "Udostępnione wpisy", "search.label": "Szukaj", "search.placeholder": "Szukaj...", "pagination.next": "Następny", @@ -2292,6 +2335,10 @@ var translations = map[string]string{ "entry.comments.title": "Zobacz komentarze", "entry.share.label": "Podzielić się", "entry.share.title": "Podzielić się ten artykuł", + "entry.unshare.label": "Unshare", + "entry.shared_entry.title": "Otwórz publiczny link", + "entry.shared_entry.label": "Udostępnianie", + "page.shared_entries.title": "Udostępnione wpisy", "page.unread.title": "Nieprzeczytane", "page.starred.title": "Oznaczone gwiazdką", "page.categories.title": "Kategorie", @@ -2407,6 +2454,7 @@ var translations = map[string]string{ "page.api_keys.table.actions": "Działania", "page.api_keys.never_used": "Nigdy nie używany", "page.new_api_key.title": "Nowy klucz API", + "alert.no_shared_entry": "Brak wspólnego wpisu.", "alert.no_bookmark": "Obecnie nie ma żadnych zakładek.", "alert.no_category": "Nie ma żadnej kategorii!", "alert.no_category_entry": "W tej kategorii nie ma żadnych artykułów", @@ -2599,6 +2647,7 @@ var translations = map[string]string{ "menu.feed_entries": "статьи", "menu.api_keys": "API-ключи", "menu.create_api_key": "Создать новый ключ API", + "menu.shared_entries": "Общие записи", "search.label": "Поиск", "search.placeholder": "Поиск…", "pagination.next": "Следующая", @@ -2626,6 +2675,10 @@ var translations = map[string]string{ "entry.comments.title": "Показать комментарии", "entry.share.label": "поделиться", "entry.share.title": "поделиться эту статью", + "entry.unshare.label": "Удалить из открытого списка", + "entry.shared_entry.title": "Открыть публичную ссылку", + "entry.shared_entry.label": "обмен", + "page.shared_entries.title": "Общие записи", "page.unread.title": "Непрочитанное", "page.starred.title": "Избранное", "page.categories.title": "Категории", @@ -2741,6 +2794,7 @@ var translations = map[string]string{ "page.api_keys.table.actions": "Действия", "page.api_keys.never_used": "Никогда не использовался", "page.new_api_key.title": "Новый ключ API", + "alert.no_shared_entry": "Там нет общей записи.", "alert.no_bookmark": "Нет закладок на данный момент.", "alert.no_category": "Категории отсутствуют.", "alert.no_category_entry": "В этой категории нет статей.", @@ -2915,6 +2969,7 @@ var translations = map[string]string{ "menu.feed_entries": "文章", "menu.api_keys": "API密钥", "menu.create_api_key": "创建一个新的API密钥", + "menu.shared_entries": "共享条目", "search.label": "搜索", "search.placeholder": "搜索…", "pagination.next": "下一页", @@ -2942,6 +2997,10 @@ var translations = map[string]string{ "entry.comments.title": "查看评论", "entry.share.label": "分享", "entry.share.title": "分享这篇文章", + "entry.unshare.label": "取消分享", + "entry.shared_entry.title": "打开公共链接", + "entry.shared_entry.label": "分享分享", + "page.shared_entries.title": "共享条目", "page.unread.title": "未读", "page.starred.title": "星标", "page.categories.title": "分类", @@ -3053,6 +3112,7 @@ var translations = map[string]string{ "page.api_keys.table.actions": "操作", "page.api_keys.never_used": "没用过", "page.new_api_key.title": "新的API密钥", + "alert.no_shared_entry": "没有共享条目。", "alert.no_bookmark": "目前没有书签", "alert.no_category": "目前没有分类", "alert.no_category_entry": "该分类下没有文章", @@ -3189,14 +3249,14 @@ var translations = map[string]string{ } var translationsChecksums = map[string]string{ - "de_DE": "7360a69e038d71e00f64c03891401cd517779687d46a907688f4a9a7b6205146", - "en_US": "5d9cad74ccfd94393aa98a25f28fa4f41895d8f758461760601d04c92f898d02", - "es_ES": "813b8cd42907dfbc19ff51f3367e0dbb013d373b013d7854df512e846652ff21", - "fr_FR": "bf770b1ecfd722bbf4fa29d2ea567b064c8bcb517141073ab3d170d27f4e53e4", - "it_IT": "fe2c7147f3c39784f482cf922d9d3a85a94f15af8ff28ebcec64723555a3fb10", - "ja_JP": "508025c0c7e7f57195ae011c4499ab58a85d043c828565c1740df879fb2376c1", - "nl_NL": "e621a5e7408928624a060a832d9fc36b74026221bd7b07894a4cce267be3cdd1", - "pl_PL": "6edcefd04e453c84b03207653c54f7be9a4a8ce4699e06c82bfcca2d7946f47e", - "ru_RU": "d7ad59bbd7a150af9d476c4c3034eb85762de7381e2925d75e373584ed45c725", - "zh_CN": "e5f169a3c83c9bd7a41e9737e001e58fec243eee7aa23a71d37bfa8e05d92860", + "de_DE": "aa7025ff266508152d31ed22e816588c3beaf6ef0ddff20012d303ab55a584d1", + "en_US": "c1cd8a4c6360881299609332bc930c0d59a30aea5ae90a6c87bcc06dbba7c69d", + "es_ES": "94f4da8c6160ca30c59294ec520f8648496b94904b61ac47c7ca24bfa4fe793b", + "fr_FR": "80a2d02c7a90e90024af4f5eb3aad710f245963df99538d7d71c0efcaae79f82", + "it_IT": "b033ade2a67a273253d3251df97e6b2da7e2adbec60a33de5d0f41198e6783b2", + "ja_JP": "2136cad37933c112a6e69f28936c216c8f262b28c47b57b18482c56a3f8932fb", + "nl_NL": "4a3a4a117f11cf62de5cf515c5adc74c8467d05feac1844fee4f81d863173887", + "pl_PL": "c088a74ad9e4dca6bdbf73b0b4fce97db60b5066cb03c6884a9462573b0f93ad", + "ru_RU": "506a34fdfa35dacf2d86b16f44d47addf2e0f1f376d2a86a474619d6654967cc", + "zh_CN": "463037b8bd51bb1e940f432fd621d62d4e81697298b8058124d287b5cbaee10f", } diff --git a/locale/translations/de_DE.json b/locale/translations/de_DE.json index bf34e833..466966ff 100644 --- a/locale/translations/de_DE.json +++ b/locale/translations/de_DE.json @@ -46,6 +46,7 @@ "menu.feed_entries": "Artikel", "menu.api_keys": "API-Schlüssel", "menu.create_api_key": "Erstellen Sie einen neuen API-Schlüssel", + "menu.shared_entries": "Geteilte Einträge", "search.label": "Suche", "search.placeholder": "Suche...", "pagination.next": "Nächste", @@ -73,6 +74,10 @@ "entry.comments.title": "Kommentare anzeigen", "entry.share.label": "Teilen", "entry.share.title": "Diesen Artikel teilen", + "entry.unshare.label": "Unshare", + "entry.shared_entry.title": "Öffnen Sie den öffentlichen Link", + "entry.shared_entry.label": "Teilen", + "page.shared_entries.title": "Geteilte Einträge", "page.unread.title": "Ungelesen", "page.starred.title": "Lesezeichen", "page.categories.title": "Kategorien", @@ -186,6 +191,7 @@ "page.api_keys.table.actions": "Aktionen", "page.api_keys.never_used": "Nie benutzt", "page.new_api_key.title": "Neuer API-Schlüssel", + "alert.no_shared_entry": "Es gibt keinen gemeinsamen Eintrag.", "alert.no_bookmark": "Es existiert derzeit kein Lesezeichen.", "alert.no_category": "Es ist keine Kategorie vorhanden.", "alert.no_category_entry": "Es befindet sich kein Artikel in dieser Kategorie.", diff --git a/locale/translations/en_US.json b/locale/translations/en_US.json index 350f40c0..d189d8ae 100644 --- a/locale/translations/en_US.json +++ b/locale/translations/en_US.json @@ -46,6 +46,7 @@ "menu.feed_entries": "Entries", "menu.api_keys": "API Keys", "menu.create_api_key": "Create a new API key", + "menu.shared_entries": "Shared entries", "search.label": "Search", "search.placeholder": "Search...", "pagination.next": "Next", @@ -73,6 +74,10 @@ "entry.comments.title": "View Comments", "entry.share.label": "Share", "entry.share.title": "Share this article", + "entry.unshare.label": "Unshare", + "entry.shared_entry.title": "Open the public link", + "entry.shared_entry.label": "Share", + "page.shared_entries.title": "Shared Entries", "page.unread.title": "Unread", "page.starred.title": "Starred", "page.categories.title": "Categories", @@ -186,6 +191,7 @@ "page.api_keys.table.actions": "Actions", "page.api_keys.never_used": "Never Used", "page.new_api_key.title": "New API Key", + "alert.no_shared_entry": "There is no shared entry.", "alert.no_bookmark": "There is no bookmark at the moment.", "alert.no_category": "There is no category.", "alert.no_category_entry": "There are no articles in this category.", diff --git a/locale/translations/es_ES.json b/locale/translations/es_ES.json index dae98986..9feeaf38 100644 --- a/locale/translations/es_ES.json +++ b/locale/translations/es_ES.json @@ -46,6 +46,7 @@ "menu.feed_entries": "Artículos", "menu.api_keys": "Claves API", "menu.create_api_key": "Crear una nueva clave API", + "menu.shared_entries": "Entradas compartidas", "search.label": "Buscar", "search.placeholder": "Búsqueda...", "pagination.next": "Siguiente", @@ -73,6 +74,10 @@ "entry.comments.title": "Ver comentarios", "entry.share.label": "Comparta", "entry.share.title": "Comparta este articulo", + "entry.unshare.label": "No compartir", + "entry.shared_entry.title": "Abrir el enlace público", + "entry.shared_entry.label": "Compartir", + "page.shared_entries.title": "Entradas compartidas", "page.unread.title": "No leídos", "page.starred.title": "Marcadores", "page.categories.title": "Categorias", @@ -186,6 +191,7 @@ "page.api_keys.table.actions": "Acciones", "page.api_keys.never_used": "Nunca usado", "page.new_api_key.title": "Nueva clave API", + "alert.no_shared_entry": "No hay entrada compartida.", "alert.no_bookmark": "No hay marcador en este momento.", "alert.no_category": "No hay categoría.", "alert.no_category_entry": "No hay artículos en esta categoria.", diff --git a/locale/translations/fr_FR.json b/locale/translations/fr_FR.json index 0afe5f97..d7ef62da 100644 --- a/locale/translations/fr_FR.json +++ b/locale/translations/fr_FR.json @@ -46,6 +46,7 @@ "menu.feed_entries": "Articles", "menu.api_keys": "Clés d'API", "menu.create_api_key": "Créer une nouvelle clé d'API", + "menu.shared_entries": "Articles partagés", "search.label": "Recherche", "search.placeholder": "Recherche...", "pagination.next": "Suivant", @@ -73,6 +74,10 @@ "entry.comments.title": "Voir les commentaires", "entry.share.label": "Partager", "entry.share.title": "Partager cet article", + "entry.unshare.label": "Enlever le partage", + "entry.shared_entry.title": "Ouvrir le lien public", + "entry.shared_entry.label": "Partage", + "page.shared_entries.title": "Articles partagés", "page.unread.title": "Non lus", "page.starred.title": "Favoris", "page.categories.title": "Catégories", @@ -186,6 +191,7 @@ "page.api_keys.table.actions": "Actions", "page.api_keys.never_used": "Jamais utilisé", "page.new_api_key.title": "Nouvelle clé d'API", + "alert.no_shared_entry": "Il n'y a pas d'article partagé.", "alert.no_bookmark": "Il n'y a aucun favoris pour le moment.", "alert.no_category": "Il n'y a aucune catégorie.", "alert.no_category_entry": "Il n'y a aucun article dans cette catégorie.", diff --git a/locale/translations/it_IT.json b/locale/translations/it_IT.json index e0a70017..54505439 100644 --- a/locale/translations/it_IT.json +++ b/locale/translations/it_IT.json @@ -46,6 +46,7 @@ "menu.feed_entries": "Articoli", "menu.api_keys": "Chiavi API", "menu.create_api_key": "Crea una nuova chiave API", + "menu.shared_entries": "Voci condivise", "search.label": "Cerca", "search.placeholder": "Cerca...", "pagination.next": "Successivo", @@ -73,6 +74,10 @@ "entry.comments.title": "Mostra i commenti", "entry.share.label": "Condividi", "entry.share.title": "Condividi questo articolo", + "entry.unshare.label": "Unshare", + "entry.shared_entry.title": "Apri il link pubblico", + "entry.shared_entry.label": "Condivisione", + "page.shared_entries.title": "Voci condivise", "page.unread.title": "Da leggere", "page.starred.title": "Preferiti", "page.categories.title": "Categorie", @@ -186,6 +191,7 @@ "page.api_keys.table.actions": "Azioni", "page.api_keys.never_used": "Mai usato", "page.new_api_key.title": "Nuova chiave API", + "alert.no_shared_entry": "Non ci sono voci condivise.", "alert.no_bookmark": "Nessun preferito disponibile.", "alert.no_category": "Nessuna categoria disponibile.", "alert.no_category_entry": "Questa categoria non contiene alcun articolo.", diff --git a/locale/translations/ja_JP.json b/locale/translations/ja_JP.json index b7a6ae38..8549ad44 100644 --- a/locale/translations/ja_JP.json +++ b/locale/translations/ja_JP.json @@ -46,6 +46,7 @@ "menu.feed_entries": "記事一覧", "menu.api_keys": "APIキー", "menu.create_api_key": "新しいAPIキーを作成する", + "menu.shared_entries": "共有エントリ", "search.label": "検索", "search.placeholder": "…を検索", "pagination.next": "次", @@ -73,6 +74,10 @@ "entry.comments.title": "コメントを見る", "entry.share.label": "共有", "entry.share.title": "この記事を共有する", + "entry.unshare.label": "共有解除", + "entry.shared_entry.title": "公開リンクを開く", + "entry.shared_entry.label": "共有する", + "page.shared_entries.title": "共有エントリ", "page.unread.title": "未読", "page.starred.title": "星付き", "page.categories.title": "カテゴリ", @@ -186,6 +191,7 @@ "page.api_keys.table.actions": "アクション", "page.api_keys.never_used": "使われたことがない", "page.new_api_key.title": "新しいAPIキー", + "alert.no_shared_entry": "共有エントリはありません。", "alert.no_bookmark": "現在星付きはありません。", "alert.no_category": "カテゴリが存在しません。", "alert.no_category_entry": "このカテゴリには記事がありません。", diff --git a/locale/translations/nl_NL.json b/locale/translations/nl_NL.json index 871271bb..34f13c16 100644 --- a/locale/translations/nl_NL.json +++ b/locale/translations/nl_NL.json @@ -46,6 +46,7 @@ "menu.feed_entries": "Lidwoord", "menu.api_keys": "API-sleutels", "menu.create_api_key": "Maak een nieuwe API-sleutel", + "menu.shared_entries": "Gedeelde vermeldingen", "search.label": "Zoeken", "search.placeholder": "Zoeken...", "pagination.next": "Volgende", @@ -73,6 +74,10 @@ "entry.comments.title": "Bekijk de reacties", "entry.share.label": "Deel", "entry.share.title": "Deel dit artikel", + "entry.unshare.label": "Delen ongedaan maken", + "entry.shared_entry.title": "Open de openbare link", + "entry.shared_entry.label": "Delen", + "page.shared_entries.title": "Gedeelde vermeldingen", "page.unread.title": "Ongelezen", "page.starred.title": "Favorieten", "page.categories.title": "Categorieën", @@ -186,6 +191,7 @@ "page.api_keys.table.actions": "Acties", "page.api_keys.never_used": "Nooit gebruikt", "page.new_api_key.title": "Nieuwe API-sleutel", + "alert.no_shared_entry": "Er is geen gedeelde toegang.", "alert.no_bookmark": "Er zijn op dit moment geen favorieten.", "alert.no_category": "Er zijn geen categorieën.", "alert.no_category_entry": "Deze categorie bevat geen feeds.", diff --git a/locale/translations/pl_PL.json b/locale/translations/pl_PL.json index 0e09c213..ab002bfc 100644 --- a/locale/translations/pl_PL.json +++ b/locale/translations/pl_PL.json @@ -46,6 +46,7 @@ "menu.feed_entries": "Artykuły", "menu.api_keys": "Klucze API", "menu.create_api_key": "Utwórz nowy klucz API", + "menu.shared_entries": "Udostępnione wpisy", "search.label": "Szukaj", "search.placeholder": "Szukaj...", "pagination.next": "Następny", @@ -73,6 +74,10 @@ "entry.comments.title": "Zobacz komentarze", "entry.share.label": "Podzielić się", "entry.share.title": "Podzielić się ten artykuł", + "entry.unshare.label": "Unshare", + "entry.shared_entry.title": "Otwórz publiczny link", + "entry.shared_entry.label": "Udostępnianie", + "page.shared_entries.title": "Udostępnione wpisy", "page.unread.title": "Nieprzeczytane", "page.starred.title": "Oznaczone gwiazdką", "page.categories.title": "Kategorie", @@ -188,6 +193,7 @@ "page.api_keys.table.actions": "Działania", "page.api_keys.never_used": "Nigdy nie używany", "page.new_api_key.title": "Nowy klucz API", + "alert.no_shared_entry": "Brak wspólnego wpisu.", "alert.no_bookmark": "Obecnie nie ma żadnych zakładek.", "alert.no_category": "Nie ma żadnej kategorii!", "alert.no_category_entry": "W tej kategorii nie ma żadnych artykułów", diff --git a/locale/translations/ru_RU.json b/locale/translations/ru_RU.json index e1332011..f55bf717 100644 --- a/locale/translations/ru_RU.json +++ b/locale/translations/ru_RU.json @@ -46,6 +46,7 @@ "menu.feed_entries": "статьи", "menu.api_keys": "API-ключи", "menu.create_api_key": "Создать новый ключ API", + "menu.shared_entries": "Общие записи", "search.label": "Поиск", "search.placeholder": "Поиск…", "pagination.next": "Следующая", @@ -73,6 +74,10 @@ "entry.comments.title": "Показать комментарии", "entry.share.label": "поделиться", "entry.share.title": "поделиться эту статью", + "entry.unshare.label": "Удалить из открытого списка", + "entry.shared_entry.title": "Открыть публичную ссылку", + "entry.shared_entry.label": "обмен", + "page.shared_entries.title": "Общие записи", "page.unread.title": "Непрочитанное", "page.starred.title": "Избранное", "page.categories.title": "Категории", @@ -188,6 +193,7 @@ "page.api_keys.table.actions": "Действия", "page.api_keys.never_used": "Никогда не использовался", "page.new_api_key.title": "Новый ключ API", + "alert.no_shared_entry": "Там нет общей записи.", "alert.no_bookmark": "Нет закладок на данный момент.", "alert.no_category": "Категории отсутствуют.", "alert.no_category_entry": "В этой категории нет статей.", diff --git a/locale/translations/zh_CN.json b/locale/translations/zh_CN.json index 1117a90b..9b67a8f1 100644 --- a/locale/translations/zh_CN.json +++ b/locale/translations/zh_CN.json @@ -46,6 +46,7 @@ "menu.feed_entries": "文章", "menu.api_keys": "API密钥", "menu.create_api_key": "创建一个新的API密钥", + "menu.shared_entries": "共享条目", "search.label": "搜索", "search.placeholder": "搜索…", "pagination.next": "下一页", @@ -73,6 +74,10 @@ "entry.comments.title": "查看评论", "entry.share.label": "分享", "entry.share.title": "分享这篇文章", + "entry.unshare.label": "取消分享", + "entry.shared_entry.title": "打开公共链接", + "entry.shared_entry.label": "分享分享", + "page.shared_entries.title": "共享条目", "page.unread.title": "未读", "page.starred.title": "星标", "page.categories.title": "分类", @@ -184,6 +189,7 @@ "page.api_keys.table.actions": "操作", "page.api_keys.never_used": "没用过", "page.new_api_key.title": "新的API密钥", + "alert.no_shared_entry": "没有共享条目。", "alert.no_bookmark": "目前没有书签", "alert.no_category": "目前没有分类", "alert.no_category_entry": "该分类下没有文章", diff --git a/storage/entry.go b/storage/entry.go index ba52be21..bf9ed357 100644 --- a/storage/entry.go +++ b/storage/entry.go @@ -272,7 +272,15 @@ func (s *Storage) ToggleBookmark(userID int64, entryID int64) error { // FlushHistory set all entries with the status "read" to "removed". func (s *Storage) FlushHistory(userID int64) error { - query := `UPDATE entries SET status=$1, changed_at=now() WHERE user_id=$2 AND status=$3 AND starred='f'` + query := ` + UPDATE + entries + SET + status=$1, + changed_at=now() + WHERE + user_id=$2 AND status=$3 AND starred='f' AND share_code='' + ` _, err := s.db.Exec(query, model.EntryStatusRemoved, userID, model.EntryStatusRead) if err != nil { return fmt.Errorf(`store: unable to flush history: %v`, err) @@ -353,34 +361,36 @@ func (s *Storage) EntryURLExists(feedID int64, entryURL string) bool { return result } -// GetEntryShareCode returns the share code of the provided entry. +// EntryShareCode returns the share code of the provided entry. // It generates a new one if not already defined. -func (s *Storage) GetEntryShareCode(userID int64, entryID int64) (shareCode string, err error) { +func (s *Storage) EntryShareCode(userID int64, entryID int64) (shareCode string, err error) { query := `SELECT share_code FROM entries WHERE user_id=$1 AND id=$2` err = s.db.QueryRow(query, userID, entryID).Scan(&shareCode) - - if err != nil || shareCode != "" { - return - } - - shareCode = crypto.GenerateRandomStringHex(20) - - query = `UPDATE entries SET share_code = $1 WHERE user_id=$2 AND id=$3` - result, err := s.db.Exec(query, shareCode, userID, entryID) if err != nil { - err = fmt.Errorf(`store: unable to set share_code for entry #%d: %v`, entryID, err) + err = fmt.Errorf(`store: unable to get share code for entry #%d: %v`, entryID, err) return } - count, err := result.RowsAffected() - if err != nil { - err = fmt.Errorf(`store: unable to set share_code for entry #%d: %v`, entryID, err) - return - } + if shareCode == "" { + shareCode = crypto.GenerateRandomStringHex(20) - if count == 0 { - err = errors.New(`store: nothing has been updated`) + query = `UPDATE entries SET share_code = $1 WHERE user_id=$2 AND id=$3` + _, err = s.db.Exec(query, shareCode, userID, entryID) + if err != nil { + err = fmt.Errorf(`store: unable to set share code for entry #%d: %v`, entryID, err) + return + } } return } + +// UnshareEntry removes the share code for the given entry. +func (s *Storage) UnshareEntry(userID int64, entryID int64) (err error) { + query := `UPDATE entries SET share_code='' WHERE user_id=$1 AND id=$2` + _, err = s.db.Exec(query, userID, entryID) + if err != nil { + err = fmt.Errorf(`store: unable to remove share code for entry #%d: %v`, entryID, err) + } + return +} diff --git a/storage/entry_query_builder.go b/storage/entry_query_builder.go index 2136cf05..8488af1c 100644 --- a/storage/entry_query_builder.go +++ b/storage/entry_query_builder.go @@ -135,6 +135,12 @@ func (e *EntryQueryBuilder) WithShareCode(shareCode string) *EntryQueryBuilder { return e } +// WithShareCodeNotEmpty adds a filter for non-empty share code. +func (e *EntryQueryBuilder) WithShareCodeNotEmpty() *EntryQueryBuilder { + e.conditions = append(e.conditions, "e.share_code <> ''") + return e +} + // WithOrder set the sorting order. func (e *EntryQueryBuilder) WithOrder(order string) *EntryQueryBuilder { e.order = order diff --git a/template/common.go b/template/common.go index 8a33fec7..1df49f1f 100644 --- a/template/common.go +++ b/template/common.go @@ -185,6 +185,16 @@ SOFTWARE. +{{ end }} +{{ define "icon_delete" }} + + + + + + + + {{ end }}`, "item_meta": `{{ define "item_meta" }}
@@ -197,6 +207,13 @@ SOFTWARE. + {{ else }} + {{ end }} diff --git a/template/html/shared_entries.html b/template/html/shared_entries.html new file mode 100644 index 00000000..86ae861e --- /dev/null +++ b/template/html/shared_entries.html @@ -0,0 +1,71 @@ +{{ define "title"}}{{ t "page.shared_entries.title" }} ({{ .total }}){{ end }} + +{{ define "content"}} + + +{{ if not .entries }} +

{{ t "alert.no_shared_entry" }}

+{{ else }} +
+ {{ range .entries }} + + {{ end }} +
+{{ end }} + +{{ end }} diff --git a/template/views.go b/template/views.go index 2a9098f6..23738bc2 100644 --- a/template/views.go +++ b/template/views.go @@ -692,10 +692,15 @@ var templateViewsMap = map[string]string{ {{ end }}
  • - {{ template "icon_share" }}{{ t "entry.share.label" }} + {{ if .entry.ShareCode }} + {{ template "icon_share" }}{{ t "entry.shared_entry.label" }} + {{ else }} + {{ template "icon_share" }}{{ t "entry.share.label" }} + {{ end }}
  • {{ t "menu.flush_history" }}
  • +
  • + {{ t "menu.shared_entries" }} +
  • + + {{ else }} + {{ end }} @@ -1319,6 +1333,78 @@ var templateViewsMap = map[string]string{
    {{ end }} +{{ end }} +`, + "shared_entries": `{{ define "title"}}{{ t "page.shared_entries.title" }} ({{ .total }}){{ end }} + +{{ define "content"}} + + +{{ if not .entries }} +

    {{ t "alert.no_shared_entry" }}

    +{{ else }} +
    + {{ range .entries }} + + {{ end }} +
    +{{ end }} + {{ end }} `, "unread_entries": `{{ define "title"}}{{ t "page.unread.title" }} {{ if gt .countUnread 0 }}({{ .countUnread }}){{ end }} {{ end }} @@ -1457,16 +1543,17 @@ var templateViewsMapChecksums = map[string]string{ "edit_category": "b1c0b38f1b714c5d884edcd61e5b5295a5f1c8b71c469b35391e4dcc97cc6d36", "edit_feed": "cc0b5dbb73f81398410958b41771ed38246bc7ae4bd548228f0d48c49a598c2a", "edit_user": "c692db9de1a084c57b93e95a14b041d39bf489846cbb91fc982a62b72b77062a", - "entry": "03814d36909f3af1a9164c407d8733bcf1f15c4aee516186d07561a81fa42eb3", + "entry": "0e405b2370aaefaaa122c955f58fd395b9f19612e4cc7b209fc79e7b00c5561b", "feed_entries": "9c70b82f55e4b311eff20be1641733612e3c1b406ce8010861e4c417d97b6dcc", "feeds": "ec7d3fa96735bd8422ba69ef0927dcccddc1cc51327e0271f0312d3f881c64fd", - "history_entries": "87e17d39de70eb3fdbc4000326283be610928758eae7924e4b08dcb446f3b6a9", + "history_entries": "93c0c4cc541eec7f07f5c2634f250ea82ac64024939179276b6f636b72c189bf", "import": "1b59b3bd55c59fcbc6fbb346b414dcdd26d1b4e0c307e437bb58b3f92ef01ad1", "integrations": "30329452743b35c668278f519245fd9be05c1726856e0384ba542f7c307f2788", "login": "79ff2ca488c0a19b37c8fa227a21f73e94472eb357a51a077197c852f7713f11", "search_entries": "274950d03298c24f3942e209c0faed580a6d57be9cf76a6c236175a7e766ac6a", "sessions": "5d5c677bddbd027e0b0c9f7a0dd95b66d9d95b4e130959f31fb955b926c2201c", "settings": "d949ecdd28a33eadafaa3a727e548b3466c5aa44b0a4bbf86cc49784704ff7f6", + "shared_entries": "19caea053664220bb9519df295eb2a17cf5836eaa9104b7ee24c60b88bb524e9", "unread_entries": "e38f7ffce17dfad3151b08cd33771a2cefe8ca9db42df04fc98bd1d675dd6075", "users": "d7ff52efc582bbad10504f4a04fa3adcc12d15890e45dff51cac281e0c446e45", } diff --git a/ui/middleware.go b/ui/middleware.go index 65c30733..e008e541 100644 --- a/ui/middleware.go +++ b/ui/middleware.go @@ -135,7 +135,7 @@ func (m *middleware) isPublicRoute(r *http.Request) bool { "favicon", "webManifest", "robots", - "share", + "sharedEntry", "healthcheck": return true default: diff --git a/ui/share.go b/ui/share.go index 62e38a80..6d3ff6e4 100644 --- a/ui/share.go +++ b/ui/share.go @@ -1,4 +1,4 @@ -// Copyright 2017 Frédéric Guillot. All rights reserved. +// 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. @@ -17,18 +17,28 @@ import ( "miniflux.app/ui/view" ) -func (h *handler) shareGenerate(w http.ResponseWriter, r *http.Request) { +func (h *handler) createSharedEntry(w http.ResponseWriter, r *http.Request) { entryID := request.RouteInt64Param(r, "entryID") - shareCode, err := h.store.GetEntryShareCode(request.UserID(r), entryID) + shareCode, err := h.store.EntryShareCode(request.UserID(r), entryID) if err != nil { html.ServerError(w, r, err) return } - html.Redirect(w, r, route.Path(h.router, "share", "shareCode", shareCode)) + html.Redirect(w, r, route.Path(h.router, "sharedEntry", "shareCode", shareCode)) } -func (h *handler) sharePage(w http.ResponseWriter, r *http.Request) { +func (h *handler) unshareEntry(w http.ResponseWriter, r *http.Request) { + entryID := request.RouteInt64Param(r, "entryID") + if err := h.store.UnshareEntry(request.UserID(r), entryID); err != nil { + html.ServerError(w, r, err) + return + } + + html.Redirect(w, r, route.Path(h.router, "sharedEntries")) +} + +func (h *handler) sharedEntry(w http.ResponseWriter, r *http.Request) { shareCode := request.RouteStringParam(r, "shareCode") if shareCode == "" { html.NotFound(w, r) diff --git a/ui/shared_entries.go b/ui/shared_entries.go new file mode 100644 index 00000000..8d599441 --- /dev/null +++ b/ui/shared_entries.go @@ -0,0 +1,52 @@ +// 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. + +package ui // import "miniflux.app/ui" + +import ( + "net/http" + + "miniflux.app/http/request" + "miniflux.app/http/response/html" + "miniflux.app/model" + "miniflux.app/ui/session" + "miniflux.app/ui/view" +) + +func (h *handler) sharedEntries(w http.ResponseWriter, r *http.Request) { + user, err := h.store.UserByID(request.UserID(r)) + if err != nil { + html.ServerError(w, r, err) + return + } + + builder := h.store.NewEntryQueryBuilder(user.ID) + builder.WithShareCodeNotEmpty() + builder.WithOrder(model.DefaultSortingOrder) + builder.WithDirection(user.EntryDirection) + + entries, err := builder.GetEntries() + if err != nil { + html.ServerError(w, r, err) + return + } + + count, err := builder.CountEntries() + if err != nil { + html.ServerError(w, r, err) + return + } + + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) + view.Set("entries", entries) + view.Set("total", count) + view.Set("menu", "history") + view.Set("user", user) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) + view.Set("hasSaveEntry", h.store.HasSaveEntry(user.ID)) + + html.OK(w, r, view.Render("shared_entries")) +} diff --git a/ui/ui.go b/ui/ui.go index e9fbf7fe..c66130c9 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -89,8 +89,10 @@ func Serve(router *mux.Router, store *storage.Storage, pool *worker.Pool, feedHa uiRouter.HandleFunc("/entry/bookmark/{entryID}", handler.toggleBookmark).Name("toggleBookmark").Methods("POST") // Share pages. - uiRouter.HandleFunc("/entry/share/{entryID}", handler.shareGenerate).Name("shareGenerate").Methods("GET") - uiRouter.HandleFunc("/share/{shareCode}", handler.sharePage).Name("share").Methods("GET") + uiRouter.HandleFunc("/entry/share/{entryID}", handler.createSharedEntry).Name("shareEntry").Methods("GET") + uiRouter.HandleFunc("/entry/unshare/{entryID}", handler.unshareEntry).Name("unshareEntry").Methods("POST") + uiRouter.HandleFunc("/share/{shareCode}", handler.sharedEntry).Name("sharedEntry").Methods("GET") + uiRouter.HandleFunc("/shares", handler.sharedEntries).Name("sharedEntries").Methods("GET") // User pages. uiRouter.HandleFunc("/users", handler.showUsersPage).Name("users").Methods("GET")