Skip to content

Commit

Permalink
implementing preview settings #80, #381
Browse files Browse the repository at this point in the history
  • Loading branch information
bpatrik committed Jan 26, 2022
1 parent b169fa6 commit 1255246
Show file tree
Hide file tree
Showing 24 changed files with 819 additions and 322 deletions.
26 changes: 26 additions & 0 deletions src/backend/middlewares/admin/SettingsMWs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
ServerIndexingConfig,
ServerJobConfig,
ServerPhotoConfig,
ServerPreviewConfig,
ServerThumbnailConfig,
ServerVideoConfig
} from '../../../common/config/private/PrivateConfig';
Expand Down Expand Up @@ -101,6 +102,31 @@ export class SettingsMWs {
}
}

public static async updatePreviewSettings(req: Request, res: Response, next: NextFunction): Promise<any> {
if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) {
return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'settings is needed'));
}

try {
await ConfigDiagnostics.testPreviewConfig(req.body.settings as ServerPreviewConfig);

Config.Server.Preview = (req.body.settings as ServerPreviewConfig);
// only updating explicitly set config (not saving config set by the diagnostics)
const original = await Config.original();
original.Server.Preview = (req.body.settings as ServerPreviewConfig);
original.save();
await ConfigDiagnostics.runDiagnostics();
Logger.info(LOG_TAG, 'new config:');
Logger.info(LOG_TAG, JSON.stringify(Config, null, '\t'));
return next();
} catch (err) {
if (err instanceof Error) {
return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + err.toString(), err));
}
return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + JSON.stringify(err, null, ' '), err));
}
}

public static async updateVideoSettings(req: Request, res: Response, next: NextFunction): Promise<any> {
if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) {
return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'settings is needed'));
Expand Down
21 changes: 21 additions & 0 deletions src/backend/model/diagnostics/ConfigDiagnostics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,12 @@ import {
ServerDataBaseConfig,
ServerJobConfig,
ServerPhotoConfig,
ServerPreviewConfig,
ServerVideoConfig
} from '../../../common/config/private/PrivateConfig';
import {SearchQueryParser} from '../../../common/SearchQueryParser';
import {SearchQueryTypes, TextSearch} from '../../../common/entities/SearchQueryDTO';
import {Utils} from '../../../common/Utils';

const LOG_TAG = '[ConfigDiagnostics]';

Expand Down Expand Up @@ -225,6 +229,13 @@ export class ConfigDiagnostics {
}


static async testPreviewConfig(settings: ServerPreviewConfig): Promise<void> {
const sp = new SearchQueryParser();
if (!Utils.equalsFilter(sp.parse(sp.stringify(settings.SearchQuery)), settings.SearchQuery)) {
throw new Error('SearchQuery is not valid');
}
}

static async runDiagnostics(): Promise<void> {

if (Config.Server.Database.type !== DatabaseType.memory) {
Expand Down Expand Up @@ -315,6 +326,16 @@ export class ConfigDiagnostics {
Config.Client.Search.enabled = false;
}


try {
await ConfigDiagnostics.testPreviewConfig(Config.Server.Preview);
} catch (ex) {
const err: Error = ex;
NotificationManager.warning('Preview settings are not valid, resetting search query', err.toString());
Logger.warn(LOG_TAG, 'Preview settings are not valid, resetting search query', err.toString());
Config.Server.Preview.SearchQuery = {type: SearchQueryTypes.any_text, text: ''} as TextSearch;
}

try {
await ConfigDiagnostics.testFacesConfig(Config.Client.Faces, Config);
} catch (ex) {
Expand Down
6 changes: 6 additions & 0 deletions src/backend/routes/admin/SettingsRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ export class SettingsRouter {
SettingsMWs.updateSearchSettings,
RenderingMWs.renderOK
);
app.put('/api/settings/preview',
AuthenticationMWs.authenticate,
AuthenticationMWs.authorise(UserRoles.Admin),
SettingsMWs.updatePreviewSettings,
RenderingMWs.renderOK
);
app.put('/api/settings/faces',
AuthenticationMWs.authenticate,
AuthenticationMWs.authorise(UserRoles.Admin),
Expand Down
4 changes: 2 additions & 2 deletions src/common/config/private/PrivateConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {ClientConfig} from '../public/ClientConfig';
import {SubConfigClass} from 'typeconfig/src/decorators/class/SubConfigClass';
import {ConfigProperty} from 'typeconfig/src/decorators/property/ConfigPropoerty';
import {DefaultsJobs} from '../../entities/job/JobDTO';
import {SearchQueryDTO} from '../../entities/SearchQueryDTO';
import {SearchQueryDTO, SearchQueryTypes, TextSearch} from '../../entities/SearchQueryDTO';
import {SortingMethods} from '../../entities/SortingMethods';
import {UserRoles} from '../../entities/UserDTO';

Expand Down Expand Up @@ -334,7 +334,7 @@ export class ServerPhotoConfig {
@SubConfigClass()
export class ServerPreviewConfig {
@ConfigProperty({type: 'object'})
SearchQuery: SearchQueryDTO = null;
SearchQuery: SearchQueryDTO = {type: SearchQueryTypes.any_text, text: ''} as TextSearch;
@ConfigProperty({arrayType: SortingMethods})
Sorting: SortingMethods[] = [
SortingMethods.descRating,
Expand Down
6 changes: 5 additions & 1 deletion src/frontend/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ import {GallerySearchQueryEntryComponent} from './ui/gallery/search/query-enrty/
import {StringifySearchQuery} from './pipes/StringifySearchQuery';
import {AutoCompleteService} from './ui/gallery/search/autocomplete.service';
import {SearchQueryParserService} from './ui/gallery/search/search-query-parser.service';
import {GallerySearchFieldComponent} from './ui/gallery/search/search-field/search-field.gallery.component';
import {GallerySearchFieldBaseComponent} from './ui/gallery/search/search-field-base/search-field-base.gallery.component';
import {AppRoutingModule} from './app.routing';
import {CookieService} from 'ngx-cookie-service';
import {LeafletMarkerClusterModule} from '@asymmetrik/ngx-leaflet-markercluster';
Expand All @@ -112,6 +112,8 @@ import {MDFilesFilterPipe} from './pipes/MDFilesFilterPipe';
import {FileDTOToPathPipe} from './pipes/FileDTOToPathPipe';
import {BlogService} from './ui/gallery/blog/blog.service';
import {PhotoFilterPipe} from './pipes/PhotoFilterPipe';
import {PreviewSettingsComponent} from './ui/settings/preview/preview.settings.component';
import {GallerySearchFieldComponent} from './ui/gallery/search/search-field/search-field.gallery.component';

@Injectable()
export class MyHammerConfig extends HammerGestureConfig {
Expand Down Expand Up @@ -206,6 +208,7 @@ Marker.prototype.options.icon = iconDefault;
FrameComponent,
GallerySearchComponent,
GallerySearchQueryEntryComponent,
GallerySearchFieldBaseComponent,
GallerySearchFieldComponent,
GallerySearchQueryBuilderComponent,
GalleryShareComponent,
Expand Down Expand Up @@ -241,6 +244,7 @@ Marker.prototype.options.icon = iconDefault;
JobProgressComponent,
JobsSettingsComponent,
JobButtonComponent,
PreviewSettingsComponent,

// Pipes
StringifyRole,
Expand Down
15 changes: 10 additions & 5 deletions src/frontend/app/ui/admin/admin.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ <h5 class="card-header" i18n>
{{notification.details | json}}
<ng-container *ngIf="notification.request">
<br/>
Request: "{{notification.request.method}}", url: "{{notification.request.url}}", status code: "{{notification.request.statusCode}}"
Request: "{{notification.request.method}}", url: "{{notification.request.url}}", status code:
"{{notification.request.statusCode}}"
</ng-container>
</div>
</ng-container>
Expand Down Expand Up @@ -74,7 +75,8 @@ <h5 class="card-header" i18n>
<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 i18n="title of left card in settings page that contains settings contents" class="card-title">Menu</h5>
<h5 i18n="title of left card in settings page that contains settings contents" 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)"
Expand Down Expand Up @@ -104,6 +106,9 @@ <h5 i18n="title of left card in settings page that contains settings contents" c
<app-settings-thumbnail #setting #thumbnail
[hidden]="!thumbnail.HasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-thumbnail>
<app-settings-preview #setting #preview
[hidden]="!preview.HasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-preview>
<app-settings-search #setting #search
[hidden]="!search.HasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-search>
Expand All @@ -126,8 +131,8 @@ <h5 i18n="title of left card in settings page that contains settings contents" c
[hidden]="!faces.HasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-faces>
<app-settings-albums #setting #albums
[hidden]="!albums.HasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-albums>
[hidden]="!albums.HasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-albums>
<app-settings-indexing #setting #indexing
[hidden]="!indexing.HasAvailableSettings"
[simplifiedMode]="simplifiedMode"></app-settings-indexing>
Expand All @@ -141,7 +146,7 @@ <h5 i18n="title of left card in settings page that contains settings contents" c
<div class="col-12">
<div class="text-right">
<ng-container i18n>Up time</ng-container><!--
-->: {{(settingsService.settings | async).Server.Environment.upTime | date:'medium'}}
-->: {{(settingsService.settings | async).Server.Environment.upTime | date:'medium'}}
</div>
</div>
</div>
Expand Down
24 changes: 24 additions & 0 deletions src/frontend/app/ui/gallery/search/AutoCompleteRenderItem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import {SearchQueryTypes} from '../../../../../common/entities/SearchQueryDTO';

export class AutoCompleteRenderItem {
public preText = '';
public highLightText = '';
public postText = '';
public type: SearchQueryTypes;
public queryHint: string;
public notSearchable: boolean;

constructor(public text: string, searchText: string, type: SearchQueryTypes, queryHint: string, notSearchable = false) {
const preIndex = text.toLowerCase().indexOf(searchText.toLowerCase());
if (preIndex > -1) {
this.preText = text.substring(0, preIndex);
this.highLightText = text.substring(preIndex, preIndex + searchText.length);
this.postText = text.substring(preIndex + searchText.length);
} else {
this.postText = text;
}
this.type = type;
this.queryHint = queryHint;
this.notSearchable = notSearchable;
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<app-gallery-search-field [(ngModel)]="rawSearchText"
(ngModelChange)="validateRawSearchText()"
(search)="search.emit()"
name="form-search-field">
<app-gallery-search-field-base [(ngModel)]="rawSearchText"
(ngModelChange)="validateRawSearchText()"
(search)="search.emit()"
[placeholder]="placeholder"
name="form-search-field">

</app-gallery-search-field>
</app-gallery-search-field-base>
<hr>
<app-gallery-search-query-entry
[(ngModel)]="searchQueryDTO"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Component, EventEmitter, forwardRef, Output} from '@angular/core';
import {Component, EventEmitter, forwardRef, Input, Output} from '@angular/core';
import {SearchQueryDTO, SearchQueryTypes, TextSearch} from '../../../../../../common/entities/SearchQueryDTO';
import {ControlValueAccessor, FormControl, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors, Validator} from '@angular/forms';
import {SearchQueryParserService} from '../search-query-parser.service';
Expand All @@ -23,6 +23,7 @@ import {SearchQueryParserService} from '../search-query-parser.service';
export class GallerySearchQueryBuilderComponent implements ControlValueAccessor, Validator {
public searchQueryDTO: SearchQueryDTO = {type: SearchQueryTypes.any_text, text: ''} as TextSearch;
@Output() search = new EventEmitter<void>();
@Input() placeholder: string;
public rawSearchText = '';


Expand Down Expand Up @@ -59,6 +60,7 @@ export class GallerySearchQueryBuilderComponent implements ControlValueAccessor,

public writeValue(obj: any): void {
this.searchQueryDTO = obj;
this.rawSearchText = this.searchQueryParserService.stringify(this.searchQueryDTO);
}

registerOnChange(fn: (_: any) => void): void {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
.autocomplete-list {
position: absolute;
left: 0;
top: 34px;
background-color: white;
width: 100%;
border: 1px solid #ccc;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
padding: 5px 0;
-webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
z-index: 7;
}

.insert-button {
margin-right: -15px;
display: none;
margin-top: 2px;
}

.autocomplete-item-selected .insert-button {
display: block;
}
@media (hover: none) {
.insert-button {
display: block;
}
}

.autocomplete-item-selected .insert-button:hover {
color: black;
}

.autocomplete-item {
cursor: pointer;
padding-top: 2px;
padding-bottom: 2px;
font-size: 17px;
}

.autocomplete-item {
color: #333;
padding: 0 20px;
line-height: 1.42857143;
font-weight: 400;
display: block;
}

.autocomplete-item-selected {
background-color: #007bff;
color: #FFF;
}


.search-text {
z-index: 6;
width: 100%;
background: transparent;
}

.search-hint {
left: 0;
z-index: 1;
width: 100%;
position: absolute;
margin-left: 0 !important;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<div class="input-group">
<input type="text"
class="form-control search-text"
[placeholder]="placeholder"
(keyup)="onSearchChange($event)"
(blur)="onFocusLost()"
[(ngModel)]="rawSearchText"
(ngModelChange)="onChange()"
(keydown.enter)="OnEnter($event)"
(keydown.arrowRight)="applyHint($event)"
(keydown.arrowUp)="selectAutocompleteUp()"
(keydown.arrowDown)="selectAutocompleteDown()"
(scroll)="Scrolled()"
(selectionchange)="Scrolled()"
#name="ngModel"
size="30"
ngControl="search"
name="srch-term"
id="srch-term"
#SearchField
autocomplete="off">
<input type="text"
class="form-control search-hint"
[ngModel]="SearchHint"
size="30"
name="srch-term-hint"
id="srch-term-hint"
#SearchHintField
autocomplete="off">


<div class="autocomplete-list" *ngIf="autoCompleteRenders.length > 0"
(mouseover)="setMouseOverAutoComplete(true)" (mouseout)="setMouseOverAutoComplete(false)">
<div class="autocomplete-item"
[ngClass]="{'autocomplete-item-selected': highlightedAutoCompleteItem === i}"
(mouseover)="setMouseOverAutoCompleteItem(i)"
(click)="searchAutoComplete(item)"
*ngFor="let item of autoCompleteRenders; let i = index">
<div>
<span [ngSwitch]="item.type">
<span *ngSwitchCase="SearchQueryTypes.caption" class="oi oi-image"></span>
<span *ngSwitchCase="SearchQueryTypes.directory" class="oi oi-folder"></span>
<span *ngSwitchCase="SearchQueryTypes.file_name" class="oi oi-image"></span>
<span *ngSwitchCase="SearchQueryTypes.keyword" class="oi oi-tag"></span>
<span *ngSwitchCase="SearchQueryTypes.person" class="oi oi-person"></span>
<span *ngSwitchCase="SearchQueryTypes.position" class="oi oi-map-marker"></span>
<span *ngSwitchCase="SearchQueryTypes.distance" class="oi oi-map-marker"></span>
</span>
{{item.preText}}<strong>{{item.highLightText}}</strong>{{item.postText}}
<span class="oi oi-chevron-right insert-button float-right" (click)="applyAutoComplete(item)"
title="Insert"
i18n-title>
</span>
</div>
</div>
</div>

</div>


Loading

0 comments on commit 1255246

Please sign in to comment.