Settings: Add "delete" feature flag and share page title #167

This commit is contained in:
Michael Mayer 2020-12-13 14:53:26 +01:00
parent 283748cace
commit 801dc49dd7
23 changed files with 133 additions and 382 deletions

View file

@ -79,8 +79,8 @@ Vue.prototype.$isMobile = isMobile;
// Register Vuetify
Vue.use(Vuetify, {"theme": config.theme});
Vue.config.language = config.values.settings.language;
Settings.defaultLocale = config.values.settings.language;
Vue.config.language = config.values.settings.ui.language;
Settings.defaultLocale = Vue.config.language;
// Register other VueJS plugins
Vue.use(GetTextPlugin, {

View file

@ -69,7 +69,7 @@ export default class Config {
Event.subscribe("count", (ev, data) => this.onCount(ev, data));
if (this.has("settings")) {
this.setTheme(this.get("settings").theme);
this.setTheme(this.get("settings").ui.theme);
} else {
this.setTheme("default");
}
@ -100,7 +100,7 @@ export default class Config {
}
if (values.settings) {
this.setTheme(values.settings.theme);
this.setTheme(values.settings.ui.theme);
}
return this;

View file

@ -22,6 +22,11 @@ body.readonly #photoprism .feature-readonly {
display: block !important;
}
body.hide-scrollbar::-webkit-scrollbar {
width: 0;
background: transparent;
}
/* Opacity */
#photoprism .opacity-0 {

View file

@ -32,8 +32,12 @@ import Api from "common/api";
import Model from "./model";
export class Settings extends Model {
changed(key) {
return (this[key] !== this.__originalValues[key]);
changed(area, key) {
if (typeof this.__originalValues[area] === "undefined") {
return false;
}
return (this[area][key] !== this.__originalValues[area][key]);
}
load() {

View file

@ -135,7 +135,7 @@
:label="$gettext('Theme')"
color="secondary-dark"
background-color="secondary-light"
v-model="settings.theme"
v-model="settings.ui.theme"
hide-details box
class="input-theme"
></v-select>
@ -149,7 +149,7 @@
:label="$gettext('Language')"
color="secondary-dark"
background-color="secondary-light"
v-model="settings.language"
v-model="settings.ui.language"
hide-details box
class="input-language"
></v-select>
@ -409,7 +409,7 @@
this.settings.load();
},
onChange() {
const reload = this.settings.changed("language");
const reload = this.settings.changed("ui", "language");
if (reload) {
this.busy = true;

View file

@ -79,8 +79,8 @@ Vue.prototype.$isMobile = isMobile;
// Register Vuetify
Vue.use(Vuetify, {"theme": config.theme});
Vue.config.language = config.values.settings.language;
Settings.defaultLocale = config.values.settings.language;
Vue.config.language = config.values.settings.ui.language;
Settings.defaultLocale = Vue.config.language;
// Register other VueJS plugins
Vue.use(GetTextPlugin, {

View file

@ -1,8 +1,8 @@
import Albums from "share/albums.vue";
import AlbumPhotos from "share/photos.vue";
import {$gettext} from "common/vm";
const c = window.__CONFIG__;
const shareTitle = c.settings.share.title ? c.settings.share.title : c.siteAuthor ? c.siteAuthor : c.name;
export default [
{
@ -14,14 +14,14 @@ export default [
name: "albums",
path: "/s/:token",
component: Albums,
meta: {title: c.siteAuthor, auth: true},
meta: {title: shareTitle, auth: true},
props: {view: "album", staticFilter: {type: "album"}},
},
{
name: "album",
path: "/s/:token/:uid",
component: AlbumPhotos,
meta: {title: c.siteAuthor, auth: true},
meta: {title: shareTitle, auth: true},
},
{
path: "*", redirect: {name: "albums"},

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -41,7 +41,7 @@ describe("model/album", () => {
const values = {id: 5, Title: "Christmas 2019", Slug: "christmas-2019", UID: 66};
const album = new Album(values);
const result = album.thumbnailUrl("xyz");
assert.equal(result, "/api/v1/albums/66/t/static/xyz");
assert.equal(result, "/api/v1/albums/66/t/public/xyz");
});
it("should get created date string", () => {

View file

@ -59,7 +59,7 @@ describe("model/file", () => {
Type: "jpg",
Name: "1/2/IMG123.jpg"};
const file = new File(values);
assert.equal(file.thumbnailUrl("abc"), "/api/v1/t/54ghtfd/static/abc");
assert.equal(file.thumbnailUrl("abc"), "/api/v1/t/54ghtfd/public/abc");
const values2 = {
InstanceID: 5,
UID: "ABC123",
@ -84,7 +84,7 @@ describe("model/file", () => {
Type: "jpg",
Name: "1/2/IMG123.jpg"};
const file = new File(values);
assert.equal(file.getDownloadUrl("abc"), "/api/v1/dl/54ghtfd?t=1uhovi0e");
assert.equal(file.getDownloadUrl("abc"), "/api/v1/dl/54ghtfd?t=2lbh9x09");
});
it("should calculate size", () => {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -14,18 +14,18 @@ mock
describe("model/settings", () => {
it("should return if key was changed", () => {
const model = new Settings({"language": "de", "download": false});
assert.equal(model.changed("download"), false);
assert.equal(model.changed("language"), false);
it("should return if key was changed", () => {
const model = new Settings({"ui": {"language": "de", "scrollbar": false}});
assert.equal(model.changed("ui", "scrollbar"), false);
assert.equal(model.changed("ui", "language"), false);
});
it("should load settings", (done) => {
const model = new Settings();
it("should load settings", (done) => {
const model = new Settings({"ui": {"language": "de", "scrollbar": false}});
model.load().then(
(response) => {
assert.equal(response.download, true);
assert.equal(response.language, "de");
assert.equal(response["ui"]["scrollbar"], false);
assert.equal(response["ui"]["language"], "de");
done();
}
).catch(
@ -35,12 +35,12 @@ describe("model/settings", () => {
);
});
it("should save settings", (done) => {
const model = new Settings({"language": "en"});
it("should save settings", (done) => {
const model = new Settings({"ui": {"language": "de", "scrollbar": false}});
model.save().then(
(response) => {
assert.equal(response.download, true);
assert.equal(response.language, "en");
assert.equal(response["ui"]["scrollbar"], false);
assert.equal(response["ui"]["language"], "de");
done();
}
).catch(

View file

@ -206,7 +206,7 @@ describe("model/thumb", () => {
assert.equal(result[0].original_w, 500);
});
it("should return downlaload url", () => {
it("should return download url", () => {
const values = {
InstanceID: 5,
UID: "ABC123",
@ -214,7 +214,7 @@ describe("model/thumb", () => {
Type: "jpg",
Name: "1/2/IMG123.jpg"};
const file = new File(values);
assert.equal(Thumb.downloadUrl(file), "/api/v1/dl/54ghtfd?t=1uhovi0e");
assert.equal(Thumb.downloadUrl(file), "/api/v1/dl/54ghtfd?t=2lbh9x09");
const values2 = {
InstanceID: 5,
UID: "ABC123",
@ -232,7 +232,7 @@ describe("model/thumb", () => {
Type: "jpg",
Name: "1/2/IMG123.jpg"};
const file = new File(values);
assert.equal(Thumb.thumbnailUrl(file, "abc"), "/api/v1/t/54ghtfd/static/abc");
assert.equal(Thumb.thumbnailUrl(file, "abc"), "/api/v1/t/54ghtfd/public/abc");
const values2 = {
InstanceID: 5,
UID: "ABC123",

View file

@ -13,9 +13,9 @@ func TestGetSettings(t *testing.T) {
app, router, _ := NewApiTest()
GetSettings(router)
r := PerformRequest(app, "GET", "/api/v1/settings")
val := gjson.Get(r.Body.String(), "theme")
val := gjson.Get(r.Body.String(), "ui.theme")
assert.NotEmpty(t, val.String())
val2 := gjson.Get(r.Body.String(), "language")
val2 := gjson.Get(r.Body.String(), "ui.language")
assert.NotEmpty(t, val2.String())
assert.Equal(t, http.StatusOK, r.Code)
})
@ -26,23 +26,23 @@ func TestSaveSettings(t *testing.T) {
app, router, _ := NewApiTest()
GetSettings(router)
r := PerformRequest(app, "GET", "/api/v1/settings")
val := gjson.Get(r.Body.String(), "language")
val := gjson.Get(r.Body.String(), "ui.language")
assert.Equal(t, "en", val.String())
assert.Equal(t, http.StatusOK, r.Code)
SaveSettings(router)
r2 := PerformRequestWithBody(app, "POST", "/api/v1/settings", `{"language": "de"}`)
r2 := PerformRequestWithBody(app, "POST", "/api/v1/settings", `{"ui":{"language": "de"}}`)
assert.Equal(t, http.StatusOK, r2.Code)
r4 := PerformRequest(app, "GET", "/api/v1/settings")
val2 := gjson.Get(r4.Body.String(), "language")
val2 := gjson.Get(r4.Body.String(), "ui.language")
assert.Equal(t, "de", val2.String())
r3 := PerformRequestWithBody(app, "POST", "/api/v1/settings", `{"language": "en"}`)
r3 := PerformRequestWithBody(app, "POST", "/api/v1/settings", `{"ui":{"language": "en"}}`)
assert.Equal(t, http.StatusOK, r3.Code)
})
t.Run("bad request", func(t *testing.T) {
app, router, _ := NewApiTest()
SaveSettings(router)
r := PerformRequestWithBody(app, "POST", "/api/v1/settings", `{"language": 123}`)
r := PerformRequestWithBody(app, "POST", "/api/v1/settings", `{"ui":{"language":123}}`)
assert.Equal(t, http.StatusBadRequest, r.Code)
})
}

View file

@ -108,6 +108,10 @@ func (c *Config) Flags() (flags []string) {
flags = append(flags, "settings")
}
if !c.Settings().UI.Scrollbar {
flags = append(flags, "hide-scrollbar")
}
return flags
}
@ -121,10 +125,10 @@ func (c *Config) PublicConfig() ClientConfig {
result := ClientConfig{
Settings: Settings{
Language: settings.Language,
Theme: settings.Theme,
UI: settings.UI,
Maps: settings.Maps,
Features: settings.Features,
Share: settings.Share,
},
Flags: strings.Join(c.Flags(), " "),
Name: c.Name(),
@ -161,10 +165,10 @@ func (c *Config) GuestConfig() ClientConfig {
result := ClientConfig{
Settings: Settings{
Language: settings.Language,
Theme: settings.Theme,
UI: settings.UI,
Maps: settings.Maps,
Features: settings.Features,
Share: settings.Share,
},
Flags: "readonly public shared",
Name: c.Name(),

View file

@ -16,7 +16,14 @@ func (c *Config) SettingsHidden() bool {
return c.params.SettingsHidden
}
// TemplateSettings represents HTML template settings for the Web UI.
// UISettings represents user interface settings.
type UISettings struct {
Scrollbar bool `json:"scrollbar" yaml:"scrollbar"`
Theme string `json:"theme" yaml:"theme"`
Language string `json:"language" yaml:"language"`
}
// TemplateSettings represents template settings for the UI and messaging.
type TemplateSettings struct {
Default string `json:"default" yaml:"default"`
}
@ -31,7 +38,6 @@ type MapsSettings struct {
type FeatureSettings struct {
Upload bool `json:"upload" yaml:"upload"`
Download bool `json:"download" yaml:"download"`
Archive bool `json:"archive" yaml:"archive"`
Private bool `json:"private" yaml:"private"`
Review bool `json:"review" yaml:"review"`
Files bool `json:"files" yaml:"files"`
@ -39,6 +45,8 @@ type FeatureSettings struct {
Labels bool `json:"labels" yaml:"labels"`
Places bool `json:"places" yaml:"places"`
Edit bool `json:"edit" yaml:"edit"`
Archive bool `json:"archive" yaml:"archive"`
Delete bool `json:"delete" yaml:"delete"`
Share bool `json:"share" yaml:"share"`
Library bool `json:"library" yaml:"library"`
Import bool `json:"import" yaml:"import"`
@ -65,23 +73,31 @@ type StackSettings struct {
Name bool `json:"name" yaml:"name"`
}
// ShareSettings represents photo sharing settings.
type ShareSettings struct {
Title string `json:"title" yaml:"title"`
}
// Settings represents user settings for Web UI, indexing, and import.
type Settings struct {
Theme string `json:"theme" yaml:"theme"`
Language string `json:"language" yaml:"language"`
UI UISettings `json:"ui" yaml:"ui"`
Templates TemplateSettings `json:"templates" yaml:"templates"`
Maps MapsSettings `json:"maps" yaml:"maps"`
Features FeatureSettings `json:"features" yaml:"features"`
Import ImportSettings `json:"import" yaml:"import"`
Index IndexSettings `json:"index" yaml:"index"`
Stack StackSettings `json:"stack" yaml:"stack"`
Share ShareSettings `json:"share" yaml:"share"`
}
// NewSettings creates a new Settings instance.
func NewSettings() *Settings {
return &Settings{
Theme: "default",
Language: "en",
UI: UISettings{
Scrollbar: true,
Theme: "default",
Language: "en",
},
Templates: TemplateSettings{
Default: "index.tmpl",
},
@ -124,7 +140,7 @@ func NewSettings() *Settings {
// Propagate updates settings in other packages as needed.
func (s *Settings) Propagate() {
i18n.SetLocale(s.Language)
i18n.SetLocale(s.UI.Language)
}
// StackSequences tests if files should be stacked based on their file name prefix (sequential names).

View file

@ -17,50 +17,54 @@ func TestSettings_Load(t *testing.T) {
t.Run("existing filename", func(t *testing.T) {
c := NewSettings()
if err := c.Load("testdata/config.yml"); err != nil {
if err := c.Load("testdata/settings.yml"); err != nil {
t.Fatal(err)
}
assert.Equal(t, "lavendel", c.Theme)
assert.Equal(t, "english", c.Language)
assert.Equal(t, "onyx", c.UI.Theme)
assert.Equal(t, "de", c.UI.Language)
})
t.Run("not existing filename", func(t *testing.T) {
c := NewSettings()
err := c.Load("testdata/config123.yml")
err := c.Load("testdata/settings_123.yml")
assert.Error(t, err)
assert.Equal(t, "default", c.Theme)
assert.Equal(t, "en", c.Language)
assert.Equal(t, "default", c.UI.Theme)
assert.Equal(t, "en", c.UI.Language)
})
}
func TestSettings_Save(t *testing.T) {
t.Run("existing filename", func(t *testing.T) {
c := NewSettings()
c.Theme = "lavendel"
c.Language = "german"
assert.Equal(t, "lavendel", c.Theme)
assert.Equal(t, "german", c.Language)
assert.Equal(t, "default", c.UI.Theme)
assert.Equal(t, "en", c.UI.Language)
if err := c.Save("testdata/configEmpty.yml"); err != nil {
c.UI.Theme = "onyx"
c.UI.Language = "de"
assert.Equal(t, "onyx", c.UI.Theme)
assert.Equal(t, "de", c.UI.Language)
if err := c.Save("testdata/settings.yml"); err != nil {
t.Fatal(err)
}
})
t.Run("not existing filename", func(t *testing.T) {
c := NewSettings()
c.Theme = "lavendel"
c.Language = "german"
c.UI.Theme = "onyx"
c.UI.Language = "de"
assert.Equal(t, "lavendel", c.Theme)
assert.Equal(t, "german", c.Language)
assert.Equal(t, "onyx", c.UI.Theme)
assert.Equal(t, "de", c.UI.Language)
if err := c.Save("testdata/configEmpty123.yml"); err != nil {
if err := c.Save("testdata/settings_tmp.yml"); err != nil {
t.Fatal(err)
}
if err := os.Remove("testdata/configEmpty123.yml"); err != nil {
if err := os.Remove("testdata/settings_tmp.yml"); err != nil {
t.Fatal(err)
}
})

View file

@ -1,5 +1,7 @@
theme: lavendel
language: german
ui:
scrollbar: true
theme: onyx
language: de
templates:
default: index.tmpl
maps:
@ -8,7 +10,6 @@ maps:
features:
upload: true
download: true
archive: true
private: true
review: true
files: true
@ -16,6 +17,8 @@ features:
labels: true
places: true
edit: true
archive: true
delete: false
share: true
library: true
import: true
@ -31,3 +34,5 @@ stack:
uuid: true
meta: true
name: false
share:
title: ""