Frontend: Fix account management issues #225
Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
parent
b592e67dfa
commit
9f400a826c
|
@ -2,16 +2,59 @@
|
|||
<v-dialog lazy v-model="show" persistent max-width="500" class="p-account-edit-dialog" @keydown.esc="cancel">
|
||||
<v-card raised elevation="24">
|
||||
<v-card-title primary-title>
|
||||
<div>
|
||||
<h3 class="headline mb-0" v-if="scope === 'sharing'">Upload Settings</h3>
|
||||
<h3 class="headline mb-0" v-else-if="scope === 'sync'">Continuous Sync</h3>
|
||||
<v-layout row wrap v-if="scope === 'sharing'">
|
||||
<v-flex xs9>
|
||||
<h3 class="headline mb-0">Upload Settings</h3>
|
||||
</v-flex>
|
||||
<v-flex xs3 text-xs-right>
|
||||
<v-switch
|
||||
v-model="model.AccShare"
|
||||
color="success"
|
||||
:true-value="true"
|
||||
:false-value="false"
|
||||
:label="model.AccShare ? 'enabled' : 'disabled'"
|
||||
:disabled="model.AccType !== 'webdav'"
|
||||
class="mt-0"
|
||||
hide-details
|
||||
></v-switch>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
<v-layout row wrap v-else-if="scope === 'sync'">
|
||||
<v-flex xs9>
|
||||
<h3 class="headline mb-0">Sync Settings</h3>
|
||||
</v-flex>
|
||||
<v-flex xs3 text-xs-right>
|
||||
<v-switch
|
||||
v-model="model.AccSync"
|
||||
color="success"
|
||||
:true-value="true"
|
||||
:false-value="false"
|
||||
:label="model.AccSync ? 'enabled' : 'disabled'"
|
||||
:disabled="model.AccType !== 'webdav'"
|
||||
class="mt-0"
|
||||
hide-details
|
||||
></v-switch>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
<v-layout row wrap v-else>
|
||||
<v-flex xs10>
|
||||
<h3 class="headline mb-0">Edit Account</h3>
|
||||
</v-flex>
|
||||
<v-flex xs2 text-xs-right>
|
||||
<v-btn icon flat :ripple="false"
|
||||
class="action-remove mt-0"
|
||||
@click.stop.prevent="remove()">
|
||||
<v-icon color="remove">delete</v-icon>
|
||||
</v-btn>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
<h3 class="headline mb-0" v-else>Edit Account</h3>
|
||||
</div>
|
||||
</v-card-title>
|
||||
<v-container fluid class="pt-0 pb-2 pr-2 pl-2">
|
||||
<v-layout row wrap v-if="scope === 'sharing'">
|
||||
<v-flex xs12 class="pa-2">
|
||||
<v-text-field
|
||||
:disabled="!model.AccShare"
|
||||
hide-details
|
||||
:label="label.SharePath"
|
||||
placeholder=""
|
||||
|
@ -22,6 +65,7 @@
|
|||
</v-flex>
|
||||
<v-flex xs12 sm6 pa-2 class="input-share-size">
|
||||
<v-select
|
||||
:disabled="!model.AccShare"
|
||||
:label="label.ShareSize"
|
||||
hide-details
|
||||
color="secondary-dark"
|
||||
|
@ -33,6 +77,7 @@
|
|||
</v-flex>
|
||||
<v-flex xs12 sm6 class="pa-2">
|
||||
<v-select
|
||||
:disabled="!model.AccShare"
|
||||
:label="label.ShareExpires"
|
||||
hide-details
|
||||
color="secondary-dark"
|
||||
|
@ -44,6 +89,7 @@
|
|||
</v-flex>
|
||||
<v-flex xs12 sm6 class="pa-2">
|
||||
<v-checkbox
|
||||
:disabled="!model.AccShare"
|
||||
hide-details
|
||||
color="secondary-dark"
|
||||
:label="label.ShareExif"
|
||||
|
@ -52,6 +98,7 @@
|
|||
</v-flex>
|
||||
<v-flex xs12 sm6 class="pa-2">
|
||||
<v-checkbox
|
||||
:disabled="!model.AccShare"
|
||||
hide-details
|
||||
color="secondary-dark"
|
||||
:label="label.ShareSidecar"
|
||||
|
@ -60,8 +107,9 @@
|
|||
</v-flex>
|
||||
</v-layout>
|
||||
<v-layout row wrap v-else-if="scope === 'sync'">
|
||||
<v-flex xs12 class="pa-2">
|
||||
<v-flex xs12 sm6 class="px-2">
|
||||
<v-text-field
|
||||
:disabled="!model.AccSync"
|
||||
hide-details
|
||||
:label="label.SyncPath"
|
||||
placeholder=""
|
||||
|
@ -70,8 +118,21 @@
|
|||
required
|
||||
></v-text-field>
|
||||
</v-flex>
|
||||
<v-flex xs12 sm6 class="px-2">
|
||||
<v-select
|
||||
:disabled="!model.AccSync"
|
||||
:label="label.SyncInterval"
|
||||
hide-details
|
||||
color="secondary-dark"
|
||||
item-text="text"
|
||||
item-value="value"
|
||||
v-model="model.SyncInterval"
|
||||
:items="items.intervals">
|
||||
</v-select>
|
||||
</v-flex>
|
||||
<v-flex xs12 sm6 class="px-2">
|
||||
<v-checkbox
|
||||
:disabled="!model.AccSync"
|
||||
hide-details
|
||||
color="secondary-dark"
|
||||
:label="label.SyncDownload"
|
||||
|
@ -80,6 +141,7 @@
|
|||
</v-flex>
|
||||
<v-flex xs12 sm6 class="px-2">
|
||||
<v-checkbox
|
||||
:disabled="!model.AccSync"
|
||||
hide-details
|
||||
color="secondary-dark"
|
||||
:label="label.SyncUpload"
|
||||
|
@ -96,6 +158,7 @@
|
|||
</v-flex -->
|
||||
<v-flex xs12 sm6 class="px-2">
|
||||
<v-checkbox
|
||||
:disabled="!model.AccSync"
|
||||
hide-details
|
||||
color="secondary-dark"
|
||||
:label="label.SyncRaw"
|
||||
|
@ -104,6 +167,7 @@
|
|||
</v-flex>
|
||||
<v-flex xs12 sm6 class="px-2">
|
||||
<v-checkbox
|
||||
:disabled="!model.AccSync"
|
||||
hide-details
|
||||
color="secondary-dark"
|
||||
:label="label.SyncVideo"
|
||||
|
@ -112,6 +176,7 @@
|
|||
</v-flex>
|
||||
<v-flex xs12 sm6 class="px-2">
|
||||
<v-checkbox
|
||||
:disabled="!model.AccSync"
|
||||
hide-details
|
||||
color="secondary-dark"
|
||||
:label="label.SyncSidecar"
|
||||
|
@ -170,6 +235,17 @@
|
|||
required
|
||||
></v-text-field>
|
||||
</v-flex>
|
||||
<v-flex xs12 sm6 pa-2 class="input-account-type">
|
||||
<v-select
|
||||
:label="label.AccType"
|
||||
hide-details
|
||||
color="secondary-dark"
|
||||
item-text="text"
|
||||
item-value="value"
|
||||
v-model="model.AccType"
|
||||
:items="items.types">
|
||||
</v-select>
|
||||
</v-flex>
|
||||
<!-- v-flex xs12 sm6 class="pa-2">
|
||||
<v-text-field
|
||||
hide-details
|
||||
|
@ -184,35 +260,11 @@
|
|||
<v-layout row wrap>
|
||||
<v-flex xs12 text-xs-right class="pt-3">
|
||||
<v-btn @click.stop="cancel" depressed color="grey lighten-3"
|
||||
class="action-cancel">
|
||||
class="action-cancel hidden-xs-only">
|
||||
<span>{{ label.cancel }}</span>
|
||||
</v-btn>
|
||||
<v-btn @click.stop="disable('AccShare')" depressed color="grey lighten-3"
|
||||
class="action-disable" v-if="model.AccShare && scope === 'sharing'">
|
||||
<span>{{ label.disable }}</span>
|
||||
</v-btn>
|
||||
<v-btn @click.stop="disable('AccSync')" depressed color="grey lighten-3"
|
||||
class="action-disable" v-if="model.AccSync && scope === 'sync'">
|
||||
<span>{{ label.disable }}</span>
|
||||
</v-btn>
|
||||
<v-btn color="blue-grey lighten-2" depressed dark @click.stop="enable('AccShare')"
|
||||
class="action-enable" v-if="!model.AccShare && scope === 'sharing'">
|
||||
<span>{{ label.enable }}</span>
|
||||
</v-btn>
|
||||
<v-btn color="blue-grey lighten-2" depressed dark @click.stop="enable('AccSync')"
|
||||
class="action-enable" v-if="!model.AccSync && scope === 'sync'">
|
||||
<span>{{ label.enable }}</span>
|
||||
</v-btn>
|
||||
<v-btn color="blue-grey lighten-2" depressed dark @click.stop="confirm"
|
||||
class="action-confirm" v-if="model.AccShare && scope === 'sharing'">
|
||||
<span>{{ label.save }}</span>
|
||||
</v-btn>
|
||||
<v-btn color="blue-grey lighten-2" depressed dark @click.stop="confirm"
|
||||
class="action-confirm" v-if="model.AccSync && scope === 'sync'">
|
||||
<span>{{ label.save }}</span>
|
||||
</v-btn>
|
||||
<v-btn color="blue-grey lighten-2" depressed dark @click.stop="confirm"
|
||||
class="action-confirm" v-if="scope === 'account'">
|
||||
<v-btn color="blue-grey lighten-2" depressed dark @click.stop="save"
|
||||
class="action-save">
|
||||
<span>{{ label.save }}</span>
|
||||
</v-btn>
|
||||
</v-flex>
|
||||
|
@ -243,15 +295,38 @@
|
|||
items: {
|
||||
thumbs: thumbs,
|
||||
sizes: this.sizes(thumbs),
|
||||
types: [
|
||||
{"value": "web", "text": "Web"},
|
||||
{"value": "webdav", "text": "WebDAV / Nextcloud"},
|
||||
{"value": "facebook", "text": "Facebook"},
|
||||
{"value": "twitter", "text": "Twitter"},
|
||||
{"value": "flickr", "text": "Flickr"},
|
||||
{"value": "instagram", "text": "Instagram"},
|
||||
{"value": "eyeem", "text": "EyeEm"},
|
||||
{"value": "telegram", "text": "Telegram"},
|
||||
{"value": "whatsapp", "text": "WhatsApp"},
|
||||
{"value": "gphotos", "text": "Google Photos"},
|
||||
{"value": "gdrive", "text": "Google Drive"},
|
||||
{"value": "onedrive", "text": "Microsoft OneDrive"},
|
||||
],
|
||||
intervals: [
|
||||
{"value": 0, "text": "Never"},
|
||||
{"value": 3600, "text": "1 hour"},
|
||||
{"value": 3600 * 4, "text": "4 hours"},
|
||||
{"value": 3600 * 12, "text": "12 hours"},
|
||||
{"value": 86400, "text": "Daily"},
|
||||
{"value": 86400 * 2, "text": "Every two days"},
|
||||
{"value": 86400 * 7, "text": "Once a week"},
|
||||
],
|
||||
expires: [
|
||||
{ "value": 0, "text": "Never" },
|
||||
{ "value": 86400, "text": "After 1 day" },
|
||||
{ "value": 86400 * 3, "text": "After 3 days" },
|
||||
{ "value": 86400 * 7, "text": "After 7 days" },
|
||||
{ "value": 86400 * 14, "text": "After two weeks" },
|
||||
{ "value": 86400 * 31, "text": "After one month" },
|
||||
{ "value": 86400 * 60, "text": "After two months" },
|
||||
{ "value": 86400 * 365, "text": "After one year" },
|
||||
{"value": 0, "text": "Never"},
|
||||
{"value": 86400, "text": "After 1 day"},
|
||||
{"value": 86400 * 3, "text": "After 3 days"},
|
||||
{"value": 86400 * 7, "text": "After 7 days"},
|
||||
{"value": 86400 * 14, "text": "After two weeks"},
|
||||
{"value": 86400 * 31, "text": "After one month"},
|
||||
{"value": 86400 * 60, "text": "After two months"},
|
||||
{"value": 86400 * 365, "text": "After one year"},
|
||||
],
|
||||
},
|
||||
readonly: this.$config.getValue("readonly"),
|
||||
|
@ -268,18 +343,19 @@
|
|||
pass: this.$gettext("Password"),
|
||||
owner: this.$gettext("Owner"),
|
||||
apiKey: this.$gettext("API Key"),
|
||||
SharePath: this.$gettext("Folder"),
|
||||
AccType: this.$gettext("Type"),
|
||||
SharePath: this.$gettext("Default Directory"),
|
||||
ShareSize: this.$gettext("Size"),
|
||||
ShareExpires: this.$gettext("Expires"),
|
||||
ShareExif: this.$gettext("Include metadata"),
|
||||
ShareSidecar: this.$gettext("Include sidecar files"),
|
||||
SyncPath: this.$gettext("Folder"),
|
||||
SyncPath: this.$gettext("Remote Directory"),
|
||||
SyncInterval: this.$gettext("Interval"),
|
||||
SyncStart: this.$gettext("Start"),
|
||||
SyncDownload: this.$gettext("Download new files"),
|
||||
SyncUpload: this.$gettext("Upload local files"),
|
||||
SyncDelete: this.$gettext("Remote delete"),
|
||||
SyncRaw: this.$gettext("Sync RAW images"),
|
||||
SyncRaw: this.$gettext("Sync RAW photos"),
|
||||
SyncVideo: this.$gettext("Sync videos"),
|
||||
SyncSidecar: this.$gettext("Sync sidecar files"),
|
||||
}
|
||||
|
@ -289,6 +365,9 @@
|
|||
cancel() {
|
||||
this.$emit('cancel');
|
||||
},
|
||||
remove() {
|
||||
this.$emit('remove');
|
||||
},
|
||||
confirm() {
|
||||
this.model.AccShare = true;
|
||||
this.save();
|
||||
|
@ -311,12 +390,12 @@
|
|||
},
|
||||
sizes(thumbs) {
|
||||
const result = [
|
||||
{"text" : this.$gettext("Original"), "value": ""}
|
||||
{"text": this.$gettext("Original"), "value": ""}
|
||||
];
|
||||
|
||||
for(let i in thumbs) {
|
||||
for (let i in thumbs) {
|
||||
const s = thumbs[i];
|
||||
result.push({"text" : s["Width"] + 'x' + s["Height"], "value": s["Name"]});
|
||||
result.push({"text": s["Width"] + 'x' + s["Height"], "value": s["Name"]});
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
@ -30,8 +30,8 @@
|
|||
<v-icon v-if="props.item.AccSync" color="secondary-dark">sync</v-icon>
|
||||
<v-icon v-else color="secondary-dark">sync_disabled</v-icon>
|
||||
</v-btn></td>
|
||||
<td class="hidden-md-and-down">{{ formatDate(props.item.SyncedAt) }}</td>
|
||||
<!-- td class="text-xs-right" nowrap>
|
||||
<td class="hidden-sm-and-down">{{ formatDate(props.item.SyncedAt) }}</td>
|
||||
<td class="hidden-xs-only text-xs-right" nowrap>
|
||||
<v-btn icon small flat :ripple="false"
|
||||
class="p-account-remove"
|
||||
@click.stop.prevent="remove(props.item)">
|
||||
|
@ -42,7 +42,7 @@
|
|||
@click.stop.prevent="edit(props.item)">
|
||||
<v-icon color="secondary-dark">edit</v-icon>
|
||||
</v-btn>
|
||||
</td -->
|
||||
</td>
|
||||
</template>
|
||||
</v-data-table>
|
||||
<v-container fluid>
|
||||
|
@ -62,7 +62,7 @@
|
|||
@confirm="onAdded"></p-account-add-dialog>
|
||||
<p-account-remove-dialog :show="dialog.remove" :model="model" @cancel="onCancel('remove')"
|
||||
@confirm="onRemoved"></p-account-remove-dialog>
|
||||
<p-account-edit-dialog :show="dialog.edit" :model="model" :scope="editScope" @cancel="onCancel('edit')"
|
||||
<p-account-edit-dialog :show="dialog.edit" :model="model" :scope="editScope" @remove="remove(model)" @cancel="onCancel('edit')"
|
||||
@confirm="onEdited"></p-account-edit-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -94,8 +94,8 @@
|
|||
{text: this.$gettext('Name'), value: 'AccName', sortable: false, align: 'left'},
|
||||
{text: this.$gettext('Share'), value: 'AccShare', sortable: false, align: 'center'},
|
||||
{text: this.$gettext('Sync'), value: 'AccSync', sortable: false, align: 'center'},
|
||||
{text: this.$gettext('Synced'), value: 'SyncedAt', sortable: false, class: 'hidden-md-and-down', align: 'left'},
|
||||
//{text: '', value: '', sortable: false, align: 'right'},
|
||||
{text: this.$gettext('Synced'), value: 'SyncedAt', sortable: false, class: 'hidden-sm-and-down', align: 'left'},
|
||||
{text: '', value: '', sortable: false, class: 'hidden-xs-only', align: 'right'},
|
||||
],
|
||||
};
|
||||
},
|
||||
|
@ -113,13 +113,15 @@
|
|||
Account.search({count: 100}).then(r => this.results = r.models);
|
||||
},
|
||||
remove(model) {
|
||||
this.model = model;
|
||||
this.model = model.clone();
|
||||
|
||||
this.dialog.edit = false;
|
||||
this.dialog.remove = true;
|
||||
},
|
||||
onRemoved() {
|
||||
this.dialog.remove = false;
|
||||
this.model = {};
|
||||
this.load();
|
||||
},
|
||||
editSharing(model) {
|
||||
this.model = model.clone();
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/ulule/deepcopier"
|
||||
)
|
||||
|
||||
|
@ -47,11 +48,7 @@ type Account struct {
|
|||
func CreateAccount(form form.Account, db *gorm.DB) (model *Account, err error) {
|
||||
model = &Account{}
|
||||
|
||||
if err := deepcopier.Copy(model).From(form); err != nil {
|
||||
return model, err
|
||||
}
|
||||
|
||||
err = db.Save(&model).Error
|
||||
err = model.Save(form, db)
|
||||
|
||||
return model, err
|
||||
}
|
||||
|
@ -62,6 +59,20 @@ func (m *Account) Save(form form.Account, db *gorm.DB) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if m.AccType != string(service.TypeWebDAV) {
|
||||
// TODO: Only WebDAV supported at the moment
|
||||
m.AccShare = false
|
||||
m.AccSync = false
|
||||
}
|
||||
|
||||
if m.SharePath == "" {
|
||||
m.SharePath = "/"
|
||||
}
|
||||
|
||||
if m.SyncPath == "" {
|
||||
m.SyncPath = "/"
|
||||
}
|
||||
|
||||
return db.Save(m).Error
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
var log = event.Log
|
||||
|
@ -27,6 +28,10 @@ const (
|
|||
TypeFacebook Type = "facebook"
|
||||
TypeTwitter Type = "twitter"
|
||||
TypeFlickr Type = "flickr"
|
||||
TypeInstagram Type = "instagram"
|
||||
TypeEyeEm Type = "eyeem"
|
||||
TypeTelegram Type = "telegram"
|
||||
TypeWhatsApp Type = "whatsapp"
|
||||
TypeGooglePhotos Type = "gphotos"
|
||||
TypeGoogleDrive Type = "gdrive"
|
||||
TypeOneDrive Type = "onedrive"
|
||||
|
@ -52,6 +57,10 @@ var Heuristics = []Heuristic{
|
|||
{TypeFacebook, []string{"facebook.com", "www.facebook.com"}, []string{}, "GET"},
|
||||
{TypeTwitter, []string{"twitter.com"}, []string{}, "GET"},
|
||||
{TypeFlickr, []string{"flickr.com", "www.flickr.com"}, []string{}, "GET"},
|
||||
{TypeInstagram, []string{"instagram.com", "www.instagram.com"}, []string{}, "GET"},
|
||||
{TypeEyeEm, []string{"eyeem.com", "www.eyeem.com"}, []string{}, "GET"},
|
||||
{TypeTelegram, []string{"web.telegram.org", "www.telegram.org", "telegram.org"}, []string{}, "GET"},
|
||||
{TypeWhatsApp, []string{"web.whatsapp.com", "www.whatsapp.com", "whatsapp.com"}, []string{}, "GET"},
|
||||
{TypeOneDrive, []string{"onedrive.live.com"}, []string{}, "GET"},
|
||||
{TypeGoogleDrive, []string{"drive.google.com"}, []string{}, "GET"},
|
||||
{TypeGooglePhotos, []string{"photos.google.com"}, []string{}, "GET"},
|
||||
|
@ -153,7 +162,13 @@ func Discover(rawUrl, user, pass string) (result Account, err error) {
|
|||
|
||||
if serviceUrl := h.Discover(u.String(), result.AccUser); serviceUrl != nil {
|
||||
serviceUrl.User = nil
|
||||
|
||||
if w := txt.Keywords(serviceUrl.Host); len(w) > 0 {
|
||||
result.AccName = strings.Title(w[0])
|
||||
} else {
|
||||
result.AccName = serviceUrl.Host
|
||||
}
|
||||
|
||||
result.AccType = string(h.ServiceType)
|
||||
result.AccURL = serviceUrl.String()
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ func TestDiscover(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.Equal(t, "webdav-dummy", r.AccName)
|
||||
assert.Equal(t, "Webdav", r.AccName)
|
||||
assert.Equal(t, "webdav", r.AccType)
|
||||
assert.Equal(t, "http://webdav-dummy/", r.AccURL)
|
||||
assert.Equal(t, "admin", r.AccUser)
|
||||
|
@ -28,7 +28,7 @@ func TestDiscover(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.Equal(t, "webdav-dummy", r.AccName)
|
||||
assert.Equal(t, "Webdav", r.AccName)
|
||||
assert.Equal(t, "webdav", r.AccType)
|
||||
assert.Equal(t, "http://webdav-dummy/", r.AccURL)
|
||||
assert.Equal(t, "admin", r.AccUser)
|
||||
|
@ -42,7 +42,7 @@ func TestDiscover(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.Equal(t, "dl.photoprism.org", r.AccName)
|
||||
assert.Equal(t, "Photoprism", r.AccName)
|
||||
assert.Equal(t, "web", r.AccType)
|
||||
assert.Equal(t, "https://dl.photoprism.org/fixtures/testdata/import/", r.AccURL)
|
||||
assert.Equal(t, "", r.AccUser)
|
||||
|
@ -56,7 +56,7 @@ func TestDiscover(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.Equal(t, "www.facebook.com", r.AccName)
|
||||
assert.Equal(t, "Facebook", r.AccName)
|
||||
assert.Equal(t, "facebook", r.AccType)
|
||||
assert.Equal(t, "https://www.facebook.com/ob.boris.palmer", r.AccURL)
|
||||
assert.Equal(t, "", r.AccUser)
|
||||
|
|
Loading…
Reference in a new issue