mirror of
https://github.com/bpatrik/pigallery2.git
synced 2025-01-12 04:23:09 +02:00
adding random photo url generator
upgrading packages
This commit is contained in:
parent
151a3782ac
commit
2ea0ea42e3
@ -23,6 +23,7 @@
|
|||||||
"node_modules/ngx-toastr/toastr.css",
|
"node_modules/ngx-toastr/toastr.css",
|
||||||
"node_modules/bootstrap/dist/css/bootstrap.css",
|
"node_modules/bootstrap/dist/css/bootstrap.css",
|
||||||
"node_modules/open-iconic/font/css/open-iconic-bootstrap.css",
|
"node_modules/open-iconic/font/css/open-iconic-bootstrap.css",
|
||||||
|
"node_modules/ngx-bootstrap/datepicker/bs-datepicker.css",
|
||||||
"frontend/styles.css"
|
"frontend/styles.css"
|
||||||
],
|
],
|
||||||
"scripts": []
|
"scripts": []
|
||||||
|
@ -103,6 +103,28 @@ export class AdminMWs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static async updateRandomPhotoSettings(req: Request, res: Response, next: NextFunction) {
|
||||||
|
if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) {
|
||||||
|
return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'settings is needed'));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// only updating explicitly set config (not saving config set by the diagnostics)
|
||||||
|
const original = Config.original();
|
||||||
|
await ConfigDiagnostics.testRandomPhotoConfig(<ClientConfig.RandomPhotoConfig>req.body.settings, original);
|
||||||
|
|
||||||
|
Config.Client.RandomPhoto = <ClientConfig.RandomPhotoConfig>req.body.settings;
|
||||||
|
original.Client.RandomPhoto = <ClientConfig.RandomPhotoConfig>req.body.settings;
|
||||||
|
original.save();
|
||||||
|
await ConfigDiagnostics.runDiagnostics();
|
||||||
|
Logger.info(LOG_TAG, 'new config:');
|
||||||
|
Logger.info(LOG_TAG, JSON.stringify(Config, null, '\t'));
|
||||||
|
return next();
|
||||||
|
} catch (err) {
|
||||||
|
return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + JSON.stringify(err, null, ' '), err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static async updateSearchSettings(req: Request, res: Response, next: NextFunction) {
|
public static async updateSearchSettings(req: Request, res: Response, next: NextFunction) {
|
||||||
if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) {
|
if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) {
|
||||||
return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'settings is needed'));
|
return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'settings is needed'));
|
||||||
|
@ -10,6 +10,7 @@ import {PhotoDTO} from '../../common/entities/PhotoDTO';
|
|||||||
import {ProjectPath} from '../ProjectPath';
|
import {ProjectPath} from '../ProjectPath';
|
||||||
import {Config} from '../../common/config/private/Config';
|
import {Config} from '../../common/config/private/Config';
|
||||||
import {UserDTO} from '../../common/entities/UserDTO';
|
import {UserDTO} from '../../common/entities/UserDTO';
|
||||||
|
import {RandomQuery} from '../model/interfaces/IGalleryManager';
|
||||||
|
|
||||||
|
|
||||||
const LOG_TAG = '[GalleryMWs]';
|
const LOG_TAG = '[GalleryMWs]';
|
||||||
@ -79,6 +80,50 @@ export class GalleryMWs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static async getRandomImage(req: Request, res: Response, next: NextFunction) {
|
||||||
|
if (Config.Client.RandomPhoto.enabled === false) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
const query: RandomQuery = {};
|
||||||
|
if (req.query.directory) {
|
||||||
|
query.directory = req.query.directory;
|
||||||
|
}
|
||||||
|
if (req.query.recursive === 'true') {
|
||||||
|
query.recursive = true;
|
||||||
|
}
|
||||||
|
if (req.query.orientation) {
|
||||||
|
query.orientation = parseInt(req.query.orientation.toString(), 10);
|
||||||
|
}
|
||||||
|
if (req.query.maxResolution) {
|
||||||
|
query.maxResolution = parseFloat(req.query.maxResolution.toString());
|
||||||
|
}
|
||||||
|
if (req.query.minResolution) {
|
||||||
|
query.minResolution = parseFloat(req.query.minResolution.toString());
|
||||||
|
}
|
||||||
|
if (req.query.fromDate) {
|
||||||
|
query.fromDate = new Date(req.query.fromDate);
|
||||||
|
}
|
||||||
|
if (req.query.toDate) {
|
||||||
|
query.toDate = new Date(req.query.toDate);
|
||||||
|
}
|
||||||
|
if (query.minResolution && query.maxResolution && query.maxResolution < query.minResolution) {
|
||||||
|
return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'Input error: min resolution is greater than the max resolution'));
|
||||||
|
}
|
||||||
|
if (query.toDate && query.fromDate && query.toDate.getTime() < query.fromDate.getTime()) {
|
||||||
|
return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'Input error: to date is earlier than from date'));
|
||||||
|
}
|
||||||
|
|
||||||
|
const photo = await ObjectManagerRepository.getInstance()
|
||||||
|
.GalleryManager.getRandomPhoto(query);
|
||||||
|
if (!photo) {
|
||||||
|
return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'No photo found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
req.params.imagePath = path.join(photo.directory.path, photo.directory.name, photo.name);
|
||||||
|
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
public static loadImage(req: Request, res: Response, next: NextFunction) {
|
public static loadImage(req: Request, res: Response, next: NextFunction) {
|
||||||
if (!(req.params.imagePath)) {
|
if (!(req.params.imagePath)) {
|
||||||
return next();
|
return next();
|
||||||
|
@ -2,6 +2,7 @@ import {NextFunction, Request, Response} from 'express';
|
|||||||
import {CreateSharingDTO, SharingDTO} from '../../common/entities/SharingDTO';
|
import {CreateSharingDTO, SharingDTO} from '../../common/entities/SharingDTO';
|
||||||
import {ObjectManagerRepository} from '../model/ObjectManagerRepository';
|
import {ObjectManagerRepository} from '../model/ObjectManagerRepository';
|
||||||
import {ErrorCodes, ErrorDTO} from '../../common/entities/Error';
|
import {ErrorCodes, ErrorDTO} from '../../common/entities/Error';
|
||||||
|
import {Config} from '../../common/config/private/Config';
|
||||||
|
|
||||||
const LOG_TAG = '[SharingMWs]';
|
const LOG_TAG = '[SharingMWs]';
|
||||||
|
|
||||||
@ -20,6 +21,9 @@ export class SharingMWs {
|
|||||||
|
|
||||||
|
|
||||||
public static async getSharing(req: Request, res: Response, next: NextFunction) {
|
public static async getSharing(req: Request, res: Response, next: NextFunction) {
|
||||||
|
if (Config.Client.Sharing.enabled === false) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
const sharingKey = req.params.sharingKey;
|
const sharingKey = req.params.sharingKey;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -33,6 +37,9 @@ export class SharingMWs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static async createSharing(req: Request, res: Response, next: NextFunction) {
|
public static async createSharing(req: Request, res: Response, next: NextFunction) {
|
||||||
|
if (Config.Client.Sharing.enabled === false) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
if ((typeof req.body === 'undefined') || (typeof req.body.createSharing === 'undefined')) {
|
if ((typeof req.body === 'undefined') || (typeof req.body.createSharing === 'undefined')) {
|
||||||
return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'createSharing filed is missing'));
|
return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'createSharing filed is missing'));
|
||||||
}
|
}
|
||||||
@ -75,6 +82,9 @@ export class SharingMWs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static async updateSharing(req: Request, res: Response, next: NextFunction) {
|
public static async updateSharing(req: Request, res: Response, next: NextFunction) {
|
||||||
|
if (Config.Client.Sharing.enabled === false) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
if ((typeof req.body === 'undefined') || (typeof req.body.updateSharing === 'undefined')) {
|
if ((typeof req.body === 'undefined') || (typeof req.body.updateSharing === 'undefined')) {
|
||||||
return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'updateSharing filed is missing'));
|
return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'updateSharing filed is missing'));
|
||||||
}
|
}
|
||||||
|
@ -110,6 +110,14 @@ export class ConfigDiagnostics {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async testRandomPhotoConfig(sharing: ClientConfig.RandomPhotoConfig, config: IPrivateConfig) {
|
||||||
|
if (sharing.enabled === true &&
|
||||||
|
config.Server.database.type === DatabaseType.memory) {
|
||||||
|
throw new Error('Memory Database do not support sharing');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static async testMapConfig(map: ClientConfig.MapConfig) {
|
static async testMapConfig(map: ClientConfig.MapConfig) {
|
||||||
if (map.enabled === true && (!map.googleApiKey || map.googleApiKey.length === 0)) {
|
if (map.enabled === true && (!map.googleApiKey || map.googleApiKey.length === 0)) {
|
||||||
throw new Error('Maps need a valid google api key');
|
throw new Error('Maps need a valid google api key');
|
||||||
@ -192,6 +200,17 @@ export class ConfigDiagnostics {
|
|||||||
Config.Client.Sharing.enabled = false;
|
Config.Client.Sharing.enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await ConfigDiagnostics.testRandomPhotoConfig(Config.Client.Sharing, Config);
|
||||||
|
} catch (ex) {
|
||||||
|
const err: Error = ex;
|
||||||
|
NotificationManager.warning('Random Photo is not supported with these settings. Disabling temporally. ' +
|
||||||
|
'Please adjust the config properly.', err.toString());
|
||||||
|
Logger.warn(LOG_TAG, 'Random Photo is not supported with these settings, switching off..', err.toString());
|
||||||
|
Config.Client.Sharing.enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await ConfigDiagnostics.testMapConfig(Config.Client.Map);
|
await ConfigDiagnostics.testMapConfig(Config.Client.Map);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
|
@ -1,8 +1,22 @@
|
|||||||
import {DirectoryDTO} from '../../../common/entities/DirectoryDTO';
|
import {DirectoryDTO} from '../../../common/entities/DirectoryDTO';
|
||||||
|
import {PhotoDTO} from '../../../common/entities/PhotoDTO';
|
||||||
|
import {OrientationType, RandomQueryDTO} from '../../../common/entities/RandomQueryDTO';
|
||||||
|
|
||||||
|
export interface RandomQuery {
|
||||||
|
directory?: string;
|
||||||
|
recursive?: boolean;
|
||||||
|
orientation?: OrientationType;
|
||||||
|
fromDate?: Date;
|
||||||
|
toDate?: Date;
|
||||||
|
minResolution?: number;
|
||||||
|
maxResolution?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IGalleryManager {
|
export interface IGalleryManager {
|
||||||
listDirectory(relativeDirectoryName: string,
|
listDirectory(relativeDirectoryName: string,
|
||||||
knownLastModified?: number,
|
knownLastModified?: number,
|
||||||
knownLastScanned?: number): Promise<DirectoryDTO>;
|
knownLastScanned?: number): Promise<DirectoryDTO>;
|
||||||
|
|
||||||
|
getRandomPhoto(queryFilter: RandomQuery): Promise<PhotoDTO>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import {DiskManager} from '../DiskManger';
|
|||||||
import {ProjectPath} from '../../ProjectPath';
|
import {ProjectPath} from '../../ProjectPath';
|
||||||
import {Config} from '../../../common/config/private/Config';
|
import {Config} from '../../../common/config/private/Config';
|
||||||
import {ReIndexingSensitivity} from '../../../common/config/private/IPrivateConfig';
|
import {ReIndexingSensitivity} from '../../../common/config/private/IPrivateConfig';
|
||||||
|
import {PhotoDTO} from '../../../common/entities/PhotoDTO';
|
||||||
|
|
||||||
export class GalleryManager implements IGalleryManager {
|
export class GalleryManager implements IGalleryManager {
|
||||||
|
|
||||||
@ -23,4 +24,7 @@ export class GalleryManager implements IGalleryManager {
|
|||||||
return DiskManager.scanDirectory(relativeDirectoryName);
|
return DiskManager.scanDirectory(relativeDirectoryName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getRandomPhoto(RandomQuery): Promise<PhotoDTO> {
|
||||||
|
throw new Error('Random photo is not supported without database');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {IGalleryManager} from '../interfaces/IGalleryManager';
|
import {IGalleryManager, RandomQuery} from '../interfaces/IGalleryManager';
|
||||||
import {DirectoryDTO} from '../../../common/entities/DirectoryDTO';
|
import {DirectoryDTO} from '../../../common/entities/DirectoryDTO';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
@ -11,6 +11,9 @@ import {ProjectPath} from '../../ProjectPath';
|
|||||||
import {Config} from '../../../common/config/private/Config';
|
import {Config} from '../../../common/config/private/Config';
|
||||||
import {ISQLGalleryManager} from './IGalleryManager';
|
import {ISQLGalleryManager} from './IGalleryManager';
|
||||||
import {ReIndexingSensitivity} from '../../../common/config/private/IPrivateConfig';
|
import {ReIndexingSensitivity} from '../../../common/config/private/IPrivateConfig';
|
||||||
|
import {PhotoDTO} from '../../../common/entities/PhotoDTO';
|
||||||
|
import {OrientationType} from '../../../common/entities/RandomQueryDTO';
|
||||||
|
import {Brackets} from 'typeorm';
|
||||||
|
|
||||||
export class GalleryManager implements IGalleryManager, ISQLGalleryManager {
|
export class GalleryManager implements IGalleryManager, ISQLGalleryManager {
|
||||||
|
|
||||||
@ -226,4 +229,61 @@ export class GalleryManager implements IGalleryManager, ISQLGalleryManager {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getRandomPhoto(queryFilter: RandomQuery): Promise<PhotoDTO> {
|
||||||
|
const connection = await SQLConnection.getConnection();
|
||||||
|
const photosRepository = connection.getRepository(PhotoEntity);
|
||||||
|
|
||||||
|
const query = photosRepository.createQueryBuilder('photo');
|
||||||
|
query.innerJoinAndSelect('photo.directory', 'directory');
|
||||||
|
|
||||||
|
if (queryFilter.directory) {
|
||||||
|
const directoryName = path.basename(queryFilter.directory);
|
||||||
|
const directoryParent = path.join(path.dirname(queryFilter.directory), path.sep);
|
||||||
|
|
||||||
|
query.where(new Brackets(qb => {
|
||||||
|
qb.where('directory.name = :name AND directory.path = :path', {
|
||||||
|
name: directoryName,
|
||||||
|
path: directoryParent
|
||||||
|
});
|
||||||
|
|
||||||
|
if (queryFilter.recursive) {
|
||||||
|
qb.orWhere('directory.name LIKE :text COLLATE utf8_general_ci', {text: '%' + queryFilter.directory + '%'});
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (queryFilter.fromDate) {
|
||||||
|
query.andWhere('photo.metadata.creationDate >= :fromDate', {
|
||||||
|
fromDate: queryFilter.fromDate.getTime()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (queryFilter.toDate) {
|
||||||
|
query.andWhere('photo.metadata.creationDate <= :toDate', {
|
||||||
|
toDate: queryFilter.toDate.getTime()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (queryFilter.minResolution) {
|
||||||
|
query.andWhere('photo.metadata.size.width * photo.metadata.size.height >= :minRes', {
|
||||||
|
minRes: queryFilter.minResolution * 1000 * 1000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (queryFilter.maxResolution) {
|
||||||
|
query.andWhere('photo.metadata.size.width * photo.metadata.size.height <= :maxRes', {
|
||||||
|
maxRes: queryFilter.maxResolution * 1000 * 1000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (queryFilter.orientation === OrientationType.landscape) {
|
||||||
|
query.andWhere('photo.metadata.size.width >= photo.metadata.size.height');
|
||||||
|
}
|
||||||
|
if (queryFilter.orientation === OrientationType.portrait) {
|
||||||
|
query.andWhere('photo.metadata.size.width <= photo.metadata.size.height');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return await query.groupBy('RANDOM()').limit(1).getOne();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import {DirectoryDTO} from '../../../common/entities/DirectoryDTO';
|
import {DirectoryDTO} from '../../../common/entities/DirectoryDTO';
|
||||||
|
import {IGalleryManager} from '../interfaces/IGalleryManager';
|
||||||
|
|
||||||
export interface ISQLGalleryManager {
|
export interface ISQLGalleryManager extends IGalleryManager{
|
||||||
listDirectory(relativeDirectoryName: string,
|
listDirectory(relativeDirectoryName: string,
|
||||||
knownLastModified?: number,
|
knownLastModified?: number,
|
||||||
knownLastScanned?: number): Promise<DirectoryDTO>;
|
knownLastScanned?: number): Promise<DirectoryDTO>;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {Metadata, SharpInstance} from 'sharp';
|
import {Metadata, Sharp} from 'sharp';
|
||||||
import {Dimensions, State} from 'gm';
|
import {Dimensions, State} from 'gm';
|
||||||
import {Logger} from '../../Logger';
|
import {Logger} from '../../Logger';
|
||||||
import {ThumbnailProcessingLib} from '../../../common/config/private/IPrivateConfig';
|
import {ThumbnailProcessingLib} from '../../../common/config/private/IPrivateConfig';
|
||||||
@ -87,7 +87,7 @@ export class RendererFactory {
|
|||||||
return async (input: RendererInput): Promise<void> => {
|
return async (input: RendererInput): Promise<void> => {
|
||||||
|
|
||||||
Logger.silly('[SharpThRenderer] rendering thumbnail:' + input.imagePath);
|
Logger.silly('[SharpThRenderer] rendering thumbnail:' + input.imagePath);
|
||||||
const image: SharpInstance = sharp(input.imagePath);
|
const image: Sharp = sharp(input.imagePath);
|
||||||
const metadata: Metadata = await image.metadata();
|
const metadata: Metadata = await image.metadata();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -110,9 +110,10 @@ export class RendererFactory {
|
|||||||
} else {
|
} else {
|
||||||
image
|
image
|
||||||
.resize(input.size, input.size, {
|
.resize(input.size, input.size, {
|
||||||
kernel: kernel
|
kernel: kernel,
|
||||||
})
|
position: sharp.gravity.centre,
|
||||||
.crop(sharp.strategy.center);
|
fit: 'cover'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
await image.jpeg().toFile(input.thPath);
|
await image.jpeg().toFile(input.thPath);
|
||||||
};
|
};
|
||||||
|
@ -84,6 +84,12 @@ export class AdminRouter {
|
|||||||
AdminMWs.updateShareSettings,
|
AdminMWs.updateShareSettings,
|
||||||
RenderingMWs.renderOK
|
RenderingMWs.renderOK
|
||||||
);
|
);
|
||||||
|
app.put('/api/settings/randomPhoto',
|
||||||
|
AuthenticationMWs.authenticate,
|
||||||
|
AuthenticationMWs.authorise(UserRoles.Admin),
|
||||||
|
AdminMWs.updateRandomPhotoSettings,
|
||||||
|
RenderingMWs.renderOK
|
||||||
|
);
|
||||||
app.put('/api/settings/basic',
|
app.put('/api/settings/basic',
|
||||||
AuthenticationMWs.authenticate,
|
AuthenticationMWs.authenticate,
|
||||||
AuthenticationMWs.authorise(UserRoles.Admin),
|
AuthenticationMWs.authorise(UserRoles.Admin),
|
||||||
|
@ -10,6 +10,7 @@ export class GalleryRouter {
|
|||||||
this.addGetImageIcon(app);
|
this.addGetImageIcon(app);
|
||||||
this.addGetImageThumbnail(app);
|
this.addGetImageThumbnail(app);
|
||||||
this.addGetImage(app);
|
this.addGetImage(app);
|
||||||
|
this.addRandom(app);
|
||||||
this.addDirectoryList(app);
|
this.addDirectoryList(app);
|
||||||
|
|
||||||
this.addSearch(app);
|
this.addSearch(app);
|
||||||
@ -38,6 +39,17 @@ export class GalleryRouter {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static addRandom(app) {
|
||||||
|
app.get(['/api/gallery/random'],
|
||||||
|
AuthenticationMWs.authenticate,
|
||||||
|
AuthenticationMWs.authorise(UserRoles.Guest),
|
||||||
|
// TODO: authorize path
|
||||||
|
GalleryMWs.getRandomImage,
|
||||||
|
GalleryMWs.loadImage,
|
||||||
|
RenderingMWs.renderFile
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private static addGetImageThumbnail(app) {
|
private static addGetImageThumbnail(app) {
|
||||||
app.get('/api/gallery/content/:imagePath(*\.(jpg|bmp|png|gif|jpeg))/thumbnail/:size?',
|
app.get('/api/gallery/content/:imagePath(*\.(jpg|bmp|png|gif|jpeg))/thumbnail/:size?',
|
||||||
AuthenticationMWs.authenticate,
|
AuthenticationMWs.authenticate,
|
||||||
|
13
common/QueryParams.ts
Normal file
13
common/QueryParams.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
export const QueryParams = {
|
||||||
|
gallery: {
|
||||||
|
random: {
|
||||||
|
directory: 'dir',
|
||||||
|
recursive: 'recursive',
|
||||||
|
orientation: 'orientation',
|
||||||
|
fromDate: 'fromDate',
|
||||||
|
toDate: 'toDate',
|
||||||
|
minResolution: 'fromRes',
|
||||||
|
maxResolution: 'toRes'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -16,6 +16,10 @@ export module ClientConfig {
|
|||||||
passwordProtected: boolean;
|
passwordProtected: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RandomPhotoConfig {
|
||||||
|
enabled: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface MapConfig {
|
export interface MapConfig {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
googleApiKey: string;
|
googleApiKey: string;
|
||||||
@ -32,6 +36,7 @@ export module ClientConfig {
|
|||||||
Search: SearchConfig;
|
Search: SearchConfig;
|
||||||
Sharing: SharingConfig;
|
Sharing: SharingConfig;
|
||||||
Map: MapConfig;
|
Map: MapConfig;
|
||||||
|
RandomPhoto: RandomPhotoConfig;
|
||||||
concurrentThumbnailGenerations: number;
|
concurrentThumbnailGenerations: number;
|
||||||
enableCache: boolean;
|
enableCache: boolean;
|
||||||
enableOnScrollRendering: boolean;
|
enableOnScrollRendering: boolean;
|
||||||
@ -73,6 +78,9 @@ export class PublicConfigClass {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
googleApiKey: ''
|
googleApiKey: ''
|
||||||
},
|
},
|
||||||
|
RandomPhoto: {
|
||||||
|
enabled: true
|
||||||
|
},
|
||||||
concurrentThumbnailGenerations: 1,
|
concurrentThumbnailGenerations: 1,
|
||||||
enableCache: true,
|
enableCache: true,
|
||||||
enableOnScrollRendering: true,
|
enableOnScrollRendering: true,
|
||||||
|
13
common/entities/RandomQueryDTO.ts
Normal file
13
common/entities/RandomQueryDTO.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
export enum OrientationType {
|
||||||
|
any = 0, portrait = 1, landscape = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RandomQueryDTO {
|
||||||
|
directory?: string;
|
||||||
|
recursive?: boolean;
|
||||||
|
orientation?: OrientationType;
|
||||||
|
fromDate?: string;
|
||||||
|
toDate?: string;
|
||||||
|
minResolution?: number;
|
||||||
|
maxResolution?: number;
|
||||||
|
}
|
@ -48,6 +48,8 @@
|
|||||||
<app-settings-share #share [hidden]="!share.hasAvailableSettings"
|
<app-settings-share #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 #map [hidden]="!map.hasAvailableSettings" [simplifiedMode]="simplifiedMode"></app-settings-map>
|
||||||
|
<app-settings-random-photo #random [hidden]="!random.hasAvailableSettings"
|
||||||
|
[simplifiedMode]="simplifiedMode"></app-settings-random-photo>
|
||||||
<app-settings-other #other [hidden]="!other.hasAvailableSettings"
|
<app-settings-other #other [hidden]="!other.hasAvailableSettings"
|
||||||
[simplifiedMode]="simplifiedMode"></app-settings-other>
|
[simplifiedMode]="simplifiedMode"></app-settings-other>
|
||||||
<app-settings-indexing #indexing [hidden]="!indexing.hasAvailableSettings"
|
<app-settings-indexing #indexing [hidden]="!indexing.hasAvailableSettings"
|
||||||
|
@ -43,11 +43,12 @@ import {GalleryShareComponent} from './gallery/share/share.gallery.component';
|
|||||||
import {ShareLoginComponent} from './sharelogin/share-login.component';
|
import {ShareLoginComponent} from './sharelogin/share-login.component';
|
||||||
import {ShareService} from './gallery/share.service';
|
import {ShareService} from './gallery/share.service';
|
||||||
import {ModalModule} from 'ngx-bootstrap/modal';
|
import {ModalModule} from 'ngx-bootstrap/modal';
|
||||||
|
import {BsDatepickerModule} from 'ngx-bootstrap/datepicker';
|
||||||
import {DatabaseSettingsComponent} from './settings/database/database.settings.component';
|
import {DatabaseSettingsComponent} from './settings/database/database.settings.component';
|
||||||
import {ToastrModule} from 'ngx-toastr';
|
import {ToastrModule} from 'ngx-toastr';
|
||||||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||||
import {NotificationService} from './model/notification.service';
|
import {NotificationService} from './model/notification.service';
|
||||||
import {JWBootstrapSwitchModule} from 'jw-bootstrap-switch-ng2';
|
import {JwBootstrapSwitchNg2Module} from 'jw-bootstrap-switch-ng2';
|
||||||
import {ClipboardModule} from 'ngx-clipboard';
|
import {ClipboardModule} from 'ngx-clipboard';
|
||||||
import {NavigationService} from './model/navigation.service';
|
import {NavigationService} from './model/navigation.service';
|
||||||
import {InfoPanelLightboxComponent} from './gallery/lightbox/infopanel/info-panel.lightbox.gallery.component';
|
import {InfoPanelLightboxComponent} from './gallery/lightbox/infopanel/info-panel.lightbox.gallery.component';
|
||||||
@ -69,6 +70,8 @@ import {I18n, MISSING_TRANSLATION_STRATEGY} from '@ngx-translate/i18n-polyfill';
|
|||||||
import {QueryService} from './model/query.service';
|
import {QueryService} from './model/query.service';
|
||||||
import {IconizeSortingMethod} from './pipes/IconizeSortingMethod';
|
import {IconizeSortingMethod} from './pipes/IconizeSortingMethod';
|
||||||
import {StringifySortingMethod} from './pipes/StringifySortingMethod';
|
import {StringifySortingMethod} from './pipes/StringifySortingMethod';
|
||||||
|
import {RandomQueryBuilderGalleryComponent} from './gallery/random-query-builder/random-query-builder.gallery.component';
|
||||||
|
import {RandomPhotoSettingsComponent} from './settings/random-photo/random-photo.settings.component';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class GoogleMapsConfig {
|
export class GoogleMapsConfig {
|
||||||
@ -117,14 +120,15 @@ export function translationsFactory(locale: string) {
|
|||||||
BrowserAnimationsModule,
|
BrowserAnimationsModule,
|
||||||
appRoutes,
|
appRoutes,
|
||||||
ClipboardModule,
|
ClipboardModule,
|
||||||
JWBootstrapSwitchModule,
|
JwBootstrapSwitchNg2Module,
|
||||||
TooltipModule.forRoot(),
|
TooltipModule.forRoot(),
|
||||||
ToastrModule.forRoot(),
|
ToastrModule.forRoot(),
|
||||||
ModalModule.forRoot(),
|
ModalModule.forRoot(),
|
||||||
CollapseModule.forRoot(),
|
CollapseModule.forRoot(),
|
||||||
BsDropdownModule.forRoot(),
|
BsDropdownModule.forRoot(),
|
||||||
AgmCoreModule.forRoot(),
|
AgmCoreModule.forRoot(),
|
||||||
SlimLoadingBarModule.forRoot()
|
SlimLoadingBarModule.forRoot(),
|
||||||
|
BsDatepickerModule.forRoot()
|
||||||
],
|
],
|
||||||
declarations: [AppComponent,
|
declarations: [AppComponent,
|
||||||
LoginComponent,
|
LoginComponent,
|
||||||
@ -148,6 +152,7 @@ export function translationsFactory(locale: string) {
|
|||||||
GalleryPhotoComponent,
|
GalleryPhotoComponent,
|
||||||
AdminComponent,
|
AdminComponent,
|
||||||
InfoPanelLightboxComponent,
|
InfoPanelLightboxComponent,
|
||||||
|
RandomQueryBuilderGalleryComponent,
|
||||||
// Settings
|
// Settings
|
||||||
UserMangerSettingsComponent,
|
UserMangerSettingsComponent,
|
||||||
DatabaseSettingsComponent,
|
DatabaseSettingsComponent,
|
||||||
@ -155,6 +160,7 @@ export function translationsFactory(locale: string) {
|
|||||||
ThumbnailSettingsComponent,
|
ThumbnailSettingsComponent,
|
||||||
SearchSettingsComponent,
|
SearchSettingsComponent,
|
||||||
ShareSettingsComponent,
|
ShareSettingsComponent,
|
||||||
|
RandomPhotoSettingsComponent,
|
||||||
BasicSettingsComponent,
|
BasicSettingsComponent,
|
||||||
OtherSettingsComponent,
|
OtherSettingsComponent,
|
||||||
IndexingSettingsComponent,
|
IndexingSettingsComponent,
|
||||||
@ -185,12 +191,12 @@ export function translationsFactory(locale: string) {
|
|||||||
deps: [LOCALE_ID]
|
deps: [LOCALE_ID]
|
||||||
},
|
},
|
||||||
I18n,
|
I18n,
|
||||||
/*
|
|
||||||
{provide: TRANSLATIONS, useValue: translationsFactory('en')},
|
{provide: TRANSLATIONS, useValue: translationsFactory('en')},
|
||||||
{provide: TRANSLATIONS_FORMAT, useValue: 'xlf'},
|
{provide: TRANSLATIONS_FORMAT, useValue: 'xlf'},
|
||||||
{provide: LOCALE_ID, useValue: 'en'},
|
{provide: LOCALE_ID, useValue: 'en'},
|
||||||
{provide: MISSING_TRANSLATION_STRATEGY, useValue: MissingTranslationStrategy.Ignore},
|
{provide: MISSING_TRANSLATION_STRATEGY, useValue: MissingTranslationStrategy.Ignore},
|
||||||
*/
|
|
||||||
],
|
],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
})
|
})
|
||||||
|
@ -69,3 +69,14 @@ app-language {
|
|||||||
padding-right: 1.0rem;
|
padding-right: 1.0rem;
|
||||||
padding-left: 1.0rem;
|
padding-left: 1.0rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
a.dropdown-item {
|
||||||
|
padding:0.3rem 1.0rem 0.3rem 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.dropdown-item span{
|
||||||
|
padding-right: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -19,26 +19,39 @@
|
|||||||
<span class="navbar-text" *ngIf="user.value">
|
<span class="navbar-text" *ngIf="user.value">
|
||||||
<span class="oi oi-person"></span> {{user.value.name}}</span>
|
<span class="oi oi-person"></span> {{user.value.name}}</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" *ngIf="isAdmin()">
|
|
||||||
<a style="cursor: pointer;"
|
|
||||||
class="nav-link admin-link"
|
|
||||||
[routerLink]="['/admin']">
|
|
||||||
<span class="oi oi-wrench"></span>
|
|
||||||
<span *ngIf="notificationService.notifications.length>0" class="badge">{{notificationService.notifications.length}}</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item ml-2">
|
<li class="nav-item ml-2">
|
||||||
<app-language class="navbar-btn" isDark="true"></app-language>
|
<app-language class="navbar-btn" isDark="true"></app-language>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item ml-2" *ngIf="authenticationRequired">
|
<div class="btn-group" dropdown placement="bottom right" container="body" >
|
||||||
<button class="btn btn-default navbar-btn"
|
<button id="button-basic" dropdownToggle
|
||||||
style="cursor: pointer"
|
type="button" class="btn btn-dark dropdown-toggle"
|
||||||
(click)="logout()">
|
aria-controls="dropdown-basic">
|
||||||
|
<span class="oi oi-menu"></span>
|
||||||
|
<span *ngIf="isAdmin() && notificationService.notifications.length>0" class="badge">{{notificationService.notifications.length}}</span>
|
||||||
|
|
||||||
|
</button>
|
||||||
|
<ul id="dropdown-basic" *dropdownMenu
|
||||||
|
class="dropdown-menu dropdown-menu-right"
|
||||||
|
role="menu" aria-labelledby="button-basic" >
|
||||||
|
<ng-content select="[navbar-menu]"></ng-content>
|
||||||
|
<li role="menuitem" *ngIf="isAdmin()">
|
||||||
|
<a class="dropdown-item" href="#" [routerLink]="['/admin']">
|
||||||
|
<span class="oi oi-wrench"></span>
|
||||||
|
<span *ngIf="notificationService.notifications.length>0" class="badge">{{notificationService.notifications.length}}</span>
|
||||||
|
<ng-container i18n>Settings</ng-container>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li role="menuitem" *ngIf="authenticationRequired">
|
||||||
|
<a class="dropdown-item" href="#" (click)="logout()" i18n>
|
||||||
<span class="oi oi-account-logout"></span>
|
<span class="oi oi-account-logout"></span>
|
||||||
<ng-container i18n>Logout</ng-container>
|
<ng-container i18n>Logout</ng-container>
|
||||||
</button>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
</div><!--/.nav-collapse -->
|
</div><!--/.nav-collapse -->
|
||||||
|
@ -8,7 +8,9 @@ export class FullScreenService {
|
|||||||
OnFullScreenChange = new Event<boolean>();
|
OnFullScreenChange = new Event<boolean>();
|
||||||
|
|
||||||
public isFullScreenEnabled(): boolean {
|
public isFullScreenEnabled(): boolean {
|
||||||
return !!(document.fullscreenElement || document['mozFullScreenElement'] || document.webkitFullscreenElement);
|
return !!(document['fullscreenElement'] ||
|
||||||
|
document['mozFullScreenElement'] ||
|
||||||
|
document['webkitFullscreenElement']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public showFullScreen(element: any) {
|
public showFullScreen(element: any) {
|
||||||
@ -37,8 +39,8 @@ export class FullScreenService {
|
|||||||
document.exitFullscreen();
|
document.exitFullscreen();
|
||||||
} else if (document['mozCancelFullScreen']) {
|
} else if (document['mozCancelFullScreen']) {
|
||||||
document['mozCancelFullScreen']();
|
document['mozCancelFullScreen']();
|
||||||
} else if (document.webkitExitFullscreen) {
|
} else if (document['webkitExitFullscreen']) {
|
||||||
document.webkitExitFullscreen();
|
document['webkitExitFullscreen']();
|
||||||
}
|
}
|
||||||
this.OnFullScreenChange.trigger(false);
|
this.OnFullScreenChange.trigger(false);
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
<app-frame>
|
<app-frame>
|
||||||
|
|
||||||
<ng-container navbar>
|
<ng-container navbar>
|
||||||
|
|
||||||
<li class="nav-item" *ngIf="countDown">
|
<li class="nav-item" *ngIf="countDown">
|
||||||
<span class="navbar-text">
|
<span class="navbar-text">
|
||||||
<ng-container i18n>Link availability</ng-container>
|
<ng-container i18n>Link availability</ng-container>
|
||||||
@ -20,6 +19,12 @@
|
|||||||
</li>
|
</li>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container navbar-menu>
|
||||||
|
<li role="menuitem" *ngIf="showRandomPhotoBuilder">
|
||||||
|
<app-gallery-random-query-builder ></app-gallery-random-query-builder>
|
||||||
|
</li>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
|
||||||
<div body class="container-fluid" style="width: 100%; padding:0" *ngIf="_galleryService.content.value.directory">
|
<div body class="container-fluid" style="width: 100%; padding:0" *ngIf="_galleryService.content.value.directory">
|
||||||
<app-gallery-navbar [directory]="_galleryService.content.value.directory"></app-gallery-navbar>
|
<app-gallery-navbar [directory]="_galleryService.content.value.directory"></app-gallery-navbar>
|
||||||
|
@ -14,8 +14,6 @@ import {UserRoles} from '../../../common/entities/UserDTO';
|
|||||||
import {interval} from 'rxjs';
|
import {interval} from 'rxjs';
|
||||||
import {ContentWrapper} from '../../../common/entities/ConentWrapper';
|
import {ContentWrapper} from '../../../common/entities/ConentWrapper';
|
||||||
import {PageHelper} from '../model/page.helper';
|
import {PageHelper} from '../model/page.helper';
|
||||||
import {QueryService} from '../model/query.service';
|
|
||||||
import {LightboxStates} from './lightbox/lightbox.gallery.component';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-gallery',
|
selector: 'app-gallery',
|
||||||
@ -29,6 +27,8 @@ export class GalleryComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
public showSearchBar = false;
|
public showSearchBar = false;
|
||||||
public showShare = false;
|
public showShare = false;
|
||||||
|
public showRandomPhotoBuilder = false;
|
||||||
|
|
||||||
public directories: DirectoryDTO[] = [];
|
public directories: DirectoryDTO[] = [];
|
||||||
public isPhotoWithLocation = false;
|
public isPhotoWithLocation = false;
|
||||||
private $counter;
|
private $counter;
|
||||||
@ -142,7 +142,7 @@ export class GalleryComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
this.showSearchBar = Config.Client.Search.enabled && this._authService.isAuthorized(UserRoles.Guest);
|
this.showSearchBar = Config.Client.Search.enabled && this._authService.isAuthorized(UserRoles.Guest);
|
||||||
this.showShare = Config.Client.Sharing.enabled && this._authService.isAuthorized(UserRoles.User);
|
this.showShare = Config.Client.Sharing.enabled && this._authService.isAuthorized(UserRoles.User);
|
||||||
|
this.showRandomPhotoBuilder = Config.Client.RandomPhoto.enabled && this._authService.isAuthorized(UserRoles.Guest);
|
||||||
this.subscription.content = this._galleryService.content.subscribe(this.onContentChange);
|
this.subscription.content = this._galleryService.content.subscribe(this.onContentChange);
|
||||||
this.subscription.route = this._route.params.subscribe(this.onRoute);
|
this.subscription.route = this._route.params.subscribe(this.onRoute);
|
||||||
|
|
||||||
|
@ -228,18 +228,18 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const event: KeyboardEvent = window.event ? <any>window.event : e;
|
const event: KeyboardEvent = window.event ? <any>window.event : e;
|
||||||
switch (event.keyCode) {
|
switch (event.key) {
|
||||||
case 37:
|
case 'ArrowLeft':
|
||||||
if (this.activePhotoId > 0) {
|
if (this.activePhotoId > 0) {
|
||||||
this.prevImage();
|
this.prevImage();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 39:
|
case 'ArrowRight':
|
||||||
if (this.activePhotoId < this.gridPhotoQL.length - 1) {
|
if (this.activePhotoId < this.gridPhotoQL.length - 1) {
|
||||||
this.nextImage();
|
this.nextImage();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 27: // escape
|
case 'Escape': // escape
|
||||||
this.hide();
|
this.hide();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -9,13 +9,14 @@
|
|||||||
<agm-map
|
<agm-map
|
||||||
[style.width.px]="mapDimension.width"
|
[style.width.px]="mapDimension.width"
|
||||||
[style.height.px]="mapDimension.height"
|
[style.height.px]="mapDimension.height"
|
||||||
[fitBounds]="latlngBounds">
|
[fitBounds]="true">
|
||||||
<agm-marker
|
<agm-marker
|
||||||
*ngFor="let photo of mapPhotos"
|
*ngFor="let photo of mapPhotos"
|
||||||
[latitude]="photo.latitude"
|
[latitude]="photo.latitude"
|
||||||
[longitude]="photo.longitude"
|
[longitude]="photo.longitude"
|
||||||
[iconUrl]="photo.iconUrl"
|
[iconUrl]="photo.iconUrl"
|
||||||
(markerClick)="loadPreview(photo)">
|
(markerClick)="loadPreview(photo)"
|
||||||
|
[agmFitBounds]="true">
|
||||||
<agm-info-window>
|
<agm-info-window>
|
||||||
<img *ngIf="photo.preview.thumbnail.Src"
|
<img *ngIf="photo.preview.thumbnail.Src"
|
||||||
[style.width.px]="photo.preview.width"
|
[style.width.px]="photo.preview.width"
|
||||||
|
@ -29,7 +29,6 @@ export class GalleryMapLightboxComponent implements OnChanges, AfterViewInit {
|
|||||||
@ViewChild('root') elementRef: ElementRef;
|
@ViewChild('root') elementRef: ElementRef;
|
||||||
|
|
||||||
@ViewChild(AgmMap) map: AgmMap;
|
@ViewChild(AgmMap) map: AgmMap;
|
||||||
public latlngBounds: LatLngBounds;
|
|
||||||
|
|
||||||
|
|
||||||
constructor(public fullScreenService: FullScreenService,
|
constructor(public fullScreenService: FullScreenService,
|
||||||
@ -136,25 +135,6 @@ export class GalleryMapLightboxComponent implements OnChanges, AfterViewInit {
|
|||||||
return obj;
|
return obj;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.findPhotosBounds().catch(console.error);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private async findPhotosBounds() {
|
|
||||||
await this.mapsAPILoader.load();
|
|
||||||
if (!window['google']) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.latlngBounds = new window['google'].maps.LatLngBounds();
|
|
||||||
|
|
||||||
for (const photo of this.mapPhotos) {
|
|
||||||
this.latlngBounds.extend(new window['google'].maps.LatLng(photo.latitude, photo.longitude));
|
|
||||||
}
|
|
||||||
const clat = this.latlngBounds.getCenter().lat();
|
|
||||||
const clng = this.latlngBounds.getCenter().lng();
|
|
||||||
this.latlngBounds.extend(new window['google'].maps.LatLng(clat + 0.5, clng + 0.5));
|
|
||||||
this.latlngBounds.extend(new window['google'].maps.LatLng(clat - 0.5, clng - 0.5));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -188,8 +168,8 @@ export class GalleryMapLightboxComponent implements OnChanges, AfterViewInit {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const event: KeyboardEvent = window.event ? <any>window.event : e;
|
const event: KeyboardEvent = window.event ? <any>window.event : e;
|
||||||
switch (event.keyCode) {
|
switch (event.key) {
|
||||||
case 27: // escape
|
case 'Escape': // escape
|
||||||
this.hide();
|
this.hide();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -8,11 +8,12 @@
|
|||||||
[usePanning]="false"
|
[usePanning]="false"
|
||||||
[draggable]="false"
|
[draggable]="false"
|
||||||
[zoom]="0"
|
[zoom]="0"
|
||||||
[fitBounds]="latlngBounds">
|
[fitBounds]="true">
|
||||||
<agm-marker
|
<agm-marker
|
||||||
*ngFor="let photo of mapPhotos"
|
*ngFor="let photo of mapPhotos"
|
||||||
[latitude]="photo.latitude"
|
[latitude]="photo.latitude"
|
||||||
[longitude]="photo.longitude">
|
[longitude]="photo.longitude"
|
||||||
|
[agmFitBounds]="true">
|
||||||
</agm-marker>
|
</agm-marker>
|
||||||
</agm-map>
|
</agm-map>
|
||||||
<div class="overlay" (click)="click()"
|
<div class="overlay" (click)="click()"
|
||||||
|
@ -4,7 +4,7 @@ import {Dimension, IRenderable} from '../../model/IRenderable';
|
|||||||
import {GalleryMapLightboxComponent} from './lightbox/lightbox.map.gallery.component';
|
import {GalleryMapLightboxComponent} from './lightbox/lightbox.map.gallery.component';
|
||||||
import {ThumbnailManagerService} from '../thumnailManager.service';
|
import {ThumbnailManagerService} from '../thumnailManager.service';
|
||||||
import {FullScreenService} from '../fullscreen.service';
|
import {FullScreenService} from '../fullscreen.service';
|
||||||
import {LatLngBounds, MapsAPILoader} from '@agm/core';
|
import {LatLngBounds, MapsAPILoader, AgmMap} from '@agm/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-gallery-map',
|
selector: 'app-gallery-map',
|
||||||
@ -17,8 +17,7 @@ export class GalleryMapComponent implements OnChanges, IRenderable, AfterViewIni
|
|||||||
@ViewChild(GalleryMapLightboxComponent) mapLightbox: GalleryMapLightboxComponent;
|
@ViewChild(GalleryMapLightboxComponent) mapLightbox: GalleryMapLightboxComponent;
|
||||||
|
|
||||||
mapPhotos: Array<{ latitude: number, longitude: number }> = [];
|
mapPhotos: Array<{ latitude: number, longitude: number }> = [];
|
||||||
public latlngBounds: LatLngBounds;
|
@ViewChild('map') mapElement: ElementRef;
|
||||||
@ViewChild('map') map: ElementRef;
|
|
||||||
height = null;
|
height = null;
|
||||||
|
|
||||||
|
|
||||||
@ -37,32 +36,14 @@ export class GalleryMapComponent implements OnChanges, IRenderable, AfterViewIni
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
this.findPhotosBounds().catch(console.error);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit() {
|
ngAfterViewInit() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.height = this.map.nativeElement.clientHeight;
|
this.height = this.mapElement.nativeElement.clientHeight;
|
||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async findPhotosBounds() {
|
|
||||||
await this.mapsAPILoader.load();
|
|
||||||
if (!window['google']) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.latlngBounds = new window['google'].maps.LatLngBounds();
|
|
||||||
|
|
||||||
for (const photo of this.mapPhotos) {
|
|
||||||
this.latlngBounds.extend(new window['google'].maps.LatLng(photo.latitude, photo.longitude));
|
|
||||||
}
|
|
||||||
const clat = this.latlngBounds.getCenter().lat();
|
|
||||||
const clng = this.latlngBounds.getCenter().lng();
|
|
||||||
this.latlngBounds.extend(new window['google'].maps.LatLng(clat + 0.5, clng + 0.5));
|
|
||||||
this.latlngBounds.extend(new window['google'].maps.LatLng(clat - 0.5, clng - 0.5));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
click() {
|
click() {
|
||||||
this.mapLightbox.show(this.getDimension());
|
this.mapLightbox.show(this.getDimension());
|
||||||
@ -70,10 +51,10 @@ export class GalleryMapComponent implements OnChanges, IRenderable, AfterViewIni
|
|||||||
|
|
||||||
public getDimension(): Dimension {
|
public getDimension(): Dimension {
|
||||||
return <Dimension>{
|
return <Dimension>{
|
||||||
top: this.map.nativeElement.offsetTop,
|
top: this.mapElement.nativeElement.offsetTop,
|
||||||
left: this.map.nativeElement.offsetLeft,
|
left: this.mapElement.nativeElement.offsetLeft,
|
||||||
width: this.map.nativeElement.offsetWidth,
|
width: this.mapElement.nativeElement.offsetWidth,
|
||||||
height: this.map.nativeElement.offsetHeight
|
height: this.mapElement.nativeElement.offsetHeight
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
.modal {
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-width {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
padding-top: 1px;
|
||||||
|
padding-bottom: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.disabled {
|
||||||
|
/* Make the disabled links grayish*/
|
||||||
|
color: gray;
|
||||||
|
/* And disable the pointer events */
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.dropdown-item {
|
||||||
|
padding:0.3rem 1.0rem 0.3rem 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.dropdown-item span{
|
||||||
|
padding-right: 0.8rem;
|
||||||
|
}
|
@ -0,0 +1,134 @@
|
|||||||
|
<a class="dropdown-item {{enabled? '' : 'disabled'}}" href="#" (click)="openModal(randomModal)">
|
||||||
|
<span class="oi oi-random"></span>
|
||||||
|
<ng-container i18n>Random link</ng-container>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<ng-template #randomModal>
|
||||||
|
<!-- sharing Modal-->
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" i18n>Random Link generator</h5>
|
||||||
|
<button type="button" class="close" (click)="hideModal()" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-7 col-sm-9">
|
||||||
|
<input id="randomLink"
|
||||||
|
name="randomLink"
|
||||||
|
placeholder="link"
|
||||||
|
class="form-control input-md"
|
||||||
|
type="text"
|
||||||
|
[ngModel]="url">
|
||||||
|
</div>
|
||||||
|
<div class="col-5 col-sm-3">
|
||||||
|
<button id="copyButton" name="copyButton"
|
||||||
|
ngxClipboard [cbContent]="url"
|
||||||
|
(cbOnSuccess)="onCopy()"
|
||||||
|
class="btn btn-primary btn-block" i18n>Copy
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr/>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-4">
|
||||||
|
<label class="control-label" i18n>In Folder:</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-8">
|
||||||
|
<input disabled type="text"
|
||||||
|
class="full-width form-control"
|
||||||
|
[ngModel]="data.directory">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-4">
|
||||||
|
<label class="control-label" i18n>Include subfolders:</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-8">
|
||||||
|
<bSwitch
|
||||||
|
class="switch"
|
||||||
|
name="includeSubfolders"
|
||||||
|
[switch-on-color]="'success'"
|
||||||
|
[switch-inverse]="'inverse'"
|
||||||
|
[switch-off-text]="text.No"
|
||||||
|
[switch-on-text]="text.Yes"
|
||||||
|
[switch-handle-width]="'100'"
|
||||||
|
[switch-label-width]="'20'"
|
||||||
|
(change)="update()"
|
||||||
|
[(ngModel)]="data.recursive">
|
||||||
|
</bSwitch>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-4">
|
||||||
|
<label class="control-label" i18n>Orientation:</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-4">
|
||||||
|
<select class="form-control" [(ngModel)]="data.orientation" (change)="update()" name="orientation"
|
||||||
|
required>
|
||||||
|
<option [ngValue]="OrientationType.any" i18n>Any</option>
|
||||||
|
<option [ngValue]="OrientationType.landscape" i18n>Landscape</option>
|
||||||
|
<option [ngValue]="OrientationType.portrait" i18n>Portrait</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<div class="col-4">
|
||||||
|
<label class="control-label" i18n>Date:</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-4 form-group">
|
||||||
|
<input type="text"
|
||||||
|
placeholder="from: YYYY-MM-DD"
|
||||||
|
class="form-control"
|
||||||
|
bsDatepicker
|
||||||
|
(bsValueChange)="update()"
|
||||||
|
[(ngModel)]="data.fromDate"
|
||||||
|
[bsConfig]="{ dateInputFormat: 'YYYY-MM-DD' }">
|
||||||
|
</div>
|
||||||
|
<div class="col-4 form-group">
|
||||||
|
<input type="text"
|
||||||
|
placeholder="to: YYYY-MM-DD"
|
||||||
|
class="form-control"
|
||||||
|
bsDatepicker
|
||||||
|
(bsValueChange)="update()"
|
||||||
|
[(ngModel)]="data.toDate"
|
||||||
|
[bsConfig]="{ dateInputFormat: 'YYYY-MM-DD' }">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-4">
|
||||||
|
<label class="control-label" i18n>Resolution:</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-4">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="number" class="form-control"
|
||||||
|
(change)="update()"
|
||||||
|
[(ngModel)]="data.minResolution"
|
||||||
|
id="minResolution" placeholder="min" step="1" min="0">
|
||||||
|
|
||||||
|
<div class="input-group-append">
|
||||||
|
<div class="input-group-text">Mpx</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-4">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="number" class="form-control"
|
||||||
|
(change)="update()"
|
||||||
|
[(ngModel)]="data.maxResolution"
|
||||||
|
id="maxResolution" placeholder="max" step="1" min="0">
|
||||||
|
<div class="input-group-append">
|
||||||
|
<div class="input-group-text">Mpx</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
@ -0,0 +1,104 @@
|
|||||||
|
import {Component, OnDestroy, OnInit, TemplateRef} from '@angular/core';
|
||||||
|
import {Utils} from '../../../../common/Utils';
|
||||||
|
import {GalleryService} from '../gallery.service';
|
||||||
|
import {ContentWrapper} from '../../../../common/entities/ConentWrapper';
|
||||||
|
import {Config} from '../../../../common/config/public/Config';
|
||||||
|
import {NotificationService} from '../../model/notification.service';
|
||||||
|
import {DirectoryDTO} from '../../../../common/entities/DirectoryDTO';
|
||||||
|
import {I18n} from '@ngx-translate/i18n-polyfill';
|
||||||
|
import {BsModalService} from 'ngx-bootstrap/modal';
|
||||||
|
import {BsModalRef} from 'ngx-bootstrap/modal/bs-modal-ref.service';
|
||||||
|
import {OrientationType, RandomQueryDTO} from '../../../../common/entities/RandomQueryDTO';
|
||||||
|
import {NetworkService} from '../../model/network/network.service';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-gallery-random-query-builder',
|
||||||
|
templateUrl: './random-query-builder.gallery.component.html',
|
||||||
|
styleUrls: ['./random-query-builder.gallery.component.css'],
|
||||||
|
})
|
||||||
|
export class RandomQueryBuilderGalleryComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
|
enabled = true;
|
||||||
|
url = '';
|
||||||
|
|
||||||
|
data: RandomQueryDTO = {
|
||||||
|
orientation: OrientationType.any,
|
||||||
|
directory: '',
|
||||||
|
recursive: true,
|
||||||
|
minResolution: null,
|
||||||
|
maxResolution: null,
|
||||||
|
toDate: null,
|
||||||
|
fromDate: null
|
||||||
|
};
|
||||||
|
contentSubscription = null;
|
||||||
|
|
||||||
|
OrientationType;
|
||||||
|
modalRef: BsModalRef;
|
||||||
|
|
||||||
|
text = {
|
||||||
|
Yes: 'Yes',
|
||||||
|
No: 'No'
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(public _galleryService: GalleryService,
|
||||||
|
private _notification: NotificationService,
|
||||||
|
public i18n: I18n,
|
||||||
|
private modalService: BsModalService) {
|
||||||
|
this.OrientationType = OrientationType;
|
||||||
|
this.text.Yes = i18n('Yes');
|
||||||
|
this.text.No = i18n('No');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.contentSubscription = this._galleryService.content.subscribe((content: ContentWrapper) => {
|
||||||
|
this.enabled = !!content.directory;
|
||||||
|
if (!this.enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.data.directory = Utils.concatUrls((<DirectoryDTO>content.directory).path, (<DirectoryDTO>content.directory).name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
if (this.contentSubscription !== null) {
|
||||||
|
this.contentSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
setTimeout(() => {
|
||||||
|
const data = Utils.clone(this.data);
|
||||||
|
for (const key of Object.keys(data)) {
|
||||||
|
if (!data[key]) {
|
||||||
|
delete data[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.url = NetworkService.buildUrl(Config.Client.publicUrl + '/api/gallery/random/', data);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
openModal(template: TemplateRef<any>) {
|
||||||
|
if (!this.enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.modalRef) {
|
||||||
|
this.modalRef.hide();
|
||||||
|
}
|
||||||
|
this.modalRef = this.modalService.show(template);
|
||||||
|
document.body.style.paddingRight = '0px';
|
||||||
|
this.update();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
onCopy() {
|
||||||
|
this._notification.success(this.i18n('Url has been copied to clipboard'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public hideModal() {
|
||||||
|
this.modalRef.hide();
|
||||||
|
this.modalRef = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -17,15 +17,7 @@ export class NetworkService {
|
|||||||
private slimLoadingBarService: SlimLoadingBarService) {
|
private slimLoadingBarService: SlimLoadingBarService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public postJson<T>(url: string, data: any = {}): Promise<T> {
|
public static buildUrl(url: string, data?: { [key: string]: any }) {
|
||||||
return this.callJson('post', url, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public putJson<T>(url: string, data: any = {}): Promise<T> {
|
|
||||||
return this.callJson('put', url, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public getJson<T>(url: string, data?: { [key: string]: any }): Promise<T> {
|
|
||||||
if (data) {
|
if (data) {
|
||||||
const keys = Object.getOwnPropertyNames(data);
|
const keys = Object.getOwnPropertyNames(data);
|
||||||
if (keys.length > 0) {
|
if (keys.length > 0) {
|
||||||
@ -38,7 +30,19 @@ export class NetworkService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this.callJson('get', url);
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public postJson<T>(url: string, data: any = {}): Promise<T> {
|
||||||
|
return this.callJson('post', url, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public putJson<T>(url: string, data: any = {}): Promise<T> {
|
||||||
|
return this.callJson('put', url, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getJson<T>(url: string, data?: { [key: string]: any }): Promise<T> {
|
||||||
|
return this.callJson('get', NetworkService.buildUrl(url, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
public deleteJson<T>(url: string): Promise<T> {
|
public deleteJson<T>(url: string): Promise<T> {
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
.panel-info {
|
||||||
|
text-align: center;
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
<form #settingsForm="ngForm">
|
||||||
|
<div class="card mb-4"
|
||||||
|
[ngClass]="settings.enabled && !_settingsService.isSupported()?'panel-warning':''">
|
||||||
|
<h5 class="card-header">
|
||||||
|
<ng-container i18n>Random Photo settings</ng-container>
|
||||||
|
<div class="switch-wrapper">
|
||||||
|
<bSwitch
|
||||||
|
class="switch"
|
||||||
|
name="enabled"
|
||||||
|
[switch-on-color]="'success'"
|
||||||
|
[switch-inverse]="'inverse'"
|
||||||
|
[switch-off-text]="text.Disabled"
|
||||||
|
[switch-on-text]="text.Enabled"
|
||||||
|
[switch-disabled]="inProgress || (!settings.enabled && !_settingsService.isSupported())"
|
||||||
|
[switch-handle-width]="'100'"
|
||||||
|
[switch-label-width]="'20'"
|
||||||
|
[(ngModel)]="settings.enabled">
|
||||||
|
</bSwitch>
|
||||||
|
</div>
|
||||||
|
</h5>
|
||||||
|
<div class="card-body">
|
||||||
|
<div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div>
|
||||||
|
|
||||||
|
<ng-container *ngIf="settings.enabled || _settingsService.isSupported()">
|
||||||
|
<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.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</ng-container>
|
||||||
|
<div class="panel-info" *ngIf="(!settings.enabled && !_settingsService.isSupported())" i18n>
|
||||||
|
Random Photo is not supported with these settings
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-success float-right"
|
||||||
|
[disabled]="!settingsForm.form.valid || !changed || inProgress"
|
||||||
|
(click)="save()" i18n>Save
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-default float-right"
|
||||||
|
(click)="reset()" i18n>Reset
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
@ -0,0 +1,31 @@
|
|||||||
|
import {Component} from '@angular/core';
|
||||||
|
import {SettingsComponent} from '../_abstract/abstract.settings.component';
|
||||||
|
import {AuthenticationService} from '../../model/network/authentication.service';
|
||||||
|
import {NavigationService} from '../../model/navigation.service';
|
||||||
|
import {NotificationService} from '../../model/notification.service';
|
||||||
|
import {ClientConfig} from '../../../../common/config/public/ConfigClass';
|
||||||
|
import {RandomPhotoSettingsService} from './random-photo.settings.service';
|
||||||
|
import {I18n} from '@ngx-translate/i18n-polyfill';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-settings-random-photo',
|
||||||
|
templateUrl: './random-photo.settings.component.html',
|
||||||
|
styleUrls: ['./random-photo.settings.component.css',
|
||||||
|
'./../_abstract/abstract.settings.component.css'],
|
||||||
|
providers: [RandomPhotoSettingsService],
|
||||||
|
})
|
||||||
|
export class RandomPhotoSettingsComponent extends SettingsComponent<ClientConfig.RandomPhotoConfig> {
|
||||||
|
|
||||||
|
constructor(_authService: AuthenticationService,
|
||||||
|
_navigation: NavigationService,
|
||||||
|
_settingsService: RandomPhotoSettingsService,
|
||||||
|
notification: NotificationService,
|
||||||
|
i18n: I18n) {
|
||||||
|
super(i18n('Random Photo'), _authService, _navigation, _settingsService, notification, i18n, s => s.Client.RandomPhoto);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
|||||||
|
import {Injectable} from '@angular/core';
|
||||||
|
import {NetworkService} from '../../model/network/network.service';
|
||||||
|
import {DatabaseType} from '../../../../common/config/private/IPrivateConfig';
|
||||||
|
import {ClientConfig} from '../../../../common/config/public/ConfigClass';
|
||||||
|
import {SettingsService} from '../settings.service';
|
||||||
|
import {AbstractSettingsService} from '../_abstract/abstract.settings.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class RandomPhotoSettingsService extends AbstractSettingsService<ClientConfig.SharingConfig> {
|
||||||
|
constructor(private _networkService: NetworkService,
|
||||||
|
_settingsService: SettingsService) {
|
||||||
|
super(_settingsService);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public isSupported(): boolean {
|
||||||
|
return this._settingsService.settings.value.Server.database.type !== DatabaseType.memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateSettings(settings: ClientConfig.SharingConfig): Promise<void> {
|
||||||
|
return this._networkService.putJson('/settings/randomPhoto', {settings: settings});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -33,6 +33,9 @@ export class SettingsService {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
googleApiKey: ''
|
googleApiKey: ''
|
||||||
},
|
},
|
||||||
|
RandomPhoto: {
|
||||||
|
enabled: true
|
||||||
|
},
|
||||||
urlBase: '',
|
urlBase: '',
|
||||||
publicUrl: '',
|
publicUrl: '',
|
||||||
applicationTitle: '',
|
applicationTitle: '',
|
||||||
|
95
package.json
95
package.json
@ -16,7 +16,7 @@
|
|||||||
"ng": "ng",
|
"ng": "ng",
|
||||||
"lint": "ng lint",
|
"lint": "ng lint",
|
||||||
"e2e": "ng e2e",
|
"e2e": "ng e2e",
|
||||||
"run-dev": "ng build --aot -w --output-path=./dist --locale en --i18n-format xlf --i18n-file frontend/translate/messages.en.xlf --missing-translation warning",
|
"run-dev": "ng build --aot --watch --output-path=./dist --i18n-locale en --i18n-format xlf --i18n-file frontend/translate/messages.en.xlf --i18n-missing-translation warning",
|
||||||
"update-translation": "gulp update-translation",
|
"update-translation": "gulp update-translation",
|
||||||
"add-translation": "gulp add-translation"
|
"add-translation": "gulp add-translation"
|
||||||
},
|
},
|
||||||
@ -33,46 +33,46 @@
|
|||||||
"cookie-parser": "1.4.3",
|
"cookie-parser": "1.4.3",
|
||||||
"cookie-session": "2.0.0-beta.3",
|
"cookie-session": "2.0.0-beta.3",
|
||||||
"ejs": "2.6.1",
|
"ejs": "2.6.1",
|
||||||
"express": "4.16.3",
|
"express": "4.16.4",
|
||||||
"jimp": "0.2.28",
|
"jimp": "0.5.4",
|
||||||
"locale": "0.1.0",
|
"locale": "0.1.0",
|
||||||
"reflect-metadata": "0.1.12",
|
"reflect-metadata": "0.1.12",
|
||||||
"sqlite3": "4.0.2",
|
"sqlite3": "4.0.2",
|
||||||
"ts-exif-parser": "0.1.24",
|
"ts-exif-parser": "0.1.24",
|
||||||
"ts-node-iptc": "1.0.10",
|
"ts-node-iptc": "1.0.10",
|
||||||
"typeconfig": "1.0.6",
|
"typeconfig": "1.0.6",
|
||||||
"typeorm": "0.2.7",
|
"typeorm": "0.2.8",
|
||||||
"winston": "2.4.2"
|
"winston": "2.4.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@agm/core": "1.0.0-beta.3",
|
"@agm/core": "1.0.0-beta.5",
|
||||||
"@angular-devkit/build-angular": "0.7.1",
|
"@angular-devkit/build-angular": "0.10.2",
|
||||||
"@angular-devkit/build-optimizer": "0.7.1",
|
"@angular-devkit/build-optimizer": "0.10.2",
|
||||||
"@angular/animations": "6.1.0",
|
"@angular/animations": "7.0.0",
|
||||||
"@angular/cli": "6.1.1",
|
"@angular/cli": "7.0.2",
|
||||||
"@angular/common": "6.1.0",
|
"@angular/common": "7.0.0",
|
||||||
"@angular/compiler": "6.1.0",
|
"@angular/compiler": "7.0.0",
|
||||||
"@angular/compiler-cli": "6.1.0",
|
"@angular/compiler-cli": "7.0.0",
|
||||||
"@angular/core": "6.1.0",
|
"@angular/core": "7.0.0",
|
||||||
"@angular/forms": "6.1.0",
|
"@angular/forms": "7.0.0",
|
||||||
"@angular/http": "6.1.0",
|
"@angular/http": "7.0.0",
|
||||||
"@angular/language-service": "^6.1.0",
|
"@angular/language-service": "7.0.0",
|
||||||
"@angular/platform-browser": "6.1.0",
|
"@angular/platform-browser": "7.0.0",
|
||||||
"@angular/platform-browser-dynamic": "6.1.0",
|
"@angular/platform-browser-dynamic": "7.0.0",
|
||||||
"@angular/router": "6.1.0",
|
"@angular/router": "7.0.0",
|
||||||
"@ngx-translate/i18n-polyfill": "1.0.0",
|
"@ngx-translate/i18n-polyfill": "1.0.0",
|
||||||
"@types/bcryptjs": "2.4.1",
|
"@types/bcryptjs": "2.4.2",
|
||||||
"@types/chai": "4.1.4",
|
"@types/chai": "4.1.6",
|
||||||
"@types/cookie-session": "2.0.35",
|
"@types/cookie-session": "2.0.36",
|
||||||
"@types/express": "4.16.0",
|
"@types/express": "4.16.0",
|
||||||
"@types/gm": "1.18.0",
|
"@types/gm": "1.18.1",
|
||||||
"@types/jasmine": "2.8.8",
|
"@types/jasmine": "2.8.9",
|
||||||
"@types/node": "10.5.4",
|
"@types/node": "10.12.0",
|
||||||
"@types/sharp": "0.17.9",
|
"@types/sharp": "0.21.0",
|
||||||
"@types/winston": "^2.3.9",
|
"@types/winston": "2.3.9",
|
||||||
"bootstrap": "4.1.3",
|
"bootstrap": "4.1.3",
|
||||||
"chai": "4.1.2",
|
"chai": "4.2.0",
|
||||||
"codelyzer": "4.4.2",
|
"codelyzer": "4.5.0",
|
||||||
"core-js": "2.5.7",
|
"core-js": "2.5.7",
|
||||||
"ejs-loader": "0.3.1",
|
"ejs-loader": "0.3.1",
|
||||||
"gulp": "3.9.1",
|
"gulp": "3.9.1",
|
||||||
@ -81,45 +81,48 @@
|
|||||||
"gulp-zip": "4.2.0",
|
"gulp-zip": "4.2.0",
|
||||||
"hammerjs": "2.0.8",
|
"hammerjs": "2.0.8",
|
||||||
"intl": "1.2.5",
|
"intl": "1.2.5",
|
||||||
"jasmine-core": "3.1.0",
|
"jasmine-core": "3.2.1",
|
||||||
"jasmine-spec-reporter": "4.2.1",
|
"jasmine-spec-reporter": "4.2.1",
|
||||||
"jw-bootstrap-switch-ng2": "1.0.10",
|
"jw-bootstrap-switch-ng2": "2.0.2",
|
||||||
"karma": "2.0.5",
|
"karma": "3.0.0",
|
||||||
"karma-chrome-launcher": "2.2.0",
|
"karma-chrome-launcher": "2.2.0",
|
||||||
"karma-cli": "1.0.1",
|
"karma-cli": "1.0.1",
|
||||||
"karma-coverage-istanbul-reporter": "2.0.1",
|
"karma-coverage-istanbul-reporter": "2.0.4",
|
||||||
"karma-jasmine": "1.1.2",
|
"karma-jasmine": "1.1.2",
|
||||||
"karma-jasmine-html-reporter": "1.2.0",
|
"karma-jasmine-html-reporter": "1.3.1",
|
||||||
"karma-remap-istanbul": "0.6.0",
|
"karma-remap-istanbul": "0.6.0",
|
||||||
"karma-systemjs": "0.16.0",
|
"karma-systemjs": "0.16.0",
|
||||||
"merge2": "1.2.2",
|
"merge2": "1.2.3",
|
||||||
"mocha": "5.2.0",
|
"mocha": "5.2.0",
|
||||||
"ng2-cookies": "1.0.12",
|
"ng2-cookies": "1.0.12",
|
||||||
"ng2-slim-loading-bar": "4.0.0",
|
"ng2-slim-loading-bar": "4.0.0",
|
||||||
"ngx-bootstrap": "3.0.1",
|
"ngx-bootstrap": "3.0.1",
|
||||||
"ngx-clipboard": "11.1.1",
|
"ngx-clipboard": "11.1.9",
|
||||||
"ngx-toastr": "8.10.0",
|
"ngx-toastr": "9.1.1",
|
||||||
"open-iconic": "1.1.1",
|
"open-iconic": "1.1.1",
|
||||||
"protractor": "5.4.0",
|
"protractor": "5.4.1",
|
||||||
"remap-istanbul": "0.11.1",
|
"remap-istanbul": "0.12.0",
|
||||||
"rimraf": "2.6.2",
|
"rimraf": "2.6.2",
|
||||||
"run-sequence": "2.2.1",
|
"run-sequence": "2.2.1",
|
||||||
"rxjs": "6.2.2",
|
"rxjs": "6.3.3",
|
||||||
"rxjs-compat": "^6.2.2",
|
"rxjs-compat": "^6.3.3",
|
||||||
"ts-helpers": "1.1.2",
|
"ts-helpers": "1.1.2",
|
||||||
"ts-node": "7.0.0",
|
"ts-node": "7.0.1",
|
||||||
"tslint": "5.11.0",
|
"tslint": "5.11.0",
|
||||||
"typescript": "2.9.2",
|
"typescript": "3.1.3",
|
||||||
"xlf-google-translate": "1.0.0-beta.11",
|
"xlf-google-translate": "1.0.0-beta.11",
|
||||||
"zone.js": "0.8.26"
|
"zone.js": "0.8.26"
|
||||||
},
|
},
|
||||||
|
"resolutions": {
|
||||||
|
"natives": "1.1.3"
|
||||||
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"mysql": "2.16.0",
|
"mysql": "2.16.0",
|
||||||
"bcrypt": "3.0.0",
|
"bcrypt": "3.0.2",
|
||||||
"gm": "1.23.1",
|
"gm": "1.23.1",
|
||||||
"sharp": "0.20.5"
|
"sharp": "0.21.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 6.9 <10.0"
|
"node": ">= 6.9 <=10.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user