Stub for settings page & api

Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
Michael Mayer 2019-11-12 04:34:37 +01:00
parent 8e1d872a7b
commit 0becb8a92d
15 changed files with 1790 additions and 936 deletions

File diff suppressed because it is too large Load diff

View file

@ -14,34 +14,34 @@
"test-firefox": "testcafe firefox:headless --selector-timeout 5000 -S -s tests/screenshots tests/acceptance"
},
"dependencies": {
"@babel/cli": "^7.6.0",
"@babel/core": "^7.6.0",
"@babel/plugin-transform-runtime": "^7.6.0",
"@babel/polyfill": "^7.6.0",
"@babel/preset-env": "^7.6.0",
"@babel/register": "^7.6.0",
"@fortawesome/fontawesome-free": "^5.10.2",
"@types/leaflet": "^1.5.1",
"@babel/cli": "^7.7.0",
"@babel/core": "^7.7.2",
"@babel/plugin-transform-runtime": "^7.6.2",
"@babel/polyfill": "^7.7.0",
"@babel/preset-env": "^7.7.1",
"@babel/register": "^7.7.0",
"@fortawesome/fontawesome-free": "^5.11.2",
"@types/leaflet": "^1.5.5",
"acorn": "^6.3.0",
"ajv": "^6.10.2",
"autoprefixer": "^9.6.1",
"autoprefixer": "^9.7.1",
"axios": "^0.19.0",
"axios-mock-adapter": "^1.17.0",
"babel-eslint": "^10.0.3",
"babel-loader": "^8.0.6",
"babel-plugin-istanbul": "^5.2.0",
"browserslist": "^4.7.0",
"browserslist": "^4.7.2",
"chai": "^4.2.0",
"chalk": "^2.4.2",
"chart.js": "^2.5.0",
"chrome-finder": "^1.0.5",
"chart.js": "^2.9.2",
"chrome-finder": "^1.0.6",
"clean-webpack-plugin": "^3.0.0",
"connect-history-api-fallback": "^1.3.0",
"copy-webpack-plugin": "^5.0.4",
"copy-webpack-plugin": "^5.0.5",
"cross-env": "^5.2.1",
"css-loader": "^2.1.1",
"cssnano": "^4.1.10",
"eslint": "^6.4.0",
"eslint": "^6.6.0",
"eslint-config-standard": "^13.0.1",
"eslint-formatter-pretty": "^2.1.1",
"eslint-friendly-formatter": "^4.0.1",
@ -57,7 +57,7 @@
"html-webpack-plugin": "^3.2.0",
"http-proxy-middleware": "^0.19.1",
"inject-loader": "^4.0.1",
"karma": "^4.3.0",
"karma": "^4.4.1",
"karma-chrome-launcher": "^3.1.0",
"karma-coverage-istanbul-reporter": "^2.1.0",
"karma-htmlfile-reporter": "^0.3.8",
@ -65,16 +65,16 @@
"karma-verbose-reporter": "^0.0.6",
"karma-webpack": "^4.0.2",
"leaflet": "^1.5.1",
"luxon": "^1.17.3",
"luxon": "^1.21.1",
"material-design-icons-iconfont": "^5.0.1",
"mini-css-extract-plugin": "^0.7.0",
"mocha": "^6.2.0",
"moment-timezone": "^0.5.26",
"mocha": "^6.2.2",
"moment-timezone": "^0.5.27",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"ora": "^3.4.0",
"photoswipe": "^4.1.3",
"pluralize": "^8.0.0",
"postcss": "^7.0.18",
"postcss": "^7.0.21",
"postcss-browser-reporter": "^0.6.0",
"postcss-import": "^12.0.1",
"postcss-loader": "^3.0.0",
@ -83,17 +83,18 @@
"postcss-url": "^8.0.0",
"pubsub-js": "^1.7.0",
"puppeteer-core": "^1.20.0",
"resolve-url-loader": "^3.1.0",
"resolve-url-loader": "^3.1.1",
"sass-loader": "^7.3.1",
"sinon": "^7.4.2",
"sinon": "^7.5.0",
"style-loader": "^0.23.1",
"sugarss": "^2.0.0",
"svg-url-loader": "^2.3.3",
"tar": "^4.4.10",
"tar": "^4.4.13",
"truncate": "^2.1.0",
"url-loader": "^1.1.2",
"vue": "^2.6.10",
"vue-fullscreen": "^2.1.5",
"vue-gettext": "^2.1.6",
"vue-infinite-scroll": "^2.0.2",
"vue-loader": "^14.2.4",
"vue-luxon": "^0.7.0",
@ -103,10 +104,10 @@
"vue2-filters": "^0.6.1",
"vue2-leaflet": "^2.2.1",
"vuelidate": "^0.7.4",
"vuetify": "^1.5.18",
"webpack": "^4.40.2",
"webpack-bundle-analyzer": "^3.5.0",
"webpack-cli": "^3.3.8",
"vuetify": "^1.5.21",
"webpack": "^4.41.2",
"webpack-bundle-analyzer": "^3.6.0",
"webpack-cli": "^3.3.10",
"webpack-hot-middleware": "^2.25.0",
"webpack-md5-hash": "0.0.6",
"webpack-merge": "^4.2.2"

View file

@ -17,6 +17,8 @@ import VueLuxon from "vue-luxon";
import VueInfiniteScroll from "vue-infinite-scroll";
import VueFullscreen from "vue-fullscreen";
import VueFilters from "vue2-filters";
import GetTextPlugin from "vue-gettext";
import translations from "./i18n/translations.json";
import { Settings } from "luxon";
// Initialize helpers
@ -49,9 +51,11 @@ Vue.use(Vuetify, {
},
});
Settings.defaultLocale = "en";
Vue.config.language = "en";
Settings.defaultLocale = Vue.config.language;
// Register other VueJS plugins
Vue.use(GetTextPlugin, {translations: translations, silent: false, defaultLanguage: Vue.config.language});
Vue.use(VueLuxon);
Vue.use(VueInfiniteScroll);
Vue.use(VueFullscreen);

View file

@ -0,0 +1,5 @@
{
"en": {
"theme": "Theme"
}
}

View file

@ -0,0 +1,48 @@
import Api from "common/api";
class Settings {
constructor(values) {
this.__originalValues = {};
if (values) {
this.setValues(values);
}
}
setValues(values) {
if(!values) return;
for(let key in values) {
if(values.hasOwnProperty(key) && key !== "__originalValues") {
this[key] = values[key];
this.__originalValues[key] = values[key];
}
}
return this;
}
getValues() {
const result = {};
for(let key in this.__originalValues) {
if(this.__originalValues.hasOwnProperty(key) && key !== "__originalValues") {
result[key] = this[key];
}
}
return result;
}
load() {
return Api.get("settings").then((response) => {
return Promise.resolve(this.setValues(response.data));
});
}
save() {
return Api.post("settings", this.getValues()).then((response) => Promise.resolve(this.setValues(response.data)));
}
}
export default Settings;

View file

@ -13,7 +13,7 @@
<v-btn
:disabled="busy"
color="blue-grey"
class="white--text ml-0"
class="white--text ml-0 mt-2"
depressed
@click.stop="startImport()"
>

View file

@ -13,7 +13,7 @@
<v-btn
:disabled="busy"
color="blue-grey"
class="white--text ml-0"
class="white--text ml-0 mt-2"
depressed
@click.stop="startIndexing()"
>

View file

@ -16,7 +16,7 @@
<v-btn
:disabled="busy"
color="blue-grey"
class="white--text ml-0"
class="white--text ml-0 mt-2"
depressed
@click.stop="uploadDialog()"
>
@ -48,7 +48,7 @@
},
methods: {
submit() {
console.log("SUBMIT");
// console.log("SUBMIT");
},
uploadDialog() {
this.$refs.upload.click();

View file

@ -7,8 +7,10 @@
</v-toolbar>
<v-container class="pt-5">
<p>Please enter the admin password to proceed:</p>
<v-form ref="form" autocomplete="off" class="p-form-login" dense>
<p class="subheading">
<span>Please enter the admin password to proceed...</span>
</p>
<v-form ref="form" autocomplete="off" class="p-form-login" @submit.prevent="login" dense>
<v-text-field
label="Password"
color="grey"

View file

@ -1,29 +1,38 @@
<template>
<div>
<v-toolbar flat color="blue-grey lighten-4">
<v-toolbar-title>Not implemented yet</v-toolbar-title>
<v-spacer></v-spacer>
</v-toolbar>
<v-container>
<p>
Issues labeled <a href="https://github.com/photoprism/photoprism/labels/help%20wanted">help wanted</a> /
<a href="https://github.com/photoprism/photoprism/labels/easy">easy</a> can be good (first)
contributions.
Our <a href="https://github.com/photoprism/photoprism/wiki">Developer Guide</a> contains all information
necessary to get you started.
</p>
</v-container>
<div class="p-page p-page-settings">
<v-tabs
v-model="active"
flat
grow
color="blue-grey lighten-4"
slider-color="blue-grey darken-1"
height="64"
>
<v-tab id="tab-upload">
General
</v-tab>
<v-tab-item>
<p-tab-general></p-tab-general>
</v-tab-item>
</v-tabs>
</div>
</template>
<script>
import tabGeneral from "pages/settings/general.vue";
export default {
name: 'todo',
data() {
return {};
name: 'p-page-settings',
components: {
'p-tab-general': tabGeneral,
},
data() {
return {
readonly: this.$config.getValue("readonly"),
active: 0,
}
},
methods: {
},
methods: {}
};
</script>

View file

@ -0,0 +1,68 @@
<template>
<div class="p-tab p-tab-general">
<v-container fluid>
<v-form ref="form" class="p-form-settings" lazy-validation @submit.prevent="save" dense>
<v-layout wrap align-center>
<v-flex xs12 sm6 class="pr-3">
<v-select
:items="languages"
label="Language"
color="blue-grey"
value="en"
flat
></v-select>
</v-flex>
<v-flex xs12 sm6 class="pr-3">
<v-select
:items="themes"
label="Theme"
color="blue-grey"
value=""
flat
></v-select>
</v-flex>
</v-layout>
<v-btn color="blue-grey"
class="white--text ml-0 mt-2"
depressed
@click.stop="save">
Save
<v-icon right dark>save</v-icon>
</v-btn>
</v-form>
</v-container>
</div>
</template>
<script>
import Settings from "model/settings";
export default {
name: 'p-tab-general',
data() {
return {
readonly: this.$config.getValue("readonly"),
active: 0,
settings: new Settings(),
list: {},
themes: [{text: "Default", value: ""}],
languages: [{text: "English", value: "en"}],
};
},
methods: {
load() {
this.settings.load().then((r) => { this.list = r.getValues(); });
},
save() {
this.settings.save().then(() => {
this.$alert.info("Settings saved");
})
},
},
created() {
this.load();
},
};
</script>

26
internal/api/settings.go Normal file
View file

@ -0,0 +1,26 @@
package api
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/config"
)
// GET /api/v1/settings
func GetSettings(router *gin.RouterGroup, conf *config.Config) {
router.GET("/settings", func(c *gin.Context) {
result := conf.Settings()
c.JSON(http.StatusOK, result)
})
}
// POST /api/v1/settings
func SaveSettings(router *gin.RouterGroup, conf *config.Config) {
router.POST("/settings", func(c *gin.Context) {
// TODO
c.JSON(http.StatusOK, gin.H{"message": "saved"})
})
}

View file

@ -542,6 +542,7 @@ func (c *Config) Init(ctx context.Context) error {
return c.connectToDatabase(ctx)
}
// Shutdown closes open database connections.
func (c *Config) Shutdown() {
if err := c.CloseDb(); err != nil {
log.Errorf("could not close database connection: %s", err)
@ -549,3 +550,8 @@ func (c *Config) Shutdown() {
log.Info("closed database connection")
}
}
// Settings returns the current user settings.
func (c *Config) Settings() *Settings {
return &Settings{}
}

View file

@ -0,0 +1,6 @@
package config
type Settings struct {
Theme string `json:"theme" yaml:"theme" flag:"theme"`
Language string `json:"language" yaml:"language" flag:"language"`
}

View file

@ -46,6 +46,9 @@ func registerRoutes(router *gin.Engine, conf *config.Config) {
api.DislikeAlbum(v1, conf)
api.AlbumThumbnail(v1, conf)
api.CreateAlbum(v1, conf)
api.GetSettings(v1, conf)
api.SaveSettings(v1, conf)
}
// Default HTML page (client-side routing implemented via Vue.js)