mirror of
https://github.com/Deutscher-Tischfussballbund/com_sportsmanager.git
synced 2026-06-10 06:27:52 +00:00
init
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
export const pjson = require('../package.json');
|
||||
|
||||
export const config = {
|
||||
browserSyncConfig: {
|
||||
ghostMode: {
|
||||
clicks: true,
|
||||
scroll: true,
|
||||
links: true,
|
||||
forms: true
|
||||
},
|
||||
server: {
|
||||
baseDir: ['/dist/']
|
||||
},
|
||||
proxy: pjson.buildconfigs.proxy,
|
||||
https: false,
|
||||
open: false,
|
||||
debugInfo: false,
|
||||
watchTask: false,
|
||||
notify: {
|
||||
styles: [
|
||||
'padding: 8px 16px;',
|
||||
'position: fixed;',
|
||||
'font-size: 12px;',
|
||||
'font-weight: bold',
|
||||
'z-index: 9999;',
|
||||
'top: inherit',
|
||||
'border-radius: 0',
|
||||
'right: 0;',
|
||||
'top: 0;',
|
||||
'color: #f4f8f9;',
|
||||
'background-color: #026277;',
|
||||
'text-transform: uppercase'
|
||||
]
|
||||
}
|
||||
},
|
||||
paths: {
|
||||
src: './src/',
|
||||
dest: './dist/',
|
||||
copy: {
|
||||
src: ['src/structure/**/**', 'src/structure/**/.*', '!src/structure/**/*.{php,html,xml,ini,less,json,js,css}', '!src/structure/**/.*.{php,html,xml,ini,less,json,js,css}'],
|
||||
replacesrc: ['src/structure/**/**.{php,html,xml,ini,less,json,js,css}', 'src/structure/**/.*.{php,html,xml,ini,less,json,js,css}'],
|
||||
dest: 'dist/',
|
||||
watch: ['src/structure/**/*.{php,html,xml,ini,less,json,js,css}'],
|
||||
},
|
||||
copyrelease: {
|
||||
src: ['src/structure/**/**', 'src/structure/**/.*', '!src/structure/**/*.{php,html,xml,ini,less,json,js,css}', '!src/structure/**/.*.{php,html,xml,ini,less,json,js,css}'],
|
||||
replacesrc: ['src/structure/**/**.{php,html,xml,ini,less,json,js,css}', 'src/structure/**/.*.{php,html,xml,ini,less,json,js,css}'],
|
||||
dest: 'releasefiles/'
|
||||
},
|
||||
cleaner: {
|
||||
releasefiles: 'releasefiles/',
|
||||
sourcefiles: 'sourcefiles/',
|
||||
archives: 'archives/',
|
||||
packages: 'package/'
|
||||
},
|
||||
},
|
||||
packagefiles: [
|
||||
{
|
||||
src: './releasefiles/components/com_kickboilerplate/**/**',
|
||||
dest: 'sourcefiles/com_kickboilerplate/site'
|
||||
},
|
||||
{
|
||||
src: './releasefiles/administrator/components/com_kickboilerplate/**/**',
|
||||
dest: 'sourcefiles/com_kickboilerplate/admin'
|
||||
},
|
||||
{
|
||||
src: './releasefiles/administrator/components/com_kickboilerplate/kickboilerplate.xml',
|
||||
dest: 'sourcefiles/com_kickboilerplate/'
|
||||
},
|
||||
{
|
||||
src: './releasefiles/administrator/components/com_kickboilerplate/script.php',
|
||||
dest: 'sourcefiles/com_kickboilerplate/'
|
||||
},
|
||||
{
|
||||
src: './releasefiles/media/com_kickboilerplate/**/**',
|
||||
dest: 'sourcefiles/com_kickboilerplate/media'
|
||||
},
|
||||
{
|
||||
src: './releasefiles/plugins/system/kickboilerplate/**/**',
|
||||
dest: 'sourcefiles/plg_system_kickboilerplate'
|
||||
}
|
||||
],
|
||||
package: [
|
||||
{
|
||||
destination : 'archives/',
|
||||
name: 'pkg_kickytp',
|
||||
types: [
|
||||
{
|
||||
extension: '.zip',
|
||||
type: 'zip'
|
||||
}
|
||||
],
|
||||
folders: [
|
||||
'package'
|
||||
],
|
||||
files: [
|
||||
]
|
||||
}
|
||||
],
|
||||
archiver: [
|
||||
{
|
||||
destination : 'package/packages/',
|
||||
name: 'plg_system_kickboilerplate',
|
||||
suffixversion: false,
|
||||
types: [
|
||||
{
|
||||
extension: '.zip',
|
||||
type: 'zip',
|
||||
options: {
|
||||
zlib: { 'level': 9 }
|
||||
}
|
||||
}
|
||||
],
|
||||
folders: [
|
||||
'sourcefiles/plg_system_kickboilerplate'
|
||||
],
|
||||
files: [
|
||||
]
|
||||
},
|
||||
{
|
||||
destination : 'package/packages/',
|
||||
name: 'com_kickboilerplate',
|
||||
suffixversion: false,
|
||||
types: [
|
||||
{
|
||||
extension: '.zip',
|
||||
type: 'zip',
|
||||
options: {
|
||||
zlib: { 'level': 9 }
|
||||
}
|
||||
}
|
||||
],
|
||||
folders: [
|
||||
'sourcefiles/com_kickboilerplate'
|
||||
],
|
||||
files: [
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export const isProd = process.env.NODE_ENV === 'production';
|
||||
|
||||
export const stringsreplace = extend({}, {"[VERSION]": pjson.version} , pjson.placeholder);
|
||||
|
||||
function extend(target) {
|
||||
var sources = [].slice.call(arguments, 1);
|
||||
sources.forEach(function (source) {
|
||||
for (var prop in source) {
|
||||
target[prop] = source[prop];
|
||||
}
|
||||
});
|
||||
return target;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* @title gulpfile.babel.js
|
||||
* @description A directory file loader to include all the gulp tasks
|
||||
*
|
||||
*/
|
||||
|
||||
// Dependencies
|
||||
import gulp from 'gulp';
|
||||
|
||||
import { boilerplate } from './tasks/boilerplate';
|
||||
import { watch } from './tasks/watch';
|
||||
import { build } from './tasks/build';
|
||||
import { copy } from './tasks/copy';
|
||||
import { copyRelease } from './tasks/copy-release';
|
||||
import { cleaner } from './tasks/clean';
|
||||
import { copyPackageFiles } from './tasks/copy-packagefiles';
|
||||
import { buildArchives } from './tasks/archives';
|
||||
import { release } from './tasks/release';
|
||||
|
||||
exports.boilerplate = boilerplate;
|
||||
exports.watch = watch;
|
||||
exports.build = build;
|
||||
exports.copyFiles = copy;
|
||||
exports.copyRelease = copyRelease;
|
||||
exports.cleaner = cleaner;
|
||||
exports.copyPackageFiles = copyPackageFiles;
|
||||
exports.archiver = buildArchives;
|
||||
exports.release = release;
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* @title Package Files
|
||||
* @description A task to copy images
|
||||
*/
|
||||
|
||||
// Dependencies
|
||||
import fs from 'fs'
|
||||
import archiver from 'archiver'
|
||||
|
||||
// Config
|
||||
import { config, pjson } from '../config';
|
||||
|
||||
// Task
|
||||
export function buildArchives(cb) {
|
||||
|
||||
const builder = async () => {
|
||||
for (const archivesetup of config.archiver) {
|
||||
await build(archivesetup)
|
||||
};
|
||||
};
|
||||
|
||||
builder().then(() => {
|
||||
cb()
|
||||
});
|
||||
}
|
||||
|
||||
const build = archivesetup => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!fs.existsSync(archivesetup.destination)){
|
||||
fs.mkdirSync(archivesetup.destination, { recursive: true });
|
||||
}
|
||||
|
||||
let finisher = 0;
|
||||
|
||||
let forEach = archivesetup.types.forEach( function(item) {
|
||||
let extensionname = archivesetup.destination + archivesetup.name + item.extension
|
||||
if (archivesetup.suffixversion) {
|
||||
extensionname = archivesetup.destination + archivesetup.name + '_' + pjson.version + item.extension;
|
||||
}
|
||||
let output = fs.createWriteStream(extensionname);
|
||||
const archive = archiver((item.type).toString(), item.options);
|
||||
output.on('close', function() {
|
||||
finisher++;
|
||||
console.log(archive.pointer() + ' total bytes');
|
||||
console.log('archiver has been finalized and the output file ('+archivesetup.name+') descriptor has closed.');
|
||||
|
||||
if(finisher == archivesetup.types.length) {
|
||||
resolve()
|
||||
}
|
||||
});
|
||||
|
||||
// This event is fired when the data source is drained no matter what was the data source.
|
||||
// It is not part of this library but rather from the NodeJS Stream API.
|
||||
// @see: https://nodejs.org/api/stream.html#stream_event_end
|
||||
output.on('end', function() {
|
||||
console.log('Data has been drained');
|
||||
});
|
||||
|
||||
// good practice to catch warnings (ie stat failures and other non-blocking errors)
|
||||
archive.on('warning', function(err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
// log warning
|
||||
} else {
|
||||
// throw error
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
|
||||
archive.on('error', function(err) {
|
||||
throw err;
|
||||
});
|
||||
|
||||
archive.pipe(output);
|
||||
|
||||
archivesetup.folders.forEach(function (folder) {
|
||||
archive.directory(folder, false);
|
||||
})
|
||||
|
||||
archive.finalize();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Automatic Release
|
||||
*
|
||||
* @description: Deploy Task for an automated Build Process
|
||||
*/
|
||||
|
||||
import { series } from 'gulp'
|
||||
|
||||
import { copyBoilerplate } from './copy-boilerplate'
|
||||
import { cleanBoilerplate } from './clean-boilerplate';
|
||||
|
||||
export const boilerplate = series(
|
||||
cleanBoilerplate,
|
||||
copyBoilerplate
|
||||
);
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Automatic Deploy
|
||||
*
|
||||
* @description: Deploy Task for an automated Build Process
|
||||
*/
|
||||
|
||||
import { series } from 'gulp'
|
||||
|
||||
import { copy } from './copy'
|
||||
|
||||
export const build = series(
|
||||
copy
|
||||
);
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* @title Copy
|
||||
* @description A task to copy files to the output directory
|
||||
*/
|
||||
|
||||
// Dependencies
|
||||
import { src } from 'gulp';
|
||||
import mergeStream from 'merge-stream';
|
||||
import plumber from "gulp-plumber";
|
||||
import errorHandler from "../util/errorHandler";
|
||||
import clean from "gulp-clean";
|
||||
|
||||
// Config
|
||||
import { pjson } from '../config';
|
||||
|
||||
// Task
|
||||
export function cleanBoilerplate() {
|
||||
return mergeStream(pjson.boilerplate.files.map(function(item) {
|
||||
return src(item.dest, {allowEmpty: true})
|
||||
.pipe(plumber({errorHandler}))
|
||||
.pipe(clean({force: true}))
|
||||
}))
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* @title Scripts
|
||||
* @description A task to concatenate and compress js files via webpack
|
||||
*/
|
||||
|
||||
// Dependencies
|
||||
import { src, series } from 'gulp';
|
||||
import plumber from 'gulp-plumber';
|
||||
import clean from 'gulp-clean';
|
||||
import errorHandler from '../util/errorHandler.js';
|
||||
|
||||
// Config
|
||||
import { config } from '../config';
|
||||
|
||||
// Task
|
||||
export function deleteReleasefilesFolder() {
|
||||
return src(config.paths.cleaner.releasefiles, {allowEmpty: true})
|
||||
.pipe(plumber({errorHandler}))
|
||||
.pipe(clean({force: true}))
|
||||
}
|
||||
|
||||
|
||||
export function deleteSourcefilesFolder() {
|
||||
return src(config.paths.cleaner.sourcefiles, {allowEmpty: true})
|
||||
.pipe(plumber({errorHandler}))
|
||||
.pipe(clean({force: true}))
|
||||
}
|
||||
|
||||
export function deleteArchivesFolder() {
|
||||
return src(config.paths.cleaner.archives, {allowEmpty: true})
|
||||
.pipe(plumber({errorHandler}))
|
||||
.pipe(clean({force: true}))
|
||||
}
|
||||
|
||||
export function deletePackageFolder() {
|
||||
return src(config.paths.cleaner.packages, {allowEmpty: true})
|
||||
.pipe(plumber({errorHandler}))
|
||||
.pipe(clean({force: true}))
|
||||
}
|
||||
|
||||
export const cleaner = series(deleteReleasefilesFolder, deleteSourcefilesFolder, deleteArchivesFolder, deletePackageFolder);
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* @title Package Files
|
||||
* @description A task to copy images
|
||||
*/
|
||||
|
||||
// Dependencies
|
||||
import {src, dest, series} from 'gulp';
|
||||
import plumber from 'gulp-plumber';
|
||||
import changed from 'gulp-changed';
|
||||
import rename from 'gulp-rename';
|
||||
import mergeStream from 'merge-stream'
|
||||
import errorHandler from '../util/errorHandler.js';
|
||||
import replaceStrings from '../util/replaceStrings.js';
|
||||
|
||||
|
||||
// Config
|
||||
import { isProd, pjson } from '../config';
|
||||
import gulpif from "gulp-if";
|
||||
|
||||
// Task
|
||||
function cleancopy() {
|
||||
return mergeStream(pjson.boilerplate.files.map(function(item) {
|
||||
return src(item.src)
|
||||
.pipe(rename(function (path) {
|
||||
path.dirname = path.dirname.replace(/joomlaboilerplate/g, pjson.casesensitive.joomlaboilerplate);
|
||||
path.basename = path.basename.replace(/joomlaboilerplate/g, pjson.casesensitive.joomlaboilerplate);
|
||||
}))
|
||||
.pipe(plumber({errorHandler}))
|
||||
.pipe(gulpif(!isProd, changed(item.dest)))
|
||||
.pipe(dest(item.dest.replace(/joomlaboilerplate/g, pjson.casesensitive.joomlaboilerplate)))
|
||||
}))
|
||||
}
|
||||
|
||||
function replacecopy() {
|
||||
return mergeStream(pjson.boilerplate.files.map(function(item) {
|
||||
return src(item.replacesrc)
|
||||
.pipe(rename(function (path) {
|
||||
path.dirname = path.dirname.replace(/joomlaboilerplate/g, pjson.casesensitive.joomlaboilerplate);
|
||||
path.basename = path.basename.replace(/joomlaboilerplate/g, pjson.casesensitive.joomlaboilerplate);
|
||||
}))
|
||||
.pipe(replaceStrings(pjson.casesensitive))
|
||||
.pipe(plumber({errorHandler}))
|
||||
.pipe(gulpif(!isProd, changed(item.dest)))
|
||||
.pipe(dest(item.dest.replace(/joomlaboilerplate/g, pjson.casesensitive.joomlaboilerplate)))
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
export const copyBoilerplate = series(
|
||||
cleancopy,
|
||||
replacecopy
|
||||
);
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* @title Package Files
|
||||
* @description A task to copy images
|
||||
*/
|
||||
|
||||
// Dependencies
|
||||
import { src, dest } from 'gulp';
|
||||
import plumber from 'gulp-plumber';
|
||||
import changed from 'gulp-changed';
|
||||
import mergeStream from 'merge-stream'
|
||||
import errorHandler from '../util/errorHandler.js';
|
||||
|
||||
// Config
|
||||
import {config, isProd} from '../config';
|
||||
import gulpif from "gulp-if";
|
||||
|
||||
// Task
|
||||
export function copyPackageFiles() {
|
||||
return mergeStream(config.packagefiles.map(function(item) {
|
||||
return src([item.src])
|
||||
.pipe(plumber({errorHandler}))
|
||||
.pipe(gulpif(!isProd, changed(item.dest)))
|
||||
.pipe(dest(item.dest))
|
||||
}))
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* @title Copy
|
||||
* @description A task to copy files to the output directory
|
||||
*/
|
||||
|
||||
// Dependencies
|
||||
import {src, dest, series} from 'gulp';
|
||||
import changed from 'gulp-changed';
|
||||
import replacestrings from '../util/replaceStrings.js';
|
||||
import gulpif from 'gulp-if';
|
||||
|
||||
// Config
|
||||
import {config, isProd, stringsreplace} from '../config';
|
||||
|
||||
function cleancopy() {
|
||||
return src(config.paths.copyrelease.src)
|
||||
.pipe(gulpif(!isProd, changed(config.paths.copyrelease.dest)))
|
||||
.pipe(dest(config.paths.copyrelease.dest));
|
||||
}
|
||||
|
||||
function replacecopy() {
|
||||
return src(config.paths.copyrelease.replacesrc)
|
||||
.pipe(replacestrings(stringsreplace))
|
||||
.pipe(gulpif(!isProd, changed(config.paths.copyrelease.dest)))
|
||||
.pipe(dest(config.paths.copyrelease.dest));
|
||||
}
|
||||
|
||||
export const copyRelease = series(
|
||||
cleancopy,
|
||||
replacecopy
|
||||
);
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* @title Copy
|
||||
* @description A task to copy files to the output directory
|
||||
*/
|
||||
|
||||
// Dependencies
|
||||
import {src, dest, series} from 'gulp';
|
||||
import changed from 'gulp-changed';
|
||||
import replacestrings from '../util/replaceStrings.js';
|
||||
import gulpif from 'gulp-if';
|
||||
|
||||
// Config
|
||||
import {config, isProd, stringsreplace} from '../config';
|
||||
|
||||
function cleancopy() {
|
||||
return src(config.paths.copy.src)
|
||||
.pipe(gulpif(!isProd, changed(config.paths.copy.dest)))
|
||||
.pipe(dest(config.paths.copy.dest));
|
||||
}
|
||||
|
||||
function replacecopy() {
|
||||
return src(config.paths.copy.replacesrc)
|
||||
.pipe(replacestrings(stringsreplace))
|
||||
.pipe(gulpif(!isProd, changed(config.paths.copy.dest)))
|
||||
.pipe(dest(config.paths.copy.dest));
|
||||
}
|
||||
|
||||
export const copy = series(
|
||||
cleancopy,
|
||||
replacecopy
|
||||
);
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Automatic Release
|
||||
*
|
||||
* @description: Deploy Task for an automated Build Process
|
||||
*/
|
||||
|
||||
import { series, parallel } from 'gulp'
|
||||
|
||||
import { copyRelease } from './copy-release'
|
||||
import { copyPackageFiles } from './copy-packagefiles'
|
||||
import { buildArchives } from './archives'
|
||||
import { cleaner, deleteReleasefilesFolder } from './clean';
|
||||
|
||||
// Config
|
||||
import { config } from '../config';
|
||||
|
||||
export const release = series(
|
||||
cleaner,
|
||||
copyRelease,
|
||||
copyPackageFiles,
|
||||
buildArchives
|
||||
);
|
||||
Executable
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* @title serve
|
||||
* @description A task to initialise browser-sync
|
||||
* either via proxy or serving the files directly
|
||||
* depending on your settings
|
||||
*/
|
||||
|
||||
// Dependencies
|
||||
import browserSync from 'browser-sync';
|
||||
import gulp from 'gulp';
|
||||
|
||||
// Config
|
||||
import {config} from '../config';
|
||||
|
||||
// Task
|
||||
// const server = browserSync.create();
|
||||
|
||||
export function serve(cb) {
|
||||
// Build a condition when Proxy is active
|
||||
let bsProxy;
|
||||
let bsServer;
|
||||
|
||||
// Condition for Proxy
|
||||
if (config.browserSyncConfig.proxy != '') {
|
||||
bsProxy = {
|
||||
target: config.browserSyncConfig.proxy,
|
||||
ws: true
|
||||
};
|
||||
bsServer = false;
|
||||
} else {
|
||||
bsProxy = false;
|
||||
bsServer = {
|
||||
baseDir: [config.paths.dest]
|
||||
};
|
||||
}
|
||||
|
||||
browserSync.init({
|
||||
server: bsServer,
|
||||
proxy: bsProxy,
|
||||
notify: config.browserSyncConfig.notify,
|
||||
open: config.browserSyncConfig.open,
|
||||
https: config.browserSyncConfig.https,
|
||||
ghostMode: config.browserSyncConfig.ghostMode,
|
||||
debugInfo: config.browserSyncConfig.debugInfo,
|
||||
watchTask: config.browserSyncConfig.watchTask,
|
||||
});
|
||||
cb();
|
||||
}
|
||||
|
||||
gulp.task('browser-sync', serve);
|
||||
|
||||
export function reload(cb) {
|
||||
browserSync.reload();
|
||||
cb();
|
||||
}
|
||||
Executable
+24
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* @title Watch
|
||||
* @description A task to start the server and watch for changes.
|
||||
*/
|
||||
|
||||
// Dependencies
|
||||
import gulp from 'gulp';
|
||||
import { series } from 'gulp';
|
||||
|
||||
// Tasks
|
||||
import { reload, serve } from './server';
|
||||
import { copy } from './copy';
|
||||
|
||||
// Config
|
||||
import { config } from '../config';
|
||||
|
||||
function watchFiles() {
|
||||
gulp.watch(config.paths.copy.watch, series(copy, reload));
|
||||
}
|
||||
|
||||
export const watch = series(
|
||||
serve,
|
||||
watchFiles
|
||||
);
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* @title Error Handler
|
||||
*/
|
||||
|
||||
import notifier from "node-notifier";
|
||||
|
||||
function errorHandler(error) {
|
||||
notifier.notify({
|
||||
title: 'Gulp Error',
|
||||
message: error.message,
|
||||
timeout: 3
|
||||
});
|
||||
console.error('\x1b[31m', error.message ,'\x1b[0m');
|
||||
this.emit('end');
|
||||
}
|
||||
|
||||
export default errorHandler;
|
||||
@@ -0,0 +1,47 @@
|
||||
'use strict';
|
||||
|
||||
var through2 = require('through2');
|
||||
var gutil = require('gulp-util');
|
||||
|
||||
var PLUGIN_NAME = 'gulp-replace-task';
|
||||
|
||||
// plugin
|
||||
|
||||
module.exports = function (opts) {
|
||||
|
||||
return through2.obj(function (file, enc, cb) {
|
||||
|
||||
if (file.isNull()) {
|
||||
this.push(file);
|
||||
return cb();
|
||||
}
|
||||
|
||||
if (file.isStream()) {
|
||||
this.emit('error', new gutil.PluginError(PLUGIN_NAME,
|
||||
'Streaming not supported'));
|
||||
return cb();
|
||||
}
|
||||
|
||||
var options = opts || {};
|
||||
var contents = file.contents.toString();
|
||||
|
||||
for (const [key, value] of Object.entries(opts)) {
|
||||
key = key.replace('[','\\[');
|
||||
key = key.replace(']','\\]');
|
||||
var re = new RegExp(key, 'g');
|
||||
contents = contents.replace(re, value);
|
||||
}
|
||||
|
||||
var result = contents;
|
||||
if (result !== false) {
|
||||
file.contents = new Buffer.from(result);
|
||||
} else {
|
||||
// preserve original file
|
||||
}
|
||||
|
||||
this.push(file);
|
||||
cb();
|
||||
|
||||
});
|
||||
|
||||
};
|
||||
Reference in New Issue
Block a user