mirror of
https://github.com/photoprism/photoprism.git
synced 2025-12-12 00:34:13 +01:00
Logs: Improve event log and messages in i18n package
Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
@@ -37,7 +37,7 @@ class Log {
|
||||
this.logs = [
|
||||
/* EXAMPLE LOG MESSAGE
|
||||
{
|
||||
"msg": "waiting for events",
|
||||
"message": "waiting for events",
|
||||
"level": "debug",
|
||||
"time": this.created.toISOString(),
|
||||
},
|
||||
|
||||
@@ -33,20 +33,20 @@ import {$gettext} from "./vm";
|
||||
|
||||
const Notify = {
|
||||
info: function (message) {
|
||||
Event.publish("notify.info", {msg: message});
|
||||
Event.publish("notify.info", {message});
|
||||
},
|
||||
warn: function (message) {
|
||||
Event.publish("notify.warning", {msg: message});
|
||||
Event.publish("notify.warning", {message});
|
||||
},
|
||||
error: function (message) {
|
||||
Event.publish("notify.error", {msg: message});
|
||||
Event.publish("notify.error", {message});
|
||||
},
|
||||
success: function (message) {
|
||||
Event.publish("notify.success", {msg: message});
|
||||
Event.publish("notify.success", {message});
|
||||
},
|
||||
logout: function (message) {
|
||||
Event.publish("notify.error", {msg: message});
|
||||
Event.publish("session.logout", {msg: message});
|
||||
Event.publish("notify.error", {message});
|
||||
Event.publish("session.logout", {message});
|
||||
},
|
||||
ajaxStart: function() {
|
||||
Event.publish("ajax.start");
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
const type = ev.split('.')[1];
|
||||
|
||||
// get message from data object
|
||||
let m = data.msg;
|
||||
let m = data.message;
|
||||
|
||||
if (!m || !m.length) {
|
||||
console.warn("notify: empty message");
|
||||
@@ -101,7 +101,7 @@
|
||||
'color': color,
|
||||
'textColor': textColor,
|
||||
'delay': delay,
|
||||
'msg': message
|
||||
'message': message
|
||||
};
|
||||
|
||||
this.messages.push(m);
|
||||
@@ -119,7 +119,7 @@
|
||||
const message = this.messages.shift();
|
||||
|
||||
if (message) {
|
||||
this.text = message.msg;
|
||||
this.text = message.message;
|
||||
this.color = message.color;
|
||||
this.textColor = message.textColor;
|
||||
this.visible = true;
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
ref="form" autocomplete="off" class="p-photo-toolbar" accept-charset="UTF-8"
|
||||
@submit.prevent="filterChange">
|
||||
<v-toolbar flat color="secondary">
|
||||
<v-text-field class="pt-3 pr-3 p-search-field"
|
||||
<v-text-field class="pt-3 pr-3 input-search"
|
||||
browser-autocomplete="off"
|
||||
single-line
|
||||
:label="labels.search"
|
||||
:label="$gettext('Search')"
|
||||
prepend-inner-icon="search"
|
||||
clearable
|
||||
color="secondary-dark"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
<v-form ref="form" class="p-labels-search" lazy-validation @submit.prevent="updateQuery" dense>
|
||||
<v-toolbar flat color="secondary">
|
||||
<v-text-field class="pt-3 pr-3 p-search-field"
|
||||
<v-text-field class="pt-3 pr-3 input-search"
|
||||
single-line
|
||||
:label="labels.search"
|
||||
prepend-inner-icon="search"
|
||||
|
||||
@@ -2,12 +2,17 @@
|
||||
<div class="p-page p-page-errors" v-infinite-scroll="loadMore" :infinite-scroll-disabled="scrollDisabled"
|
||||
:infinite-scroll-distance="10" :infinite-scroll-listen-for-event="'scrollRefresh'">
|
||||
<v-toolbar flat color="secondary">
|
||||
<v-toolbar-title v-if="errors.length > 0">
|
||||
<translate>Event Log</translate>
|
||||
</v-toolbar-title>
|
||||
<v-toolbar-title v-else>
|
||||
<translate>No warnings or errors</translate>
|
||||
</v-toolbar-title>
|
||||
<v-text-field class="pt-3 pr-3 input-search"
|
||||
browser-autocomplete="off"
|
||||
single-line
|
||||
:label="$gettext('Search')"
|
||||
prepend-inner-icon="search"
|
||||
clearable
|
||||
color="secondary-dark"
|
||||
@click:clear="clearQuery"
|
||||
v-model="filter.q"
|
||||
@keyup.enter.native="updateQuery"
|
||||
></v-text-field>
|
||||
|
||||
<v-spacer></v-spacer>
|
||||
|
||||
@@ -19,8 +24,10 @@
|
||||
<v-icon>bug_report</v-icon>
|
||||
</v-btn>
|
||||
</v-toolbar>
|
||||
|
||||
<v-list dense two-line v-if="errors.length > 0">
|
||||
<v-container fluid class="pa-4" v-if="loading">
|
||||
<v-progress-linear color="secondary-dark" :indeterminate="true"></v-progress-linear>
|
||||
</v-container>
|
||||
<v-list dense two-line v-else-if="errors.length > 0">
|
||||
<v-list-tile
|
||||
v-for="(err, index) in errors" :key="index"
|
||||
avatar
|
||||
@@ -37,8 +44,11 @@
|
||||
</v-list-tile>
|
||||
</v-list>
|
||||
<v-card v-else class="errors-empty secondary-light lighten-1 ma-0 pa-2" flat>
|
||||
<v-card-title primary-title>
|
||||
<translate>When PhotoPrism found broken files or there are other potential issues, you'll see a short message on this page.</translate>
|
||||
<v-card-title primary-title v-if="filter.q !== ''">
|
||||
<translate>No results. Try again with a different search term.</translate>
|
||||
</v-card-title>
|
||||
<v-card-title primary-title v-else>
|
||||
<translate>Related log messages will appear here whenever PhotoPrism comes across broken files or there are other potential issues.</translate>
|
||||
</v-card-title>
|
||||
</v-card>
|
||||
|
||||
@@ -77,23 +87,55 @@
|
||||
|
||||
export default {
|
||||
name: 'p-page-errors',
|
||||
watch: {
|
||||
'$route'() {
|
||||
const query = this.$route.query;
|
||||
this.filter.q = query['q'] ? query['q'] : '';
|
||||
this.reload();
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const query = this.$route.query;
|
||||
const q = query["q"] ? query["q"] : "";
|
||||
|
||||
return {
|
||||
errors: [],
|
||||
dirty: false,
|
||||
results: [],
|
||||
loading: false,
|
||||
scrollDisabled: false,
|
||||
filter: {q},
|
||||
pageSize: 100,
|
||||
offset: 0,
|
||||
page: 0,
|
||||
errors: [],
|
||||
results: [],
|
||||
details: {
|
||||
show: false,
|
||||
err: {"Level": "", "Message": "", "Time": ""},
|
||||
},
|
||||
loading: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
updateQuery() {
|
||||
const query = {};
|
||||
|
||||
Object.assign(query, this.filter);
|
||||
|
||||
for (let key in query) {
|
||||
if (query[key] === undefined || !query[key]) {
|
||||
delete query[key];
|
||||
}
|
||||
}
|
||||
|
||||
if (JSON.stringify(this.$route.query) === JSON.stringify(query)) {
|
||||
return
|
||||
}
|
||||
|
||||
this.$router.replace({query});
|
||||
},
|
||||
clearQuery() {
|
||||
this.filter.q = "";
|
||||
this.updateQuery();
|
||||
},
|
||||
showDetails(err) {
|
||||
this.details.err = err;
|
||||
this.details.show = true;
|
||||
@@ -116,11 +158,9 @@
|
||||
|
||||
const count = this.dirty ? (this.page + 2) * this.pageSize : this.pageSize;
|
||||
const offset = this.dirty ? 0 : this.offset;
|
||||
const q = this.filter.q;
|
||||
|
||||
const params = {
|
||||
count: count,
|
||||
offset: offset,
|
||||
};
|
||||
const params = {count, offset, q};
|
||||
|
||||
Api.get("errors", {params}).then((resp) => {
|
||||
if (!resp.data) {
|
||||
@@ -139,7 +179,10 @@
|
||||
this.offset = offset + count;
|
||||
this.page++;
|
||||
}
|
||||
}).finally(() => this.loading = false)
|
||||
}).finally(() => {
|
||||
this.loading = false;
|
||||
this.dirty = false;
|
||||
});
|
||||
},
|
||||
level(s) {
|
||||
return s.substr(0, 4).toUpperCase();
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<translate>Nothing to see here yet. Be patient.</translate>
|
||||
</p>
|
||||
<p v-for="(log, index) in logs" :key="index.id" class="p-log-message" :class="'p-log-' + log.level">
|
||||
{{ formatTime(log.time) }} {{ level(log) }} <span>{{ log.msg }}</span>
|
||||
{{ formatTime(log.time) }} {{ level(log) }} <span>{{ log.message }}</span>
|
||||
</p>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
@@ -28,6 +28,10 @@
|
||||
return log.level.substr(0, 4).toUpperCase();
|
||||
},
|
||||
formatTime(s) {
|
||||
if (!s) {
|
||||
return this.$gettext("Unknown");
|
||||
}
|
||||
|
||||
return DateTime.fromISO(s).toFormat("yyyy-LL-dd HH:mm:ss");
|
||||
},
|
||||
},
|
||||
|
||||
@@ -327,7 +327,7 @@
|
||||
return
|
||||
}
|
||||
|
||||
this.$router.replace({query: query});
|
||||
this.$router.replace({query});
|
||||
},
|
||||
searchParams() {
|
||||
const params = {
|
||||
|
||||
Binary file not shown.
@@ -203,7 +203,7 @@ msgstr "Höhe (m)"
|
||||
|
||||
#: src/common/api.js:76
|
||||
msgid "An error occurred - are you offline?"
|
||||
msgstr "Server konnte nicht erreicht werden - keine Verbindung?"
|
||||
msgstr "Server nicht erreichbar - offline?"
|
||||
|
||||
#: src/pages/settings/general.vue:773
|
||||
msgid "Animation"
|
||||
@@ -348,7 +348,7 @@ msgid "Checked"
|
||||
msgstr "Geprüft"
|
||||
|
||||
#: src/dialog/photo/details.vue:120 src/dialog/share.vue:85
|
||||
#: src/pages/library/errors.vue:56
|
||||
#: src/pages/library/errors.vue:54
|
||||
msgid "Close"
|
||||
msgstr "Schließen"
|
||||
|
||||
@@ -558,10 +558,6 @@ msgstr "Fehler"
|
||||
msgid "Errors"
|
||||
msgstr "Fehler"
|
||||
|
||||
#: src/pages/library/errors.vue:4
|
||||
msgid "Event Log"
|
||||
msgstr "Ereignisprotokoll"
|
||||
|
||||
#: src/resources/options.js:159
|
||||
msgid "Every two days"
|
||||
msgstr "Jeden zweiten Tag"
|
||||
@@ -1045,14 +1041,14 @@ msgstr "Keine Bilder gefunden"
|
||||
msgid "No photos or videos found"
|
||||
msgstr "Keine Fotos oder Videos gefunden"
|
||||
|
||||
#: src/pages/library/errors.vue:30
|
||||
msgid "No results. Try again with a different search term."
|
||||
msgstr "Keine Ergebnisse für diesen Suchbegriff."
|
||||
|
||||
#: src/pages/settings/sync.vue:17
|
||||
msgid "No servers configured."
|
||||
msgstr "Keine Backup-Server eingerichtet."
|
||||
|
||||
#: src/pages/library/errors.vue:7
|
||||
msgid "No warnings or errors"
|
||||
msgstr "Keine Warnungen oder Fehler vorhanden"
|
||||
|
||||
#: src/component/photo/cards.vue:12 src/component/photo/list.vue:101
|
||||
#: src/component/photo/mosaic.vue:12 src/dialog/upload.vue:50
|
||||
#: src/pages/settings/general.vue:99 src/share/photo/list.vue:84
|
||||
@@ -1287,6 +1283,14 @@ msgstr "Zuletzt hinzugefügt"
|
||||
msgid "Red"
|
||||
msgstr "Rot"
|
||||
|
||||
#: src/pages/library/errors.vue:33
|
||||
msgid ""
|
||||
"Related log messages will appear here whenever PhotoPrism comes across "
|
||||
"broken files or there are other potential issues."
|
||||
msgstr ""
|
||||
"Warnungen und Fehler können hier gefunden werden, sobald PhotoPrism "
|
||||
"beschädigte Dateien findet oder es andere Probleme gibt."
|
||||
|
||||
#: src/pages/settings/general.vue:377
|
||||
msgid "Reloading…"
|
||||
msgstr "Wird neu geladen…"
|
||||
@@ -1350,9 +1354,10 @@ msgid "Scans"
|
||||
msgstr "Scans"
|
||||
|
||||
#: src/component/album/toolbar.vue:116 src/component/photo/toolbar.vue:197
|
||||
#: src/dialog/album/edit.vue:114 src/dialog/photo/details.vue:429
|
||||
#: src/dialog/photo/labels.vue:114 src/pages/albums.vue:239
|
||||
#: src/pages/labels.vue:196 src/pages/library/files.vue:176
|
||||
#: src/component/photo/toolbar.vue:33 src/dialog/album/edit.vue:114
|
||||
#: src/dialog/photo/details.vue:429 src/dialog/photo/labels.vue:114
|
||||
#: src/pages/albums.vue:239 src/pages/labels.vue:196
|
||||
#: src/pages/library/errors.vue:33 src/pages/library/files.vue:176
|
||||
#: src/pages/places.vue:174 src/routes.js:235 src/share/albums.vue:161
|
||||
msgid "Search"
|
||||
msgstr "Suche"
|
||||
@@ -1432,7 +1437,7 @@ msgstr "Server-Ereignisprotokoll anzeigen, um Fehler zu finden."
|
||||
|
||||
#: src/pages/photos.vue:290
|
||||
msgid "Showing all %{n} results"
|
||||
msgstr "Es werden alle %{n} Ergebnisse angezeigt"
|
||||
msgstr "Alle %{n} Ergebnisse werden angezeigt"
|
||||
|
||||
#: src/model/file.js:198
|
||||
msgid "Sidecar"
|
||||
@@ -1614,9 +1619,10 @@ msgstr "Gruppierung auflösen"
|
||||
#: src/dialog/photo/details.vue:423 src/dialog/photo/info.vue:221
|
||||
#: src/model/photo.js:417 src/model/photo.js:431 src/model/photo.js:454
|
||||
#: src/model/photo.js:466 src/model/photo.js:543 src/model/photo.js:556
|
||||
#: src/pages/library/errors.vue:149 src/pages/library/errors.vue:156
|
||||
#: src/resources/options.js:15 src/resources/options.js:29
|
||||
#: src/resources/options.js:43 src/resources/options.js:57
|
||||
#: src/pages/library/errors.vue:173 src/pages/library/errors.vue:180
|
||||
#: src/pages/library/logs.vue:32 src/resources/options.js:15
|
||||
#: src/resources/options.js:29 src/resources/options.js:43
|
||||
#: src/resources/options.js:57
|
||||
msgid "Unknown"
|
||||
msgstr "Unbekannt"
|
||||
|
||||
@@ -1727,14 +1733,6 @@ msgstr "WebDAV Upload"
|
||||
msgid "Whatever it is, we'd love to hear from you!"
|
||||
msgstr "Wir freuen uns, von dir zu hören!"
|
||||
|
||||
#: src/pages/library/errors.vue:35
|
||||
msgid ""
|
||||
"When PhotoPrism found broken files or there are other potential issues, "
|
||||
"you'll see a short message on this page."
|
||||
msgstr ""
|
||||
"Auf dieser Seite erscheint ein Hinweis, falls PhotoPrism beschädigte Dateien "
|
||||
"findet oder es andere Probleme gibt."
|
||||
|
||||
#: src/resources/options.js:188
|
||||
msgid "White"
|
||||
msgstr "Weiß"
|
||||
@@ -1782,6 +1780,19 @@ msgstr ""
|
||||
"Das Anbieten oder Bewerben kommerzieller Produkte, Waren oder "
|
||||
"Dienstleistungen ist nur nach vorheriger, schriftlicher Genehmigung erlaubt."
|
||||
|
||||
#~ msgid "Event Log"
|
||||
#~ msgstr "Ereignisprotokoll"
|
||||
|
||||
#~ msgid "No warnings or errors"
|
||||
#~ msgstr "Keine Warnungen oder Fehler vorhanden"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "When PhotoPrism found broken files or there are other potential issues, "
|
||||
#~ "you'll see a short message on this page."
|
||||
#~ msgstr ""
|
||||
#~ "Auf dieser Seite erscheint ein Hinweis, falls PhotoPrism beschädigte "
|
||||
#~ "Dateien findet oder es andere Probleme gibt."
|
||||
|
||||
#~ msgid "Sort By"
|
||||
#~ msgstr "Sortierung"
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -372,7 +372,7 @@ msgstr ""
|
||||
|
||||
#: src/dialog/photo/details.vue:120
|
||||
#: src/dialog/share.vue:85
|
||||
#: src/pages/library/errors.vue:56
|
||||
#: src/pages/library/errors.vue:54
|
||||
msgid "Close"
|
||||
msgstr ""
|
||||
|
||||
@@ -604,10 +604,6 @@ msgstr ""
|
||||
msgid "Errors"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/library/errors.vue:4
|
||||
msgid "Event Log"
|
||||
msgstr ""
|
||||
|
||||
#: src/resources/options.js:159
|
||||
msgid "Every two days"
|
||||
msgstr ""
|
||||
@@ -1129,12 +1125,12 @@ msgstr ""
|
||||
msgid "No photos or videos found"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/settings/sync.vue:17
|
||||
msgid "No servers configured."
|
||||
#: src/pages/library/errors.vue:30
|
||||
msgid "No results. Try again with a different search term."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/library/errors.vue:7
|
||||
msgid "No warnings or errors"
|
||||
#: src/pages/settings/sync.vue:17
|
||||
msgid "No servers configured."
|
||||
msgstr ""
|
||||
|
||||
#: src/component/photo/cards.vue:12
|
||||
@@ -1378,6 +1374,10 @@ msgstr ""
|
||||
msgid "Red"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/library/errors.vue:33
|
||||
msgid "Related log messages will appear here whenever PhotoPrism comes across broken files or there are other potential issues."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/settings/general.vue:377
|
||||
msgid "Reloading…"
|
||||
msgstr ""
|
||||
@@ -1443,11 +1443,13 @@ msgstr ""
|
||||
|
||||
#: src/component/album/toolbar.vue:116
|
||||
#: src/component/photo/toolbar.vue:197
|
||||
#: src/component/photo/toolbar.vue:33
|
||||
#: src/dialog/album/edit.vue:114
|
||||
#: src/dialog/photo/details.vue:429
|
||||
#: src/dialog/photo/labels.vue:114
|
||||
#: src/pages/albums.vue:239
|
||||
#: src/pages/labels.vue:196
|
||||
#: src/pages/library/errors.vue:33
|
||||
#: src/pages/library/files.vue:176
|
||||
#: src/pages/places.vue:174
|
||||
#: src/routes.js:235
|
||||
@@ -1732,8 +1734,9 @@ msgstr ""
|
||||
#: src/model/photo.js:466
|
||||
#: src/model/photo.js:543
|
||||
#: src/model/photo.js:556
|
||||
#: src/pages/library/errors.vue:149
|
||||
#: src/pages/library/errors.vue:156
|
||||
#: src/pages/library/errors.vue:173
|
||||
#: src/pages/library/errors.vue:180
|
||||
#: src/pages/library/logs.vue:32
|
||||
#: src/resources/options.js:15
|
||||
#: src/resources/options.js:29
|
||||
#: src/resources/options.js:43
|
||||
@@ -1853,10 +1856,6 @@ msgstr ""
|
||||
msgid "Whatever it is, we'd love to hear from you!"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/library/errors.vue:35
|
||||
msgid "When PhotoPrism found broken files or there are other potential issues, you'll see a short message on this page."
|
||||
msgstr ""
|
||||
|
||||
#: src/resources/options.js:188
|
||||
msgid "White"
|
||||
msgstr ""
|
||||
|
||||
@@ -6,7 +6,7 @@ export default class Page {
|
||||
this.camera = Selector('div.p-camera-select', {timeout: 15000});
|
||||
this.countries = Selector('div.p-countries-select', {timeout: 15000});
|
||||
this.time = Selector('div.p-time-select', {timeout: 15000});
|
||||
this.search1 = Selector('div.p-search-field input', {timeout: 15000});
|
||||
this.search1 = Selector('div.input-search input', {timeout: 15000});
|
||||
}
|
||||
|
||||
async setFilter(filter, option) {
|
||||
|
||||
@@ -18,7 +18,6 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/internal/workers"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
// GET /api/v1/accounts
|
||||
@@ -297,12 +296,12 @@ func DeleteAccount(router *gin.RouterGroup) {
|
||||
m, err := query.AccountByID(id)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrAccountNotFound)
|
||||
Abort(c, http.StatusNotFound, i18n.ErrAccountNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
if err := m.Delete(); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, txt.UcFirst(err.Error()))
|
||||
Error(c, http.StatusInternalServerError, err, i18n.ErrDeleteFailed)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -193,7 +193,7 @@ func TestDeleteAccount(t *testing.T) {
|
||||
DeleteAccount(router)
|
||||
r := PerformRequest(app, "DELETE", "/api/v1/accounts/xxx")
|
||||
val := gjson.Get(r.Body.String(), "error")
|
||||
assert.Equal(t, "Account not found", val.String())
|
||||
assert.Equal(t, i18n.Msg(i18n.ErrAccountNotFound), val.String())
|
||||
assert.Equal(t, http.StatusNotFound, r.Code)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -321,7 +321,7 @@ func CloneAlbums(router *gin.RouterGroup) {
|
||||
PublishAlbumEvent(EntityUpdated, a.AlbumUID, c)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": i18n.Msg(i18n.MsgAlbumCloned), "album": a, "added": added})
|
||||
c.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "message": i18n.Msg(i18n.MsgAlbumCloned), "album": a, "added": added})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -370,7 +370,7 @@ func AddPhotosToAlbum(router *gin.RouterGroup) {
|
||||
PublishAlbumEvent(EntityUpdated, a.AlbumUID, c)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": i18n.Msg(i18n.MsgChangesSaved), "album": a, "photos": photos.UIDs(), "added": added})
|
||||
c.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "message": i18n.Msg(i18n.MsgChangesSaved), "album": a, "photos": photos.UIDs(), "added": added})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -415,7 +415,7 @@ func RemovePhotosFromAlbum(router *gin.RouterGroup) {
|
||||
PublishAlbumEvent(EntityUpdated, a.AlbumUID, c)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": i18n.Msg(i18n.MsgChangesSaved), "album": a, "photos": f.Photos, "removed": removed})
|
||||
c.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "message": i18n.Msg(i18n.MsgChangesSaved), "album": a, "photos": f.Photos, "removed": removed})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/internal/i18n"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
var log = event.Log
|
||||
@@ -56,7 +57,20 @@ func UpdateClientConfig() {
|
||||
|
||||
func Abort(c *gin.Context, code int, id i18n.Message, params ...interface{}) {
|
||||
resp := i18n.NewResponse(code, id, params...)
|
||||
log.Debugf("api: %s", resp.String())
|
||||
|
||||
log.Debugf("api: abort %s with code %d (%s)", c.FullPath(), code, resp.String())
|
||||
|
||||
c.AbortWithStatusJSON(code, resp)
|
||||
}
|
||||
|
||||
func Error(c *gin.Context, code int, err error, id i18n.Message, params ...interface{}) {
|
||||
resp := i18n.NewResponse(code, id, params...)
|
||||
|
||||
if err != nil {
|
||||
resp.Details = err.Error()
|
||||
log.Errorf("api: error %s with code %d in %s (%s)", txt.Quote(err.Error()), code, c.FullPath(), resp.String())
|
||||
}
|
||||
|
||||
c.AbortWithStatusJSON(code, resp)
|
||||
}
|
||||
|
||||
@@ -83,3 +97,7 @@ func AbortBadRequest(c *gin.Context) {
|
||||
func AbortAlreadyExists(c *gin.Context, s string) {
|
||||
Abort(c, http.StatusConflict, i18n.ErrAlreadyExists, s)
|
||||
}
|
||||
|
||||
func AbortFeatureDisabled(c *gin.Context) {
|
||||
Abort(c, http.StatusForbidden, i18n.ErrFeatureDisabled)
|
||||
}
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/photoprism/photoprism/internal/acl"
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/internal/i18n"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// POST /api/v1/batch/photos/archive
|
||||
@@ -26,8 +23,6 @@ func BatchPhotosArchive(router *gin.RouterGroup) {
|
||||
return
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
|
||||
var f form.Selection
|
||||
|
||||
if err := c.BindJSON(&f); err != nil {
|
||||
@@ -36,8 +31,7 @@ func BatchPhotosArchive(router *gin.RouterGroup) {
|
||||
}
|
||||
|
||||
if len(f.Photos) == 0 {
|
||||
log.Error("no items selected")
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst("no items selected")})
|
||||
Abort(c, http.StatusBadRequest, i18n.ErrNoItemsSelected)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -58,13 +52,11 @@ func BatchPhotosArchive(router *gin.RouterGroup) {
|
||||
log.Errorf("photos: %s", err)
|
||||
}
|
||||
|
||||
elapsed := int(time.Since(start).Seconds())
|
||||
|
||||
UpdateClientConfig()
|
||||
|
||||
event.EntitiesArchived("photos", f.Photos)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("photos archived in %d s", elapsed)})
|
||||
c.JSON(http.StatusOK, i18n.NewResponse(http.StatusOK, i18n.MsgSelectionArchived))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -78,8 +70,6 @@ func BatchPhotosRestore(router *gin.RouterGroup) {
|
||||
return
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
|
||||
var f form.Selection
|
||||
|
||||
if err := c.BindJSON(&f); err != nil {
|
||||
@@ -88,8 +78,7 @@ func BatchPhotosRestore(router *gin.RouterGroup) {
|
||||
}
|
||||
|
||||
if len(f.Photos) == 0 {
|
||||
log.Error("no items selected")
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst("no items selected")})
|
||||
Abort(c, http.StatusBadRequest, i18n.ErrNoItemsSelected)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -107,13 +96,11 @@ func BatchPhotosRestore(router *gin.RouterGroup) {
|
||||
log.Errorf("photos: %s", err)
|
||||
}
|
||||
|
||||
elapsed := int(time.Since(start).Seconds())
|
||||
|
||||
UpdateClientConfig()
|
||||
|
||||
event.EntitiesRestored("photos", f.Photos)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("photos restored in %d s", elapsed)})
|
||||
c.JSON(http.StatusOK, i18n.NewResponse(http.StatusOK, i18n.MsgSelectionRestored))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -135,8 +122,7 @@ func BatchAlbumsDelete(router *gin.RouterGroup) {
|
||||
}
|
||||
|
||||
if len(f.Albums) == 0 {
|
||||
log.Error("no albums selected")
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst("no albums selected")})
|
||||
Abort(c, http.StatusBadRequest, i18n.ErrNoAlbumsSelected)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -149,7 +135,7 @@ func BatchAlbumsDelete(router *gin.RouterGroup) {
|
||||
|
||||
event.EntitiesDeleted("albums", f.Albums)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("albums deleted")})
|
||||
c.JSON(http.StatusOK, i18n.NewResponse(http.StatusOK, i18n.MsgAlbumsDeleted))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -163,8 +149,6 @@ func BatchPhotosPrivate(router *gin.RouterGroup) {
|
||||
return
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
|
||||
var f form.Selection
|
||||
|
||||
if err := c.BindJSON(&f); err != nil {
|
||||
@@ -173,8 +157,7 @@ func BatchPhotosPrivate(router *gin.RouterGroup) {
|
||||
}
|
||||
|
||||
if len(f.Photos) == 0 {
|
||||
log.Error("no items selected")
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst("no items selected")})
|
||||
Abort(c, http.StatusBadRequest, i18n.ErrNoItemsSelected)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -198,9 +181,7 @@ func BatchPhotosPrivate(router *gin.RouterGroup) {
|
||||
|
||||
UpdateClientConfig()
|
||||
|
||||
elapsed := time.Since(start)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("photos marked as private in %s", elapsed)})
|
||||
c.JSON(http.StatusOK, i18n.NewResponse(http.StatusOK, i18n.MsgSelectionProtected))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -223,7 +204,7 @@ func BatchLabelsDelete(router *gin.RouterGroup) {
|
||||
|
||||
if len(f.Labels) == 0 {
|
||||
log.Error("no labels selected")
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst("no labels selected")})
|
||||
Abort(c, http.StatusBadRequest, i18n.ErrNoLabelsSelected)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -232,8 +213,7 @@ func BatchLabelsDelete(router *gin.RouterGroup) {
|
||||
var labels entity.Labels
|
||||
|
||||
if err := entity.Db().Where("label_uid IN (?)", f.Labels).Find(&labels).Error; err != nil {
|
||||
logError("labels", err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, ErrDeleteFailed)
|
||||
Error(c, http.StatusInternalServerError, err, i18n.ErrDeleteFailed)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -245,6 +225,6 @@ func BatchLabelsDelete(router *gin.RouterGroup) {
|
||||
|
||||
event.EntitiesDeleted("labels", f.Labels)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("labels deleted")})
|
||||
c.JSON(http.StatusOK, i18n.NewResponse(http.StatusOK, i18n.MsgLabelsDeleted))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/photoprism/photoprism/internal/i18n"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tidwall/gjson"
|
||||
"net/http"
|
||||
@@ -20,7 +22,7 @@ func TestBatchPhotosArchive(t *testing.T) {
|
||||
BatchPhotosArchive(router)
|
||||
r2 := PerformRequestWithBody(app, "POST", "/api/v1/batch/photos/archive", `{"photos": ["pt9jtdre2lvl0yh7", "pt9jtdre2lvl0ycc"]}`)
|
||||
val2 := gjson.Get(r2.Body.String(), "message")
|
||||
assert.Contains(t, val2.String(), "photos archived")
|
||||
assert.Contains(t, val2.String(), "Selection archived")
|
||||
assert.Equal(t, http.StatusOK, r2.Code)
|
||||
|
||||
r3 := PerformRequest(app, "GET", "/api/v1/photos/pt9jtdre2lvl0yh7")
|
||||
@@ -33,7 +35,7 @@ func TestBatchPhotosArchive(t *testing.T) {
|
||||
BatchPhotosArchive(router)
|
||||
r := PerformRequestWithBody(app, "POST", "/api/v1/batch/photos/archive", `{"photos": []}`)
|
||||
val := gjson.Get(r.Body.String(), "error")
|
||||
assert.Equal(t, "No items selected", val.String())
|
||||
assert.Equal(t, i18n.Msg(i18n.ErrNoItemsSelected), val.String())
|
||||
assert.Equal(t, http.StatusBadRequest, r.Code)
|
||||
})
|
||||
t.Run("invalid request", func(t *testing.T) {
|
||||
@@ -51,7 +53,7 @@ func TestBatchPhotosRestore(t *testing.T) {
|
||||
BatchPhotosArchive(router)
|
||||
r2 := PerformRequestWithBody(app, "POST", "/api/v1/batch/photos/archive", `{"photos": ["pt9jtdre2lvl0yh8", "pt9jtdre2lvl0ycc"]}`)
|
||||
val2 := gjson.Get(r2.Body.String(), "message")
|
||||
assert.Contains(t, val2.String(), "photos archived")
|
||||
assert.Contains(t, val2.String(), "Selection archived")
|
||||
assert.Equal(t, http.StatusOK, r2.Code)
|
||||
|
||||
GetPhoto(router)
|
||||
@@ -63,7 +65,7 @@ func TestBatchPhotosRestore(t *testing.T) {
|
||||
BatchPhotosRestore(router)
|
||||
r := PerformRequestWithBody(app, "POST", "/api/v1/batch/photos/restore", `{"photos": ["pt9jtdre2lvl0yh8", "pt9jtdre2lvl0ycc"]}`)
|
||||
val := gjson.Get(r.Body.String(), "message")
|
||||
assert.Contains(t, val.String(), "photos restored")
|
||||
assert.Contains(t, val.String(), "Selection restored")
|
||||
assert.Equal(t, http.StatusOK, r.Code)
|
||||
|
||||
r4 := PerformRequest(app, "GET", "/api/v1/photos/pt9jtdre2lvl0yh8")
|
||||
@@ -76,7 +78,7 @@ func TestBatchPhotosRestore(t *testing.T) {
|
||||
BatchPhotosRestore(router)
|
||||
r := PerformRequestWithBody(app, "POST", "/api/v1/batch/photos/restore", `{"photos": []}`)
|
||||
val := gjson.Get(r.Body.String(), "error")
|
||||
assert.Equal(t, "No items selected", val.String())
|
||||
assert.Equal(t, i18n.Msg(i18n.ErrNoItemsSelected), val.String())
|
||||
assert.Equal(t, http.StatusBadRequest, r.Code)
|
||||
})
|
||||
t.Run("invalid request", func(t *testing.T) {
|
||||
@@ -105,12 +107,12 @@ func TestBatchAlbumsDelete(t *testing.T) {
|
||||
BatchAlbumsDelete(router)
|
||||
r2 := PerformRequestWithBody(app, "POST", "/api/v1/batch/albums/delete", fmt.Sprintf(`{"albums": ["%s", "pt9jtdre2lvl0ycc"]}`, uid))
|
||||
val2 := gjson.Get(r2.Body.String(), "message")
|
||||
assert.Contains(t, val2.String(), "albums deleted")
|
||||
assert.Contains(t, val2.String(), i18n.Msg(i18n.MsgAlbumsDeleted))
|
||||
assert.Equal(t, http.StatusOK, r2.Code)
|
||||
|
||||
r3 := PerformRequest(app, "GET", "/api/v1/albums/"+uid)
|
||||
val3 := gjson.Get(r3.Body.String(), "error")
|
||||
assert.Equal(t, "Album not found", val3.String())
|
||||
assert.Equal(t, i18n.Msg(i18n.ErrAlbumNotFound), val3.String())
|
||||
assert.Equal(t, http.StatusNotFound, r3.Code)
|
||||
})
|
||||
t.Run("no albums selected", func(t *testing.T) {
|
||||
@@ -118,7 +120,7 @@ func TestBatchAlbumsDelete(t *testing.T) {
|
||||
BatchAlbumsDelete(router)
|
||||
r := PerformRequestWithBody(app, "POST", "/api/v1/batch/albums/delete", `{"albums": []}`)
|
||||
val := gjson.Get(r.Body.String(), "error")
|
||||
assert.Equal(t, "No albums selected", val.String())
|
||||
assert.Equal(t, i18n.Msg(i18n.ErrNoAlbumsSelected), val.String())
|
||||
assert.Equal(t, http.StatusBadRequest, r.Code)
|
||||
})
|
||||
t.Run("invalid request", func(t *testing.T) {
|
||||
@@ -141,7 +143,7 @@ func TestBatchPhotosPrivate(t *testing.T) {
|
||||
BatchPhotosPrivate(router)
|
||||
r2 := PerformRequestWithBody(app, "POST", "/api/v1/batch/photos/private", `{"photos": ["pt9jtdre2lvl0yh8", "pt9jtdre2lvl0ycc"]}`)
|
||||
val2 := gjson.Get(r2.Body.String(), "message")
|
||||
assert.Contains(t, val2.String(), "photos marked as private")
|
||||
assert.Contains(t, val2.String(), "Selection marked as private")
|
||||
assert.Equal(t, http.StatusOK, r2.Code)
|
||||
|
||||
r3 := PerformRequest(app, "GET", "/api/v1/photos/pt9jtdre2lvl0yh8")
|
||||
@@ -154,7 +156,7 @@ func TestBatchPhotosPrivate(t *testing.T) {
|
||||
BatchPhotosPrivate(router)
|
||||
r := PerformRequestWithBody(app, "POST", "/api/v1/batch/photos/private", `{"photos": []}`)
|
||||
val := gjson.Get(r.Body.String(), "error")
|
||||
assert.Equal(t, "No items selected", val.String())
|
||||
assert.Equal(t, i18n.Msg(i18n.ErrNoItemsSelected), val.String())
|
||||
assert.Equal(t, http.StatusBadRequest, r.Code)
|
||||
})
|
||||
t.Run("invalid request", func(t *testing.T) {
|
||||
@@ -175,9 +177,18 @@ func TestBatchLabelsDelete(t *testing.T) {
|
||||
|
||||
BatchLabelsDelete(router)
|
||||
r2 := PerformRequestWithBody(app, "POST", "/api/v1/batch/labels/delete", fmt.Sprintf(`{"labels": ["lt9k3pw1wowuy3c6", "pt9jtdre2lvl0ycc"]}`))
|
||||
val2 := gjson.Get(r2.Body.String(), "message")
|
||||
assert.Contains(t, val2.String(), "labels deleted")
|
||||
|
||||
var resp i18n.Response
|
||||
|
||||
if err := json.Unmarshal(r2.Body.Bytes(), &resp); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.True(t, resp.Success())
|
||||
assert.Equal(t, i18n.Msg(i18n.MsgLabelsDeleted), resp.Msg)
|
||||
assert.Equal(t, i18n.Msg(i18n.MsgLabelsDeleted), resp.String())
|
||||
assert.Equal(t, http.StatusOK, r2.Code)
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
r3 := PerformRequest(app, "GET", "/api/v1/labels?count=15")
|
||||
val3 := gjson.Get(r3.Body.String(), `#(Name=="BatchDelete").Slug`)
|
||||
@@ -188,7 +199,7 @@ func TestBatchLabelsDelete(t *testing.T) {
|
||||
BatchLabelsDelete(router)
|
||||
r := PerformRequestWithBody(app, "POST", "/api/v1/batch/labels/delete", `{"labels": []}`)
|
||||
val := gjson.Get(r.Body.String(), "error")
|
||||
assert.Equal(t, "No labels selected", val.String())
|
||||
assert.Equal(t, i18n.Msg(i18n.ErrNoLabelsSelected), val.String())
|
||||
assert.Equal(t, http.StatusBadRequest, r.Code)
|
||||
})
|
||||
t.Run("invalid request", func(t *testing.T) {
|
||||
|
||||
@@ -6,32 +6,10 @@ import (
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/photoprism/photoprism/internal/acl"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrUnauthorized = gin.H{"code": http.StatusUnauthorized, "error": txt.UcFirst(config.ErrUnauthorized.Error())}
|
||||
ErrReadOnly = gin.H{"code": http.StatusForbidden, "error": txt.UcFirst(config.ErrReadOnly.Error())}
|
||||
ErrUploadNSFW = gin.H{"code": http.StatusForbidden, "error": txt.UcFirst(config.ErrUploadNSFW.Error())}
|
||||
ErrPublic = gin.H{"code": http.StatusForbidden, "error": "Not available in public mode"}
|
||||
ErrAccountNotFound = gin.H{"code": http.StatusNotFound, "error": "Account not found"}
|
||||
ErrConnectionFailed = gin.H{"code": http.StatusConflict, "error": "Failed to connect"}
|
||||
ErrAlbumNotFound = gin.H{"code": http.StatusNotFound, "error": "Album not found"}
|
||||
ErrPhotoNotFound = gin.H{"code": http.StatusNotFound, "error": "Photo not found"}
|
||||
ErrLabelNotFound = gin.H{"code": http.StatusNotFound, "error": "Label not found"}
|
||||
ErrFileNotFound = gin.H{"code": http.StatusNotFound, "error": "File not found"}
|
||||
ErrSessionNotFound = gin.H{"code": http.StatusNotFound, "error": "Session not found"}
|
||||
ErrUnexpectedError = gin.H{"code": http.StatusInternalServerError, "error": "Unexpected error"}
|
||||
ErrSaveFailed = gin.H{"code": http.StatusInternalServerError, "error": "Changes could not be saved"}
|
||||
ErrDeleteFailed = gin.H{"code": http.StatusInternalServerError, "error": "Changes could not be saved"}
|
||||
ErrFormInvalid = gin.H{"code": http.StatusBadRequest, "error": "Changes could not be saved"}
|
||||
ErrFeatureDisabled = gin.H{"code": http.StatusForbidden, "error": "Feature disabled"}
|
||||
ErrNotFound = gin.H{"code": http.StatusNotFound, "error": "Not found"}
|
||||
ErrInvalidPassword = gin.H{"code": http.StatusBadRequest, "error": "Invalid password, please try again"}
|
||||
)
|
||||
|
||||
func GetErrors(router *gin.RouterGroup) {
|
||||
router.GET("/errors", func(c *gin.Context) {
|
||||
s := Auth(SessionID(c), acl.ResourceLogs, acl.ActionSearch)
|
||||
@@ -44,7 +22,7 @@ func GetErrors(router *gin.RouterGroup) {
|
||||
limit := txt.Int(c.Query("count"))
|
||||
offset := txt.Int(c.Query("offset"))
|
||||
|
||||
if resp, err := query.Errors(limit, offset); err != nil {
|
||||
if resp, err := query.Errors(limit, offset, c.Query("q")); err != nil {
|
||||
c.AbortWithStatusJSON(400, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
return
|
||||
} else {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -12,6 +11,7 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/acl"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/internal/i18n"
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
@@ -31,7 +31,7 @@ func StartImport(router *gin.RouterGroup) {
|
||||
conf := service.Config()
|
||||
|
||||
if conf.ReadOnly() || !conf.Settings().Features.Import {
|
||||
c.AbortWithStatusJSON(http.StatusForbidden, ErrFeatureDisabled)
|
||||
AbortFeatureDisabled(c)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -62,10 +62,10 @@ func StartImport(router *gin.RouterGroup) {
|
||||
var opt photoprism.ImportOptions
|
||||
|
||||
if f.Move {
|
||||
event.Info(fmt.Sprintf("moving files from %s", txt.Quote(filepath.Base(path))))
|
||||
event.InfoMsg(i18n.MsgMovingFilesFrom, txt.Quote(filepath.Base(path)))
|
||||
opt = photoprism.ImportOptionsMove(path)
|
||||
} else {
|
||||
event.Info(fmt.Sprintf("copying files from %s", txt.Quote(filepath.Base(path))))
|
||||
event.InfoMsg(i18n.MsgCopyingFilesFrom, txt.Quote(filepath.Base(path)))
|
||||
opt = photoprism.ImportOptionsCopy(path)
|
||||
}
|
||||
|
||||
@@ -92,7 +92,9 @@ func StartImport(router *gin.RouterGroup) {
|
||||
|
||||
elapsed := int(time.Since(start).Seconds())
|
||||
|
||||
event.Success(fmt.Sprintf("import completed in %d s", elapsed))
|
||||
msg := i18n.Msg(i18n.MsgImportCompletedIn, elapsed)
|
||||
|
||||
event.Success(msg)
|
||||
event.Publish("import.completed", event.Data{"path": path, "seconds": elapsed})
|
||||
event.Publish("index.completed", event.Data{"path": path, "seconds": elapsed})
|
||||
|
||||
@@ -102,7 +104,7 @@ func StartImport(router *gin.RouterGroup) {
|
||||
|
||||
UpdateClientConfig()
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("import completed in %d s", elapsed)})
|
||||
c.JSON(http.StatusOK, i18n.Response{Code: http.StatusOK, Msg: msg})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -119,7 +121,7 @@ func CancelImport(router *gin.RouterGroup) {
|
||||
conf := service.Config()
|
||||
|
||||
if conf.ReadOnly() || !conf.Settings().Features.Import {
|
||||
c.AbortWithStatusJSON(http.StatusForbidden, ErrFeatureDisabled)
|
||||
AbortFeatureDisabled(c)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -127,6 +129,6 @@ func CancelImport(router *gin.RouterGroup) {
|
||||
|
||||
imp.Cancel()
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "import canceled"})
|
||||
c.JSON(http.StatusOK, i18n.NewResponse(http.StatusOK, i18n.MsgImportCanceled))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/i18n"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
func TestCancelImport(t *testing.T) {
|
||||
@@ -13,8 +14,17 @@ func TestCancelImport(t *testing.T) {
|
||||
app, router, _ := NewApiTest()
|
||||
CancelImport(router)
|
||||
r := PerformRequest(app, "DELETE", "/api/v1/import")
|
||||
val := gjson.Get(r.Body.String(), "message")
|
||||
assert.Equal(t, "import canceled", val.String())
|
||||
|
||||
var resp i18n.Response
|
||||
|
||||
if err := json.Unmarshal(r.Body.Bytes(), &resp); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.True(t, resp.Success())
|
||||
assert.Equal(t, i18n.Msg(i18n.MsgImportCanceled), resp.Msg)
|
||||
assert.Equal(t, i18n.Msg(i18n.MsgImportCanceled), resp.String())
|
||||
assert.Equal(t, http.StatusOK, r.Code)
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"time"
|
||||
@@ -10,6 +9,7 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/acl"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/internal/i18n"
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
@@ -28,7 +28,7 @@ func StartIndexing(router *gin.RouterGroup) {
|
||||
conf := service.Config()
|
||||
|
||||
if !conf.Settings().Features.Library {
|
||||
c.AbortWithStatusJSON(http.StatusForbidden, ErrFeatureDisabled)
|
||||
AbortFeatureDisabled(c)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -52,9 +52,9 @@ func StartIndexing(router *gin.RouterGroup) {
|
||||
}
|
||||
|
||||
if len(indOpt.Path) > 1 {
|
||||
event.Info(fmt.Sprintf("indexing files in %s", txt.Quote(indOpt.Path)))
|
||||
event.InfoMsg(i18n.MsgIndexingFiles, txt.Quote(indOpt.Path))
|
||||
} else {
|
||||
event.Info("indexing originals...")
|
||||
event.InfoMsg(i18n.MsgIndexingOriginals)
|
||||
}
|
||||
|
||||
indexed := ind.Start(indOpt)
|
||||
@@ -70,7 +70,7 @@ func StartIndexing(router *gin.RouterGroup) {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
return
|
||||
} else if len(files) > 0 || len(photos) > 0 {
|
||||
event.Info(fmt.Sprintf("removed %d files and %d photos", len(files), len(photos)))
|
||||
event.InfoMsg(i18n.MsgRemovedFilesAndPhotos, len(files), len(photos))
|
||||
}
|
||||
|
||||
moments := service.Moments()
|
||||
@@ -81,12 +81,14 @@ func StartIndexing(router *gin.RouterGroup) {
|
||||
|
||||
elapsed := int(time.Since(start).Seconds())
|
||||
|
||||
event.Success(fmt.Sprintf("indexing completed in %d s", elapsed))
|
||||
msg := i18n.Msg(i18n.MsgIndexingCompletedIn, elapsed)
|
||||
|
||||
event.Success(msg)
|
||||
event.Publish("index.completed", event.Data{"path": path, "seconds": elapsed})
|
||||
|
||||
UpdateClientConfig()
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("indexing completed in %d s", elapsed)})
|
||||
c.JSON(http.StatusOK, i18n.Response{Code: http.StatusOK, Msg: msg})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -103,7 +105,7 @@ func CancelIndexing(router *gin.RouterGroup) {
|
||||
conf := service.Config()
|
||||
|
||||
if !conf.Settings().Features.Library {
|
||||
c.AbortWithStatusJSON(http.StatusForbidden, ErrFeatureDisabled)
|
||||
AbortFeatureDisabled(c)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -111,6 +113,6 @@ func CancelIndexing(router *gin.RouterGroup) {
|
||||
|
||||
ind.Cancel()
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "indexing canceled"})
|
||||
c.JSON(http.StatusOK, i18n.NewResponse(http.StatusOK, i18n.MsgIndexingCanceled))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tidwall/gjson"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/i18n"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCancelIndex(t *testing.T) {
|
||||
@@ -12,8 +14,17 @@ func TestCancelIndex(t *testing.T) {
|
||||
app, router, _ := NewApiTest()
|
||||
CancelIndexing(router)
|
||||
r := PerformRequest(app, "DELETE", "/api/v1/index")
|
||||
val := gjson.Get(r.Body.String(), "message")
|
||||
assert.Equal(t, "indexing canceled", val.String())
|
||||
|
||||
var resp i18n.Response
|
||||
|
||||
if err := json.Unmarshal(r.Body.Bytes(), &resp); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.True(t, resp.Success())
|
||||
assert.Equal(t, i18n.Msg(i18n.MsgIndexingCanceled), resp.Msg)
|
||||
assert.Equal(t, i18n.Msg(i18n.MsgIndexingCanceled), resp.String())
|
||||
assert.Equal(t, http.StatusOK, r.Code)
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/internal/i18n"
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
@@ -77,14 +78,14 @@ func UpdateLabel(router *gin.RouterGroup) {
|
||||
m, err := query.LabelByUID(id)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrLabelNotFound)
|
||||
Abort(c, http.StatusNotFound, i18n.ErrLabelNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
m.SetName(f.LabelName)
|
||||
entity.Db().Save(&m)
|
||||
|
||||
event.Success("label saved")
|
||||
event.SuccessMsg(i18n.MsgLabelSaved)
|
||||
|
||||
PublishLabelEvent(EntityUpdated, id, c)
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/internal/i18n"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
@@ -120,7 +121,7 @@ func CreateLink(c *gin.Context) {
|
||||
func CreateAlbumLink(router *gin.RouterGroup) {
|
||||
router.POST("/albums/:uid/links", func(c *gin.Context) {
|
||||
if _, err := query.AlbumByUID(c.Param("uid")); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrAlbumNotFound)
|
||||
Abort(c, http.StatusNotFound, i18n.ErrAlbumNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -148,7 +149,7 @@ func GetAlbumLinks(router *gin.RouterGroup) {
|
||||
m, err := query.AlbumByUID(c.Param("uid"))
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrAlbumNotFound)
|
||||
Abort(c, http.StatusNotFound, i18n.ErrAlbumNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -188,7 +189,7 @@ func GetPhotoLinks(router *gin.RouterGroup) {
|
||||
m, err := query.PhotoByUID(c.Param("uid"))
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrAlbumNotFound)
|
||||
Abort(c, http.StatusNotFound, i18n.ErrAlbumNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -200,7 +201,7 @@ func GetPhotoLinks(router *gin.RouterGroup) {
|
||||
func CreateLabelLink(router *gin.RouterGroup) {
|
||||
router.POST("/labels/:uid/links", func(c *gin.Context) {
|
||||
if _, err := query.LabelByUID(c.Param("uid")); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrLabelNotFound)
|
||||
Abort(c, http.StatusNotFound, i18n.ErrLabelNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -228,7 +229,7 @@ func GetLabelLinks(router *gin.RouterGroup) {
|
||||
m, err := query.LabelByUID(c.Param("uid"))
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrAlbumNotFound)
|
||||
Abort(c, http.StatusNotFound, i18n.ErrAlbumNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ func GetSettings(router *gin.RouterGroup) {
|
||||
if settings := service.Config().Settings(); settings != nil {
|
||||
c.JSON(http.StatusOK, settings)
|
||||
} else {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrNotFound)
|
||||
Abort(c, http.StatusNotFound, i18n.ErrNotFound)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
@@ -10,6 +9,7 @@ import (
|
||||
|
||||
"github.com/photoprism/photoprism/internal/acl"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/internal/i18n"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
|
||||
@@ -21,7 +21,7 @@ func Upload(router *gin.RouterGroup) {
|
||||
router.POST("/upload/:path", func(c *gin.Context) {
|
||||
conf := service.Config()
|
||||
if conf.ReadOnly() || !conf.Settings().Features.Upload {
|
||||
c.AbortWithStatusJSON(http.StatusForbidden, ErrReadOnly)
|
||||
Abort(c, http.StatusForbidden, i18n.ErrReadOnly)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -97,15 +97,17 @@ func Upload(router *gin.RouterGroup) {
|
||||
}
|
||||
}
|
||||
|
||||
c.AbortWithStatusJSON(http.StatusForbidden, ErrUploadNSFW)
|
||||
Abort(c, http.StatusForbidden, i18n.ErrOffensiveUpload)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
elapsed := time.Since(start)
|
||||
elapsed := int(time.Since(start).Seconds())
|
||||
|
||||
log.Infof("%d files uploaded in %s", uploaded, elapsed)
|
||||
msg := i18n.Msg(i18n.MsgFilesUploadedIn, uploaded, elapsed)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("%d files uploaded in %s", uploaded, elapsed)})
|
||||
log.Info(msg)
|
||||
|
||||
c.JSON(http.StatusOK, i18n.Response{Code: http.StatusOK, Msg: msg})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/acl"
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/internal/i18n"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
)
|
||||
|
||||
@@ -16,7 +17,7 @@ func ChangePassword(router *gin.RouterGroup) {
|
||||
conf := service.Config()
|
||||
|
||||
if conf.Public() {
|
||||
c.AbortWithStatusJSON(http.StatusForbidden, ErrPublic)
|
||||
Abort(c, http.StatusForbidden, i18n.ErrPublic)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -31,31 +32,27 @@ func ChangePassword(router *gin.RouterGroup) {
|
||||
m := entity.FindPersonByUID(uid)
|
||||
|
||||
if m == nil {
|
||||
log.Errorf("change password: user not found")
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrInvalidPassword)
|
||||
Abort(c, http.StatusNotFound, i18n.ErrUserNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
f := form.ChangePassword{}
|
||||
|
||||
if err := c.BindJSON(&f); err != nil {
|
||||
log.Errorf("change password: %s", err)
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, ErrInvalidPassword)
|
||||
Error(c, http.StatusBadRequest, err, i18n.ErrInvalidPassword)
|
||||
return
|
||||
}
|
||||
|
||||
if m.InvalidPassword(f.OldPassword) {
|
||||
log.Errorf("change password: invalid password")
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, ErrInvalidPassword)
|
||||
Abort(c, http.StatusBadRequest, i18n.ErrInvalidPassword)
|
||||
return
|
||||
}
|
||||
|
||||
if err := m.SetPassword(f.NewPassword); err != nil {
|
||||
log.Errorf("change password: %s", err)
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"code": http.StatusBadRequest, "error": err.Error()})
|
||||
Error(c, http.StatusBadRequest, err, i18n.ErrInvalidPassword)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "message": "password changed"})
|
||||
c.JSON(http.StatusOK, i18n.NewResponse(http.StatusOK, i18n.MsgPasswordChanged))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/photoprism/photoprism/internal/acl"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/internal/i18n"
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
@@ -35,7 +36,7 @@ func CreateZip(router *gin.RouterGroup) {
|
||||
conf := service.Config()
|
||||
|
||||
if !conf.Settings().Features.Download {
|
||||
c.AbortWithStatusJSON(http.StatusForbidden, ErrFeatureDisabled)
|
||||
AbortFeatureDisabled(c)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -48,17 +49,17 @@ func CreateZip(router *gin.RouterGroup) {
|
||||
}
|
||||
|
||||
if f.Empty() {
|
||||
c.AbortWithStatusJSON(400, gin.H{"error": txt.UcFirst("no items selected")})
|
||||
Abort(c, http.StatusBadRequest, i18n.ErrNoItemsSelected)
|
||||
return
|
||||
}
|
||||
|
||||
files, err := query.FileSelection(f)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(400, gin.H{"error": err.Error()})
|
||||
Error(c, http.StatusBadRequest, err, i18n.ErrZipFailed)
|
||||
return
|
||||
} else if len(files) == 0 {
|
||||
c.AbortWithStatusJSON(404, gin.H{"error": txt.UcFirst("no files available for download")})
|
||||
Abort(c, http.StatusNotFound, i18n.ErrNoFilesForDownload)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -69,16 +70,14 @@ func CreateZip(router *gin.RouterGroup) {
|
||||
zipFileName := path.Join(zipPath, zipBaseName)
|
||||
|
||||
if err := os.MkdirAll(zipPath, 0700); err != nil {
|
||||
log.Error(err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UcFirst("failed to create zip folder")})
|
||||
Error(c, http.StatusInternalServerError, err, i18n.ErrZipFailed)
|
||||
return
|
||||
}
|
||||
|
||||
newZipFile, err := os.Create(zipFileName)
|
||||
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
Error(c, http.StatusInternalServerError, err, i18n.ErrZipFailed)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -93,8 +92,7 @@ func CreateZip(router *gin.RouterGroup) {
|
||||
|
||||
if fs.FileExists(fileName) {
|
||||
if err := addFileToZip(zipWriter, fileName, fileAlias); err != nil {
|
||||
log.Error(err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UcFirst("failed to create zip file")})
|
||||
Error(c, http.StatusInternalServerError, err, i18n.ErrZipFailed)
|
||||
return
|
||||
}
|
||||
log.Infof("zip: added %s as %s", txt.Quote(f.FileName), txt.Quote(fileAlias))
|
||||
@@ -108,7 +106,7 @@ func CreateZip(router *gin.RouterGroup) {
|
||||
|
||||
log.Infof("zip: archive %s created in %s", txt.Quote(zipBaseName), time.Since(start))
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("zip created in %d s", elapsed), "filename": zipBaseName})
|
||||
c.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "message": i18n.Msg(i18n.MsgZipCreatedIn, elapsed), "filename": zipBaseName})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ func TestCreateZip(t *testing.T) {
|
||||
CreateZip(router)
|
||||
r := PerformRequestWithBody(app, "POST", "/api/v1/zip", `{"photos": ["pt9jtdre2lvl0y12", "pt9jtdre2lvl0y11"]}`)
|
||||
val := gjson.Get(r.Body.String(), "message")
|
||||
assert.Contains(t, val.String(), "zip created")
|
||||
assert.Contains(t, val.String(), "Zip created")
|
||||
assert.Equal(t, http.StatusOK, r.Code)
|
||||
})
|
||||
t.Run("no items selected", func(t *testing.T) {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package entity
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Details stores additional metadata fields for each photo to improve search performance.
|
||||
type Details struct {
|
||||
@@ -22,6 +25,10 @@ func NewDetails(photo Photo) Details {
|
||||
|
||||
// Create inserts a new row to the database.
|
||||
func (m *Details) Create() error {
|
||||
if m.PhotoID == 0 {
|
||||
return fmt.Errorf("details: photo id must not be empty (create)")
|
||||
}
|
||||
|
||||
return Db().Create(m).Error
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ func SaveErrorMessages() {
|
||||
|
||||
newError := Error{ErrorLevel: logLevel.String()}
|
||||
|
||||
if val, ok := msg.Fields["msg"]; ok {
|
||||
if val, ok := msg.Fields["message"]; ok {
|
||||
newError.ErrorMessage = val.(string)
|
||||
}
|
||||
|
||||
|
||||
@@ -147,19 +147,29 @@ func (m *File) AllFilesMissing() bool {
|
||||
// Create inserts a new row to the database.
|
||||
func (m *File) Create() error {
|
||||
if m.PhotoID == 0 {
|
||||
return fmt.Errorf("file: photo id is empty (create)")
|
||||
return fmt.Errorf("file: photo id must not be empty (create)")
|
||||
}
|
||||
|
||||
return UnscopedDb().Create(m).Error
|
||||
if err := UnscopedDb().Create(m).Error; err != nil {
|
||||
log.Errorf("file: %s (create)", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Saves the file in the database.
|
||||
func (m *File) Save() error {
|
||||
if m.PhotoID == 0 {
|
||||
return fmt.Errorf("file: photo id is empty (%s)", m.FileUID)
|
||||
return fmt.Errorf("file: photo id must not be empty (save %s)", m.FileUID)
|
||||
}
|
||||
|
||||
return UnscopedDb().Save(m).Error
|
||||
if err := UnscopedDb().Save(m).Error; err != nil {
|
||||
log.Errorf("file: %s (save %s)", err, m.FileUID)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateVideoInfos updates related video infos based on this file.
|
||||
|
||||
@@ -110,6 +110,6 @@ func TestFile_Save(t *testing.T) {
|
||||
t.Fatalf("file id should be 0: %d", file.ID)
|
||||
}
|
||||
|
||||
assert.Equal(t, "file: photo id is empty (123)", err.Error())
|
||||
assert.Equal(t, "file: photo id must not be empty (save 123)", err.Error())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -188,7 +188,7 @@ func (m *Photo) Create() error {
|
||||
}
|
||||
|
||||
if err := UnscopedDb().Save(m.GetDetails()).Error; err != nil {
|
||||
log.Errorf("photo: %s (save details after create)", err)
|
||||
log.Errorf("photo: %s (save details for %s)", err, m.PhotoUID)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -198,12 +198,12 @@ func (m *Photo) Create() error {
|
||||
// Save updates the existing or inserts a new row.
|
||||
func (m *Photo) Save() error {
|
||||
if err := UnscopedDb().Save(m).Error; err != nil {
|
||||
log.Errorf("photo: %s (save)", err)
|
||||
log.Errorf("photo: %s (save %s)", err, m.PhotoUID)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := UnscopedDb().Save(m.GetDetails()).Error; err != nil {
|
||||
log.Errorf("photo: %s (save details)", err)
|
||||
log.Errorf("photo: %s (save details for %s)", err, m.PhotoUID)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -22,22 +22,22 @@ func SharedHub() *Hub {
|
||||
|
||||
func Error(msg string) {
|
||||
Log.Error(msg)
|
||||
Publish("notify.error", Data{"msg": msg})
|
||||
Publish("notify.error", Data{"message": msg})
|
||||
}
|
||||
|
||||
func Success(msg string) {
|
||||
Log.Info(msg)
|
||||
Publish("notify.success", Data{"msg": msg})
|
||||
Publish("notify.success", Data{"message": msg})
|
||||
}
|
||||
|
||||
func Info(msg string) {
|
||||
Log.Info(msg)
|
||||
Publish("notify.info", Data{"msg": msg})
|
||||
Publish("notify.info", Data{"message": msg})
|
||||
}
|
||||
|
||||
func Warning(msg string) {
|
||||
Log.Warn(msg)
|
||||
Publish("notify.warning", Data{"msg": msg})
|
||||
Publish("notify.warning", Data{"message": msg})
|
||||
}
|
||||
|
||||
func ErrorMsg(id i18n.Message, params ...interface{}) {
|
||||
|
||||
@@ -39,7 +39,7 @@ func TestError(t *testing.T) {
|
||||
msg := <-s.Receiver
|
||||
|
||||
assert.Equal(t, "notify.error", msg.Name)
|
||||
assert.Equal(t, Data{"msg": "error message"}, msg.Fields)
|
||||
assert.Equal(t, Data{"message": "error message"}, msg.Fields)
|
||||
|
||||
Unsubscribe(s)
|
||||
}
|
||||
@@ -53,7 +53,7 @@ func TestSuccess(t *testing.T) {
|
||||
msg := <-s.Receiver
|
||||
|
||||
assert.Equal(t, "notify.success", msg.Name)
|
||||
assert.Equal(t, Data{"msg": "success message"}, msg.Fields)
|
||||
assert.Equal(t, Data{"message": "success message"}, msg.Fields)
|
||||
|
||||
Unsubscribe(s)
|
||||
}
|
||||
@@ -67,7 +67,7 @@ func TestInfo(t *testing.T) {
|
||||
msg := <-s.Receiver
|
||||
|
||||
assert.Equal(t, "notify.info", msg.Name)
|
||||
assert.Equal(t, Data{"msg": "info message"}, msg.Fields)
|
||||
assert.Equal(t, Data{"message": "info message"}, msg.Fields)
|
||||
|
||||
Unsubscribe(s)
|
||||
}
|
||||
@@ -81,7 +81,7 @@ func TestWarning(t *testing.T) {
|
||||
msg := <-s.Receiver
|
||||
|
||||
assert.Equal(t, "notify.warning", msg.Name)
|
||||
assert.Equal(t, Data{"msg": "warning message"}, msg.Fields)
|
||||
assert.Equal(t, Data{"message": "warning message"}, msg.Fields)
|
||||
|
||||
Unsubscribe(s)
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@ func (h *Hook) Fire(entry *logrus.Entry) error {
|
||||
h.hub.Publish(Message{
|
||||
Name: "log." + entry.Level.String(),
|
||||
Fields: Data{
|
||||
"time": entry.Time,
|
||||
"level": entry.Level.String(),
|
||||
"msg": entry.Message,
|
||||
"time": entry.Time,
|
||||
"level": entry.Level.String(),
|
||||
"message": entry.Message,
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -1,33 +1,67 @@
|
||||
package i18n
|
||||
|
||||
var MsgGerman = MessageMap{
|
||||
ErrUnexpected: "Unerwarteter Fehler, bitte erneut versuchen",
|
||||
ErrSaveFailed: "Daten gekonnten nicht gespeichert werden",
|
||||
ErrBadRequest: "Ungültige Anfrage",
|
||||
ErrAlreadyExists: "%s existiert bereits",
|
||||
ErrEntityNotFound: "Eintrag nicht gefunden",
|
||||
ErrAccountNotFound: "Unbekannter Account",
|
||||
ErrAlbumNotFound: "Album nicht gefunden - gelöscht?",
|
||||
ErrReadOnly: "Funktion im 'read-only' Modus nicht verfügbar",
|
||||
ErrUnauthorized: "Anmeldung erforderlich",
|
||||
ErrUploadNSFW: "Inhalt könnte anstößig sein und wurde abgelehnt",
|
||||
ErrNoItemsSelected: "Auswahl ist leer, bitte erneut versuchen",
|
||||
ErrCreateFile: "Datei konnte nicht angelegt werden",
|
||||
ErrCreateFolder: "Verzeichnis konnte nicht angelegt werden",
|
||||
ErrConnectionFailed: "Could not connect, please try again",
|
||||
// Error messages:
|
||||
ErrUnexpected: "Unerwarteter Fehler, bitte erneut versuchen",
|
||||
ErrSaveFailed: "Fehler beim Speichern der Daten",
|
||||
ErrBadRequest: "Ungültige Anfrage",
|
||||
ErrAlreadyExists: "%s existiert bereits",
|
||||
ErrEntityNotFound: "Nicht auf Server gefunden, gelöscht?",
|
||||
ErrAccountNotFound: "Unbekannter Account",
|
||||
ErrAlbumNotFound: "Album nicht gefunden - gelöscht?",
|
||||
ErrReadOnly: "Funktion im 'read-only' Modus nicht verfügbar",
|
||||
ErrUnauthorized: "Anmeldung erforderlich",
|
||||
ErrOffensiveUpload: "Inhalt könnte anstößig sein und wurde abgelehnt",
|
||||
ErrNoItemsSelected: "Auswahl ist leer, bitte erneut versuchen",
|
||||
ErrCreateFile: "Datei konnte nicht angelegt werden",
|
||||
ErrCreateFolder: "Verzeichnis konnte nicht angelegt werden",
|
||||
ErrConnectionFailed: "Verbindung fehlgeschlagen",
|
||||
ErrDeleteFailed: "Konnte nicht gelöscht werden",
|
||||
ErrNotFound: "Nicht auf Server gefunden, gelöscht?",
|
||||
ErrFileNotFound: "Datei konnte nicht gefunden werden",
|
||||
ErrSelectionNotFound: "Nicht auf Server gefunden, gelöscht?",
|
||||
ErrUserNotFound: "Nutzer nicht gefunden",
|
||||
ErrLabelNotFound: "Kategorie nicht gefunden",
|
||||
ErrPublic: "Im öffentlichen Modus nicht verfügbar",
|
||||
ErrInvalidPassword: "Ungültiges Passwort",
|
||||
ErrFeatureDisabled: "Funktion deaktiviert",
|
||||
ErrNoLabelsSelected: "Keine Kategorien ausgewählt",
|
||||
ErrNoAlbumsSelected: "Keine Alben ausgewählt",
|
||||
ErrNoFilesForDownload: "Nicht zum Download verfügbar",
|
||||
ErrZipFailed: "Zip-Datei konnte nicht erstellt werden",
|
||||
|
||||
MsgChangesSaved: "Änderungen erfolgreich gespeichert",
|
||||
MsgAlbumCreated: "Album erstellt",
|
||||
MsgAlbumSaved: "Album gespeichert",
|
||||
MsgAlbumDeleted: "Album %s gelöscht",
|
||||
MsgAlbumCloned: "Album-Einträge kopiert",
|
||||
MsgFileUngrouped: "Datei-Gruppierung aufgehoben",
|
||||
MsgSelectionAddedTo: "Auswahl zu %s hinzugefügt",
|
||||
MsgEntryAddedTo: "Ein Eintrag zu %s hinzugefügt",
|
||||
MsgEntriesAddedTo: "%d Einträge zu %s hinzugefügt",
|
||||
MsgEntryRemovedFrom: "Ein Eintrag aus %s entfernt",
|
||||
MsgEntriesRemovedFrom: "%d Einträge aus %s entfernt",
|
||||
MsgAccountCreated: "Server-Konfiguration angelegt",
|
||||
MsgAccountSaved: "Server-Konfiguration gespeichert",
|
||||
MsgAccountDeleted: "Server-Konfiguration gelöscht",
|
||||
// Info and confirmation messages:
|
||||
MsgChangesSaved: "Änderungen erfolgreich gespeichert",
|
||||
MsgAlbumCreated: "Album erstellt",
|
||||
MsgAlbumSaved: "Album gespeichert",
|
||||
MsgAlbumDeleted: "Album %s gelöscht",
|
||||
MsgAlbumCloned: "Album-Einträge kopiert",
|
||||
MsgFileUngrouped: "Datei-Gruppierung aufgehoben",
|
||||
MsgSelectionAddedTo: "Auswahl zu %s hinzugefügt",
|
||||
MsgEntryAddedTo: "Ein Eintrag zu %s hinzugefügt",
|
||||
MsgEntriesAddedTo: "%d Einträge zu %s hinzugefügt",
|
||||
MsgEntryRemovedFrom: "Ein Eintrag aus %s entfernt",
|
||||
MsgEntriesRemovedFrom: "%d Einträge aus %s entfernt",
|
||||
MsgAccountCreated: "Server-Konfiguration angelegt",
|
||||
MsgAccountSaved: "Server-Konfiguration gespeichert",
|
||||
MsgAccountDeleted: "Server-Konfiguration gelöscht",
|
||||
MsgSettingsSaved: "Einstellungen gespeichert",
|
||||
MsgPasswordChanged: "Passwort geändert",
|
||||
MsgImportCompletedIn: "Import in %d s abgeschlossen",
|
||||
MsgImportCanceled: "Import abgebrochen",
|
||||
MsgIndexingCompletedIn: "Indizierung in %d s abgeschlossen",
|
||||
MsgIndexingOriginals: "Indiziere Dateien...",
|
||||
MsgIndexingFiles: "Indiziere Dateien in %s",
|
||||
MsgIndexingCanceled: "Indizierung abgebrochen",
|
||||
MsgRemovedFilesAndPhotos: "%d Dateien und %d Fotos wurden entfernt",
|
||||
MsgMovingFilesFrom: "Verschiebe Dateien von %s",
|
||||
MsgCopyingFilesFrom: "Kopiere Dateien von %s",
|
||||
MsgLabelsDeleted: "Kategorien gelöscht",
|
||||
MsgLabelSaved: "Kategorie gespeichert",
|
||||
MsgFilesUploadedIn: "%d Dateien hochgeladen in %d s",
|
||||
MsgSelectionArchived: "Auswahl archiviert",
|
||||
MsgSelectionRestored: "Auswahl wiederhergestellt",
|
||||
MsgSelectionProtected: "Auswahl als privat markiert",
|
||||
MsgAlbumsDeleted: "Alben gelöscht",
|
||||
MsgZipCreatedIn: "Zip-Datei erstellt in %d s",
|
||||
}
|
||||
|
||||
@@ -4,17 +4,30 @@ const (
|
||||
ErrUnexpected Message = iota + 1
|
||||
ErrBadRequest
|
||||
ErrSaveFailed
|
||||
ErrDeleteFailed
|
||||
ErrAlreadyExists
|
||||
ErrNotFound
|
||||
ErrFileNotFound
|
||||
ErrSelectionNotFound
|
||||
ErrEntityNotFound
|
||||
ErrAccountNotFound
|
||||
ErrUserNotFound
|
||||
ErrLabelNotFound
|
||||
ErrAlbumNotFound
|
||||
ErrPublic
|
||||
ErrReadOnly
|
||||
ErrUnauthorized
|
||||
ErrUploadNSFW
|
||||
ErrOffensiveUpload
|
||||
ErrNoItemsSelected
|
||||
ErrCreateFile
|
||||
ErrCreateFolder
|
||||
ErrConnectionFailed
|
||||
ErrInvalidPassword
|
||||
ErrFeatureDisabled
|
||||
ErrNoLabelsSelected
|
||||
ErrNoAlbumsSelected
|
||||
ErrNoFilesForDownload
|
||||
ErrZipFailed
|
||||
|
||||
MsgChangesSaved
|
||||
MsgAlbumCreated
|
||||
@@ -31,37 +44,88 @@ const (
|
||||
MsgAccountSaved
|
||||
MsgAccountDeleted
|
||||
MsgSettingsSaved
|
||||
MsgPasswordChanged
|
||||
MsgImportCompletedIn
|
||||
MsgImportCanceled
|
||||
MsgIndexingCompletedIn
|
||||
MsgIndexingOriginals
|
||||
MsgIndexingFiles
|
||||
MsgIndexingCanceled
|
||||
MsgRemovedFilesAndPhotos
|
||||
MsgMovingFilesFrom
|
||||
MsgCopyingFilesFrom
|
||||
MsgLabelsDeleted
|
||||
MsgLabelSaved
|
||||
MsgFilesUploadedIn
|
||||
MsgSelectionArchived
|
||||
MsgSelectionRestored
|
||||
MsgSelectionProtected
|
||||
MsgAlbumsDeleted
|
||||
MsgZipCreatedIn
|
||||
)
|
||||
|
||||
var MsgEnglish = MessageMap{
|
||||
ErrUnexpected: "Unexpected error, please try again",
|
||||
ErrBadRequest: "Invalid request",
|
||||
ErrSaveFailed: "Changes could not be saved",
|
||||
ErrAlreadyExists: "%s already exists",
|
||||
ErrEntityNotFound: "Unknown entity",
|
||||
ErrAccountNotFound: "Unknown account",
|
||||
ErrAlbumNotFound: "Album not found",
|
||||
ErrReadOnly: "not available in read-only mode",
|
||||
ErrUnauthorized: "Please log in and try again",
|
||||
ErrUploadNSFW: "Upload might be offensive",
|
||||
ErrNoItemsSelected: "No items selected",
|
||||
ErrCreateFile: "Failed creating file, please check permissions",
|
||||
ErrCreateFolder: "Failed creating folder, please check permissions",
|
||||
ErrConnectionFailed: "Could not connect, please try again",
|
||||
// Error messages:
|
||||
ErrUnexpected: "Unexpected error, please try again",
|
||||
ErrBadRequest: "Invalid request",
|
||||
ErrSaveFailed: "Changes could not be saved",
|
||||
ErrDeleteFailed: "Could not be deleted",
|
||||
ErrAlreadyExists: "%s already exists",
|
||||
ErrNotFound: "Not found on server, deleted?",
|
||||
ErrFileNotFound: "File not found",
|
||||
ErrSelectionNotFound: "Selection not found",
|
||||
ErrEntityNotFound: "Not found on server, deleted?",
|
||||
ErrAccountNotFound: "Account not found",
|
||||
ErrUserNotFound: "User not found",
|
||||
ErrLabelNotFound: "Label not found",
|
||||
ErrAlbumNotFound: "Album not found",
|
||||
ErrPublic: "Not available in public mode",
|
||||
ErrReadOnly: "not available in read-only mode",
|
||||
ErrUnauthorized: "Please log in and try again",
|
||||
ErrOffensiveUpload: "Upload might be offensive",
|
||||
ErrNoItemsSelected: "No items selected",
|
||||
ErrCreateFile: "Failed creating file, please check permissions",
|
||||
ErrCreateFolder: "Failed creating folder, please check permissions",
|
||||
ErrConnectionFailed: "Could not connect, please try again",
|
||||
ErrInvalidPassword: "Invalid password, please try again",
|
||||
ErrFeatureDisabled: "Feature disabled",
|
||||
ErrNoLabelsSelected: "No labels selected",
|
||||
ErrNoAlbumsSelected: "No albums selected",
|
||||
ErrNoFilesForDownload: "No files available for download",
|
||||
ErrZipFailed: "Failed to create zip file",
|
||||
|
||||
MsgChangesSaved: "Changes successfully saved",
|
||||
MsgAlbumCreated: "Album created",
|
||||
MsgAlbumSaved: "Album saved",
|
||||
MsgAlbumDeleted: "Album %s deleted",
|
||||
MsgAlbumCloned: "Album contents cloned",
|
||||
MsgFileUngrouped: "File successfully ungrouped",
|
||||
MsgSelectionAddedTo: "Selection added to %s",
|
||||
MsgEntryAddedTo: "One entry added to %s",
|
||||
MsgEntriesAddedTo: "%d entries added to %s",
|
||||
MsgEntryRemovedFrom: "One entry removed from %s",
|
||||
MsgEntriesRemovedFrom: "%d entries removed from %s",
|
||||
MsgAccountCreated: "Account created",
|
||||
MsgAccountSaved: "Account saved",
|
||||
MsgAccountDeleted: "Account deleted",
|
||||
MsgSettingsSaved: "Settings saved",
|
||||
// Info and confirmation messages:
|
||||
MsgChangesSaved: "Changes successfully saved",
|
||||
MsgAlbumCreated: "Album created",
|
||||
MsgAlbumSaved: "Album saved",
|
||||
MsgAlbumDeleted: "Album %s deleted",
|
||||
MsgAlbumCloned: "Album contents cloned",
|
||||
MsgFileUngrouped: "File successfully ungrouped",
|
||||
MsgSelectionAddedTo: "Selection added to %s",
|
||||
MsgEntryAddedTo: "One entry added to %s",
|
||||
MsgEntriesAddedTo: "%d entries added to %s",
|
||||
MsgEntryRemovedFrom: "One entry removed from %s",
|
||||
MsgEntriesRemovedFrom: "%d entries removed from %s",
|
||||
MsgAccountCreated: "Account created",
|
||||
MsgAccountSaved: "Account saved",
|
||||
MsgAccountDeleted: "Account deleted",
|
||||
MsgSettingsSaved: "Settings saved",
|
||||
MsgPasswordChanged: "Password changed",
|
||||
MsgImportCompletedIn: "Import completed in %d s",
|
||||
MsgImportCanceled: "Import canceled",
|
||||
MsgIndexingCompletedIn: "Indexing completed in %d s",
|
||||
MsgIndexingOriginals: "Indexing originals...",
|
||||
MsgIndexingFiles: "Indexing files in %s",
|
||||
MsgIndexingCanceled: "Indexing canceled",
|
||||
MsgRemovedFilesAndPhotos: "Removed %d files and %d photos",
|
||||
MsgMovingFilesFrom: "Moving files from %s",
|
||||
MsgCopyingFilesFrom: "Copying files from %s",
|
||||
MsgLabelsDeleted: "Labels deleted",
|
||||
MsgLabelSaved: "Label saved",
|
||||
MsgFilesUploadedIn: "%d files uploaded in %d s",
|
||||
MsgSelectionArchived: "Selection archived",
|
||||
MsgSelectionRestored: "Selection restored",
|
||||
MsgSelectionProtected: "Selection marked as private",
|
||||
MsgAlbumsDeleted: "Albums deleted",
|
||||
MsgZipCreatedIn: "Zip created in %d s",
|
||||
}
|
||||
|
||||
67
internal/i18n/lang-es.go
Normal file
67
internal/i18n/lang-es.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package i18n
|
||||
|
||||
var MsgSpanish = MessageMap{
|
||||
// Error messages:
|
||||
ErrUnexpected: "Unexpected error, please try again",
|
||||
ErrBadRequest: "Invalid request",
|
||||
ErrSaveFailed: "Changes could not be saved",
|
||||
ErrDeleteFailed: "Could not be deleted",
|
||||
ErrAlreadyExists: "%s already exists",
|
||||
ErrNotFound: "Not found on server, deleted?",
|
||||
ErrFileNotFound: "File not found",
|
||||
ErrSelectionNotFound: "Selection not found",
|
||||
ErrEntityNotFound: "Not found on server, deleted?",
|
||||
ErrAccountNotFound: "Account not found",
|
||||
ErrUserNotFound: "User not found",
|
||||
ErrLabelNotFound: "Label not found",
|
||||
ErrAlbumNotFound: "Album not found",
|
||||
ErrPublic: "Not available in public mode",
|
||||
ErrReadOnly: "not available in read-only mode",
|
||||
ErrUnauthorized: "Please log in and try again",
|
||||
ErrOffensiveUpload: "Upload might be offensive",
|
||||
ErrNoItemsSelected: "No items selected",
|
||||
ErrCreateFile: "Failed creating file, please check permissions",
|
||||
ErrCreateFolder: "Failed creating folder, please check permissions",
|
||||
ErrConnectionFailed: "Could not connect, please try again",
|
||||
ErrInvalidPassword: "Invalid password, please try again",
|
||||
ErrFeatureDisabled: "Feature disabled",
|
||||
ErrNoLabelsSelected: "No labels selected",
|
||||
ErrNoAlbumsSelected: "No albums selected",
|
||||
ErrNoFilesForDownload: "No files available for download",
|
||||
ErrZipFailed: "Failed to create zip file",
|
||||
|
||||
// Info and confirmation messages:
|
||||
MsgChangesSaved: "Changes successfully saved",
|
||||
MsgAlbumCreated: "Album created",
|
||||
MsgAlbumSaved: "Album saved",
|
||||
MsgAlbumDeleted: "Album %s deleted",
|
||||
MsgAlbumCloned: "Album contents cloned",
|
||||
MsgFileUngrouped: "File successfully ungrouped",
|
||||
MsgSelectionAddedTo: "Selection added to %s",
|
||||
MsgEntryAddedTo: "One entry added to %s",
|
||||
MsgEntriesAddedTo: "%d entries added to %s",
|
||||
MsgEntryRemovedFrom: "One entry removed from %s",
|
||||
MsgEntriesRemovedFrom: "%d entries removed from %s",
|
||||
MsgAccountCreated: "Account created",
|
||||
MsgAccountSaved: "Account saved",
|
||||
MsgAccountDeleted: "Account deleted",
|
||||
MsgSettingsSaved: "Settings saved",
|
||||
MsgPasswordChanged: "Password changed",
|
||||
MsgImportCompletedIn: "Import completed in %d s",
|
||||
MsgImportCanceled: "Import canceled",
|
||||
MsgIndexingCompletedIn: "Indexing completed in %d s",
|
||||
MsgIndexingOriginals: "Indexing originals...",
|
||||
MsgIndexingFiles: "Indexing files in %s",
|
||||
MsgIndexingCanceled: "Indexing canceled",
|
||||
MsgRemovedFilesAndPhotos: "Removed %d files and %d photos",
|
||||
MsgMovingFilesFrom: "Moving files from %s",
|
||||
MsgCopyingFilesFrom: "Copying files from %s",
|
||||
MsgLabelsDeleted: "Labels deleted",
|
||||
MsgLabelSaved: "Label saved",
|
||||
MsgFilesUploadedIn: "%d files uploaded in %d s",
|
||||
MsgSelectionArchived: "Selection archived",
|
||||
MsgSelectionRestored: "Selection restored",
|
||||
MsgSelectionProtected: "Selection marked as private",
|
||||
MsgAlbumsDeleted: "Albums deleted",
|
||||
MsgZipCreatedIn: "Zip created in %d s",
|
||||
}
|
||||
@@ -1,32 +1,67 @@
|
||||
package i18n
|
||||
|
||||
var MsgFrench = MessageMap{
|
||||
ErrUnexpected: "Unexpected error, please try again",
|
||||
ErrBadRequest: "Invalid request, please try again",
|
||||
ErrSaveFailed: "Changes could not be saved",
|
||||
ErrAlreadyExists: "%s already exists",
|
||||
ErrEntityNotFound: "Unknown entity",
|
||||
ErrAccountNotFound: "Unknown account",
|
||||
ErrAlbumNotFound: "Album not found",
|
||||
ErrReadOnly: "not available in read-only mode",
|
||||
ErrUnauthorized: "please log in and try again",
|
||||
ErrUploadNSFW: "Upload might be offensive",
|
||||
ErrNoItemsSelected: "No items selected",
|
||||
ErrCreateFile: "Failed creating file, please check permissions",
|
||||
ErrCreateFolder: "Failed creating folder, please check permissions",
|
||||
// Error messages:
|
||||
ErrUnexpected: "Unexpected error, please try again",
|
||||
ErrBadRequest: "Invalid request",
|
||||
ErrSaveFailed: "Changes could not be saved",
|
||||
ErrDeleteFailed: "Could not be deleted",
|
||||
ErrAlreadyExists: "%s already exists",
|
||||
ErrNotFound: "Not found on server, deleted?",
|
||||
ErrFileNotFound: "File not found",
|
||||
ErrSelectionNotFound: "Selection not found",
|
||||
ErrEntityNotFound: "Not found on server, deleted?",
|
||||
ErrAccountNotFound: "Account not found",
|
||||
ErrUserNotFound: "User not found",
|
||||
ErrLabelNotFound: "Label not found",
|
||||
ErrAlbumNotFound: "Album not found",
|
||||
ErrPublic: "Not available in public mode",
|
||||
ErrReadOnly: "not available in read-only mode",
|
||||
ErrUnauthorized: "Please log in and try again",
|
||||
ErrOffensiveUpload: "Upload might be offensive",
|
||||
ErrNoItemsSelected: "No items selected",
|
||||
ErrCreateFile: "Failed creating file, please check permissions",
|
||||
ErrCreateFolder: "Failed creating folder, please check permissions",
|
||||
ErrConnectionFailed: "Could not connect, please try again",
|
||||
ErrInvalidPassword: "Invalid password, please try again",
|
||||
ErrFeatureDisabled: "Feature disabled",
|
||||
ErrNoLabelsSelected: "No labels selected",
|
||||
ErrNoAlbumsSelected: "No albums selected",
|
||||
ErrNoFilesForDownload: "No files available for download",
|
||||
ErrZipFailed: "Failed to create zip file",
|
||||
|
||||
MsgChangesSaved: "Changes successfully saved",
|
||||
MsgAlbumCreated: "Album created",
|
||||
MsgAlbumSaved: "Album saved",
|
||||
MsgAlbumDeleted: "Album %s deleted",
|
||||
MsgAlbumCloned: "Album contents cloned",
|
||||
MsgFileUngrouped: "File successfully ungrouped",
|
||||
MsgSelectionAddedTo: "Selection added to %s",
|
||||
MsgEntryAddedTo: "One entry added to %s",
|
||||
MsgEntriesAddedTo: "%d entries added to %s",
|
||||
MsgEntryRemovedFrom: "One entry removed from %s",
|
||||
MsgEntriesRemovedFrom: "%d entries removed from %s",
|
||||
MsgAccountCreated: "Account created",
|
||||
MsgAccountSaved: "Account saved",
|
||||
MsgAccountDeleted: "Account deleted",
|
||||
// Info and confirmation messages:
|
||||
MsgChangesSaved: "Changes successfully saved",
|
||||
MsgAlbumCreated: "Album created",
|
||||
MsgAlbumSaved: "Album saved",
|
||||
MsgAlbumDeleted: "Album %s deleted",
|
||||
MsgAlbumCloned: "Album contents cloned",
|
||||
MsgFileUngrouped: "File successfully ungrouped",
|
||||
MsgSelectionAddedTo: "Selection added to %s",
|
||||
MsgEntryAddedTo: "One entry added to %s",
|
||||
MsgEntriesAddedTo: "%d entries added to %s",
|
||||
MsgEntryRemovedFrom: "One entry removed from %s",
|
||||
MsgEntriesRemovedFrom: "%d entries removed from %s",
|
||||
MsgAccountCreated: "Account created",
|
||||
MsgAccountSaved: "Account saved",
|
||||
MsgAccountDeleted: "Account deleted",
|
||||
MsgSettingsSaved: "Settings saved",
|
||||
MsgPasswordChanged: "Password changed",
|
||||
MsgImportCompletedIn: "Import completed in %d s",
|
||||
MsgImportCanceled: "Import canceled",
|
||||
MsgIndexingCompletedIn: "Indexing completed in %d s",
|
||||
MsgIndexingOriginals: "Indexing originals...",
|
||||
MsgIndexingFiles: "Indexing files in %s",
|
||||
MsgIndexingCanceled: "Indexing canceled",
|
||||
MsgRemovedFilesAndPhotos: "Removed %d files and %d photos",
|
||||
MsgMovingFilesFrom: "Moving files from %s",
|
||||
MsgCopyingFilesFrom: "Copying files from %s",
|
||||
MsgLabelsDeleted: "Labels deleted",
|
||||
MsgLabelSaved: "Label saved",
|
||||
MsgFilesUploadedIn: "%d files uploaded in %d s",
|
||||
MsgSelectionArchived: "Selection archived",
|
||||
MsgSelectionRestored: "Selection restored",
|
||||
MsgSelectionProtected: "Selection marked as private",
|
||||
MsgAlbumsDeleted: "Albums deleted",
|
||||
MsgZipCreatedIn: "Zip created in %d s",
|
||||
}
|
||||
|
||||
@@ -1,32 +1,67 @@
|
||||
package i18n
|
||||
|
||||
var MsgDutch = MessageMap{
|
||||
ErrUnexpected: "Unexpected error, please try again",
|
||||
ErrBadRequest: "Invalid request, please try again",
|
||||
ErrSaveFailed: "Changes could not be saved",
|
||||
ErrAlreadyExists: "%s already exists",
|
||||
ErrEntityNotFound: "Unknown entity",
|
||||
ErrAccountNotFound: "Unknown account",
|
||||
ErrAlbumNotFound: "Album not found",
|
||||
ErrReadOnly: "not available in read-only mode",
|
||||
ErrUnauthorized: "please log in and try again",
|
||||
ErrUploadNSFW: "Upload might be offensive",
|
||||
ErrNoItemsSelected: "No items selected",
|
||||
ErrCreateFile: "Failed creating file, please check permissions",
|
||||
ErrCreateFolder: "Failed creating folder, please check permissions",
|
||||
// Error messages:
|
||||
ErrUnexpected: "Unexpected error, please try again",
|
||||
ErrBadRequest: "Invalid request",
|
||||
ErrSaveFailed: "Changes could not be saved",
|
||||
ErrDeleteFailed: "Could not be deleted",
|
||||
ErrAlreadyExists: "%s already exists",
|
||||
ErrNotFound: "Not found on server, deleted?",
|
||||
ErrFileNotFound: "File not found",
|
||||
ErrSelectionNotFound: "Selection not found",
|
||||
ErrEntityNotFound: "Not found on server, deleted?",
|
||||
ErrAccountNotFound: "Account not found",
|
||||
ErrUserNotFound: "User not found",
|
||||
ErrLabelNotFound: "Label not found",
|
||||
ErrAlbumNotFound: "Album not found",
|
||||
ErrPublic: "Not available in public mode",
|
||||
ErrReadOnly: "not available in read-only mode",
|
||||
ErrUnauthorized: "Please log in and try again",
|
||||
ErrOffensiveUpload: "Upload might be offensive",
|
||||
ErrNoItemsSelected: "No items selected",
|
||||
ErrCreateFile: "Failed creating file, please check permissions",
|
||||
ErrCreateFolder: "Failed creating folder, please check permissions",
|
||||
ErrConnectionFailed: "Could not connect, please try again",
|
||||
ErrInvalidPassword: "Invalid password, please try again",
|
||||
ErrFeatureDisabled: "Feature disabled",
|
||||
ErrNoLabelsSelected: "No labels selected",
|
||||
ErrNoAlbumsSelected: "No albums selected",
|
||||
ErrNoFilesForDownload: "No files available for download",
|
||||
ErrZipFailed: "Failed to create zip file",
|
||||
|
||||
MsgChangesSaved: "Changes successfully saved",
|
||||
MsgAlbumCreated: "Album created",
|
||||
MsgAlbumSaved: "Album saved",
|
||||
MsgAlbumDeleted: "Album %s deleted",
|
||||
MsgAlbumCloned: "Album contents cloned",
|
||||
MsgFileUngrouped: "File successfully ungrouped",
|
||||
MsgSelectionAddedTo: "Selection added to %s",
|
||||
MsgEntryAddedTo: "One entry added to %s",
|
||||
MsgEntriesAddedTo: "%d entries added to %s",
|
||||
MsgEntryRemovedFrom: "One entry removed from %s",
|
||||
MsgEntriesRemovedFrom: "%d entries removed from %s",
|
||||
MsgAccountCreated: "Account created",
|
||||
MsgAccountSaved: "Account saved",
|
||||
MsgAccountDeleted: "Account deleted",
|
||||
// Info and confirmation messages:
|
||||
MsgChangesSaved: "Changes successfully saved",
|
||||
MsgAlbumCreated: "Album created",
|
||||
MsgAlbumSaved: "Album saved",
|
||||
MsgAlbumDeleted: "Album %s deleted",
|
||||
MsgAlbumCloned: "Album contents cloned",
|
||||
MsgFileUngrouped: "File successfully ungrouped",
|
||||
MsgSelectionAddedTo: "Selection added to %s",
|
||||
MsgEntryAddedTo: "One entry added to %s",
|
||||
MsgEntriesAddedTo: "%d entries added to %s",
|
||||
MsgEntryRemovedFrom: "One entry removed from %s",
|
||||
MsgEntriesRemovedFrom: "%d entries removed from %s",
|
||||
MsgAccountCreated: "Account created",
|
||||
MsgAccountSaved: "Account saved",
|
||||
MsgAccountDeleted: "Account deleted",
|
||||
MsgSettingsSaved: "Settings saved",
|
||||
MsgPasswordChanged: "Password changed",
|
||||
MsgImportCompletedIn: "Import completed in %d s",
|
||||
MsgImportCanceled: "Import canceled",
|
||||
MsgIndexingCompletedIn: "Indexing completed in %d s",
|
||||
MsgIndexingOriginals: "Indexing originals...",
|
||||
MsgIndexingFiles: "Indexing files in %s",
|
||||
MsgIndexingCanceled: "Indexing canceled",
|
||||
MsgRemovedFilesAndPhotos: "Removed %d files and %d photos",
|
||||
MsgMovingFilesFrom: "Moving files from %s",
|
||||
MsgCopyingFilesFrom: "Copying files from %s",
|
||||
MsgLabelsDeleted: "Labels deleted",
|
||||
MsgLabelSaved: "Label saved",
|
||||
MsgFilesUploadedIn: "%d files uploaded in %d s",
|
||||
MsgSelectionArchived: "Selection archived",
|
||||
MsgSelectionRestored: "Selection restored",
|
||||
MsgSelectionProtected: "Selection marked as private",
|
||||
MsgAlbumsDeleted: "Albums deleted",
|
||||
MsgZipCreatedIn: "Zip created in %d s",
|
||||
}
|
||||
|
||||
67
internal/i18n/lang-pt.go
Normal file
67
internal/i18n/lang-pt.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package i18n
|
||||
|
||||
var MsgPortuguese = MessageMap{
|
||||
// Error messages:
|
||||
ErrUnexpected: "Unexpected error, please try again",
|
||||
ErrBadRequest: "Invalid request",
|
||||
ErrSaveFailed: "Changes could not be saved",
|
||||
ErrDeleteFailed: "Could not be deleted",
|
||||
ErrAlreadyExists: "%s already exists",
|
||||
ErrNotFound: "Not found on server, deleted?",
|
||||
ErrFileNotFound: "File not found",
|
||||
ErrSelectionNotFound: "Selection not found",
|
||||
ErrEntityNotFound: "Not found on server, deleted?",
|
||||
ErrAccountNotFound: "Account not found",
|
||||
ErrUserNotFound: "User not found",
|
||||
ErrLabelNotFound: "Label not found",
|
||||
ErrAlbumNotFound: "Album not found",
|
||||
ErrPublic: "Not available in public mode",
|
||||
ErrReadOnly: "not available in read-only mode",
|
||||
ErrUnauthorized: "Please log in and try again",
|
||||
ErrOffensiveUpload: "Upload might be offensive",
|
||||
ErrNoItemsSelected: "No items selected",
|
||||
ErrCreateFile: "Failed creating file, please check permissions",
|
||||
ErrCreateFolder: "Failed creating folder, please check permissions",
|
||||
ErrConnectionFailed: "Could not connect, please try again",
|
||||
ErrInvalidPassword: "Invalid password, please try again",
|
||||
ErrFeatureDisabled: "Feature disabled",
|
||||
ErrNoLabelsSelected: "No labels selected",
|
||||
ErrNoAlbumsSelected: "No albums selected",
|
||||
ErrNoFilesForDownload: "No files available for download",
|
||||
ErrZipFailed: "Failed to create zip file",
|
||||
|
||||
// Info and confirmation messages:
|
||||
MsgChangesSaved: "Changes successfully saved",
|
||||
MsgAlbumCreated: "Album created",
|
||||
MsgAlbumSaved: "Album saved",
|
||||
MsgAlbumDeleted: "Album %s deleted",
|
||||
MsgAlbumCloned: "Album contents cloned",
|
||||
MsgFileUngrouped: "File successfully ungrouped",
|
||||
MsgSelectionAddedTo: "Selection added to %s",
|
||||
MsgEntryAddedTo: "One entry added to %s",
|
||||
MsgEntriesAddedTo: "%d entries added to %s",
|
||||
MsgEntryRemovedFrom: "One entry removed from %s",
|
||||
MsgEntriesRemovedFrom: "%d entries removed from %s",
|
||||
MsgAccountCreated: "Account created",
|
||||
MsgAccountSaved: "Account saved",
|
||||
MsgAccountDeleted: "Account deleted",
|
||||
MsgSettingsSaved: "Settings saved",
|
||||
MsgPasswordChanged: "Password changed",
|
||||
MsgImportCompletedIn: "Import completed in %d s",
|
||||
MsgImportCanceled: "Import canceled",
|
||||
MsgIndexingCompletedIn: "Indexing completed in %d s",
|
||||
MsgIndexingOriginals: "Indexing originals...",
|
||||
MsgIndexingFiles: "Indexing files in %s",
|
||||
MsgIndexingCanceled: "Indexing canceled",
|
||||
MsgRemovedFilesAndPhotos: "Removed %d files and %d photos",
|
||||
MsgMovingFilesFrom: "Moving files from %s",
|
||||
MsgCopyingFilesFrom: "Copying files from %s",
|
||||
MsgLabelsDeleted: "Labels deleted",
|
||||
MsgLabelSaved: "Label saved",
|
||||
MsgFilesUploadedIn: "%d files uploaded in %d s",
|
||||
MsgSelectionArchived: "Selection archived",
|
||||
MsgSelectionRestored: "Selection restored",
|
||||
MsgSelectionProtected: "Selection marked as private",
|
||||
MsgAlbumsDeleted: "Albums deleted",
|
||||
MsgZipCreatedIn: "Zip created in %d s",
|
||||
}
|
||||
@@ -1,32 +1,67 @@
|
||||
package i18n
|
||||
|
||||
var MsgRussian = MessageMap{
|
||||
ErrUnexpected: "Unexpected error, please try again",
|
||||
ErrBadRequest: "Invalid request, please try again",
|
||||
ErrSaveFailed: "Changes could not be saved",
|
||||
ErrAlreadyExists: "%s already exists",
|
||||
ErrEntityNotFound: "Unknown entity",
|
||||
ErrAccountNotFound: "Unknown account",
|
||||
ErrAlbumNotFound: "Album not found",
|
||||
ErrReadOnly: "not available in read-only mode",
|
||||
ErrUnauthorized: "please log in and try again",
|
||||
ErrUploadNSFW: "Upload might be offensive",
|
||||
ErrNoItemsSelected: "No items selected",
|
||||
ErrCreateFile: "Failed creating file, please check permissions",
|
||||
ErrCreateFolder: "Failed creating folder, please check permissions",
|
||||
// Error messages:
|
||||
ErrUnexpected: "Unexpected error, please try again",
|
||||
ErrBadRequest: "Invalid request",
|
||||
ErrSaveFailed: "Changes could not be saved",
|
||||
ErrDeleteFailed: "Could not be deleted",
|
||||
ErrAlreadyExists: "%s already exists",
|
||||
ErrNotFound: "Not found on server, deleted?",
|
||||
ErrFileNotFound: "File not found",
|
||||
ErrSelectionNotFound: "Selection not found",
|
||||
ErrEntityNotFound: "Not found on server, deleted?",
|
||||
ErrAccountNotFound: "Account not found",
|
||||
ErrUserNotFound: "User not found",
|
||||
ErrLabelNotFound: "Label not found",
|
||||
ErrAlbumNotFound: "Album not found",
|
||||
ErrPublic: "Not available in public mode",
|
||||
ErrReadOnly: "not available in read-only mode",
|
||||
ErrUnauthorized: "Please log in and try again",
|
||||
ErrOffensiveUpload: "Upload might be offensive",
|
||||
ErrNoItemsSelected: "No items selected",
|
||||
ErrCreateFile: "Failed creating file, please check permissions",
|
||||
ErrCreateFolder: "Failed creating folder, please check permissions",
|
||||
ErrConnectionFailed: "Could not connect, please try again",
|
||||
ErrInvalidPassword: "Invalid password, please try again",
|
||||
ErrFeatureDisabled: "Feature disabled",
|
||||
ErrNoLabelsSelected: "No labels selected",
|
||||
ErrNoAlbumsSelected: "No albums selected",
|
||||
ErrNoFilesForDownload: "No files available for download",
|
||||
ErrZipFailed: "Failed to create zip file",
|
||||
|
||||
MsgChangesSaved: "Changes successfully saved",
|
||||
MsgAlbumCreated: "Album created",
|
||||
MsgAlbumSaved: "Album saved",
|
||||
MsgAlbumDeleted: "Album %s deleted",
|
||||
MsgAlbumCloned: "Album contents cloned",
|
||||
MsgFileUngrouped: "File successfully ungrouped",
|
||||
MsgSelectionAddedTo: "Selection added to %s",
|
||||
MsgEntryAddedTo: "One entry added to %s",
|
||||
MsgEntriesAddedTo: "%d entries added to %s",
|
||||
MsgEntryRemovedFrom: "One entry removed from %s",
|
||||
MsgEntriesRemovedFrom: "%d entries removed from %s",
|
||||
MsgAccountCreated: "Account created",
|
||||
MsgAccountSaved: "Account saved",
|
||||
MsgAccountDeleted: "Account deleted",
|
||||
// Info and confirmation messages:
|
||||
MsgChangesSaved: "Changes successfully saved",
|
||||
MsgAlbumCreated: "Album created",
|
||||
MsgAlbumSaved: "Album saved",
|
||||
MsgAlbumDeleted: "Album %s deleted",
|
||||
MsgAlbumCloned: "Album contents cloned",
|
||||
MsgFileUngrouped: "File successfully ungrouped",
|
||||
MsgSelectionAddedTo: "Selection added to %s",
|
||||
MsgEntryAddedTo: "One entry added to %s",
|
||||
MsgEntriesAddedTo: "%d entries added to %s",
|
||||
MsgEntryRemovedFrom: "One entry removed from %s",
|
||||
MsgEntriesRemovedFrom: "%d entries removed from %s",
|
||||
MsgAccountCreated: "Account created",
|
||||
MsgAccountSaved: "Account saved",
|
||||
MsgAccountDeleted: "Account deleted",
|
||||
MsgSettingsSaved: "Settings saved",
|
||||
MsgPasswordChanged: "Password changed",
|
||||
MsgImportCompletedIn: "Import completed in %d s",
|
||||
MsgImportCanceled: "Import canceled",
|
||||
MsgIndexingCompletedIn: "Indexing completed in %d s",
|
||||
MsgIndexingOriginals: "Indexing originals...",
|
||||
MsgIndexingFiles: "Indexing files in %s",
|
||||
MsgIndexingCanceled: "Indexing canceled",
|
||||
MsgRemovedFilesAndPhotos: "Removed %d files and %d photos",
|
||||
MsgMovingFilesFrom: "Moving files from %s",
|
||||
MsgCopyingFilesFrom: "Copying files from %s",
|
||||
MsgLabelsDeleted: "Labels deleted",
|
||||
MsgLabelSaved: "Label saved",
|
||||
MsgFilesUploadedIn: "%d files uploaded in %d s",
|
||||
MsgSelectionArchived: "Selection archived",
|
||||
MsgSelectionRestored: "Selection restored",
|
||||
MsgSelectionProtected: "Selection marked as private",
|
||||
MsgAlbumsDeleted: "Albums deleted",
|
||||
MsgZipCreatedIn: "Zip created in %d s",
|
||||
}
|
||||
|
||||
67
internal/i18n/lang-zh.go
Normal file
67
internal/i18n/lang-zh.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package i18n
|
||||
|
||||
var MsgChinese = MessageMap{
|
||||
// Error messages:
|
||||
ErrUnexpected: "Unexpected error, please try again",
|
||||
ErrBadRequest: "Invalid request",
|
||||
ErrSaveFailed: "Changes could not be saved",
|
||||
ErrDeleteFailed: "Could not be deleted",
|
||||
ErrAlreadyExists: "%s already exists",
|
||||
ErrNotFound: "Not found on server, deleted?",
|
||||
ErrFileNotFound: "File not found",
|
||||
ErrSelectionNotFound: "Selection not found",
|
||||
ErrEntityNotFound: "Not found on server, deleted?",
|
||||
ErrAccountNotFound: "Account not found",
|
||||
ErrUserNotFound: "User not found",
|
||||
ErrLabelNotFound: "Label not found",
|
||||
ErrAlbumNotFound: "Album not found",
|
||||
ErrPublic: "Not available in public mode",
|
||||
ErrReadOnly: "not available in read-only mode",
|
||||
ErrUnauthorized: "Please log in and try again",
|
||||
ErrOffensiveUpload: "Upload might be offensive",
|
||||
ErrNoItemsSelected: "No items selected",
|
||||
ErrCreateFile: "Failed creating file, please check permissions",
|
||||
ErrCreateFolder: "Failed creating folder, please check permissions",
|
||||
ErrConnectionFailed: "Could not connect, please try again",
|
||||
ErrInvalidPassword: "Invalid password, please try again",
|
||||
ErrFeatureDisabled: "Feature disabled",
|
||||
ErrNoLabelsSelected: "No labels selected",
|
||||
ErrNoAlbumsSelected: "No albums selected",
|
||||
ErrNoFilesForDownload: "No files available for download",
|
||||
ErrZipFailed: "Failed to create zip file",
|
||||
|
||||
// Info and confirmation messages:
|
||||
MsgChangesSaved: "Changes successfully saved",
|
||||
MsgAlbumCreated: "Album created",
|
||||
MsgAlbumSaved: "Album saved",
|
||||
MsgAlbumDeleted: "Album %s deleted",
|
||||
MsgAlbumCloned: "Album contents cloned",
|
||||
MsgFileUngrouped: "File successfully ungrouped",
|
||||
MsgSelectionAddedTo: "Selection added to %s",
|
||||
MsgEntryAddedTo: "One entry added to %s",
|
||||
MsgEntriesAddedTo: "%d entries added to %s",
|
||||
MsgEntryRemovedFrom: "One entry removed from %s",
|
||||
MsgEntriesRemovedFrom: "%d entries removed from %s",
|
||||
MsgAccountCreated: "Account created",
|
||||
MsgAccountSaved: "Account saved",
|
||||
MsgAccountDeleted: "Account deleted",
|
||||
MsgSettingsSaved: "Settings saved",
|
||||
MsgPasswordChanged: "Password changed",
|
||||
MsgImportCompletedIn: "Import completed in %d s",
|
||||
MsgImportCanceled: "Import canceled",
|
||||
MsgIndexingCompletedIn: "Indexing completed in %d s",
|
||||
MsgIndexingOriginals: "Indexing originals...",
|
||||
MsgIndexingFiles: "Indexing files in %s",
|
||||
MsgIndexingCanceled: "Indexing canceled",
|
||||
MsgRemovedFilesAndPhotos: "Removed %d files and %d photos",
|
||||
MsgMovingFilesFrom: "Moving files from %s",
|
||||
MsgCopyingFilesFrom: "Copying files from %s",
|
||||
MsgLabelsDeleted: "Labels deleted",
|
||||
MsgLabelSaved: "Label saved",
|
||||
MsgFilesUploadedIn: "%d files uploaded in %d s",
|
||||
MsgSelectionArchived: "Selection archived",
|
||||
MsgSelectionRestored: "Selection restored",
|
||||
MsgSelectionProtected: "Selection marked as private",
|
||||
MsgAlbumsDeleted: "Albums deleted",
|
||||
MsgZipCreatedIn: "Zip created in %d s",
|
||||
}
|
||||
@@ -7,20 +7,26 @@ type Language string
|
||||
type LanguageMap map[Language]MessageMap
|
||||
|
||||
const (
|
||||
English Language = "en"
|
||||
Dutch Language = "nl"
|
||||
French Language = "fr"
|
||||
German Language = "de"
|
||||
Russian Language = "ru"
|
||||
Default = English
|
||||
German Language = "de"
|
||||
English Language = "en"
|
||||
Spanish Language = "es"
|
||||
French Language = "fr"
|
||||
Dutch Language = "nl"
|
||||
Portuguese Language = "pt"
|
||||
Russian Language = "ru"
|
||||
Chinese Language = "zh"
|
||||
Default = English
|
||||
)
|
||||
|
||||
var Languages = LanguageMap{
|
||||
English: MsgEnglish,
|
||||
Dutch: MsgDutch,
|
||||
French: MsgFrench,
|
||||
German: MsgGerman,
|
||||
Russian: MsgRussian,
|
||||
German: MsgGerman,
|
||||
English: MsgEnglish,
|
||||
Spanish: MsgSpanish,
|
||||
French: MsgFrench,
|
||||
Dutch: MsgDutch,
|
||||
Portuguese: MsgPortuguese,
|
||||
Russian: MsgRussian,
|
||||
Chinese: MsgChinese,
|
||||
}
|
||||
|
||||
var Lang = Default
|
||||
|
||||
@@ -3,9 +3,10 @@ package i18n
|
||||
import "strings"
|
||||
|
||||
type Response struct {
|
||||
Code int `json:"code"`
|
||||
Err string `json:"error,omitempty"`
|
||||
Msg string `json:"success,omitempty"`
|
||||
Code int `json:"code"`
|
||||
Err string `json:"error,omitempty"`
|
||||
Msg string `json:"message,omitempty"`
|
||||
Details string `json:"details,omitempty"`
|
||||
}
|
||||
|
||||
func (r Response) String() string {
|
||||
@@ -24,6 +25,10 @@ func (r Response) Error() string {
|
||||
return r.Err
|
||||
}
|
||||
|
||||
func (r Response) Success() bool {
|
||||
return r.Err == "" && r.Code < 400
|
||||
}
|
||||
|
||||
func NewResponse(code int, id Message, params ...interface{}) Response {
|
||||
if code < 400 {
|
||||
return Response{Code: code, Msg: Msg(id, params...)}
|
||||
|
||||
@@ -32,7 +32,7 @@ func TestNewResponse(t *testing.T) {
|
||||
if s, err := json.Marshal(resp); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
assert.Equal(t, `{"code":200,"success":"Changes successfully saved"}`, string(s))
|
||||
assert.Equal(t, `{"code":200,"message":"Changes successfully saved"}`, string(s))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
package query
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
)
|
||||
|
||||
// Errors returns the error log.
|
||||
func Errors(limit, offset int) (results entity.Errors, err error) {
|
||||
// Errors returns the error log filtered with an optional search string.
|
||||
func Errors(limit, offset int, s string) (results entity.Errors, err error) {
|
||||
stmt := Db()
|
||||
|
||||
s = strings.TrimSpace(s)
|
||||
|
||||
if len(s) >= 3 {
|
||||
stmt = stmt.Where("error_message LIKE ?", "%"+s+"%")
|
||||
}
|
||||
|
||||
err = stmt.Order("error_time DESC").Limit(limit).Offset(offset).Find(&results).Error
|
||||
|
||||
return results, err
|
||||
|
||||
Reference in New Issue
Block a user