1
0
mirror of https://github.com/bpatrik/pigallery2.git synced 2025-01-12 04:23:09 +02:00

improving setting ui

This commit is contained in:
Patrik J. Braun 2019-12-15 10:52:56 +01:00
parent cd06bc00ec
commit 95b06ffc63
36 changed files with 286 additions and 158 deletions

View File

@ -183,7 +183,7 @@ export class GalleryMWs {
if (!(req.resultPipe)) {
return next();
}
const fullMediaPath = path.join(ProjectPath.ImageFolder, req.resultPipe);
const fullMediaPath: string = req.resultPipe;
if (fs.statSync(fullMediaPath).isDirectory()) {
return next();

View File

@ -6,9 +6,13 @@ import {Config} from '../../../common/config/private/Config';
export class PhotoConverterMWs {
public static async convertPhoto(req: Request, res: Response, next: NextFunction) {
if (!(req.resultPipe || Config.Server.Media.Photo.converting.enabled === false)) {
if (!req.resultPipe) {
return next();
}
// if conversion is not enabled redirect, so browser can cache the full
if (Config.Client.Media.Photo.Converting.enabled === false) {
return res.redirect(req.originalUrl.slice(0, -1 * '\\bestFit'.length));
}
const fullMediaPath = req.resultPipe;
const convertedVideo = PhotoProcessing.generateConvertedFileName(fullMediaPath);
@ -19,12 +23,12 @@ export class PhotoConverterMWs {
return next();
}
if (Config.Server.Media.Photo.converting.onTheFly) {
if (Config.Server.Media.Photo.Converting.onTheFly) {
req.resultPipe = await PhotoProcessing.convertPhoto(fullMediaPath,
Config.Server.Media.Photo.converting.resolution);
Config.Server.Media.Photo.Converting.resolution);
}
return next();
// not converted and won't be now
return res.redirect(req.originalUrl.slice(0, -1 * '\\bestFit'.length));
}
}

View File

@ -108,7 +108,7 @@ export class PhotoProcessing {
public static generateConvertedFileName(photoPath: string): string {
const extension = path.extname(photoPath);
const file = path.basename(photoPath, extension);
const postfix = Config.Server.Media.Photo.converting.resolution;
const postfix = Config.Server.Media.Photo.Converting.resolution;
return path.join(ProjectPath.TranscodedFolder,
ProjectPath.getRelativePathToImages(path.dirname(photoPath)), file +
'_' + postfix + '.jpg');

View File

@ -21,7 +21,7 @@ export class PhotoConvertingTask extends FileTask<string> {
}
public get Supported(): boolean {
return Config.Server.Media.Photo.converting.enabled === true;
return Config.Server.Media.Photo.Converting.enabled === true;
}
protected async processDirectory(directory: DirectoryDTO): Promise<string[]> {
@ -40,7 +40,7 @@ export class PhotoConvertingTask extends FileTask<string> {
}
protected async processFile(file: string): Promise<void> {
await PhotoProcessing.generateThumbnail(file, Config.Server.Media.Photo.converting.resolution, ThumbnailSourceType.Photo, false);
await PhotoProcessing.generateThumbnail(file, Config.Server.Media.Photo.Converting.resolution, ThumbnailSourceType.Photo, false);
}

View File

@ -15,9 +15,9 @@ export module ServerConfig {
}
export enum ThumbnailProcessingLib {
sharp = 3,
Jimp = 1,
gm = 2,
sharp = 3
}
export interface MySQLConfig {
@ -98,8 +98,7 @@ export module ServerConfig {
export interface PhotoConfig {
converting: {
enabled: boolean;
Converting: {
onTheFly: boolean;
resolution: resolutionType
};

View File

@ -20,8 +20,7 @@ export class PrivateConfigDefaultsClass extends PublicConfigClass implements IPr
personFaceMargin: 0.6
},
Photo: {
converting: {
enabled: true,
Converting: {
onTheFly: true,
resolution: 1080
}

View File

@ -68,9 +68,16 @@ export module ClientConfig {
enabled: boolean;
}
export interface PhotoConfig {
Converting: {
enabled: boolean;
};
}
export interface MediaConfig {
Thumbnail: ThumbnailConfig;
Video: VideoConfig;
Photo: PhotoConfig;
}
export interface MetaFileConfig {
@ -115,6 +122,11 @@ export class PublicConfigClass {
Video: {
enabled: true
},
Photo: {
Converting: {
enabled: true
}
},
Thumbnail: {
concurrentThumbnailGenerations: 1,
thumbnailSizes: [160, 240, 480],

View File

@ -1,5 +1,6 @@
export interface BasicConfigDTO {
imagesFolder: string;
tempFolder: string;
publicUrl: string;
urlBase: string;
applicationTitle: string;

View File

@ -68,5 +68,7 @@ const ROUTES: Routes = [
{path: '**', redirectTo: '/login', pathMatch: 'full'}
];
export const appRoutes: ModuleWithProviders = RouterModule.forRoot(ROUTES);
export const appRoutes: ModuleWithProviders = RouterModule.forRoot(ROUTES, {
anchorScrolling: 'enabled'
});

View File

@ -1,7 +1,11 @@
.version{
.version {
color: #6c757d;
}
.version:hover{
.version:hover {
text-decoration: underline;
}
*:focus {
outline: none;
}

View File

@ -1,5 +1,5 @@
<app-frame>
<div body class="container">
<div body class="container-fluid">
<div class="card mb-4" *ngIf="notificationService.notifications.length>0">
<h5 class="card-header" i18n>
@ -23,8 +23,7 @@
<div class="form-horizontal">
<div class="d-flex justify-content-between">
<a class="version" href="https://github.com/bpatrik/pigallery2/releases">
<span i18n>App version:</span>&nbsp;
v<span>{{appVersion}}</span>
<span i18n>App version:</span>&nbsp;v<span>{{appVersion}}</span>
</a>
<div class="form-group">
<label class="control-label align-self-end mr-2" for="simplifiedMode" i18n>Mode</label>
@ -44,29 +43,66 @@
</div>
</div>
</div>
<app-settings-basic [simplifiedMode]="simplifiedMode"></app-settings-basic>
<app-settings-usermanager></app-settings-usermanager>
<app-settings-database [simplifiedMode]="simplifiedMode"></app-settings-database>
<app-settings-thumbnail #thumbnail [hidden]="!thumbnail.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-thumbnail>
<app-settings-video #video [hidden]="!video.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-video>
<app-settings-search #search [hidden]="!search.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-search>
<app-settings-share #share [hidden]="!share.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-share>
<app-settings-map #map [hidden]="!map.hasAvailableSettings" [simplifiedMode]="simplifiedMode"></app-settings-map>
<app-settings-meta-file #metaFile [hidden]="!metaFile.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-meta-file>
<app-settings-other #other [hidden]="!other.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-other>
<app-settings-random-photo #random [hidden]="!random.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-random-photo>
<app-settings-faces #random [hidden]="!random.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-faces>
<app-settings-indexing #indexing [hidden]="!indexing.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-indexing>
<app-settings-tasks #tasks [hidden]="!tasks.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-tasks>
<div class="row">
<div class="col-md-2 ">
<nav class="nav flex-column sticky-top">
<div class="card">
<div class="card-body text-md-left text-center align-content-md-start align-content-center">
<h5 class="card-title">Menu</h5>
<button class="btn btn-link nav-link text-md-left py-md-1 px-md-0"
*ngFor="let s of contents; let i=index;"
(click)="scrollTo(i)"
[hidden]="!s.hasAvailableSettings"
>{{s.Name}}</button>
</div>
</div>
</nav>
</div>
<div class="col-md-10">
<app-settings-basic #setting #basic
[simplifiedMode]="simplifiedMode"
[hidden]="!basic.hasAvailableSettings"></app-settings-basic>
<app-settings-usermanager #setting #userManager
[hidden]="!userManager.hasAvailableSettings"></app-settings-usermanager>
<app-settings-database #setting #database
[simplifiedMode]="simplifiedMode"
[hidden]="!database.hasAvailableSettings"></app-settings-database>
<app-settings-thumbnail #setting #thumbnail
[hidden]="!thumbnail.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-thumbnail>
<app-settings-video #setting #video
[hidden]="!video.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-video>
<app-settings-search #setting #search
[hidden]="!search.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-search>
<app-settings-share #setting #share
[hidden]="!share.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-share>
<app-settings-map #setting #map
[hidden]="!map.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-map>
<app-settings-meta-file #setting #metaFile
[hidden]="!metaFile.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-meta-file>
<app-settings-other #setting #other
[hidden]="!other.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-other>
<app-settings-random-photo #setting #random
[hidden]="!random.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-random-photo>
<app-settings-faces #setting #faces
[hidden]="!faces.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-faces>
<app-settings-indexing #setting #indexing
[hidden]="!indexing.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-indexing>
<app-settings-tasks #setting #tasks
[hidden]="!tasks.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-tasks>
</div>
</div>
</div>
</app-frame>

View File

@ -1,4 +1,4 @@
import {Component, OnInit} from '@angular/core';
import {AfterViewInit, Component, ElementRef, OnInit, QueryList, ViewChildren} from '@angular/core';
import {AuthenticationService} from '../../model/network/authentication.service';
import {UserRoles} from '../../../../common/entities/UserDTO';
import {NotificationService} from '../../model/notification.service';
@ -6,20 +6,24 @@ import {NotificationType} from '../../../../common/entities/NotificationDTO';
import {NavigationService} from '../../model/navigation.service';
import {I18n} from '@ngx-translate/i18n-polyfill';
import {Config} from '../../../../common/config/public/Config';
import {ISettingsComponent} from '../settings/_abstract/ISettingsComponent';
import {PageHelper} from '../../model/page.helper';
@Component({
selector: 'app-admin',
templateUrl: './admin.component.html',
styleUrls: ['./admin.component.css']
})
export class AdminComponent implements OnInit {
export class AdminComponent implements OnInit, AfterViewInit {
simplifiedMode = true;
text = {
Advanced: 'Advanced',
Simplified: 'Simplified'
};
appVersion = Config.Client.appVersion;
@ViewChildren('setting') settingsComponents: QueryList<ISettingsComponent>;
@ViewChildren('setting', {read: ElementRef}) settingsComponents2: QueryList<ElementRef>;
contents: ISettingsComponent[] = [];
constructor(private _authService: AuthenticationService,
private _navigation: NavigationService,
@ -29,6 +33,15 @@ export class AdminComponent implements OnInit {
this.text.Simplified = i18n('Simplified');
}
ngAfterViewInit(): void {
setTimeout(() => this.contents = this.settingsComponents.toArray(), 0);
}
scrollTo(i: number) {
PageHelper.ScrollY = this.settingsComponents2.toArray()[i].nativeElement.getBoundingClientRect().top +
PageHelper.ScrollY;
}
ngOnInit() {
if (!this._authService.isAuthenticated()
|| this._authService.user.value.role < UserRoles.Admin) {
@ -37,6 +50,7 @@ export class AdminComponent implements OnInit {
}
}
public getCss(type: NotificationType) {
switch (type) {
case NotificationType.error:

View File

@ -5,11 +5,11 @@
[style.transform]="ImageTransform"
[src]="thumbnailSrc"/>
<img *ngIf="gridMedia !== null && gridMedia.isPhoto() && photoSrc"
<img *ngIf="gridMedia !== null && gridMedia.isPhoto() && photo.src"
[style.width.%]="imageSize.width"
[style.height.%]="imageSize.height"
[style.transform]="ImageTransform"
[src]="photoSrc"
[src]="photo.src"
[alt]="gridMedia.media.name"
(load)="onImageLoad()"
(error)="onImageError()"/>

View File

@ -4,6 +4,7 @@ import {FixOrientationPipe} from '../../../../pipes/FixOrientationPipe';
import {MediaDTO} from '../../../../../../common/entities/MediaDTO';
import {DomSanitizer, SafeStyle} from '@angular/platform-browser';
import {SupportedFormats} from '../../../../../../common/SupportedFormats';
import {Config} from '../../../../../../common/config/public/Config';
@Component({
selector: 'app-gallery-lightbox-media',
@ -26,8 +27,10 @@ export class GalleryLightboxMediaComponent implements OnChanges {
public imageSize = {width: 'auto', height: '100'};
public imageLoadFinished = false;
thumbnailSrc: string = null;
photoSrc: string = null;
isPhotoSrcBestFit = true;
photo = {
src: <string>null,
isBestFit: false
};
public transcodeNeedVideos = SupportedFormats.TranscodeNeed.Videos;
private mediaLoaded = false;
private videoProgress = 0;
@ -98,10 +101,11 @@ export class GalleryLightboxMediaComponent implements OnChanges {
}
ngOnChanges() {
// media changed
if (this.prevGirdPhoto !== this.gridMedia) {
this.prevGirdPhoto = this.gridMedia;
this.thumbnailSrc = null;
this.photoSrc = null;
this.photo.src = null;
this.mediaLoaded = false;
this.imageLoadFinished = false;
this.setImageSize();
@ -111,24 +115,7 @@ export class GalleryLightboxMediaComponent implements OnChanges {
.then((src) => this.thumbnailSrc = src);
}
if (this.zoom === 1) {
if (this.photoSrc == null && this.gridMedia && this.loadMedia) {
FixOrientationPipe.transform(this.gridMedia.getBestFitMediaPath(), this.gridMedia.Orientation)
.then((src) => {
this.photoSrc = src;
this.isPhotoSrcBestFit = true;
});
}
// on zoom load high res photo
} else if ((this.isPhotoSrcBestFit === true ||
this.photoSrc == null) && this.gridMedia && this.loadMedia) {
FixOrientationPipe.transform(this.gridMedia.getMediaPath(), this.gridMedia.Orientation)
.then((src) => {
this.photoSrc = src;
this.isPhotoSrcBestFit = false;
});
}
this.loadPhoto().catch(console.error);
}
@ -174,6 +161,28 @@ export class GalleryLightboxMediaComponent implements OnChanges {
this.videoSourceError.emit();
}
private async loadPhoto() {
if (!this.gridMedia || !this.loadMedia || !this.gridMedia.isPhoto()) {
return;
}
if (this.zoom === 1) {
if (this.photo.src == null) {
if (Config.Client.Media.Photo.Converting.enabled === true) {
this.photo.src = await FixOrientationPipe.transform(this.gridMedia.getBestFitMediaPath(), this.gridMedia.Orientation);
this.photo.isBestFit = true;
} else {
this.photo.src = await FixOrientationPipe.transform(this.gridMedia.getMediaPath(), this.gridMedia.Orientation);
this.photo.isBestFit = false;
}
}
// on zoom load high res photo
} else if ((this.photo.isBestFit === true || this.photo.src == null)) {
this.photo.src = await FixOrientationPipe.transform(this.gridMedia.getMediaPath(), this.gridMedia.Orientation);
this.photo.isBestFit = false;
}
}
/** Video **/
private onVideoProgress() {
this.videoProgress = (100 / this.video.nativeElement.duration) * this.video.nativeElement.currentTime;

View File

@ -0,0 +1,5 @@
export interface ISettingsComponent {
hasAvailableSettings: boolean;
Name: string;
}

View File

@ -9,10 +9,11 @@ import {AbstractSettingsService} from './abstract.settings.service';
import {IPrivateConfig} from '../../../../../common/config/private/IPrivateConfig';
import {I18n} from '@ngx-translate/i18n-polyfill';
import {Subscription} from 'rxjs';
import {ISettingsComponent} from './ISettingsComponent';
export abstract class SettingsComponent<T extends { [key: string]: any }, S extends AbstractSettingsService<T> = AbstractSettingsService<T>>
implements OnInit, OnDestroy, OnChanges {
implements OnInit, OnDestroy, OnChanges, ISettingsComponent {
@Input()
public simplifiedMode = true;
@ -54,6 +55,11 @@ export abstract class SettingsComponent<T extends { [key: string]: any }, S exte
this.text.High = i18n('High');
}
get Name(): string {
return this.name;
}
onNewSettings = (s: IPrivateConfig) => {
this.settings = Utils.clone(this.sliceFN(s));
this.original = Utils.clone(this.settings);
@ -137,7 +143,7 @@ export abstract class SettingsComponent<T extends { [key: string]: any }, S exte
try {
await this._settingsService.updateSettings(this.settings);
await this.getSettings();
this.notification.success(this.name + ' ' + this.i18n('settings saved'), this.i18n('Success'));
this.notification.success(this.Name + ' ' + this.i18n('settings saved'), this.i18n('Success'));
this.inProgress = false;
return true;
} catch (err) {

View File

@ -1,7 +1,7 @@
<form #settingsForm="ngForm" class="form-horizontal">
<div class="card mb-4">
<h5 class="card-header">
<ng-container i18n>Basic settings</ng-container><ng-container *ngIf="changed">*</ng-container>
{{Name}}<ng-container *ngIf="changed">*</ng-container>
</h5>
<div class="card-body">
<div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div>
@ -53,6 +53,17 @@
</div>
</div>
<div class="form-group row">
<label class="col-md-2 control-label" for="tempFolder" i18n>Temp folder</label>
<div class="col-md-10">
<input type="text" class="form-control" placeholder="path"
id="tempFolder"
[(ngModel)]="settings.tempFolder"
name="tempFolder" required>
<small class="form-text text-muted" i18n>Thumbnails, coverted photos, videos will be stored here (write permission required)</small>
</div>
</div>
<div class="form-group row" [hidden]="simplifiedMode">
<label class="col-md-2 control-label" for="publicUrl" i18n>Page public url</label>
<div class="col-md-10">

View File

@ -29,6 +29,7 @@ export class BasicSettingsComponent extends SettingsComponent<BasicConfigDTO> {
port: s.Server.port,
host: s.Server.host,
imagesFolder: s.Server.Media.folder,
tempFolder: s.Server.Media.tempFolder,
applicationTitle: s.Client.applicationTitle,
publicUrl: s.Client.publicUrl,
urlBase: s.Client.urlBase

View File

@ -1,44 +1,85 @@
<div class="card mb-4">
<h5 class="card-header">
<ng-container i18n>Database settings</ng-container><ng-container *ngIf="changed">*</ng-container>
</h5>
<div class="card-body">
<div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div>
<form #settingsForm="ngForm">
<p class="title" i18n>Type:</p>
<select class="form-control" [(ngModel)]="settings.type" name="type" required>
<option *ngFor="let type of types" [ngValue]="type.key">{{type.value}}
</option>
</select>
<small *ngIf="settings.type == DatabaseType.mysql"
class="form-text text-muted" i18n>Install manually mysql node module to use mysql (npm install mysql)
</small>
<form #settingsForm="ngForm" class="form-horizontal">
<div class="card mb-4">
<h5 class="card-header">
{{Name}}
<ng-container *ngIf="changed">*</ng-container>
</h5>
<div class="card-body">
<div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div>
<div class="form-group row">
<label class="col-md-2 control-label" for="Type" i18n>Type</label>
<div class="col-md-10">
<select name="Type" id="Type"
class="form-control" [(ngModel)]="settings.type" required>
<option *ngFor="let type of types" [ngValue]="type.key">{{type.value}}
</option>
</select>
<small *ngIf="settings.type == DatabaseType.mysql"
class="form-text text-muted" i18n>Install manually mysql node module to use mysql (npm install mysql)
</small>
</div>
</div>
<ng-container *ngIf="settings.type == DatabaseType.mysql">
<p class="title" i18n>MySQL settings:</p>
<input type="text" class="form-control" i18n-placeholder placeholder="Host"
[(ngModel)]="settings.mysql.host" name="host" required>
<input type="text" class="form-control" i18n-placeholder placeholder="Database"
[(ngModel)]="settings.mysql.database" name="database" required>
<input type="text" class="form-control" i18n-placeholder placeholder="Username"
[(ngModel)]="settings.mysql.username" name="username">
<input type="password" class="form-control" i18n-placeholder placeholder="Password"
[(ngModel)]="settings.mysql.password" name="password">
<div class="form-group row">
<label class="col-md-2 control-label" for="host" i18n>Host</label>
<div class="col-md-10">
<input type="text" class="form-control" placeholder="localhost"
[(ngModel)]="settings.mysql.host" id="host" name="host" required>
</div>
</div>
<div class="form-group row">
<label class="col-md-2 control-label" for="database" i18n>Database</label>
<div class="col-md-10">
<input type="text" class="form-control" placeholder="pigallery2"
[(ngModel)]="settings.mysql.database" id="database" name="database" required>
</div>
</div>
<div class="form-group row">
<label class="col-md-2 control-label" for="username" i18n>Username</label>
<div class="col-md-10">
<input type="text" class="form-control" placeholder="username"
[(ngModel)]="settings.mysql.username" id="username" name="username" required>
</div>
</div>
<div class="form-group row">
<label class="col-md-2 control-label" for="password" i18n>Password</label>
<div class="col-md-10">
<input type="password" class="form-control" placeholder="password"
[(ngModel)]="settings.mysql.password" id="password" name="password" required>
</div>
</div>
</ng-container>
<ng-container *ngIf="settings.type == DatabaseType.sqlite">
<p class="title" i18n>SQLite settings:</p>
<input type="text" class="form-control" placeholder="storage"
[(ngModel)]="settings.sqlite.storage" name="host" required>
<div class="form-group row">
<label class="col-md-2 control-label" for="storage" i18n>Storage file</label>
<div class="col-md-10">
<input type="text" class="form-control" placeholder="sqlite.db"
[(ngModel)]="settings.sqlite.storage" id="storage" name="storage" required>
</div>
</div>
</ng-container>
<ng-container *ngIf="settings.type == DatabaseType.memory">
<div class="form-group row">
<label class="col-md-2 control-label" for="usersFile" i18n>User's file</label>
<div class="col-md-10">
<input type="text" class="form-control" placeholder="users.db"
[(ngModel)]="settings.memory.usersFile" id="usersFile" name="usersFile" required>
</div>
</div>
</ng-container>
</form>
<button class="btn btn-success float-right"
[disabled]="!settingsForm.form.valid || !changed || inProgress"
(click)="save()" i18n>Save
</button>
<button class="btn btn-secondary float-right"
[disabled]=" !changed || inProgress"
(click)="reset()" i18n>Reset
</button>
<button class="btn btn-success float-right"
[disabled]="!settingsForm.form.valid || !changed || inProgress"
(click)="save()" i18n>Save
</button>
<button class="btn btn-secondary float-right"
[disabled]=" !changed || inProgress"
(click)="reset()" i18n>Reset
</button>
</div>
</div>
</div>
</form>

View File

@ -17,8 +17,8 @@ import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig'
})
export class DatabaseSettingsComponent extends SettingsComponent<ServerConfig.DataBaseConfig> implements OnInit {
public types: { key: number, value: string }[] = [];
public DatabaseType: any;
public types = Utils.enumToArray(ServerConfig.DatabaseType);
public DatabaseType = ServerConfig.DatabaseType;
constructor(_authService: AuthenticationService,
_navigation: NavigationService,
@ -30,8 +30,6 @@ export class DatabaseSettingsComponent extends SettingsComponent<ServerConfig.Da
ngOnInit() {
super.ngOnInit();
this.types = Utils.enumToArray(ServerConfig.DatabaseType);
this.DatabaseType = ServerConfig.DatabaseType;
}

View File

@ -2,7 +2,7 @@
<div class="card mb-4"
[ngClass]="settings.enabled && !_settingsService.isSupported()?'panel-warning':''">
<h5 class="card-header">
<ng-container i18n>Faces settings</ng-container><ng-container *ngIf="changed">*</ng-container>
{{Name}}<ng-container *ngIf="changed">*</ng-container>
<div class="switch-wrapper">
<bSwitch
class="switch"

View File

@ -1,8 +1,7 @@
<form #settingsForm="ngForm" class="form-horizontal">
<div class="card mb-4">
<h5 class="card-header">
<ng-container i18n>Folder indexing</ng-container>
<ng-container *ngIf="changed">*</ng-container>
{{Name}}<ng-container *ngIf="changed">*</ng-container>
</h5>
<div class="card-body">
<div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div>

View File

@ -33,7 +33,7 @@ export class IndexingSettingsComponent extends SettingsComponent<ServerConfig.In
notification: NotificationService,
i18n: I18n) {
super(i18n('Indexing'),
super(i18n('Folder indexing'),
_authService,
_navigation,
<any>_settingsService,

View File

@ -1,7 +1,6 @@
<form #settingsForm="ngForm" class="form-horizontal">
<div class="card mb-4">
<h5 class="card-header">
<ng-container i18n>Map settings</ng-container><ng-container *ngIf="changed">*</ng-container>
<h5 class="card-header">{{Name}}<ng-container *ngIf="changed">*</ng-container>
<div class="switch-wrapper">
<bSwitch
class="switch"

View File

@ -1,7 +1,7 @@
<form #settingsForm="ngForm" class="form-horizontal">
<div class="card mb-4">
<h5 class="card-header">
<ng-container i18n>Meta file settings</ng-container><ng-container *ngIf="changed">*</ng-container>
{{Name}}<ng-container *ngIf="changed">*</ng-container>
<div class="switch-wrapper">
<bSwitch
class="switch"

View File

@ -1,7 +1,7 @@
<form #settingsForm="ngForm" class="form-horizontal" >
<div class="card mb-4">
<h5 class="card-header">
<ng-container i18n>Other settings</ng-container><ng-container *ngIf="changed">*</ng-container>
{{Name}}<ng-container *ngIf="changed">*</ng-container>
</h5>
<div class="card-body">
<div [hidden]="!error" class="alert alert-danger" role="alert"><strong i18n>Error: </strong>{{error}}</div>

View File

@ -2,7 +2,7 @@
<div class="card mb-4"
[ngClass]="settings.enabled && !_settingsService.isSupported()?'panel-warning':''">
<h5 class="card-header">
<ng-container i18n>Random Photo settings</ng-container><ng-container *ngIf="changed">*</ng-container>
{{Name}}<ng-container *ngIf="changed">*</ng-container>
<div class="switch-wrapper">
<bSwitch
class="switch"
@ -25,7 +25,7 @@
<div class="panel-info" i18n>
This feature enables you to generate 'random photo' urls.
That URL returns a photo random selected from your gallery.
You can use the url with 3rd party like random changing desktop background.
You can use the url with 3rd party application like random changing desktop background.
</div>
</ng-container>

View File

@ -21,7 +21,7 @@ export class RandomPhotoSettingsComponent extends SettingsComponent<ClientConfig
_settingsService: RandomPhotoSettingsService,
notification: NotificationService,
i18n: I18n) {
super(i18n('Random Media'), _authService, _navigation, _settingsService, notification, i18n, s => s.Client.RandomPhoto);
super(i18n('Random Photo'), _authService, _navigation, _settingsService, notification, i18n, s => s.Client.RandomPhoto);
}

View File

@ -2,7 +2,7 @@
<div class="card mb-4"
[ngClass]="settings.enabled && !_settingsService.isSupported()?'panel-warning':''">
<h5 class="card-header">
<ng-container i18n>Search settings</ng-container><ng-container *ngIf="changed">*</ng-container>
{{Name}}<ng-container *ngIf="changed">*</ng-container>
<div class="switch-wrapper">
<bSwitch
class="switch"

View File

@ -2,7 +2,7 @@
<div class="card mb-4"
[ngClass]="settings.enabled && !_settingsService.isSupported()?'panel-warning':''">
<h5 class="card-header">
<ng-container i18n>Share settings</ng-container><ng-container *ngIf="changed">*</ng-container>
{{Name}}<ng-container *ngIf="changed">*</ng-container>
<div class="switch-wrapper">
<bSwitch
class="switch"

View File

@ -1,8 +1,7 @@
<form #settingsForm="ngForm" class="form-horizontal">
<div class="card mb-4">
<h5 class="card-header">
<ng-container i18n>Tasks</ng-container>
<ng-container *ngIf="changed">*</ng-container>
{{Name}}<ng-container *ngIf="changed">*</ng-container>
</h5>
<div class="card-body">
<div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div>

View File

@ -1,7 +1,7 @@
<form #settingsForm="ngForm" class="form-horizontal">
<div class="card mb-4">
<h5 class="card-header">
<ng-container i18n>Thumbnail settings</ng-container><ng-container *ngIf="changed">*</ng-container>
{{Name}}<ng-container *ngIf="changed">*</ng-container>
</h5>
<div class="card-body">
<div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div>
@ -34,18 +34,6 @@
</div>
</div>
<!-- <div class="form-group row">-->
<!-- <label class="col-md-2 control-label" for="th_folder" i18n>Thumbnail folder</label>-->
<!-- <div class="col-md-10">-->
<!-- <input type="text" class="form-control" placeholder="path"-->
<!-- id="th_folder"-->
<!-- [(ngModel)]="settings.server.folder"-->
<!-- name="path" required>-->
<!-- <small class="form-text text-muted" i18n>Thumbnails will be saved in this folder. Write access is required-->
<!-- </small>-->
<!-- </div>-->
<!-- </div>-->
<div class="form-group row" [hidden]="simplifiedMode">
<label class="col-md-2 control-label" for="quality" i18n>Thumbnail Quality</label>
<div class="col-md-10">
@ -84,15 +72,14 @@
<div class="form-group row" [hidden]="simplifiedMode">
<label class="col-md-2 control-label" for="thumbnailSizes" i18n>Thumbnail sizes</label>
<div class="col-md-10">
<input type="text" class="form-control" placeholder="200; 400"
<input type="text" class="form-control" placeholder="160; 240"
id="thumbnailSizes"
[(ngModel)]="ThumbnailSizes"
name="thumbnailSizes" required>
<small class="form-text text-muted">
<ng-container i18n>Size of the thumbnails.</ng-container><br/>
<ng-container i18n>The best matching size will be generated. (More size gives better quality, but use storage to store and CPU to render.)</ng-container><br/>
<ng-container
i18n>';' separated integers. If size is 200, that thumbnail will have 200^2 pixels.</ng-container>
<ng-container i18n>The best matching size will be generated. (More sizes give better quality, but use more storage and CPU to render.)</ng-container><br/>
<ng-container i18n>';' separated integers. If size is 160, that shorter side of the thumbnail will have 160 pixels.</ng-container>
</small>
</div>

View File

@ -51,9 +51,8 @@ export class ThumbnailSettingsComponent
.enumToArray(ServerConfig.ThumbnailProcessingLib).map((v) => {
if (v.value.toLowerCase() === 'sharp') {
v.value += ' ' + this.i18n('(recommended)');
}
if (v.value.toLowerCase() === 'gm') {
v.value += ' ' + this.i18n('(deprecated)');
} else {
v.value += ' ' + this.i18n('(deprecated, will be removed)');
}
return v;
});

View File

@ -1,6 +1,6 @@
<div class="card mb-4">
<h5 class="card-header">
<ng-container i18n>Password protection</ng-container>
{{Name}}
<div class="switch-wrapper">
<bSwitch
class="switch"

View File

@ -8,6 +8,7 @@ import {NavigationService} from '../../../model/navigation.service';
import {NotificationService} from '../../../model/notification.service';
import {ErrorCodes, ErrorDTO} from '../../../../../common/entities/Error';
import {I18n} from '@ngx-translate/i18n-polyfill';
import {ISettingsComponent} from '../_abstract/ISettingsComponent';
@Component({
selector: 'app-settings-usermanager',
@ -16,7 +17,7 @@ import {I18n} from '@ngx-translate/i18n-polyfill';
'../_abstract/abstract.settings.component.css'],
providers: [UserManagerSettingsService],
})
export class UserMangerSettingsComponent implements OnInit {
export class UserMangerSettingsComponent implements OnInit, ISettingsComponent {
@ViewChild('userModal', {static: false}) public childModal: ModalDirective;
public newUser = <UserDTO>{};
public userRoles: Array<any> = [];
@ -24,6 +25,8 @@ export class UserMangerSettingsComponent implements OnInit {
public enabled = true;
public error: string = null;
public inProgress = false;
Name: string;
hasAvailableSettings = true;
text = {
@ -39,6 +42,7 @@ export class UserMangerSettingsComponent implements OnInit {
private _userSettings: UserManagerSettingsService,
private notification: NotificationService,
public i18n: I18n) {
this.Name = i18n('Password protection');
this.text.Enabled = i18n('Enabled');
this.text.Disabled = i18n('Disabled');
this.text.Low = i18n('Low');
@ -48,15 +52,15 @@ export class UserMangerSettingsComponent implements OnInit {
ngOnInit() {
if (!this._authService.isAuthenticated() ||
this._authService.user.value.role < UserRoles.Admin) {
this._authService.user.value.role < UserRoles.Admin) {
this._navigation.toLogin();
return;
}
this.userRoles = Utils
.enumToArray(UserRoles)
.filter(r => r.key !== UserRoles.LimitedGuest)
.filter(r => r.key <= this._authService.user.value.role)
.sort((a, b) => a.key - b.key);
.enumToArray(UserRoles)
.filter(r => r.key !== UserRoles.LimitedGuest)
.filter(r => r.key <= this._authService.user.value.role)
.sort((a, b) => a.key - b.key);
this.getSettings();
this.getUsersList();

View File

@ -1,8 +1,7 @@
<form #settingsForm="ngForm" class="form-horizontal">
<div class="card mb-4">
<h5 class="card-header">
<ng-container i18n>Video settings</ng-container>
<ng-container *ngIf="changed">*</ng-container>
{{Name}}<ng-container *ngIf="changed">*</ng-container>
<div class="switch-wrapper">
<bSwitch
class="switch"