Added thumbnail generation + insert image files display + Create page fix
This commit is contained in:
parent
567e1307da
commit
c0be18a8d8
100
agent.js
100
agent.js
@ -39,6 +39,7 @@ global.WSInternalKey = process.argv[2];
|
||||
winston.info('[AGENT] Background Agent is initializing...');
|
||||
|
||||
var appconfig = require('./models/config')('./config.yml');
|
||||
let lcdata = require('./models/localdata').init(appconfig, 'agent');
|
||||
|
||||
global.git = require('./models/git').init(appconfig);
|
||||
global.entries = require('./models/entries').init(appconfig);
|
||||
@ -50,8 +51,12 @@ var Promise = require('bluebird');
|
||||
var fs = Promise.promisifyAll(require("fs-extra"));
|
||||
var path = require('path');
|
||||
var cron = require('cron').CronJob;
|
||||
var wsClient = require('socket.io-client');
|
||||
global.ws = wsClient('http://localhost:' + appconfig.wsPort, { reconnectionAttempts: 10 });
|
||||
var readChunk = require('read-chunk');
|
||||
var fileType = require('file-type');
|
||||
|
||||
global.ws = require('socket.io-client')('http://localhost:' + appconfig.wsPort, { reconnectionAttempts: 10 });
|
||||
|
||||
const mimeImgTypes = ['image/png', 'image/jpg']
|
||||
|
||||
// ----------------------------------------
|
||||
// Start Cron
|
||||
@ -75,6 +80,7 @@ var job = new cron({
|
||||
|
||||
let jobs = [];
|
||||
let repoPath = path.resolve(ROOTPATH, appconfig.datadir.repo);
|
||||
let dataPath = path.resolve(ROOTPATH, appconfig.datadir.db);
|
||||
let uploadsPath = path.join(repoPath, 'uploads');
|
||||
|
||||
// ----------------------------------------
|
||||
@ -151,11 +157,97 @@ var job = new cron({
|
||||
|
||||
return Promise.map(ls, (f) => {
|
||||
return fs.statAsync(path.join(uploadsPath, f)).then((s) => { return { filename: f, stat: s }; });
|
||||
}).filter((s) => { return s.stat.isDirectory(); }).then((arrStats) => {
|
||||
}).filter((s) => { return s.stat.isDirectory(); }).then((arrDirs) => {
|
||||
|
||||
let folderNames = _.map(arrDirs, 'filename');
|
||||
folderNames.unshift('');
|
||||
|
||||
ws.emit('uploadsSetFolders', {
|
||||
auth: WSInternalKey,
|
||||
content: _.map(arrStats, 'filename')
|
||||
content: folderNames
|
||||
});
|
||||
|
||||
let allFiles = [];
|
||||
|
||||
// Travel each directory
|
||||
|
||||
return Promise.map(folderNames, (fldName) => {
|
||||
let fldPath = path.join(uploadsPath, fldName);
|
||||
return fs.readdirAsync(fldPath).then((fList) => {
|
||||
return Promise.map(fList, (f) => {
|
||||
let fPath = path.join(fldPath, f);
|
||||
let fPathObj = path.parse(fPath);
|
||||
|
||||
return fs.statAsync(fPath)
|
||||
.then((s) => {
|
||||
|
||||
if(!s.isFile()) { return false; }
|
||||
|
||||
// Get MIME info
|
||||
|
||||
let mimeInfo = fileType(readChunk.sync(fPath, 0, 262));
|
||||
|
||||
// Images
|
||||
|
||||
if(s.size < 3145728) { // ignore files larger than 3MB
|
||||
if(_.includes(['image/png', 'image/jpeg', 'image/gif', 'image/webp'], mimeInfo.mime)) {
|
||||
return lcdata.getImageMetadata(fPath).then((mData) => {
|
||||
|
||||
let cacheThumbnailPath = path.parse(path.join(dataPath, 'thumbs', fldName, fPathObj.name + '.png'));
|
||||
let cacheThumbnailPathStr = path.format(cacheThumbnailPath);
|
||||
|
||||
mData = _.pick(mData, ['format', 'width', 'height', 'density', 'hasAlpha', 'orientation']);
|
||||
mData.category = 'image';
|
||||
mData.mime = mimeInfo.mime;
|
||||
mData.folder = fldName;
|
||||
mData.filename = f;
|
||||
mData.basename = fPathObj.name;
|
||||
mData.filesize = s.size;
|
||||
mData.uploadedOn = moment().utc();
|
||||
allFiles.push(mData);
|
||||
|
||||
// Generate thumbnail
|
||||
|
||||
return fs.statAsync(cacheThumbnailPathStr).then((st) => {
|
||||
return st.isFile();
|
||||
}).catch((err) => {
|
||||
return false;
|
||||
}).then((thumbExists) => {
|
||||
|
||||
return (thumbExists) ? true : fs.ensureDirAsync(cacheThumbnailPath.dir).then(() => {
|
||||
return lcdata.generateThumbnail(fPath, cacheThumbnailPathStr);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Other Files
|
||||
|
||||
allFiles.push({
|
||||
category: 'file',
|
||||
mime: mimeInfo.mime,
|
||||
folder: fldName,
|
||||
filename: f,
|
||||
basename: fPathObj.name,
|
||||
filesize: s.size,
|
||||
uploadedOn: moment().utc()
|
||||
});
|
||||
|
||||
});
|
||||
}, {concurrency: 3});
|
||||
});
|
||||
}, {concurrency: 1}).finally(() => {
|
||||
|
||||
ws.emit('uploadsSetFiles', {
|
||||
auth: WSInternalKey,
|
||||
content: allFiles
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -46,7 +46,10 @@ let vueImage = new Vue({
|
||||
vueImage.isLoadingText = 'Fetching images...';
|
||||
Vue.nextTick(() => {
|
||||
socket.emit('uploadsGetImages', { folder: vueImage.currentFolder }, (data) => {
|
||||
vueImage.images = data;
|
||||
vueImage.images = _.map(data, (f) => {
|
||||
f.thumbpath = (f.folder === '') ? f.basename + '.png' : _.join([ f.folder, f.basename + '.png' ], '/');
|
||||
return f;
|
||||
});
|
||||
vueImage.isLoading = false;
|
||||
});
|
||||
});
|
||||
|
@ -7,6 +7,7 @@ $orange: #FB8C00;
|
||||
$blue: #039BE5;
|
||||
$turquoise: #00ACC1;
|
||||
$green: #7CB342;
|
||||
$purple: #673AB7;
|
||||
|
||||
$warning: $orange;
|
||||
|
||||
|
@ -9,7 +9,7 @@ html {
|
||||
padding-top: 52px;
|
||||
}
|
||||
|
||||
//$family-sans-serif: "Roboto", "Helvetica", "Arial", sans-serif;
|
||||
$family-monospace: monospace;
|
||||
$family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
|
||||
[v-cloak] {
|
||||
|
@ -35,7 +35,7 @@
|
||||
border-bottom: 1px dotted $grey-light;
|
||||
padding-bottom: 4px;
|
||||
font-weight: 400;
|
||||
color: desaturate(darken($purple, 15%), 10%);
|
||||
color: desaturate($purple, 20%);
|
||||
}
|
||||
|
||||
a.toc-anchor {
|
||||
|
@ -5,11 +5,32 @@ var router = express.Router();
|
||||
var _ = require('lodash');
|
||||
|
||||
var validPathRe = new RegExp("^([a-z0-9\\/-]+\\.[a-z0-9]+)$");
|
||||
var validPathThumbsRe = new RegExp("^([a-z0-9\\/-]+\\.png)$");
|
||||
|
||||
// ==========================================
|
||||
// SERVE UPLOADS FILES
|
||||
// ==========================================
|
||||
|
||||
router.get('/t/*', (req, res, next) => {
|
||||
|
||||
let fileName = req.params[0];
|
||||
if(!validPathThumbsRe.test(fileName)) {
|
||||
return res.sendStatus(404).end();
|
||||
}
|
||||
|
||||
//todo: Authentication-based access
|
||||
|
||||
res.sendFile(fileName, {
|
||||
root: lcdata.getThumbsPath(),
|
||||
dotfiles: 'deny'
|
||||
}, (err) => {
|
||||
if (err) {
|
||||
res.status(err.status).end();
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
router.get('/*', (req, res, next) => {
|
||||
|
||||
let fileName = req.params[0];
|
||||
|
3427
lib/mimes.json
Normal file
3427
lib/mimes.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -5,8 +5,6 @@ var Promise = require('bluebird'),
|
||||
fs = Promise.promisifyAll(require("fs-extra")),
|
||||
_ = require('lodash'),
|
||||
farmhash = require('farmhash'),
|
||||
BSONModule = require('bson'),
|
||||
BSON = new BSONModule.BSONPure.BSON(),
|
||||
moment = require('moment');
|
||||
|
||||
/**
|
||||
@ -82,7 +80,7 @@ module.exports = {
|
||||
// Load from cache
|
||||
|
||||
return fs.readFileAsync(cpath).then((contents) => {
|
||||
return BSON.deserialize(contents);
|
||||
return JSON.parse(contents);
|
||||
}).catch((err) => {
|
||||
winston.error('Corrupted cache file. Deleting it...');
|
||||
fs.unlinkSync(cpath);
|
||||
@ -156,7 +154,7 @@ module.exports = {
|
||||
// Cache to disk
|
||||
|
||||
if(options.cache) {
|
||||
let cacheData = BSON.serialize(_.pick(pageData, ['html', 'meta', 'tree', 'parent']), false, false, false);
|
||||
let cacheData = JSON.stringify(_.pick(pageData, ['html', 'meta', 'tree', 'parent']), false, false, false);
|
||||
return fs.writeFileAsync(cpath, cacheData).catch((err) => {
|
||||
winston.error('Unable to write to cache! Performance may be affected.');
|
||||
return true;
|
||||
@ -257,7 +255,7 @@ module.exports = {
|
||||
* @return {String} The full cache path.
|
||||
*/
|
||||
getCachePath(entryPath) {
|
||||
return path.join(this._cachePath, farmhash.fingerprint32(entryPath) + '.bson');
|
||||
return path.join(this._cachePath, farmhash.fingerprint32(entryPath) + '.json');
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
var fs = require('fs'),
|
||||
path = require('path'),
|
||||
loki = require('lokijs'),
|
||||
Promise = require('bluebird'),
|
||||
_ = require('lodash');
|
||||
|
||||
/**
|
||||
@ -12,7 +14,9 @@ var fs = require('fs'),
|
||||
module.exports = {
|
||||
|
||||
_uploadsPath: './repo/uploads',
|
||||
_uploadsThumbsPath: './data/thumbs',
|
||||
_uploadsFolders: [],
|
||||
_uploadsDb: null,
|
||||
|
||||
/**
|
||||
* Initialize Local Data Storage model
|
||||
@ -20,22 +24,88 @@ module.exports = {
|
||||
* @param {Object} appconfig The application config
|
||||
* @return {Object} Local Data Storage model instance
|
||||
*/
|
||||
init(appconfig, skipFolderCreation = false) {
|
||||
init(appconfig, mode = 'server') {
|
||||
|
||||
let self = this;
|
||||
|
||||
self._uploadsPath = path.join(ROOTPATH, appconfig.datadir.db, 'uploads');
|
||||
self._uploadsPath = path.resolve(ROOTPATH, appconfig.datadir.repo, 'uploads');
|
||||
self._uploadsThumbsPath = path.resolve(ROOTPATH, appconfig.datadir.db, 'thumbs');
|
||||
|
||||
// Create data directories
|
||||
|
||||
if(!skipFolderCreation) {
|
||||
self.createBaseDirectories(appconfig);
|
||||
// Start in full or bare mode
|
||||
|
||||
switch(mode) {
|
||||
case 'agent':
|
||||
//todo
|
||||
break;
|
||||
case 'server':
|
||||
self.createBaseDirectories(appconfig);
|
||||
break;
|
||||
case 'ws':
|
||||
self.initDb(appconfig);
|
||||
break;
|
||||
}
|
||||
|
||||
return self;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize Uploads DB
|
||||
*
|
||||
* @param {Object} appconfig The application config
|
||||
* @return {boolean} Void
|
||||
*/
|
||||
initDb(appconfig) {
|
||||
|
||||
let self = this;
|
||||
|
||||
let dbReadyResolve;
|
||||
let dbReady = new Promise((resolve, reject) => {
|
||||
dbReadyResolve = resolve;
|
||||
});
|
||||
|
||||
// Initialize Loki.js
|
||||
|
||||
let dbModel = {
|
||||
Store: new loki(path.join(appconfig.datadir.db, 'uploads.db'), {
|
||||
env: 'NODEJS',
|
||||
autosave: true,
|
||||
autosaveInterval: 15000
|
||||
}),
|
||||
onReady: dbReady
|
||||
};
|
||||
|
||||
// Load Models
|
||||
|
||||
dbModel.Store.loadDatabase({}, () => {
|
||||
|
||||
dbModel.Files = dbModel.Store.getCollection('Files');
|
||||
if(!dbModel.Files) {
|
||||
dbModel.Files = dbModel.Store.addCollection('Files', {
|
||||
indices: ['category', 'folder']
|
||||
});
|
||||
}
|
||||
|
||||
dbReadyResolve();
|
||||
|
||||
});
|
||||
|
||||
self._uploadsDb = dbModel;
|
||||
|
||||
return true;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the thumbnails folder path.
|
||||
*
|
||||
* @return {String} The thumbs path.
|
||||
*/
|
||||
getThumbsPath() {
|
||||
return this._uploadsThumbsPath;
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a base directories (Synchronous).
|
||||
*
|
||||
@ -99,6 +169,77 @@ module.exports = {
|
||||
*/
|
||||
getUploadsFolders() {
|
||||
return this._uploadsFolders;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the uploads files.
|
||||
*
|
||||
* @param {Array<Object>} arrFiles The uploads files
|
||||
* @return {Void} Void
|
||||
*/
|
||||
setUploadsFiles(arrFiles) {
|
||||
|
||||
let self = this;
|
||||
|
||||
if(_.isArray(arrFiles) && arrFiles.length > 0) {
|
||||
self._uploadsDb.Files.clear();
|
||||
self._uploadsDb.Files.insert(arrFiles);
|
||||
self._uploadsDb.Files.ensureIndex('category', true);
|
||||
self._uploadsDb.Files.ensureIndex('folder', true);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the uploads files.
|
||||
*
|
||||
* @param {String} cat Category type
|
||||
* @param {String} fld Folder
|
||||
* @return {Array<Object>} The files matching the query
|
||||
*/
|
||||
getUploadsFiles(cat, fld) {
|
||||
|
||||
return this._uploadsDb.Files.find({
|
||||
'$and': [{ 'category' : cat },{ 'folder' : fld }]
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Generate thumbnail of image
|
||||
*
|
||||
* @param {String} sourcePath The source path
|
||||
* @return {Promise<Object>} Promise returning the resized image info
|
||||
*/
|
||||
generateThumbnail(sourcePath, destPath) {
|
||||
|
||||
let sharp = require('sharp');
|
||||
|
||||
return sharp(sourcePath)
|
||||
.withoutEnlargement()
|
||||
.resize(150,150)
|
||||
.background('white')
|
||||
.embed()
|
||||
.flatten()
|
||||
.toFormat('png')
|
||||
.toFile(destPath);
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the image metadata.
|
||||
*
|
||||
* @param {String} sourcePath The source path
|
||||
* @return {Object} The image metadata.
|
||||
*/
|
||||
getImageMetadata(sourcePath) {
|
||||
|
||||
let sharp = require('sharp');
|
||||
|
||||
return sharp(sourcePath).metadata();
|
||||
|
||||
}
|
||||
|
||||
};
|
@ -22,7 +22,7 @@ module.exports = {
|
||||
init(appconfig) {
|
||||
|
||||
let self = this;
|
||||
let dbPath = path.resolve(ROOTPATH, appconfig.datadir.db, 'search-index');
|
||||
let dbPath = path.resolve(ROOTPATH, appconfig.datadir.db, 'search');
|
||||
|
||||
searchIndex({
|
||||
deletable: true,
|
||||
|
20
package.json
20
package.json
@ -49,31 +49,33 @@
|
||||
"express-brute": "^1.0.0",
|
||||
"express-brute-loki": "^1.0.0",
|
||||
"express-session": "^1.14.1",
|
||||
"express-validator": "^2.20.8",
|
||||
"express-validator": "^2.20.10",
|
||||
"farmhash": "^1.2.1",
|
||||
"file-type": "^3.8.0",
|
||||
"fs-extra": "^0.30.0",
|
||||
"git-wrapper2-promise": "^0.2.9",
|
||||
"highlight.js": "^9.6.0",
|
||||
"i18next": "^3.4.2",
|
||||
"highlight.js": "^9.7.0",
|
||||
"i18next": "^3.4.3",
|
||||
"i18next-express-middleware": "^1.0.2",
|
||||
"i18next-node-fs-backend": "^0.1.2",
|
||||
"js-yaml": "^3.6.1",
|
||||
"lodash": "^4.15.0",
|
||||
"lodash": "^4.16.1",
|
||||
"lokijs": "^1.4.1",
|
||||
"markdown-it": "^8.0.0",
|
||||
"markdown-it-abbr": "^1.0.4",
|
||||
"markdown-it-anchor": "^2.5.0",
|
||||
"markdown-it-attrs": "^0.7.0",
|
||||
"markdown-it-attrs": "^0.7.1",
|
||||
"markdown-it-emoji": "^1.2.0",
|
||||
"markdown-it-expand-tabs": "^1.0.11",
|
||||
"markdown-it-external-links": "0.0.5",
|
||||
"markdown-it-footnote": "^3.0.1",
|
||||
"markdown-it-task-lists": "^1.4.1",
|
||||
"moment": "^2.15.0",
|
||||
"moment": "^2.15.1",
|
||||
"moment-timezone": "^0.5.5",
|
||||
"passport": "^0.3.2",
|
||||
"passport-local": "^1.0.0",
|
||||
"pug": "^2.0.0-beta6",
|
||||
"read-chunk": "^2.0.0",
|
||||
"remove-markdown": "^0.1.0",
|
||||
"search-index": "^0.8.15",
|
||||
"serve-favicon": "^2.3.0",
|
||||
@ -89,7 +91,7 @@
|
||||
"devDependencies": {
|
||||
"ace-builds": "^1.2.5",
|
||||
"babel-preset-es2015": "^6.14.0",
|
||||
"bulma": "^0.1.2",
|
||||
"bulma": "^0.2.0",
|
||||
"chai": "^3.5.0",
|
||||
"chai-as-promised": "^5.3.0",
|
||||
"codacy-coverage": "^2.0.0",
|
||||
@ -107,7 +109,7 @@
|
||||
"gulp-uglify": "^2.0.0",
|
||||
"gulp-zip": "^3.2.0",
|
||||
"istanbul": "^0.4.5",
|
||||
"jquery": "^3.1.0",
|
||||
"jquery": "^3.1.1",
|
||||
"jquery-smooth-scroll": "^2.0.0",
|
||||
"merge-stream": "^1.0.0",
|
||||
"mocha": "^3.0.2",
|
||||
@ -115,7 +117,7 @@
|
||||
"nodemon": "^1.10.2",
|
||||
"sticky-js": "^1.0.5",
|
||||
"twemoji-awesome": "^1.0.4",
|
||||
"vue": "^1.0.26"
|
||||
"vue": "^1.0.27"
|
||||
},
|
||||
"snyk": true
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
global.ROOTPATH = __dirname;
|
||||
|
||||
// ----------------------------------------
|
||||
// Load global modules
|
||||
// Load Winston
|
||||
// ----------------------------------------
|
||||
|
||||
var _isDebug = process.env.NODE_ENV === 'development';
|
||||
@ -24,9 +24,12 @@ winston.add(winston.transports.Console, {
|
||||
|
||||
winston.info('[SERVER] Requarks Wiki is initializing...');
|
||||
|
||||
var appconfig = require('./models/config')('./config.yml');
|
||||
let lcdata = require('./models/localdata').init(appconfig, false);
|
||||
// ----------------------------------------
|
||||
// Load global modules
|
||||
// ----------------------------------------
|
||||
|
||||
var appconfig = require('./models/config')('./config.yml');
|
||||
global.lcdata = require('./models/localdata').init(appconfig, 'server');
|
||||
global.db = require('./models/db')(appconfig);
|
||||
global.git = require('./models/git').init(appconfig, false);
|
||||
global.entries = require('./models/entries').init(appconfig);
|
||||
|
@ -37,17 +37,13 @@
|
||||
p.menu-label
|
||||
| Folders
|
||||
ul.menu-list
|
||||
li
|
||||
a(v-on:click="selectFolder('')", v-bind:class="{ 'is-active': currentFolder === '' }")
|
||||
span.icon.is-small: i.fa.fa-folder-o
|
||||
span /
|
||||
li(v-for="fld in folders")
|
||||
a(v-on:click="selectFolder(fld)", v-bind:class="{ 'is-active': currentFolder === fld }")
|
||||
span.icon.is-small: i.fa.fa-folder
|
||||
span / {{ fld }}
|
||||
span /{{ fld }}
|
||||
.column
|
||||
figure.image.is-128x128
|
||||
img(src='http://placehold.it/128x128')
|
||||
figure.image.is-128x128(v-for="img in images")
|
||||
img(v-bind:src="'/uploads/t/' + img.thumbpath")
|
||||
|
||||
.modal(v-bind:class="{ 'is-active': newFolderShow }")
|
||||
.modal-background
|
||||
|
@ -22,3 +22,6 @@ block content
|
||||
textarea#mk-editor= pageData.markdown
|
||||
|
||||
include ../modals/create-discard.pug
|
||||
include ../modals/editor-link.pug
|
||||
include ../modals/editor-image.pug
|
||||
include ../modals/editor-codeblock.pug
|
14
ws-server.js
14
ws-server.js
@ -33,20 +33,20 @@ if(!process.argv[2] || process.argv[2].length !== 40) {
|
||||
global.internalAuth = require('./lib/internalAuth').init(process.argv[2]);;
|
||||
|
||||
// ----------------------------------------
|
||||
// Load modules
|
||||
// Load global modules
|
||||
// ----------------------------------------
|
||||
|
||||
winston.info('[WS] WS Server is initializing...');
|
||||
|
||||
var appconfig = require('./models/config')('./config.yml');
|
||||
let lcdata = require('./models/localdata').init(appconfig, true);
|
||||
let lcdata = require('./models/localdata').init(appconfig, 'ws');
|
||||
|
||||
global.entries = require('./models/entries').init(appconfig);
|
||||
global.mark = require('./models/markdown');
|
||||
global.search = require('./models/search').init(appconfig);
|
||||
|
||||
// ----------------------------------------
|
||||
// Load modules
|
||||
// Load local modules
|
||||
// ----------------------------------------
|
||||
|
||||
var _ = require('lodash');
|
||||
@ -141,8 +141,14 @@ io.on('connection', (socket) => {
|
||||
cb(lcdata.getUploadsFolders());
|
||||
});
|
||||
|
||||
socket.on('uploadsSetFiles', (data, cb) => {
|
||||
if(internalAuth.validateKey(data.auth)) {
|
||||
lcdata.setUploadsFiles(data.content);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('uploadsGetImages', (data, cb) => {
|
||||
cb([]);
|
||||
cb(lcdata.getUploadsFiles('image', data.folder));
|
||||
});
|
||||
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user