1
0
mirror of https://github.com/xuthus83/pigallery2.git synced 2025-01-14 14:43:17 +08: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)) { if (!(req.resultPipe)) {
return next(); return next();
} }
const fullMediaPath = path.join(ProjectPath.ImageFolder, req.resultPipe); const fullMediaPath: string = req.resultPipe;
if (fs.statSync(fullMediaPath).isDirectory()) { if (fs.statSync(fullMediaPath).isDirectory()) {
return next(); return next();

View File

@ -6,9 +6,13 @@ import {Config} from '../../../common/config/private/Config';
export class PhotoConverterMWs { export class PhotoConverterMWs {
public static async convertPhoto(req: Request, res: Response, next: NextFunction) { 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(); 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 fullMediaPath = req.resultPipe;
const convertedVideo = PhotoProcessing.generateConvertedFileName(fullMediaPath); const convertedVideo = PhotoProcessing.generateConvertedFileName(fullMediaPath);
@ -19,12 +23,12 @@ export class PhotoConverterMWs {
return next(); return next();
} }
if (Config.Server.Media.Photo.converting.onTheFly) { if (Config.Server.Media.Photo.Converting.onTheFly) {
req.resultPipe = await PhotoProcessing.convertPhoto(fullMediaPath, req.resultPipe = await PhotoProcessing.convertPhoto(fullMediaPath,
Config.Server.Media.Photo.converting.resolution); Config.Server.Media.Photo.Converting.resolution);
} }
// not converted and won't be now
return next(); 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 { public static generateConvertedFileName(photoPath: string): string {
const extension = path.extname(photoPath); const extension = path.extname(photoPath);
const file = path.basename(photoPath, extension); 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, return path.join(ProjectPath.TranscodedFolder,
ProjectPath.getRelativePathToImages(path.dirname(photoPath)), file + ProjectPath.getRelativePathToImages(path.dirname(photoPath)), file +
'_' + postfix + '.jpg'); '_' + postfix + '.jpg');

View File

@ -21,7 +21,7 @@ export class PhotoConvertingTask extends FileTask<string> {
} }
public get Supported(): boolean { 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[]> { protected async processDirectory(directory: DirectoryDTO): Promise<string[]> {
@ -40,7 +40,7 @@ export class PhotoConvertingTask extends FileTask<string> {
} }
protected async processFile(file: string): Promise<void> { 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 { export enum ThumbnailProcessingLib {
sharp = 3,
Jimp = 1, Jimp = 1,
gm = 2, gm = 2,
sharp = 3
} }
export interface MySQLConfig { export interface MySQLConfig {
@ -98,8 +98,7 @@ export module ServerConfig {
export interface PhotoConfig { export interface PhotoConfig {
converting: { Converting: {
enabled: boolean;
onTheFly: boolean; onTheFly: boolean;
resolution: resolutionType resolution: resolutionType
}; };

View File

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

View File

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

View File

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

View File

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

View File

@ -5,3 +5,7 @@
.version:hover { .version:hover {
text-decoration: underline; text-decoration: underline;
} }
*:focus {
outline: none;
}

View File

@ -1,5 +1,5 @@
<app-frame> <app-frame>
<div body class="container"> <div body class="container-fluid">
<div class="card mb-4" *ngIf="notificationService.notifications.length>0"> <div class="card mb-4" *ngIf="notificationService.notifications.length>0">
<h5 class="card-header" i18n> <h5 class="card-header" i18n>
@ -23,8 +23,7 @@
<div class="form-horizontal"> <div class="form-horizontal">
<div class="d-flex justify-content-between"> <div class="d-flex justify-content-between">
<a class="version" href="https://github.com/bpatrik/pigallery2/releases"> <a class="version" href="https://github.com/bpatrik/pigallery2/releases">
<span i18n>App version:</span>&nbsp; <span i18n>App version:</span>&nbsp;v<span>{{appVersion}}</span>
v<span>{{appVersion}}</span>
</a> </a>
<div class="form-group"> <div class="form-group">
<label class="control-label align-self-end mr-2" for="simplifiedMode" i18n>Mode</label> <label class="control-label align-self-end mr-2" for="simplifiedMode" i18n>Mode</label>
@ -44,29 +43,66 @@
</div> </div>
</div> </div>
</div> </div>
<app-settings-basic [simplifiedMode]="simplifiedMode"></app-settings-basic>
<app-settings-usermanager></app-settings-usermanager> <div class="row">
<app-settings-database [simplifiedMode]="simplifiedMode"></app-settings-database> <div class="col-md-2 ">
<app-settings-thumbnail #thumbnail [hidden]="!thumbnail.hasAvailableSettings" <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> [simplifiedMode]="simplifiedMode"></app-settings-thumbnail>
<app-settings-video #video [hidden]="!video.hasAvailableSettings" <app-settings-video #setting #video
[hidden]="!video.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-video> [simplifiedMode]="simplifiedMode"></app-settings-video>
<app-settings-search #search [hidden]="!search.hasAvailableSettings" <app-settings-search #setting #search
[hidden]="!search.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-search> [simplifiedMode]="simplifiedMode"></app-settings-search>
<app-settings-share #share [hidden]="!share.hasAvailableSettings" <app-settings-share #setting #share
[hidden]="!share.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-share> [simplifiedMode]="simplifiedMode"></app-settings-share>
<app-settings-map #map [hidden]="!map.hasAvailableSettings" [simplifiedMode]="simplifiedMode"></app-settings-map> <app-settings-map #setting #map
<app-settings-meta-file #metaFile [hidden]="!metaFile.hasAvailableSettings" [hidden]="!map.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-map>
<app-settings-meta-file #setting #metaFile
[hidden]="!metaFile.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-meta-file> [simplifiedMode]="simplifiedMode"></app-settings-meta-file>
<app-settings-other #other [hidden]="!other.hasAvailableSettings" <app-settings-other #setting #other
[hidden]="!other.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-other> [simplifiedMode]="simplifiedMode"></app-settings-other>
<app-settings-random-photo #random [hidden]="!random.hasAvailableSettings" <app-settings-random-photo #setting #random
[hidden]="!random.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-random-photo> [simplifiedMode]="simplifiedMode"></app-settings-random-photo>
<app-settings-faces #random [hidden]="!random.hasAvailableSettings" <app-settings-faces #setting #faces
[hidden]="!faces.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-faces> [simplifiedMode]="simplifiedMode"></app-settings-faces>
<app-settings-indexing #indexing [hidden]="!indexing.hasAvailableSettings" <app-settings-indexing #setting #indexing
[hidden]="!indexing.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-indexing> [simplifiedMode]="simplifiedMode"></app-settings-indexing>
<app-settings-tasks #tasks [hidden]="!tasks.hasAvailableSettings" <app-settings-tasks #setting #tasks
[hidden]="!tasks.hasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-tasks> [simplifiedMode]="simplifiedMode"></app-settings-tasks>
</div> </div>
</div>
</div>
</app-frame> </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 {AuthenticationService} from '../../model/network/authentication.service';
import {UserRoles} from '../../../../common/entities/UserDTO'; import {UserRoles} from '../../../../common/entities/UserDTO';
import {NotificationService} from '../../model/notification.service'; import {NotificationService} from '../../model/notification.service';
@ -6,20 +6,24 @@ import {NotificationType} from '../../../../common/entities/NotificationDTO';
import {NavigationService} from '../../model/navigation.service'; import {NavigationService} from '../../model/navigation.service';
import {I18n} from '@ngx-translate/i18n-polyfill'; import {I18n} from '@ngx-translate/i18n-polyfill';
import {Config} from '../../../../common/config/public/Config'; import {Config} from '../../../../common/config/public/Config';
import {ISettingsComponent} from '../settings/_abstract/ISettingsComponent';
import {PageHelper} from '../../model/page.helper';
@Component({ @Component({
selector: 'app-admin', selector: 'app-admin',
templateUrl: './admin.component.html', templateUrl: './admin.component.html',
styleUrls: ['./admin.component.css'] styleUrls: ['./admin.component.css']
}) })
export class AdminComponent implements OnInit { export class AdminComponent implements OnInit, AfterViewInit {
simplifiedMode = true; simplifiedMode = true;
text = { text = {
Advanced: 'Advanced', Advanced: 'Advanced',
Simplified: 'Simplified' Simplified: 'Simplified'
}; };
appVersion = Config.Client.appVersion; appVersion = Config.Client.appVersion;
@ViewChildren('setting') settingsComponents: QueryList<ISettingsComponent>;
@ViewChildren('setting', {read: ElementRef}) settingsComponents2: QueryList<ElementRef>;
contents: ISettingsComponent[] = [];
constructor(private _authService: AuthenticationService, constructor(private _authService: AuthenticationService,
private _navigation: NavigationService, private _navigation: NavigationService,
@ -29,6 +33,15 @@ export class AdminComponent implements OnInit {
this.text.Simplified = i18n('Simplified'); 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() { ngOnInit() {
if (!this._authService.isAuthenticated() if (!this._authService.isAuthenticated()
|| this._authService.user.value.role < UserRoles.Admin) { || this._authService.user.value.role < UserRoles.Admin) {
@ -37,6 +50,7 @@ export class AdminComponent implements OnInit {
} }
} }
public getCss(type: NotificationType) { public getCss(type: NotificationType) {
switch (type) { switch (type) {
case NotificationType.error: case NotificationType.error:

View File

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

View File

@ -4,6 +4,7 @@ import {FixOrientationPipe} from '../../../../pipes/FixOrientationPipe';
import {MediaDTO} from '../../../../../../common/entities/MediaDTO'; import {MediaDTO} from '../../../../../../common/entities/MediaDTO';
import {DomSanitizer, SafeStyle} from '@angular/platform-browser'; import {DomSanitizer, SafeStyle} from '@angular/platform-browser';
import {SupportedFormats} from '../../../../../../common/SupportedFormats'; import {SupportedFormats} from '../../../../../../common/SupportedFormats';
import {Config} from '../../../../../../common/config/public/Config';
@Component({ @Component({
selector: 'app-gallery-lightbox-media', selector: 'app-gallery-lightbox-media',
@ -26,8 +27,10 @@ export class GalleryLightboxMediaComponent implements OnChanges {
public imageSize = {width: 'auto', height: '100'}; public imageSize = {width: 'auto', height: '100'};
public imageLoadFinished = false; public imageLoadFinished = false;
thumbnailSrc: string = null; thumbnailSrc: string = null;
photoSrc: string = null; photo = {
isPhotoSrcBestFit = true; src: <string>null,
isBestFit: false
};
public transcodeNeedVideos = SupportedFormats.TranscodeNeed.Videos; public transcodeNeedVideos = SupportedFormats.TranscodeNeed.Videos;
private mediaLoaded = false; private mediaLoaded = false;
private videoProgress = 0; private videoProgress = 0;
@ -98,10 +101,11 @@ export class GalleryLightboxMediaComponent implements OnChanges {
} }
ngOnChanges() { ngOnChanges() {
// media changed
if (this.prevGirdPhoto !== this.gridMedia) { if (this.prevGirdPhoto !== this.gridMedia) {
this.prevGirdPhoto = this.gridMedia; this.prevGirdPhoto = this.gridMedia;
this.thumbnailSrc = null; this.thumbnailSrc = null;
this.photoSrc = null; this.photo.src = null;
this.mediaLoaded = false; this.mediaLoaded = false;
this.imageLoadFinished = false; this.imageLoadFinished = false;
this.setImageSize(); this.setImageSize();
@ -111,24 +115,7 @@ export class GalleryLightboxMediaComponent implements OnChanges {
.then((src) => this.thumbnailSrc = src); .then((src) => this.thumbnailSrc = src);
} }
if (this.zoom === 1) { this.loadPhoto().catch(console.error);
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;
});
}
} }
@ -174,6 +161,28 @@ export class GalleryLightboxMediaComponent implements OnChanges {
this.videoSourceError.emit(); 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 **/ /** Video **/
private onVideoProgress() { private onVideoProgress() {
this.videoProgress = (100 / this.video.nativeElement.duration) * this.video.nativeElement.currentTime; 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 {IPrivateConfig} from '../../../../../common/config/private/IPrivateConfig';
import {I18n} from '@ngx-translate/i18n-polyfill'; import {I18n} from '@ngx-translate/i18n-polyfill';
import {Subscription} from 'rxjs'; import {Subscription} from 'rxjs';
import {ISettingsComponent} from './ISettingsComponent';
export abstract class SettingsComponent<T extends { [key: string]: any }, S extends AbstractSettingsService<T> = AbstractSettingsService<T>> 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() @Input()
public simplifiedMode = true; public simplifiedMode = true;
@ -54,6 +55,11 @@ export abstract class SettingsComponent<T extends { [key: string]: any }, S exte
this.text.High = i18n('High'); this.text.High = i18n('High');
} }
get Name(): string {
return this.name;
}
onNewSettings = (s: IPrivateConfig) => { onNewSettings = (s: IPrivateConfig) => {
this.settings = Utils.clone(this.sliceFN(s)); this.settings = Utils.clone(this.sliceFN(s));
this.original = Utils.clone(this.settings); this.original = Utils.clone(this.settings);
@ -137,7 +143,7 @@ export abstract class SettingsComponent<T extends { [key: string]: any }, S exte
try { try {
await this._settingsService.updateSettings(this.settings); await this._settingsService.updateSettings(this.settings);
await this.getSettings(); 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; this.inProgress = false;
return true; return true;
} catch (err) { } catch (err) {

View File

@ -1,7 +1,7 @@
<form #settingsForm="ngForm" class="form-horizontal"> <form #settingsForm="ngForm" class="form-horizontal">
<div class="card mb-4"> <div class="card mb-4">
<h5 class="card-header"> <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> </h5>
<div class="card-body"> <div class="card-body">
<div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div> <div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div>
@ -53,6 +53,17 @@
</div> </div>
</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"> <div class="form-group row" [hidden]="simplifiedMode">
<label class="col-md-2 control-label" for="publicUrl" i18n>Page public url</label> <label class="col-md-2 control-label" for="publicUrl" i18n>Page public url</label>
<div class="col-md-10"> <div class="col-md-10">

View File

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

View File

@ -1,37 +1,77 @@
<form #settingsForm="ngForm" class="form-horizontal">
<div class="card mb-4"> <div class="card mb-4">
<h5 class="card-header"> <h5 class="card-header">
<ng-container i18n>Database settings</ng-container><ng-container *ngIf="changed">*</ng-container> {{Name}}
<ng-container *ngIf="changed">*</ng-container>
</h5> </h5>
<div class="card-body"> <div class="card-body">
<div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div> <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> <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 *ngFor="let type of types" [ngValue]="type.key">{{type.value}}
</option> </option>
</select> </select>
<small *ngIf="settings.type == DatabaseType.mysql" <small *ngIf="settings.type == DatabaseType.mysql"
class="form-text text-muted" i18n>Install manually mysql node module to use mysql (npm install mysql) class="form-text text-muted" i18n>Install manually mysql node module to use mysql (npm install mysql)
</small> </small>
</div>
</div>
<ng-container *ngIf="settings.type == DatabaseType.mysql"> <ng-container *ngIf="settings.type == DatabaseType.mysql">
<p class="title" i18n>MySQL settings:</p>
<input type="text" class="form-control" i18n-placeholder placeholder="Host" <div class="form-group row">
[(ngModel)]="settings.mysql.host" name="host" required> <label class="col-md-2 control-label" for="host" i18n>Host</label>
<input type="text" class="form-control" i18n-placeholder placeholder="Database" <div class="col-md-10">
[(ngModel)]="settings.mysql.database" name="database" required> <input type="text" class="form-control" placeholder="localhost"
<input type="text" class="form-control" i18n-placeholder placeholder="Username" [(ngModel)]="settings.mysql.host" id="host" name="host" required>
[(ngModel)]="settings.mysql.username" name="username"> </div>
<input type="password" class="form-control" i18n-placeholder placeholder="Password" </div>
[(ngModel)]="settings.mysql.password" name="password"> <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>
<ng-container *ngIf="settings.type == DatabaseType.sqlite"> <ng-container *ngIf="settings.type == DatabaseType.sqlite">
<p class="title" i18n>SQLite settings:</p> <div class="form-group row">
<input type="text" class="form-control" placeholder="storage" <label class="col-md-2 control-label" for="storage" i18n>Storage file</label>
[(ngModel)]="settings.sqlite.storage" name="host" required> <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> </ng-container>
</form>
<button class="btn btn-success float-right" <button class="btn btn-success float-right"
[disabled]="!settingsForm.form.valid || !changed || inProgress" [disabled]="!settingsForm.form.valid || !changed || inProgress"
(click)="save()" i18n>Save (click)="save()" i18n>Save
@ -42,3 +82,4 @@
</button> </button>
</div> </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 { export class DatabaseSettingsComponent extends SettingsComponent<ServerConfig.DataBaseConfig> implements OnInit {
public types: { key: number, value: string }[] = []; public types = Utils.enumToArray(ServerConfig.DatabaseType);
public DatabaseType: any; public DatabaseType = ServerConfig.DatabaseType;
constructor(_authService: AuthenticationService, constructor(_authService: AuthenticationService,
_navigation: NavigationService, _navigation: NavigationService,
@ -30,8 +30,6 @@ export class DatabaseSettingsComponent extends SettingsComponent<ServerConfig.Da
ngOnInit() { ngOnInit() {
super.ngOnInit(); super.ngOnInit();
this.types = Utils.enumToArray(ServerConfig.DatabaseType);
this.DatabaseType = ServerConfig.DatabaseType;
} }

View File

@ -2,7 +2,7 @@
<div class="card mb-4" <div class="card mb-4"
[ngClass]="settings.enabled && !_settingsService.isSupported()?'panel-warning':''"> [ngClass]="settings.enabled && !_settingsService.isSupported()?'panel-warning':''">
<h5 class="card-header"> <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"> <div class="switch-wrapper">
<bSwitch <bSwitch
class="switch" class="switch"

View File

@ -1,8 +1,7 @@
<form #settingsForm="ngForm" class="form-horizontal"> <form #settingsForm="ngForm" class="form-horizontal">
<div class="card mb-4"> <div class="card mb-4">
<h5 class="card-header"> <h5 class="card-header">
<ng-container i18n>Folder indexing</ng-container> {{Name}}<ng-container *ngIf="changed">*</ng-container>
<ng-container *ngIf="changed">*</ng-container>
</h5> </h5>
<div class="card-body"> <div class="card-body">
<div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div> <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, notification: NotificationService,
i18n: I18n) { i18n: I18n) {
super(i18n('Indexing'), super(i18n('Folder indexing'),
_authService, _authService,
_navigation, _navigation,
<any>_settingsService, <any>_settingsService,

View File

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

View File

@ -1,7 +1,7 @@
<form #settingsForm="ngForm" class="form-horizontal"> <form #settingsForm="ngForm" class="form-horizontal">
<div class="card mb-4"> <div class="card mb-4">
<h5 class="card-header"> <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"> <div class="switch-wrapper">
<bSwitch <bSwitch
class="switch" class="switch"

View File

@ -1,7 +1,7 @@
<form #settingsForm="ngForm" class="form-horizontal" > <form #settingsForm="ngForm" class="form-horizontal" >
<div class="card mb-4"> <div class="card mb-4">
<h5 class="card-header"> <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> </h5>
<div class="card-body"> <div class="card-body">
<div [hidden]="!error" class="alert alert-danger" role="alert"><strong i18n>Error: </strong>{{error}}</div> <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" <div class="card mb-4"
[ngClass]="settings.enabled && !_settingsService.isSupported()?'panel-warning':''"> [ngClass]="settings.enabled && !_settingsService.isSupported()?'panel-warning':''">
<h5 class="card-header"> <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"> <div class="switch-wrapper">
<bSwitch <bSwitch
class="switch" class="switch"
@ -25,7 +25,7 @@
<div class="panel-info" i18n> <div class="panel-info" i18n>
This feature enables you to generate 'random photo' urls. This feature enables you to generate 'random photo' urls.
That URL returns a photo random selected from your gallery. 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> </div>
</ng-container> </ng-container>

View File

@ -21,7 +21,7 @@ export class RandomPhotoSettingsComponent extends SettingsComponent<ClientConfig
_settingsService: RandomPhotoSettingsService, _settingsService: RandomPhotoSettingsService,
notification: NotificationService, notification: NotificationService,
i18n: I18n) { 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" <div class="card mb-4"
[ngClass]="settings.enabled && !_settingsService.isSupported()?'panel-warning':''"> [ngClass]="settings.enabled && !_settingsService.isSupported()?'panel-warning':''">
<h5 class="card-header"> <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"> <div class="switch-wrapper">
<bSwitch <bSwitch
class="switch" class="switch"

View File

@ -2,7 +2,7 @@
<div class="card mb-4" <div class="card mb-4"
[ngClass]="settings.enabled && !_settingsService.isSupported()?'panel-warning':''"> [ngClass]="settings.enabled && !_settingsService.isSupported()?'panel-warning':''">
<h5 class="card-header"> <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"> <div class="switch-wrapper">
<bSwitch <bSwitch
class="switch" class="switch"

View File

@ -1,8 +1,7 @@
<form #settingsForm="ngForm" class="form-horizontal"> <form #settingsForm="ngForm" class="form-horizontal">
<div class="card mb-4"> <div class="card mb-4">
<h5 class="card-header"> <h5 class="card-header">
<ng-container i18n>Tasks</ng-container> {{Name}}<ng-container *ngIf="changed">*</ng-container>
<ng-container *ngIf="changed">*</ng-container>
</h5> </h5>
<div class="card-body"> <div class="card-body">
<div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div> <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"> <form #settingsForm="ngForm" class="form-horizontal">
<div class="card mb-4"> <div class="card mb-4">
<h5 class="card-header"> <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> </h5>
<div class="card-body"> <div class="card-body">
<div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div> <div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div>
@ -34,18 +34,6 @@
</div> </div>
</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"> <div class="form-group row" [hidden]="simplifiedMode">
<label class="col-md-2 control-label" for="quality" i18n>Thumbnail Quality</label> <label class="col-md-2 control-label" for="quality" i18n>Thumbnail Quality</label>
<div class="col-md-10"> <div class="col-md-10">
@ -84,15 +72,14 @@
<div class="form-group row" [hidden]="simplifiedMode"> <div class="form-group row" [hidden]="simplifiedMode">
<label class="col-md-2 control-label" for="thumbnailSizes" i18n>Thumbnail sizes</label> <label class="col-md-2 control-label" for="thumbnailSizes" i18n>Thumbnail sizes</label>
<div class="col-md-10"> <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" id="thumbnailSizes"
[(ngModel)]="ThumbnailSizes" [(ngModel)]="ThumbnailSizes"
name="thumbnailSizes" required> name="thumbnailSizes" required>
<small class="form-text text-muted"> <small class="form-text text-muted">
<ng-container i18n>Size of the thumbnails.</ng-container><br/> <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>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 <ng-container i18n>';' separated integers. If size is 160, that shorter side of the thumbnail will have 160 pixels.</ng-container>
i18n>';' separated integers. If size is 200, that thumbnail will have 200^2 pixels.</ng-container>
</small> </small>
</div> </div>

View File

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

View File

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

View File

@ -8,6 +8,7 @@ import {NavigationService} from '../../../model/navigation.service';
import {NotificationService} from '../../../model/notification.service'; import {NotificationService} from '../../../model/notification.service';
import {ErrorCodes, ErrorDTO} from '../../../../../common/entities/Error'; import {ErrorCodes, ErrorDTO} from '../../../../../common/entities/Error';
import {I18n} from '@ngx-translate/i18n-polyfill'; import {I18n} from '@ngx-translate/i18n-polyfill';
import {ISettingsComponent} from '../_abstract/ISettingsComponent';
@Component({ @Component({
selector: 'app-settings-usermanager', selector: 'app-settings-usermanager',
@ -16,7 +17,7 @@ import {I18n} from '@ngx-translate/i18n-polyfill';
'../_abstract/abstract.settings.component.css'], '../_abstract/abstract.settings.component.css'],
providers: [UserManagerSettingsService], providers: [UserManagerSettingsService],
}) })
export class UserMangerSettingsComponent implements OnInit { export class UserMangerSettingsComponent implements OnInit, ISettingsComponent {
@ViewChild('userModal', {static: false}) public childModal: ModalDirective; @ViewChild('userModal', {static: false}) public childModal: ModalDirective;
public newUser = <UserDTO>{}; public newUser = <UserDTO>{};
public userRoles: Array<any> = []; public userRoles: Array<any> = [];
@ -24,6 +25,8 @@ export class UserMangerSettingsComponent implements OnInit {
public enabled = true; public enabled = true;
public error: string = null; public error: string = null;
public inProgress = false; public inProgress = false;
Name: string;
hasAvailableSettings = true;
text = { text = {
@ -39,6 +42,7 @@ export class UserMangerSettingsComponent implements OnInit {
private _userSettings: UserManagerSettingsService, private _userSettings: UserManagerSettingsService,
private notification: NotificationService, private notification: NotificationService,
public i18n: I18n) { public i18n: I18n) {
this.Name = i18n('Password protection');
this.text.Enabled = i18n('Enabled'); this.text.Enabled = i18n('Enabled');
this.text.Disabled = i18n('Disabled'); this.text.Disabled = i18n('Disabled');
this.text.Low = i18n('Low'); this.text.Low = i18n('Low');

View File

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