Add option to change the number of entries per page (fixes #40)

This commit is contained in:
logan 2020-07-09 01:24:54 +02:00 committed by Frédéric Guillot
parent e32fa059e5
commit 5f266319a3
35 changed files with 174 additions and 52 deletions

View File

@ -109,6 +109,7 @@ type userModification struct {
Language *string `json:"language"` Language *string `json:"language"`
Timezone *string `json:"timezone"` Timezone *string `json:"timezone"`
EntryDirection *string `json:"entry_sorting_direction"` EntryDirection *string `json:"entry_sorting_direction"`
EntriesPerPage *int `json:"entries_per_page"`
} }
func (u *userModification) Update(user *model.User) { func (u *userModification) Update(user *model.User) {
@ -139,6 +140,10 @@ func (u *userModification) Update(user *model.User) {
if u.EntryDirection != nil { if u.EntryDirection != nil {
user.EntryDirection = *u.EntryDirection user.EntryDirection = *u.EntryDirection
} }
if u.EntriesPerPage != nil {
user.EntriesPerPage = *u.EntriesPerPage
}
} }
func decodeUserModificationPayload(r io.ReadCloser) (*userModification, error) { func decodeUserModificationPayload(r io.ReadCloser) (*userModification, error) {

View File

@ -26,6 +26,7 @@ type User struct {
Language string `json:"language"` Language string `json:"language"`
Timezone string `json:"timezone"` Timezone string `json:"timezone"`
EntryDirection string `json:"entry_sorting_direction"` EntryDirection string `json:"entry_sorting_direction"`
EntriesPerPage int `json:"entries_per_page"`
LastLoginAt *time.Time `json:"last_login_at"` LastLoginAt *time.Time `json:"last_login_at"`
Extra map[string]string `json:"extra"` Extra map[string]string `json:"extra"`
} }
@ -43,6 +44,7 @@ type UserModification struct {
Language *string `json:"language"` Language *string `json:"language"`
Timezone *string `json:"timezone"` Timezone *string `json:"timezone"`
EntryDirection *string `json:"entry_sorting_direction"` EntryDirection *string `json:"entry_sorting_direction"`
EntriesPerPage *int `json:"entries_per_page"`
} }
// Users represents a list of users. // Users represents a list of users.

View File

@ -12,7 +12,7 @@ import (
"miniflux.app/logger" "miniflux.app/logger"
) )
const schemaVersion = 31 const schemaVersion = 32
// Migrate executes database migrations. // Migrate executes database migrations.
func Migrate(db *sql.DB) { func Migrate(db *sql.DB) {

View File

@ -183,6 +183,8 @@ create unique index entries_share_code_idx on entries using btree(share_code) wh
create index entries_user_feed_idx on entries (user_id, feed_id); create index entries_user_feed_idx on entries (user_id, feed_id);
`, `,
"schema_version_31": `alter table feeds add column ignore_http_cache bool default false;`, "schema_version_31": `alter table feeds add column ignore_http_cache bool default false;`,
"schema_version_32": `alter table users add column entries_per_page int default 100;
`,
"schema_version_4": `create type entry_sorting_direction as enum('asc', 'desc'); "schema_version_4": `create type entry_sorting_direction as enum('asc', 'desc');
alter table users add column entry_direction entry_sorting_direction default 'asc'; alter table users add column entry_direction entry_sorting_direction default 'asc';
`, `,
@ -237,6 +239,7 @@ var SqlMapChecksums = map[string]string{
"schema_version_3": "a54745dbc1c51c000f74d4e5068f1e2f43e83309f023415b1749a47d5c1e0f12", "schema_version_3": "a54745dbc1c51c000f74d4e5068f1e2f43e83309f023415b1749a47d5c1e0f12",
"schema_version_30": "3ec48a9b2e7a0fc32c85f31652f723565c34213f5f2d7e5e5076aad8f0b40d23", "schema_version_30": "3ec48a9b2e7a0fc32c85f31652f723565c34213f5f2d7e5e5076aad8f0b40d23",
"schema_version_31": "9290ef295731b03ddfe32dcaded0be70d41b63572420ad379cf2874a9b54581c", "schema_version_31": "9290ef295731b03ddfe32dcaded0be70d41b63572420ad379cf2874a9b54581c",
"schema_version_32": "5b4de8dd2d7e3c6ae4150e0e3931df2ee989f2c667145bd67294e5a5f3fae456",
"schema_version_4": "216ea3a7d3e1704e40c797b5dc47456517c27dbb6ca98bf88812f4f63d74b5d9", "schema_version_4": "216ea3a7d3e1704e40c797b5dc47456517c27dbb6ca98bf88812f4f63d74b5d9",
"schema_version_5": "46397e2f5f2c82116786127e9f6a403e975b14d2ca7b652a48cd1ba843e6a27c", "schema_version_5": "46397e2f5f2c82116786127e9f6a403e975b14d2ca7b652a48cd1ba843e6a27c",
"schema_version_6": "9d05b4fb223f0e60efc716add5048b0ca9c37511cf2041721e20505d6d798ce4", "schema_version_6": "9d05b4fb223f0e60efc716add5048b0ca9c37511cf2041721e20505d6d798ce4",

View File

@ -0,0 +1 @@
alter table users add column entries_per_page int default 100;

View File

@ -234,6 +234,7 @@ var translations = map[string]string{
"error.different_passwords": "Passwörter stimmen nicht überein.", "error.different_passwords": "Passwörter stimmen nicht überein.",
"error.password_min_length": "Wenigstens 6 Zeichen müssen genutzt werden.", "error.password_min_length": "Wenigstens 6 Zeichen müssen genutzt werden.",
"error.settings_mandatory_fields": "Die Felder für Benutzername, Thema, Sprache und Zeitzone sind obligatorisch.", "error.settings_mandatory_fields": "Die Felder für Benutzername, Thema, Sprache und Zeitzone sind obligatorisch.",
"error.entries_per_page_invalid": "Die Anzahl der Einträge pro Seite ist ungültig.",
"error.feed_mandatory_fields": "Die URL und die Kategorie sind obligatorisch.", "error.feed_mandatory_fields": "Die URL und die Kategorie sind obligatorisch.",
"error.user_mandatory_fields": "Der Benutzername ist obligatorisch.", "error.user_mandatory_fields": "Der Benutzername ist obligatorisch.",
"error.api_key_already_exists": "Dieser API-Schlüssel ist bereits vorhanden.", "error.api_key_already_exists": "Dieser API-Schlüssel ist bereits vorhanden.",
@ -259,6 +260,7 @@ var translations = map[string]string{
"form.prefs.label.timezone": "Zeitzone", "form.prefs.label.timezone": "Zeitzone",
"form.prefs.label.theme": "Thema", "form.prefs.label.theme": "Thema",
"form.prefs.label.entry_sorting": "Sortierung der Artikel", "form.prefs.label.entry_sorting": "Sortierung der Artikel",
"form.prefs.label.entries_per_page": "Einträge pro Seite",
"form.prefs.select.older_first": "Älteste Artikel zuerst", "form.prefs.select.older_first": "Älteste Artikel zuerst",
"form.prefs.select.recent_first": "Neueste Artikel zuerst", "form.prefs.select.recent_first": "Neueste Artikel zuerst",
"form.prefs.label.keyboard_shortcuts": "Tastaturkürzel aktivieren", "form.prefs.label.keyboard_shortcuts": "Tastaturkürzel aktivieren",
@ -572,6 +574,7 @@ var translations = map[string]string{
"error.different_passwords": "Passwords are not the same.", "error.different_passwords": "Passwords are not the same.",
"error.password_min_length": "The password must have at least 6 characters.", "error.password_min_length": "The password must have at least 6 characters.",
"error.settings_mandatory_fields": "The username, theme, language and timezone fields are mandatory.", "error.settings_mandatory_fields": "The username, theme, language and timezone fields are mandatory.",
"error.entries_per_page_invalid": "The number of entries per page is not valid.",
"error.feed_mandatory_fields": "The URL and the category are mandatory.", "error.feed_mandatory_fields": "The URL and the category are mandatory.",
"error.user_mandatory_fields": "The username is mandatory.", "error.user_mandatory_fields": "The username is mandatory.",
"error.api_key_already_exists": "This API Key already exists.", "error.api_key_already_exists": "This API Key already exists.",
@ -597,6 +600,7 @@ var translations = map[string]string{
"form.prefs.label.timezone": "Timezone", "form.prefs.label.timezone": "Timezone",
"form.prefs.label.theme": "Theme", "form.prefs.label.theme": "Theme",
"form.prefs.label.entry_sorting": "Entry Sorting", "form.prefs.label.entry_sorting": "Entry Sorting",
"form.prefs.label.entries_per_page": "Entries per page",
"form.prefs.select.older_first": "Older entries first", "form.prefs.select.older_first": "Older entries first",
"form.prefs.select.recent_first": "Recent entries first", "form.prefs.select.recent_first": "Recent entries first",
"form.prefs.label.keyboard_shortcuts": "Enable keyboard shortcuts", "form.prefs.label.keyboard_shortcuts": "Enable keyboard shortcuts",
@ -890,6 +894,7 @@ var translations = map[string]string{
"error.different_passwords": "Las contraseñas no son las mismas.", "error.different_passwords": "Las contraseñas no son las mismas.",
"error.password_min_length": "La contraseña debería tener al menos 6 caracteres.", "error.password_min_length": "La contraseña debería tener al menos 6 caracteres.",
"error.settings_mandatory_fields": "Los campos de nombre de usuario, tema, idioma y zona horaria son obligatorios.", "error.settings_mandatory_fields": "Los campos de nombre de usuario, tema, idioma y zona horaria son obligatorios.",
"error.entries_per_page_invalid": "El número de entradas por página no es válido.",
"error.feed_mandatory_fields": "Los campos de URL y categoría son obligatorios.", "error.feed_mandatory_fields": "Los campos de URL y categoría son obligatorios.",
"error.user_mandatory_fields": "El nombre de usuario es obligatorio.", "error.user_mandatory_fields": "El nombre de usuario es obligatorio.",
"error.api_key_already_exists": "Esta clave API ya existe.", "error.api_key_already_exists": "Esta clave API ya existe.",
@ -915,6 +920,7 @@ var translations = map[string]string{
"form.prefs.label.timezone": "Zona horaria", "form.prefs.label.timezone": "Zona horaria",
"form.prefs.label.theme": "Tema", "form.prefs.label.theme": "Tema",
"form.prefs.label.entry_sorting": "Clasificación de entradas", "form.prefs.label.entry_sorting": "Clasificación de entradas",
"form.prefs.label.entries_per_page": "Entradas por página",
"form.prefs.select.older_first": "Entradas más viejas primero", "form.prefs.select.older_first": "Entradas más viejas primero",
"form.prefs.select.recent_first": "Entradas recientes primero", "form.prefs.select.recent_first": "Entradas recientes primero",
"form.prefs.label.keyboard_shortcuts": "Habilitar atajos de teclado", "form.prefs.label.keyboard_shortcuts": "Habilitar atajos de teclado",
@ -1208,6 +1214,7 @@ var translations = map[string]string{
"error.different_passwords": "Les mots de passe ne sont pas les mêmes.", "error.different_passwords": "Les mots de passe ne sont pas les mêmes.",
"error.password_min_length": "Vous devez utiliser au moins 6 caractères pour le mot de passe.", "error.password_min_length": "Vous devez utiliser au moins 6 caractères pour le mot de passe.",
"error.settings_mandatory_fields": "Le nom d'utilisateur, le thème, la langue et le fuseau horaire sont obligatoire.", "error.settings_mandatory_fields": "Le nom d'utilisateur, le thème, la langue et le fuseau horaire sont obligatoire.",
"error.entries_per_page_invalid": "Le nombre d'entrées par page n'est pas valide.",
"error.feed_mandatory_fields": "L'URL et la catégorie sont obligatoire.", "error.feed_mandatory_fields": "L'URL et la catégorie sont obligatoire.",
"error.user_mandatory_fields": "Le nom d'utilisateur est obligatoire.", "error.user_mandatory_fields": "Le nom d'utilisateur est obligatoire.",
"error.api_key_already_exists": "Cette clé d'API existe déjà.", "error.api_key_already_exists": "Cette clé d'API existe déjà.",
@ -1233,6 +1240,7 @@ var translations = map[string]string{
"form.prefs.label.timezone": "Fuseau horaire", "form.prefs.label.timezone": "Fuseau horaire",
"form.prefs.label.theme": "Thème", "form.prefs.label.theme": "Thème",
"form.prefs.label.entry_sorting": "Ordre des éléments", "form.prefs.label.entry_sorting": "Ordre des éléments",
"form.prefs.label.entries_per_page": "Entrées par page",
"form.prefs.select.older_first": "Ancien éléments en premier", "form.prefs.select.older_first": "Ancien éléments en premier",
"form.prefs.select.recent_first": "Éléments récents en premier", "form.prefs.select.recent_first": "Éléments récents en premier",
"form.prefs.label.keyboard_shortcuts": "Activer les raccourcis clavier", "form.prefs.label.keyboard_shortcuts": "Activer les raccourcis clavier",
@ -1546,6 +1554,7 @@ var translations = map[string]string{
"error.different_passwords": "Le password non coincidono.", "error.different_passwords": "Le password non coincidono.",
"error.password_min_length": "La password deve contenere almeno 6 caratteri.", "error.password_min_length": "La password deve contenere almeno 6 caratteri.",
"error.settings_mandatory_fields": "Il nome utente, il tema, la lingua ed il fuso orario sono campi obbligatori.", "error.settings_mandatory_fields": "Il nome utente, il tema, la lingua ed il fuso orario sono campi obbligatori.",
"error.entries_per_page_invalid": "Il numero di articoli per pagina non è valido.",
"error.feed_mandatory_fields": "L'URL e la categoria sono obbligatori.", "error.feed_mandatory_fields": "L'URL e la categoria sono obbligatori.",
"error.user_mandatory_fields": "Il nome utente è obbligatorio.", "error.user_mandatory_fields": "Il nome utente è obbligatorio.",
"error.api_key_already_exists": "Questa chiave API esiste già.", "error.api_key_already_exists": "Questa chiave API esiste già.",
@ -1571,6 +1580,7 @@ var translations = map[string]string{
"form.prefs.label.timezone": "Fuso orario", "form.prefs.label.timezone": "Fuso orario",
"form.prefs.label.theme": "Tema", "form.prefs.label.theme": "Tema",
"form.prefs.label.entry_sorting": "Ordinamento articoli", "form.prefs.label.entry_sorting": "Ordinamento articoli",
"form.prefs.label.entries_per_page": "Articoli per pagina",
"form.prefs.select.older_first": "Prima i più vecchi", "form.prefs.select.older_first": "Prima i più vecchi",
"form.prefs.select.recent_first": "Prima i più recenti", "form.prefs.select.recent_first": "Prima i più recenti",
"form.prefs.label.keyboard_shortcuts": "Abilita le scorciatoie da tastiera", "form.prefs.label.keyboard_shortcuts": "Abilita le scorciatoie da tastiera",
@ -1864,6 +1874,7 @@ var translations = map[string]string{
"error.different_passwords": "パスワードが一致しません。", "error.different_passwords": "パスワードが一致しません。",
"error.password_min_length": "パスワードは6文字以上である必要があります。", "error.password_min_length": "パスワードは6文字以上である必要があります。",
"error.settings_mandatory_fields": "ユーザー名、テーマ、言語、タイムゾーンの全てが必要です。", "error.settings_mandatory_fields": "ユーザー名、テーマ、言語、タイムゾーンの全てが必要です。",
"error.entries_per_page_invalid": "ページあたりのエントリ数が無効です。",
"error.feed_mandatory_fields": "URL と カテゴリが必要です。", "error.feed_mandatory_fields": "URL と カテゴリが必要です。",
"error.user_mandatory_fields": "ユーザー名が必要です。", "error.user_mandatory_fields": "ユーザー名が必要です。",
"error.api_key_already_exists": "このAPIキーは既に存在します。", "error.api_key_already_exists": "このAPIキーは既に存在します。",
@ -1889,6 +1900,7 @@ var translations = map[string]string{
"form.prefs.label.timezone": "タイムゾーン", "form.prefs.label.timezone": "タイムゾーン",
"form.prefs.label.theme": "テーマ", "form.prefs.label.theme": "テーマ",
"form.prefs.label.entry_sorting": "記事の並べ替え", "form.prefs.label.entry_sorting": "記事の並べ替え",
"form.prefs.label.entries_per_page": "ページあたりのエントリ",
"form.prefs.select.older_first": "古い記事を最初に", "form.prefs.select.older_first": "古い記事を最初に",
"form.prefs.select.recent_first": "新しい記事を最初に", "form.prefs.select.recent_first": "新しい記事を最初に",
"form.prefs.label.keyboard_shortcuts": "キーボード・ショートカットを有効にする", "form.prefs.label.keyboard_shortcuts": "キーボード・ショートカットを有効にする",
@ -2182,6 +2194,7 @@ var translations = map[string]string{
"error.different_passwords": "Wachtwoorden zijn niet hetzelfde.", "error.different_passwords": "Wachtwoorden zijn niet hetzelfde.",
"error.password_min_length": "Je moet minstens 6 tekens gebruiken.", "error.password_min_length": "Je moet minstens 6 tekens gebruiken.",
"error.settings_mandatory_fields": "Gebruikersnaam, skin, taal en tijdzone zijn verplicht.", "error.settings_mandatory_fields": "Gebruikersnaam, skin, taal en tijdzone zijn verplicht.",
"error.entries_per_page_invalid": "Het aantal inzendingen per pagina is niet geldig.",
"error.feed_mandatory_fields": "The URL en de categorie zijn verplicht.", "error.feed_mandatory_fields": "The URL en de categorie zijn verplicht.",
"error.user_mandatory_fields": "Gebruikersnaam is verplicht", "error.user_mandatory_fields": "Gebruikersnaam is verplicht",
"error.api_key_already_exists": "This API Key already exists.", "error.api_key_already_exists": "This API Key already exists.",
@ -2207,6 +2220,7 @@ var translations = map[string]string{
"form.prefs.label.timezone": "Tijdzone", "form.prefs.label.timezone": "Tijdzone",
"form.prefs.label.theme": "Skin", "form.prefs.label.theme": "Skin",
"form.prefs.label.entry_sorting": "Volgorde van items", "form.prefs.label.entry_sorting": "Volgorde van items",
"form.prefs.label.entries_per_page": "Inzendingen per pagina",
"form.prefs.select.older_first": "Oudere items eerst", "form.prefs.select.older_first": "Oudere items eerst",
"form.prefs.select.recent_first": "Recente items eerst", "form.prefs.select.recent_first": "Recente items eerst",
"form.prefs.label.keyboard_shortcuts": "Schakel sneltoetsen in", "form.prefs.label.keyboard_shortcuts": "Schakel sneltoetsen in",
@ -2520,6 +2534,7 @@ var translations = map[string]string{
"error.different_passwords": "Hasła nie są identyczne.", "error.different_passwords": "Hasła nie są identyczne.",
"error.password_min_length": "Musisz użyć co najmniej 6 znaków.", "error.password_min_length": "Musisz użyć co najmniej 6 znaków.",
"error.settings_mandatory_fields": "Pola nazwy użytkownika, tematu, języka i strefy czasowej są obowiązkowe.", "error.settings_mandatory_fields": "Pola nazwy użytkownika, tematu, języka i strefy czasowej są obowiązkowe.",
"error.entries_per_page_invalid": "Liczba wpisów na stronę jest nieprawidłowa.",
"error.feed_mandatory_fields": "URL i kategoria są obowiązkowe.", "error.feed_mandatory_fields": "URL i kategoria są obowiązkowe.",
"error.user_mandatory_fields": "Nazwa użytkownika jest obowiązkowa.", "error.user_mandatory_fields": "Nazwa użytkownika jest obowiązkowa.",
"error.api_key_already_exists": "Deze API-sleutel bestaat al.", "error.api_key_already_exists": "Deze API-sleutel bestaat al.",
@ -2545,6 +2560,7 @@ var translations = map[string]string{
"form.prefs.label.timezone": "Strefa czasowa", "form.prefs.label.timezone": "Strefa czasowa",
"form.prefs.label.theme": "Wygląd", "form.prefs.label.theme": "Wygląd",
"form.prefs.label.entry_sorting": "Sortowanie artykułów", "form.prefs.label.entry_sorting": "Sortowanie artykułów",
"form.prefs.label.entries_per_page": "Wpisy na stronie",
"form.prefs.select.older_first": "Najstarsze wpisy jako pierwsze", "form.prefs.select.older_first": "Najstarsze wpisy jako pierwsze",
"form.prefs.label.keyboard_shortcuts": "Włącz skróty klawiaturowe", "form.prefs.label.keyboard_shortcuts": "Włącz skróty klawiaturowe",
"form.prefs.select.recent_first": "Najnowsze wpisy jako pierwsze", "form.prefs.select.recent_first": "Najnowsze wpisy jako pierwsze",
@ -2864,6 +2880,7 @@ var translations = map[string]string{
"error.different_passwords": "Пароли не совпадают.", "error.different_passwords": "Пароли не совпадают.",
"error.password_min_length": "Вы должны использовать минимум 6 символов.", "error.password_min_length": "Вы должны использовать минимум 6 символов.",
"error.settings_mandatory_fields": "Имя пользователя, тема, язык и часовой пояс обязательны.", "error.settings_mandatory_fields": "Имя пользователя, тема, язык и часовой пояс обязательны.",
"error.entries_per_page_invalid": "Количество записей на странице недействительно.",
"error.feed_mandatory_fields": "URL и категория обязательны.", "error.feed_mandatory_fields": "URL и категория обязательны.",
"error.user_mandatory_fields": "Имя пользователя обязательно.", "error.user_mandatory_fields": "Имя пользователя обязательно.",
"error.api_key_already_exists": "Этот ключ API уже существует.", "error.api_key_already_exists": "Этот ключ API уже существует.",
@ -2889,6 +2906,7 @@ var translations = map[string]string{
"form.prefs.label.timezone": "Часовой пояс", "form.prefs.label.timezone": "Часовой пояс",
"form.prefs.label.theme": "Тема", "form.prefs.label.theme": "Тема",
"form.prefs.label.entry_sorting": "Сортировка записей", "form.prefs.label.entry_sorting": "Сортировка записей",
"form.prefs.label.entries_per_page": "Записи на странице",
"form.prefs.select.older_first": "Сначала старые записи", "form.prefs.select.older_first": "Сначала старые записи",
"form.prefs.select.recent_first": "Сначала последние записи", "form.prefs.select.recent_first": "Сначала последние записи",
"form.prefs.label.keyboard_shortcuts": "Включить сочетания клавиш", "form.prefs.label.keyboard_shortcuts": "Включить сочетания клавиш",
@ -3186,6 +3204,7 @@ var translations = map[string]string{
"error.different_passwords": "两次输入的密码不同", "error.different_passwords": "两次输入的密码不同",
"error.password_min_length": "请至少使用6个字符", "error.password_min_length": "请至少使用6个字符",
"error.settings_mandatory_fields": "必须填写用户名、主题、语言以及时区", "error.settings_mandatory_fields": "必须填写用户名、主题、语言以及时区",
"error.entries_per_page_invalid": "每页的条目数无效。",
"error.feed_mandatory_fields": "必须填写 URL 和分类", "error.feed_mandatory_fields": "必须填写 URL 和分类",
"error.user_mandatory_fields": "必须填写用户名", "error.user_mandatory_fields": "必须填写用户名",
"error.api_key_already_exists": "此API密钥已存在。", "error.api_key_already_exists": "此API密钥已存在。",
@ -3211,6 +3230,7 @@ var translations = map[string]string{
"form.prefs.label.timezone": "时区", "form.prefs.label.timezone": "时区",
"form.prefs.label.theme": "主题", "form.prefs.label.theme": "主题",
"form.prefs.label.entry_sorting": "内容排序", "form.prefs.label.entry_sorting": "内容排序",
"form.prefs.label.entries_per_page": "每页条目",
"form.prefs.select.older_first": "旧->新", "form.prefs.select.older_first": "旧->新",
"form.prefs.select.recent_first": "新->旧", "form.prefs.select.recent_first": "新->旧",
"form.prefs.label.keyboard_shortcuts": "启用键盘快捷键", "form.prefs.label.keyboard_shortcuts": "启用键盘快捷键",
@ -3289,14 +3309,14 @@ var translations = map[string]string{
} }
var translationsChecksums = map[string]string{ var translationsChecksums = map[string]string{
"de_DE": "ddb063682852c86361af350be616d3bd328373ecb927804824008d016aa7c67c", "de_DE": "e986a40b1748968725ddede18ae6451e4d1ae270b9c4c033daa81ee50b1d306e",
"en_US": "350b835f759212abd2110322394aa00b666fbf27d752532a7700fb52d5af3f02", "en_US": "b27169fc7767e51e6f7610ff1844708e8111e527c7931e3f888864a66826e293",
"es_ES": "26efc79faaf35efe5a33528cedc2522496987d290c9e86d8fff3a9bcbed3e441", "es_ES": "20a713468ca6ce00e899a80354912e927ded61cf8a79ad9d976c78f515e242dd",
"fr_FR": "e8736791d5373b955cacce215b3ae67d56280bfa5d4596899e4e5e37ff962afd", "fr_FR": "251eb14fe8521bde772d293fa748307ecd4cae4b0597da03aad39e745a382f11",
"it_IT": "8ec2311e00c45b4d2b939ad0280fe49277f5c851a4cd521f42be1a88baef4c34", "it_IT": "8ab664ec8d826aa3702a4f5294c3a3e87193437e64b0ef4990a3a9609b782786",
"ja_JP": "237f49939be015b509d4b3a02890691c3766df8878109114493624cfd13c0cad", "ja_JP": "7dc146dc5815a8d6dbae2f7f467deea598a85099bbee63e92bf3862d445519af",
"nl_NL": "c70e1eaa3c2e8c0130522189c3932b52ee6e9ff91c91b0090eb9178f2f23c588", "nl_NL": "fd106f08b2f8902712a68716a0e33b063bdce32a8440f7a2b296b4f822088403",
"pl_PL": "1d5e05789a3150a8f1ddbe57616d509d1d33c61b60200c563a5e23571671209e", "pl_PL": "85de665d29e873f6099ef5ea40efe569a05ec3cbf08e4ca7741778bf3d5c8593",
"ru_RU": "b0408b7a150bd79e411376ced3acb706a12e6b28e564a6abfedbdebc2d552915", "ru_RU": "6e765e44e250469fe1c5666f8ff24e5e07e6b04098c1325c2663a1f722e0bfe9",
"zh_CN": "7732905e498d087c9a11ecc3eae8736e758c6b053da13de64fd6599ca40d8ee6", "zh_CN": "0dc8c5b86a03f0ce58f6d2633ab3011d9bc8004af18f922944a65d151e54beda",
} }

View File

@ -229,6 +229,7 @@
"error.different_passwords": "Passwörter stimmen nicht überein.", "error.different_passwords": "Passwörter stimmen nicht überein.",
"error.password_min_length": "Wenigstens 6 Zeichen müssen genutzt werden.", "error.password_min_length": "Wenigstens 6 Zeichen müssen genutzt werden.",
"error.settings_mandatory_fields": "Die Felder für Benutzername, Thema, Sprache und Zeitzone sind obligatorisch.", "error.settings_mandatory_fields": "Die Felder für Benutzername, Thema, Sprache und Zeitzone sind obligatorisch.",
"error.entries_per_page_invalid": "Die Anzahl der Einträge pro Seite ist ungültig.",
"error.feed_mandatory_fields": "Die URL und die Kategorie sind obligatorisch.", "error.feed_mandatory_fields": "Die URL und die Kategorie sind obligatorisch.",
"error.user_mandatory_fields": "Der Benutzername ist obligatorisch.", "error.user_mandatory_fields": "Der Benutzername ist obligatorisch.",
"error.api_key_already_exists": "Dieser API-Schlüssel ist bereits vorhanden.", "error.api_key_already_exists": "Dieser API-Schlüssel ist bereits vorhanden.",
@ -254,6 +255,7 @@
"form.prefs.label.timezone": "Zeitzone", "form.prefs.label.timezone": "Zeitzone",
"form.prefs.label.theme": "Thema", "form.prefs.label.theme": "Thema",
"form.prefs.label.entry_sorting": "Sortierung der Artikel", "form.prefs.label.entry_sorting": "Sortierung der Artikel",
"form.prefs.label.entries_per_page": "Einträge pro Seite",
"form.prefs.select.older_first": "Älteste Artikel zuerst", "form.prefs.select.older_first": "Älteste Artikel zuerst",
"form.prefs.select.recent_first": "Neueste Artikel zuerst", "form.prefs.select.recent_first": "Neueste Artikel zuerst",
"form.prefs.label.keyboard_shortcuts": "Tastaturkürzel aktivieren", "form.prefs.label.keyboard_shortcuts": "Tastaturkürzel aktivieren",

View File

@ -229,6 +229,7 @@
"error.different_passwords": "Passwords are not the same.", "error.different_passwords": "Passwords are not the same.",
"error.password_min_length": "The password must have at least 6 characters.", "error.password_min_length": "The password must have at least 6 characters.",
"error.settings_mandatory_fields": "The username, theme, language and timezone fields are mandatory.", "error.settings_mandatory_fields": "The username, theme, language and timezone fields are mandatory.",
"error.entries_per_page_invalid": "The number of entries per page is not valid.",
"error.feed_mandatory_fields": "The URL and the category are mandatory.", "error.feed_mandatory_fields": "The URL and the category are mandatory.",
"error.user_mandatory_fields": "The username is mandatory.", "error.user_mandatory_fields": "The username is mandatory.",
"error.api_key_already_exists": "This API Key already exists.", "error.api_key_already_exists": "This API Key already exists.",
@ -254,6 +255,7 @@
"form.prefs.label.timezone": "Timezone", "form.prefs.label.timezone": "Timezone",
"form.prefs.label.theme": "Theme", "form.prefs.label.theme": "Theme",
"form.prefs.label.entry_sorting": "Entry Sorting", "form.prefs.label.entry_sorting": "Entry Sorting",
"form.prefs.label.entries_per_page": "Entries per page",
"form.prefs.select.older_first": "Older entries first", "form.prefs.select.older_first": "Older entries first",
"form.prefs.select.recent_first": "Recent entries first", "form.prefs.select.recent_first": "Recent entries first",
"form.prefs.label.keyboard_shortcuts": "Enable keyboard shortcuts", "form.prefs.label.keyboard_shortcuts": "Enable keyboard shortcuts",

View File

@ -229,6 +229,7 @@
"error.different_passwords": "Las contraseñas no son las mismas.", "error.different_passwords": "Las contraseñas no son las mismas.",
"error.password_min_length": "La contraseña debería tener al menos 6 caracteres.", "error.password_min_length": "La contraseña debería tener al menos 6 caracteres.",
"error.settings_mandatory_fields": "Los campos de nombre de usuario, tema, idioma y zona horaria son obligatorios.", "error.settings_mandatory_fields": "Los campos de nombre de usuario, tema, idioma y zona horaria son obligatorios.",
"error.entries_per_page_invalid": "El número de entradas por página no es válido.",
"error.feed_mandatory_fields": "Los campos de URL y categoría son obligatorios.", "error.feed_mandatory_fields": "Los campos de URL y categoría son obligatorios.",
"error.user_mandatory_fields": "El nombre de usuario es obligatorio.", "error.user_mandatory_fields": "El nombre de usuario es obligatorio.",
"error.api_key_already_exists": "Esta clave API ya existe.", "error.api_key_already_exists": "Esta clave API ya existe.",
@ -254,6 +255,7 @@
"form.prefs.label.timezone": "Zona horaria", "form.prefs.label.timezone": "Zona horaria",
"form.prefs.label.theme": "Tema", "form.prefs.label.theme": "Tema",
"form.prefs.label.entry_sorting": "Clasificación de entradas", "form.prefs.label.entry_sorting": "Clasificación de entradas",
"form.prefs.label.entries_per_page": "Entradas por página",
"form.prefs.select.older_first": "Entradas más viejas primero", "form.prefs.select.older_first": "Entradas más viejas primero",
"form.prefs.select.recent_first": "Entradas recientes primero", "form.prefs.select.recent_first": "Entradas recientes primero",
"form.prefs.label.keyboard_shortcuts": "Habilitar atajos de teclado", "form.prefs.label.keyboard_shortcuts": "Habilitar atajos de teclado",

View File

@ -229,6 +229,7 @@
"error.different_passwords": "Les mots de passe ne sont pas les mêmes.", "error.different_passwords": "Les mots de passe ne sont pas les mêmes.",
"error.password_min_length": "Vous devez utiliser au moins 6 caractères pour le mot de passe.", "error.password_min_length": "Vous devez utiliser au moins 6 caractères pour le mot de passe.",
"error.settings_mandatory_fields": "Le nom d'utilisateur, le thème, la langue et le fuseau horaire sont obligatoire.", "error.settings_mandatory_fields": "Le nom d'utilisateur, le thème, la langue et le fuseau horaire sont obligatoire.",
"error.entries_per_page_invalid": "Le nombre d'entrées par page n'est pas valide.",
"error.feed_mandatory_fields": "L'URL et la catégorie sont obligatoire.", "error.feed_mandatory_fields": "L'URL et la catégorie sont obligatoire.",
"error.user_mandatory_fields": "Le nom d'utilisateur est obligatoire.", "error.user_mandatory_fields": "Le nom d'utilisateur est obligatoire.",
"error.api_key_already_exists": "Cette clé d'API existe déjà.", "error.api_key_already_exists": "Cette clé d'API existe déjà.",
@ -254,6 +255,7 @@
"form.prefs.label.timezone": "Fuseau horaire", "form.prefs.label.timezone": "Fuseau horaire",
"form.prefs.label.theme": "Thème", "form.prefs.label.theme": "Thème",
"form.prefs.label.entry_sorting": "Ordre des éléments", "form.prefs.label.entry_sorting": "Ordre des éléments",
"form.prefs.label.entries_per_page": "Entrées par page",
"form.prefs.select.older_first": "Ancien éléments en premier", "form.prefs.select.older_first": "Ancien éléments en premier",
"form.prefs.select.recent_first": "Éléments récents en premier", "form.prefs.select.recent_first": "Éléments récents en premier",
"form.prefs.label.keyboard_shortcuts": "Activer les raccourcis clavier", "form.prefs.label.keyboard_shortcuts": "Activer les raccourcis clavier",

View File

@ -229,6 +229,7 @@
"error.different_passwords": "Le password non coincidono.", "error.different_passwords": "Le password non coincidono.",
"error.password_min_length": "La password deve contenere almeno 6 caratteri.", "error.password_min_length": "La password deve contenere almeno 6 caratteri.",
"error.settings_mandatory_fields": "Il nome utente, il tema, la lingua ed il fuso orario sono campi obbligatori.", "error.settings_mandatory_fields": "Il nome utente, il tema, la lingua ed il fuso orario sono campi obbligatori.",
"error.entries_per_page_invalid": "Il numero di articoli per pagina non è valido.",
"error.feed_mandatory_fields": "L'URL e la categoria sono obbligatori.", "error.feed_mandatory_fields": "L'URL e la categoria sono obbligatori.",
"error.user_mandatory_fields": "Il nome utente è obbligatorio.", "error.user_mandatory_fields": "Il nome utente è obbligatorio.",
"error.api_key_already_exists": "Questa chiave API esiste già.", "error.api_key_already_exists": "Questa chiave API esiste già.",
@ -254,6 +255,7 @@
"form.prefs.label.timezone": "Fuso orario", "form.prefs.label.timezone": "Fuso orario",
"form.prefs.label.theme": "Tema", "form.prefs.label.theme": "Tema",
"form.prefs.label.entry_sorting": "Ordinamento articoli", "form.prefs.label.entry_sorting": "Ordinamento articoli",
"form.prefs.label.entries_per_page": "Articoli per pagina",
"form.prefs.select.older_first": "Prima i più vecchi", "form.prefs.select.older_first": "Prima i più vecchi",
"form.prefs.select.recent_first": "Prima i più recenti", "form.prefs.select.recent_first": "Prima i più recenti",
"form.prefs.label.keyboard_shortcuts": "Abilita le scorciatoie da tastiera", "form.prefs.label.keyboard_shortcuts": "Abilita le scorciatoie da tastiera",

View File

@ -229,6 +229,7 @@
"error.different_passwords": "パスワードが一致しません。", "error.different_passwords": "パスワードが一致しません。",
"error.password_min_length": "パスワードは6文字以上である必要があります。", "error.password_min_length": "パスワードは6文字以上である必要があります。",
"error.settings_mandatory_fields": "ユーザー名、テーマ、言語、タイムゾーンの全てが必要です。", "error.settings_mandatory_fields": "ユーザー名、テーマ、言語、タイムゾーンの全てが必要です。",
"error.entries_per_page_invalid": "ページあたりのエントリ数が無効です。",
"error.feed_mandatory_fields": "URL と カテゴリが必要です。", "error.feed_mandatory_fields": "URL と カテゴリが必要です。",
"error.user_mandatory_fields": "ユーザー名が必要です。", "error.user_mandatory_fields": "ユーザー名が必要です。",
"error.api_key_already_exists": "このAPIキーは既に存在します。", "error.api_key_already_exists": "このAPIキーは既に存在します。",
@ -254,6 +255,7 @@
"form.prefs.label.timezone": "タイムゾーン", "form.prefs.label.timezone": "タイムゾーン",
"form.prefs.label.theme": "テーマ", "form.prefs.label.theme": "テーマ",
"form.prefs.label.entry_sorting": "記事の並べ替え", "form.prefs.label.entry_sorting": "記事の並べ替え",
"form.prefs.label.entries_per_page": "ページあたりのエントリ",
"form.prefs.select.older_first": "古い記事を最初に", "form.prefs.select.older_first": "古い記事を最初に",
"form.prefs.select.recent_first": "新しい記事を最初に", "form.prefs.select.recent_first": "新しい記事を最初に",
"form.prefs.label.keyboard_shortcuts": "キーボード・ショートカットを有効にする", "form.prefs.label.keyboard_shortcuts": "キーボード・ショートカットを有効にする",

View File

@ -229,6 +229,7 @@
"error.different_passwords": "Wachtwoorden zijn niet hetzelfde.", "error.different_passwords": "Wachtwoorden zijn niet hetzelfde.",
"error.password_min_length": "Je moet minstens 6 tekens gebruiken.", "error.password_min_length": "Je moet minstens 6 tekens gebruiken.",
"error.settings_mandatory_fields": "Gebruikersnaam, skin, taal en tijdzone zijn verplicht.", "error.settings_mandatory_fields": "Gebruikersnaam, skin, taal en tijdzone zijn verplicht.",
"error.entries_per_page_invalid": "Het aantal inzendingen per pagina is niet geldig.",
"error.feed_mandatory_fields": "The URL en de categorie zijn verplicht.", "error.feed_mandatory_fields": "The URL en de categorie zijn verplicht.",
"error.user_mandatory_fields": "Gebruikersnaam is verplicht", "error.user_mandatory_fields": "Gebruikersnaam is verplicht",
"error.api_key_already_exists": "This API Key already exists.", "error.api_key_already_exists": "This API Key already exists.",
@ -254,6 +255,7 @@
"form.prefs.label.timezone": "Tijdzone", "form.prefs.label.timezone": "Tijdzone",
"form.prefs.label.theme": "Skin", "form.prefs.label.theme": "Skin",
"form.prefs.label.entry_sorting": "Volgorde van items", "form.prefs.label.entry_sorting": "Volgorde van items",
"form.prefs.label.entries_per_page": "Inzendingen per pagina",
"form.prefs.select.older_first": "Oudere items eerst", "form.prefs.select.older_first": "Oudere items eerst",
"form.prefs.select.recent_first": "Recente items eerst", "form.prefs.select.recent_first": "Recente items eerst",
"form.prefs.label.keyboard_shortcuts": "Schakel sneltoetsen in", "form.prefs.label.keyboard_shortcuts": "Schakel sneltoetsen in",

View File

@ -231,6 +231,7 @@
"error.different_passwords": "Hasła nie są identyczne.", "error.different_passwords": "Hasła nie są identyczne.",
"error.password_min_length": "Musisz użyć co najmniej 6 znaków.", "error.password_min_length": "Musisz użyć co najmniej 6 znaków.",
"error.settings_mandatory_fields": "Pola nazwy użytkownika, tematu, języka i strefy czasowej są obowiązkowe.", "error.settings_mandatory_fields": "Pola nazwy użytkownika, tematu, języka i strefy czasowej są obowiązkowe.",
"error.entries_per_page_invalid": "Liczba wpisów na stronę jest nieprawidłowa.",
"error.feed_mandatory_fields": "URL i kategoria są obowiązkowe.", "error.feed_mandatory_fields": "URL i kategoria są obowiązkowe.",
"error.user_mandatory_fields": "Nazwa użytkownika jest obowiązkowa.", "error.user_mandatory_fields": "Nazwa użytkownika jest obowiązkowa.",
"error.api_key_already_exists": "Deze API-sleutel bestaat al.", "error.api_key_already_exists": "Deze API-sleutel bestaat al.",
@ -256,6 +257,7 @@
"form.prefs.label.timezone": "Strefa czasowa", "form.prefs.label.timezone": "Strefa czasowa",
"form.prefs.label.theme": "Wygląd", "form.prefs.label.theme": "Wygląd",
"form.prefs.label.entry_sorting": "Sortowanie artykułów", "form.prefs.label.entry_sorting": "Sortowanie artykułów",
"form.prefs.label.entries_per_page": "Wpisy na stronie",
"form.prefs.select.older_first": "Najstarsze wpisy jako pierwsze", "form.prefs.select.older_first": "Najstarsze wpisy jako pierwsze",
"form.prefs.label.keyboard_shortcuts": "Włącz skróty klawiaturowe", "form.prefs.label.keyboard_shortcuts": "Włącz skróty klawiaturowe",
"form.prefs.select.recent_first": "Najnowsze wpisy jako pierwsze", "form.prefs.select.recent_first": "Najnowsze wpisy jako pierwsze",

View File

@ -231,6 +231,7 @@
"error.different_passwords": "Пароли не совпадают.", "error.different_passwords": "Пароли не совпадают.",
"error.password_min_length": "Вы должны использовать минимум 6 символов.", "error.password_min_length": "Вы должны использовать минимум 6 символов.",
"error.settings_mandatory_fields": "Имя пользователя, тема, язык и часовой пояс обязательны.", "error.settings_mandatory_fields": "Имя пользователя, тема, язык и часовой пояс обязательны.",
"error.entries_per_page_invalid": "Количество записей на странице недействительно.",
"error.feed_mandatory_fields": "URL и категория обязательны.", "error.feed_mandatory_fields": "URL и категория обязательны.",
"error.user_mandatory_fields": "Имя пользователя обязательно.", "error.user_mandatory_fields": "Имя пользователя обязательно.",
"error.api_key_already_exists": "Этот ключ API уже существует.", "error.api_key_already_exists": "Этот ключ API уже существует.",
@ -256,6 +257,7 @@
"form.prefs.label.timezone": "Часовой пояс", "form.prefs.label.timezone": "Часовой пояс",
"form.prefs.label.theme": "Тема", "form.prefs.label.theme": "Тема",
"form.prefs.label.entry_sorting": "Сортировка записей", "form.prefs.label.entry_sorting": "Сортировка записей",
"form.prefs.label.entries_per_page": "Записи на странице",
"form.prefs.select.older_first": "Сначала старые записи", "form.prefs.select.older_first": "Сначала старые записи",
"form.prefs.select.recent_first": "Сначала последние записи", "form.prefs.select.recent_first": "Сначала последние записи",
"form.prefs.label.keyboard_shortcuts": "Включить сочетания клавиш", "form.prefs.label.keyboard_shortcuts": "Включить сочетания клавиш",

View File

@ -227,6 +227,7 @@
"error.different_passwords": "两次输入的密码不同", "error.different_passwords": "两次输入的密码不同",
"error.password_min_length": "请至少使用6个字符", "error.password_min_length": "请至少使用6个字符",
"error.settings_mandatory_fields": "必须填写用户名、主题、语言以及时区", "error.settings_mandatory_fields": "必须填写用户名、主题、语言以及时区",
"error.entries_per_page_invalid": "每页的条目数无效。",
"error.feed_mandatory_fields": "必须填写 URL 和分类", "error.feed_mandatory_fields": "必须填写 URL 和分类",
"error.user_mandatory_fields": "必须填写用户名", "error.user_mandatory_fields": "必须填写用户名",
"error.api_key_already_exists": "此API密钥已存在。", "error.api_key_already_exists": "此API密钥已存在。",
@ -252,6 +253,7 @@
"form.prefs.label.timezone": "时区", "form.prefs.label.timezone": "时区",
"form.prefs.label.theme": "主题", "form.prefs.label.theme": "主题",
"form.prefs.label.entry_sorting": "内容排序", "form.prefs.label.entry_sorting": "内容排序",
"form.prefs.label.entries_per_page": "每页条目",
"form.prefs.select.older_first": "旧->新", "form.prefs.select.older_first": "旧->新",
"form.prefs.select.recent_first": "新->旧", "form.prefs.select.recent_first": "新->旧",
"form.prefs.label.keyboard_shortcuts": "启用键盘快捷键", "form.prefs.label.keyboard_shortcuts": "启用键盘快捷键",

View File

@ -21,6 +21,7 @@ type User struct {
Language string `json:"language"` Language string `json:"language"`
Timezone string `json:"timezone"` Timezone string `json:"timezone"`
EntryDirection string `json:"entry_sorting_direction"` EntryDirection string `json:"entry_sorting_direction"`
EntriesPerPage int `json:"entries_per_page"`
KeyboardShortcuts bool `json:"keyboard_shortcuts"` KeyboardShortcuts bool `json:"keyboard_shortcuts"`
LastLoginAt *time.Time `json:"last_login_at,omitempty"` LastLoginAt *time.Time `json:"last_login_at,omitempty"`
Extra map[string]string `json:"extra"` Extra map[string]string `json:"extra"`

View File

@ -64,7 +64,7 @@ func (s *Storage) CreateUser(user *model.User) (err error) {
VALUES VALUES
(LOWER($1), $2, $3, $4) (LOWER($1), $2, $3, $4)
RETURNING RETURNING
id, username, is_admin, language, theme, timezone, entry_direction, keyboard_shortcuts id, username, is_admin, language, theme, timezone, entry_direction, entries_per_page, keyboard_shortcuts
` `
err = s.db.QueryRow(query, user.Username, password, user.IsAdmin, extra).Scan( err = s.db.QueryRow(query, user.Username, password, user.IsAdmin, extra).Scan(
@ -75,6 +75,7 @@ func (s *Storage) CreateUser(user *model.User) (err error) {
&user.Theme, &user.Theme,
&user.Timezone, &user.Timezone,
&user.EntryDirection, &user.EntryDirection,
&user.EntriesPerPage,
&user.KeyboardShortcuts, &user.KeyboardShortcuts,
) )
if err != nil { if err != nil {
@ -123,9 +124,10 @@ func (s *Storage) UpdateUser(user *model.User) error {
language=$5, language=$5,
timezone=$6, timezone=$6,
entry_direction=$7, entry_direction=$7,
keyboard_shortcuts=$8 entries_per_page=$8,
keyboard_shortcuts=$9
WHERE WHERE
id=$9 id=$10
` `
_, err = s.db.Exec( _, err = s.db.Exec(
@ -137,6 +139,7 @@ func (s *Storage) UpdateUser(user *model.User) error {
user.Language, user.Language,
user.Timezone, user.Timezone,
user.EntryDirection, user.EntryDirection,
user.EntriesPerPage,
user.KeyboardShortcuts, user.KeyboardShortcuts,
user.ID, user.ID,
) )
@ -152,9 +155,10 @@ func (s *Storage) UpdateUser(user *model.User) error {
language=$4, language=$4,
timezone=$5, timezone=$5,
entry_direction=$6, entry_direction=$6,
keyboard_shortcuts=$7 entries_per_page=$7,
keyboard_shortcuts=$8
WHERE WHERE
id=$8 id=$9
` `
_, err := s.db.Exec( _, err := s.db.Exec(
@ -165,6 +169,7 @@ func (s *Storage) UpdateUser(user *model.User) error {
user.Language, user.Language,
user.Timezone, user.Timezone,
user.EntryDirection, user.EntryDirection,
user.EntriesPerPage,
user.KeyboardShortcuts, user.KeyboardShortcuts,
user.ID, user.ID,
) )
@ -202,6 +207,7 @@ func (s *Storage) UserByID(userID int64) (*model.User, error) {
language, language,
timezone, timezone,
entry_direction, entry_direction,
entries_per_page,
keyboard_shortcuts, keyboard_shortcuts,
last_login_at, last_login_at,
extra extra
@ -224,6 +230,7 @@ func (s *Storage) UserByUsername(username string) (*model.User, error) {
language, language,
timezone, timezone,
entry_direction, entry_direction,
entries_per_page,
keyboard_shortcuts, keyboard_shortcuts,
last_login_at, last_login_at,
extra extra
@ -246,6 +253,7 @@ func (s *Storage) UserByExtraField(field, value string) (*model.User, error) {
language, language,
timezone, timezone,
entry_direction, entry_direction,
entries_per_page,
keyboard_shortcuts, keyboard_shortcuts,
last_login_at, last_login_at,
extra extra
@ -268,6 +276,7 @@ func (s *Storage) UserByAPIKey(token string) (*model.User, error) {
u.language, u.language,
u.timezone, u.timezone,
u.entry_direction, u.entry_direction,
u.entries_per_page,
u.keyboard_shortcuts, u.keyboard_shortcuts,
u.last_login_at, u.last_login_at,
u.extra u.extra
@ -293,6 +302,7 @@ func (s *Storage) fetchUser(query string, args ...interface{}) (*model.User, err
&user.Language, &user.Language,
&user.Timezone, &user.Timezone,
&user.EntryDirection, &user.EntryDirection,
&user.EntriesPerPage,
&user.KeyboardShortcuts, &user.KeyboardShortcuts,
&user.LastLoginAt, &user.LastLoginAt,
&extra, &extra,
@ -348,6 +358,7 @@ func (s *Storage) Users() (model.Users, error) {
language, language,
timezone, timezone,
entry_direction, entry_direction,
entries_per_page,
keyboard_shortcuts, keyboard_shortcuts,
last_login_at, last_login_at,
extra extra
@ -373,6 +384,7 @@ func (s *Storage) Users() (model.Users, error) {
&user.Language, &user.Language,
&user.Timezone, &user.Timezone,
&user.EntryDirection, &user.EntryDirection,
&user.EntriesPerPage,
&user.KeyboardShortcuts, &user.KeyboardShortcuts,
&user.LastLoginAt, &user.LastLoginAt,
&extra, &extra,

View File

@ -49,6 +49,9 @@
<option value="desc" {{ if eq "desc" $.form.EntryDirection }}selected="selected"{{ end }}>{{ t "form.prefs.select.recent_first" }}</option> <option value="desc" {{ if eq "desc" $.form.EntryDirection }}selected="selected"{{ end }}>{{ t "form.prefs.select.recent_first" }}</option>
</select> </select>
<label for="form-entries-per-page">{{ t "form.prefs.label.entries_per_page" }}</label>
<input type="number" name="entries_per_page" id="form-entries-per-page" value="{{ .form.EntriesPerPage }}" min="1">
<label><input type="checkbox" name="keyboard_shortcuts" value="1" {{ if .form.KeyboardShortcuts }}checked{{ end }}> {{ t "form.prefs.label.keyboard_shortcuts" }}</label> <label><input type="checkbox" name="keyboard_shortcuts" value="1" {{ if .form.KeyboardShortcuts }}checked{{ end }}> {{ t "form.prefs.label.keyboard_shortcuts" }}</label>
<label>{{t "form.prefs.label.custom_css" }}</label><textarea name="custom_css" cols="40" rows="5">{{ .form.CustomCSS }}</textarea> <label>{{t "form.prefs.label.custom_css" }}</label><textarea name="custom_css" cols="40" rows="5">{{ .form.CustomCSS }}</textarea>

View File

@ -1320,6 +1320,9 @@ var templateViewsMap = map[string]string{
<option value="desc" {{ if eq "desc" $.form.EntryDirection }}selected="selected"{{ end }}>{{ t "form.prefs.select.recent_first" }}</option> <option value="desc" {{ if eq "desc" $.form.EntryDirection }}selected="selected"{{ end }}>{{ t "form.prefs.select.recent_first" }}</option>
</select> </select>
<label for="form-entries-per-page">{{ t "form.prefs.label.entries_per_page" }}</label>
<input type="number" name="entries_per_page" id="form-entries-per-page" value="{{ .form.EntriesPerPage }}" min="1">
<label><input type="checkbox" name="keyboard_shortcuts" value="1" {{ if .form.KeyboardShortcuts }}checked{{ end }}> {{ t "form.prefs.label.keyboard_shortcuts" }}</label> <label><input type="checkbox" name="keyboard_shortcuts" value="1" {{ if .form.KeyboardShortcuts }}checked{{ end }}> {{ t "form.prefs.label.keyboard_shortcuts" }}</label>
<label>{{t "form.prefs.label.custom_css" }}</label><textarea name="custom_css" cols="40" rows="5">{{ .form.CustomCSS }}</textarea> <label>{{t "form.prefs.label.custom_css" }}</label><textarea name="custom_css" cols="40" rows="5">{{ .form.CustomCSS }}</textarea>
@ -1565,7 +1568,7 @@ var templateViewsMapChecksums = map[string]string{
"login": "79ff2ca488c0a19b37c8fa227a21f73e94472eb357a51a077197c852f7713f11", "login": "79ff2ca488c0a19b37c8fa227a21f73e94472eb357a51a077197c852f7713f11",
"search_entries": "c0786ddc6b17e865007b975eefb97417935cbc601f5917cca1ee0d3f584594bc", "search_entries": "c0786ddc6b17e865007b975eefb97417935cbc601f5917cca1ee0d3f584594bc",
"sessions": "5d5c677bddbd027e0b0c9f7a0dd95b66d9d95b4e130959f31fb955b926c2201c", "sessions": "5d5c677bddbd027e0b0c9f7a0dd95b66d9d95b4e130959f31fb955b926c2201c",
"settings": "3ab566c3220c62edc3edc51f2e93c1101b728e9f62f52f23de6bc6322d86aeb6", "settings": "3d6dd0d7fa0ca48cfd9a5edb43c055af8b816eb4460f16b71ae22db40ed9b754",
"shared_entries": "1494d81e46f6af534a73cf6a91f8dfda1932a477bb3a70143513896ac0f0220b", "shared_entries": "1494d81e46f6af534a73cf6a91f8dfda1932a477bb3a70143513896ac0f0220b",
"unread_entries": "e0080d0cf3583cda51d865422960137c8556c432853657086e43daf6bd5b73be", "unread_entries": "e0080d0cf3583cda51d865422960137c8556c432853657086e43daf6bd5b73be",
"users": "d7ff52efc582bbad10504f4a04fa3adcc12d15890e45dff51cac281e0c446e45", "users": "d7ff52efc582bbad10504f4a04fa3adcc12d15890e45dff51cac281e0c446e45",

View File

@ -78,6 +78,10 @@ func TestGetUsers(t *testing.T) {
if !users[0].IsAdmin { if !users[0].IsAdmin {
t.Fatalf(`Invalid role, got "%v"`, users[0].IsAdmin) t.Fatalf(`Invalid role, got "%v"`, users[0].IsAdmin)
} }
if users[0].EntriesPerPage != 100 {
t.Fatalf(`Invalid entries per page, got "%v"`, users[0].EntriesPerPage)
}
} }
func TestCreateStandardUser(t *testing.T) { func TestCreateStandardUser(t *testing.T) {
@ -119,6 +123,10 @@ func TestCreateStandardUser(t *testing.T) {
if user.LastLoginAt != nil { if user.LastLoginAt != nil {
t.Fatalf(`Invalid last login date, got "%v"`, user.LastLoginAt) t.Fatalf(`Invalid last login date, got "%v"`, user.LastLoginAt)
} }
if user.EntriesPerPage != 100 {
t.Fatalf(`Invalid entries per page, got "%v"`, user.EntriesPerPage)
}
} }
func TestRemoveUser(t *testing.T) { func TestRemoveUser(t *testing.T) {
@ -183,6 +191,10 @@ func TestGetUserByID(t *testing.T) {
if user.LastLoginAt != nil { if user.LastLoginAt != nil {
t.Fatalf(`Invalid last login date, got "%v"`, user.LastLoginAt) t.Fatalf(`Invalid last login date, got "%v"`, user.LastLoginAt)
} }
if user.EntriesPerPage != 100 {
t.Fatalf(`Invalid entries per page, got "%v"`, user.EntriesPerPage)
}
} }
func TestGetUserByUsername(t *testing.T) { func TestGetUserByUsername(t *testing.T) {
@ -234,6 +246,10 @@ func TestGetUserByUsername(t *testing.T) {
if user.LastLoginAt != nil { if user.LastLoginAt != nil {
t.Fatalf(`Invalid last login date, got "%v"`, user.LastLoginAt) t.Fatalf(`Invalid last login date, got "%v"`, user.LastLoginAt)
} }
if user.EntriesPerPage != 100 {
t.Fatalf(`Invalid entries per page, got "%v"`, user.EntriesPerPage)
}
} }
func TestUpdateUserTheme(t *testing.T) { func TestUpdateUserTheme(t *testing.T) {

View File

@ -29,7 +29,7 @@ func (h *handler) showStarredPage(w http.ResponseWriter, r *http.Request) {
builder.WithOrder(model.DefaultSortingOrder) builder.WithOrder(model.DefaultSortingOrder)
builder.WithDirection(user.EntryDirection) builder.WithDirection(user.EntryDirection)
builder.WithOffset(offset) builder.WithOffset(offset)
builder.WithLimit(nbItemsPerPage) builder.WithLimit(user.EntriesPerPage)
entries, err := builder.GetEntries() entries, err := builder.GetEntries()
if err != nil { if err != nil {
@ -48,7 +48,7 @@ func (h *handler) showStarredPage(w http.ResponseWriter, r *http.Request) {
view.Set("total", count) view.Set("total", count)
view.Set("entries", entries) view.Set("entries", entries)
view.Set("pagination", getPagination(route.Path(h.router, "starred"), count, offset)) view.Set("pagination", getPagination(route.Path(h.router, "starred"), count, offset, user.EntriesPerPage))
view.Set("menu", "starred") view.Set("menu", "starred")
view.Set("user", user) view.Set("user", user)
view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) view.Set("countUnread", h.store.CountUnreadEntries(user.ID))

View File

@ -41,7 +41,7 @@ func (h *handler) showCategoryEntriesPage(w http.ResponseWriter, r *http.Request
builder.WithDirection(user.EntryDirection) builder.WithDirection(user.EntryDirection)
builder.WithStatus(model.EntryStatusUnread) builder.WithStatus(model.EntryStatusUnread)
builder.WithOffset(offset) builder.WithOffset(offset)
builder.WithLimit(nbItemsPerPage) builder.WithLimit(user.EntriesPerPage)
entries, err := builder.GetEntries() entries, err := builder.GetEntries()
if err != nil { if err != nil {
@ -60,7 +60,7 @@ func (h *handler) showCategoryEntriesPage(w http.ResponseWriter, r *http.Request
view.Set("category", category) view.Set("category", category)
view.Set("total", count) view.Set("total", count)
view.Set("entries", entries) view.Set("entries", entries)
view.Set("pagination", getPagination(route.Path(h.router, "categoryEntries", "categoryID", category.ID), count, offset)) view.Set("pagination", getPagination(route.Path(h.router, "categoryEntries", "categoryID", category.ID), count, offset, user.EntriesPerPage))
view.Set("menu", "categories") view.Set("menu", "categories")
view.Set("user", user) view.Set("user", user)
view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) view.Set("countUnread", h.store.CountUnreadEntries(user.ID))

View File

@ -41,7 +41,7 @@ func (h *handler) showCategoryEntriesAllPage(w http.ResponseWriter, r *http.Requ
builder.WithDirection(user.EntryDirection) builder.WithDirection(user.EntryDirection)
builder.WithoutStatus(model.EntryStatusRemoved) builder.WithoutStatus(model.EntryStatusRemoved)
builder.WithOffset(offset) builder.WithOffset(offset)
builder.WithLimit(nbItemsPerPage) builder.WithLimit(user.EntriesPerPage)
entries, err := builder.GetEntries() entries, err := builder.GetEntries()
if err != nil { if err != nil {
@ -60,7 +60,7 @@ func (h *handler) showCategoryEntriesAllPage(w http.ResponseWriter, r *http.Requ
view.Set("category", category) view.Set("category", category)
view.Set("total", count) view.Set("total", count)
view.Set("entries", entries) view.Set("entries", entries)
view.Set("pagination", getPagination(route.Path(h.router, "categoryEntriesAll", "categoryID", category.ID), count, offset)) view.Set("pagination", getPagination(route.Path(h.router, "categoryEntriesAll", "categoryID", category.ID), count, offset, user.EntriesPerPage))
view.Set("menu", "categories") view.Set("menu", "categories")
view.Set("user", user) view.Set("user", user)
view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) view.Set("countUnread", h.store.CountUnreadEntries(user.ID))

View File

@ -41,7 +41,7 @@ func (h *handler) showFeedEntriesPage(w http.ResponseWriter, r *http.Request) {
builder.WithOrder(model.DefaultSortingOrder) builder.WithOrder(model.DefaultSortingOrder)
builder.WithDirection(user.EntryDirection) builder.WithDirection(user.EntryDirection)
builder.WithOffset(offset) builder.WithOffset(offset)
builder.WithLimit(nbItemsPerPage) builder.WithLimit(user.EntriesPerPage)
entries, err := builder.GetEntries() entries, err := builder.GetEntries()
if err != nil { if err != nil {
@ -60,7 +60,7 @@ func (h *handler) showFeedEntriesPage(w http.ResponseWriter, r *http.Request) {
view.Set("feed", feed) view.Set("feed", feed)
view.Set("entries", entries) view.Set("entries", entries)
view.Set("total", count) view.Set("total", count)
view.Set("pagination", getPagination(route.Path(h.router, "feedEntries", "feedID", feed.ID), count, offset)) view.Set("pagination", getPagination(route.Path(h.router, "feedEntries", "feedID", feed.ID), count, offset, user.EntriesPerPage))
view.Set("menu", "feeds") view.Set("menu", "feeds")
view.Set("user", user) view.Set("user", user)
view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) view.Set("countUnread", h.store.CountUnreadEntries(user.ID))

View File

@ -41,7 +41,7 @@ func (h *handler) showFeedEntriesAllPage(w http.ResponseWriter, r *http.Request)
builder.WithOrder(model.DefaultSortingOrder) builder.WithOrder(model.DefaultSortingOrder)
builder.WithDirection(user.EntryDirection) builder.WithDirection(user.EntryDirection)
builder.WithOffset(offset) builder.WithOffset(offset)
builder.WithLimit(nbItemsPerPage) builder.WithLimit(user.EntriesPerPage)
entries, err := builder.GetEntries() entries, err := builder.GetEntries()
if err != nil { if err != nil {
@ -60,7 +60,7 @@ func (h *handler) showFeedEntriesAllPage(w http.ResponseWriter, r *http.Request)
view.Set("feed", feed) view.Set("feed", feed)
view.Set("entries", entries) view.Set("entries", entries)
view.Set("total", count) view.Set("total", count)
view.Set("pagination", getPagination(route.Path(h.router, "feedEntriesAll", "feedID", feed.ID), count, offset)) view.Set("pagination", getPagination(route.Path(h.router, "feedEntriesAll", "feedID", feed.ID), count, offset, user.EntriesPerPage))
view.Set("menu", "feeds") view.Set("menu", "feeds")
view.Set("user", user) view.Set("user", user)
view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) view.Set("countUnread", h.store.CountUnreadEntries(user.ID))

View File

@ -6,6 +6,7 @@ package form // import "miniflux.app/ui/form"
import ( import (
"net/http" "net/http"
"strconv"
"miniflux.app/errors" "miniflux.app/errors"
"miniflux.app/model" "miniflux.app/model"
@ -20,6 +21,7 @@ type SettingsForm struct {
Language string Language string
Timezone string Timezone string
EntryDirection string EntryDirection string
EntriesPerPage int
KeyboardShortcuts bool KeyboardShortcuts bool
CustomCSS string CustomCSS string
} }
@ -31,6 +33,7 @@ func (s *SettingsForm) Merge(user *model.User) *model.User {
user.Language = s.Language user.Language = s.Language
user.Timezone = s.Timezone user.Timezone = s.Timezone
user.EntryDirection = s.EntryDirection user.EntryDirection = s.EntryDirection
user.EntriesPerPage = s.EntriesPerPage
user.KeyboardShortcuts = s.KeyboardShortcuts user.KeyboardShortcuts = s.KeyboardShortcuts
user.Extra["custom_css"] = s.CustomCSS user.Extra["custom_css"] = s.CustomCSS
@ -47,6 +50,10 @@ func (s *SettingsForm) Validate() error {
return errors.NewLocalizedError("error.settings_mandatory_fields") return errors.NewLocalizedError("error.settings_mandatory_fields")
} }
if s.EntriesPerPage < 1 {
return errors.NewLocalizedError("error.entries_per_page_invalid")
}
if s.Confirmation == "" { if s.Confirmation == "" {
// Firefox insists on auto-completing the password field. // Firefox insists on auto-completing the password field.
// If the confirmation field is blank, the user probably // If the confirmation field is blank, the user probably
@ -67,6 +74,10 @@ func (s *SettingsForm) Validate() error {
// NewSettingsForm returns a new SettingsForm. // NewSettingsForm returns a new SettingsForm.
func NewSettingsForm(r *http.Request) *SettingsForm { func NewSettingsForm(r *http.Request) *SettingsForm {
entriesPerPage, err := strconv.ParseInt(r.FormValue("entries_per_page"), 10, 64)
if err != nil {
entriesPerPage = 0
}
return &SettingsForm{ return &SettingsForm{
Username: r.FormValue("username"), Username: r.FormValue("username"),
Password: r.FormValue("password"), Password: r.FormValue("password"),
@ -75,6 +86,7 @@ func NewSettingsForm(r *http.Request) *SettingsForm {
Language: r.FormValue("language"), Language: r.FormValue("language"),
Timezone: r.FormValue("timezone"), Timezone: r.FormValue("timezone"),
EntryDirection: r.FormValue("entry_direction"), EntryDirection: r.FormValue("entry_direction"),
EntriesPerPage: int(entriesPerPage),
KeyboardShortcuts: r.FormValue("keyboard_shortcuts") == "1", KeyboardShortcuts: r.FormValue("keyboard_shortcuts") == "1",
CustomCSS: r.FormValue("custom_css"), CustomCSS: r.FormValue("custom_css"),
} }

View File

@ -13,6 +13,7 @@ func TestValid(t *testing.T) {
Language: "en_US", Language: "en_US",
Timezone: "UTC", Timezone: "UTC",
EntryDirection: "asc", EntryDirection: "asc",
EntriesPerPage: 50,
} }
err := settings.Validate() err := settings.Validate()
@ -30,6 +31,7 @@ func TestConfirmationEmpty(t *testing.T) {
Language: "en_US", Language: "en_US",
Timezone: "UTC", Timezone: "UTC",
EntryDirection: "asc", EntryDirection: "asc",
EntriesPerPage: 50,
} }
err := settings.Validate() err := settings.Validate()
@ -51,6 +53,25 @@ func TestConfirmationIncorrect(t *testing.T) {
Language: "en_US", Language: "en_US",
Timezone: "UTC", Timezone: "UTC",
EntryDirection: "asc", EntryDirection: "asc",
EntriesPerPage: 50,
}
err := settings.Validate()
if err == nil {
t.Error("Validate should return an error")
}
}
func TestEntriesPerPageNotValid(t *testing.T) {
settings := &SettingsForm{
Username: "user",
Password: "hunter2",
Confirmation: "hunter2",
Theme: "default",
Language: "en_US",
Timezone: "UTC",
EntryDirection: "asc",
EntriesPerPage: 0,
} }
err := settings.Validate() err := settings.Validate()

View File

@ -28,7 +28,7 @@ func (h *handler) showHistoryPage(w http.ResponseWriter, r *http.Request) {
builder.WithOrder("changed_at") builder.WithOrder("changed_at")
builder.WithDirection("desc") builder.WithDirection("desc")
builder.WithOffset(offset) builder.WithOffset(offset)
builder.WithLimit(nbItemsPerPage) builder.WithLimit(user.EntriesPerPage)
entries, err := builder.GetEntries() entries, err := builder.GetEntries()
if err != nil { if err != nil {
@ -46,7 +46,7 @@ func (h *handler) showHistoryPage(w http.ResponseWriter, r *http.Request) {
view := view.New(h.tpl, r, sess) view := view.New(h.tpl, r, sess)
view.Set("entries", entries) view.Set("entries", entries)
view.Set("total", count) view.Set("total", count)
view.Set("pagination", getPagination(route.Path(h.router, "history"), count, offset)) view.Set("pagination", getPagination(route.Path(h.router, "history"), count, offset, user.EntriesPerPage))
view.Set("menu", "history") view.Set("menu", "history")
view.Set("user", user) view.Set("user", user)
view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) view.Set("countUnread", h.store.CountUnreadEntries(user.ID))

View File

@ -4,10 +4,6 @@
package ui // import "miniflux.app/ui" package ui // import "miniflux.app/ui"
const (
nbItemsPerPage = 100
)
type pagination struct { type pagination struct {
Route string Route string
Total int Total int
@ -20,7 +16,7 @@ type pagination struct {
SearchQuery string SearchQuery string
} }
func getPagination(route string, total, offset int) pagination { func getPagination(route string, total, offset, nbItemsPerPage int) pagination {
nextOffset := 0 nextOffset := 0
prevOffset := 0 prevOffset := 0
showNext := (total - offset) > nbItemsPerPage showNext := (total - offset) > nbItemsPerPage

View File

@ -28,7 +28,7 @@ func (h *handler) showSearchEntriesPage(w http.ResponseWriter, r *http.Request)
builder.WithSearchQuery(searchQuery) builder.WithSearchQuery(searchQuery)
builder.WithoutStatus(model.EntryStatusRemoved) builder.WithoutStatus(model.EntryStatusRemoved)
builder.WithOffset(offset) builder.WithOffset(offset)
builder.WithLimit(nbItemsPerPage) builder.WithLimit(user.EntriesPerPage)
entries, err := builder.GetEntries() entries, err := builder.GetEntries()
if err != nil { if err != nil {
@ -44,7 +44,7 @@ func (h *handler) showSearchEntriesPage(w http.ResponseWriter, r *http.Request)
sess := session.New(h.store, request.SessionID(r)) sess := session.New(h.store, request.SessionID(r))
view := view.New(h.tpl, r, sess) view := view.New(h.tpl, r, sess)
pagination := getPagination(route.Path(h.router, "searchEntries"), count, offset) pagination := getPagination(route.Path(h.router, "searchEntries"), count, offset, user.EntriesPerPage)
pagination.SearchQuery = searchQuery pagination.SearchQuery = searchQuery
view.Set("searchQuery", searchQuery) view.Set("searchQuery", searchQuery)

View File

@ -32,6 +32,7 @@ func (h *handler) showSettingsPage(w http.ResponseWriter, r *http.Request) {
Language: user.Language, Language: user.Language,
Timezone: user.Timezone, Timezone: user.Timezone,
EntryDirection: user.EntryDirection, EntryDirection: user.EntryDirection,
EntriesPerPage: user.EntriesPerPage,
KeyboardShortcuts: user.KeyboardShortcuts, KeyboardShortcuts: user.KeyboardShortcuts,
CustomCSS: user.Extra["custom_css"], CustomCSS: user.Extra["custom_css"],
} }

File diff suppressed because one or more lines are too long

View File

@ -353,7 +353,8 @@ select {
input[type="search"], input[type="search"],
input[type="url"], input[type="url"],
input[type="password"], input[type="password"],
input[type="text"] { input[type="text"],
input[type="number"] {
color: var(--input-color); color: var(--input-color);
background: var(--input-background); background: var(--input-background);
border: var(--input-border); border: var(--input-border);
@ -369,13 +370,18 @@ input[type="text"] {
input[type="search"]:focus, input[type="search"]:focus,
input[type="url"]:focus, input[type="url"]:focus,
input[type="password"]:focus, input[type="password"]:focus,
input[type="text"]:focus { input[type="text"]:focus,
input[type="number"]:focus {
color: var(--input-focus-color); color: var(--input-focus-color);
border-color: var(--input-focus-border-color); border-color: var(--input-focus-border-color);
outline: 0; outline: 0;
box-shadow: var(--input-focus-box-shadow); box-shadow: var(--input-focus-box-shadow);
} }
#form-entries-per-page {
max-width: 80px;
}
input[type="checkbox"] { input[type="checkbox"] {
margin-bottom: 15px; margin-bottom: 15px;
} }

View File

@ -43,7 +43,7 @@ func (h *handler) showUnreadPage(w http.ResponseWriter, r *http.Request) {
builder.WithOrder(model.DefaultSortingOrder) builder.WithOrder(model.DefaultSortingOrder)
builder.WithDirection(user.EntryDirection) builder.WithDirection(user.EntryDirection)
builder.WithOffset(offset) builder.WithOffset(offset)
builder.WithLimit(nbItemsPerPage) builder.WithLimit(user.EntriesPerPage)
entries, err := builder.GetEntries() entries, err := builder.GetEntries()
if err != nil { if err != nil {
html.ServerError(w, r, err) html.ServerError(w, r, err)
@ -51,7 +51,7 @@ func (h *handler) showUnreadPage(w http.ResponseWriter, r *http.Request) {
} }
view.Set("entries", entries) view.Set("entries", entries)
view.Set("pagination", getPagination(route.Path(h.router, "unread"), countUnread, offset)) view.Set("pagination", getPagination(route.Path(h.router, "unread"), countUnread, offset, user.EntriesPerPage))
view.Set("menu", "unread") view.Set("menu", "unread")
view.Set("user", user) view.Set("user", user)
view.Set("countUnread", countUnread) view.Set("countUnread", countUnread)