jsharmony-cms
Version:
746 lines (641 loc) • 37.1 kB
JavaScript
/*
Copyright 2019 apHarmony
This file is part of jsHarmony.
jsHarmony is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
jsHarmony is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this package. If not, see <http://www.gnu.org/licenses/>.
*/
var Helper = require('jsharmony/Helper');
var HelperFS = require('jsharmony/HelperFS');
var mime = require('jsharmony/lib/mime');
var multiparty = require('jsharmony/lib/multiparty');
var _ = require('lodash');
var path = require('path');
var async = require('async');
var querystring = require('querystring');
var fs = require('fs');
var crypto = require('crypto');
module.exports = exports = function(module, funcs){
var exports = {};
var _t = module._t;
exports.getMediaFilename = function(media_file_id, media_ext, thumbnail_id, thumbnail_config){
var fname = media_file_id.toString();
if(thumbnail_id) fname += '.' + thumbnail_id;
else if(thumbnail_config && thumbnail_config.resize){
fname += '.custom.'+(thumbnail_config.resize[0]||'').toString()+'x'+(thumbnail_config.resize[1]||'').toString();
}
else if(thumbnail_config) throw new Error('Unsupported media file name: '+JSON.stringify(thumbnail_config));
fname += ((thumbnail_config && thumbnail_config.format) ? '.' + thumbnail_config.format : media_ext);
return path.join(path.join(module.jsh.Config.datadir,'media'),fname);
};
exports.getMediaFile = function(media_file_id, media_filename, media_ext, thumbnail_id, thumbnail_config, callback){
var jsh = module.jsh;
var srcpath = funcs.getMediaFilename(media_file_id, media_ext);
var fpath = funcs.getMediaFilename(media_file_id, media_ext, thumbnail_id, thumbnail_config);
var fname = path.basename(media_filename);
if(thumbnail_config && thumbnail_config.format){
var fext = path.extname(fname);
if(fext.length > 1) fname = fname.substr(0, fname.length - fext.length) + '.' + thumbnail_config.format;
}
var transformMedia = function(transform_callback){
if('resize' in thumbnail_config) jsh.Extensions.image.resize(srcpath, fpath, thumbnail_config.resize, thumbnail_config.format, transform_callback);
else if('crop' in thumbnail_config) jsh.Extensions.image.crop(srcpath, fpath, thumbnail_config.crop, thumbnail_config.format, transform_callback);
else if('format' in thumbnail_config) jsh.Extensions.image.resample(srcpath, fpath, thumbnail_config.format, transform_callback);
else return transform_callback(new Error('Invalid thumbnail_config: '+JSON.stringify(thumbnail_config)));
};
fs.stat(fpath, function (err, stat) {
if(err){
if(HelperFS.fileNotFound(err)){
if(!thumbnail_config) return callback(new Error('Media file not found'));
//Generate thumbnail
transformMedia(function(err){
if(err) return callback(err);
fs.stat(fpath, function (err, stat) {
if(err) return callback(err);
return callback(null, fpath, fname, stat);
});
});
}
else return callback(err);
}
else return callback(null, fpath, fname, stat);
});
};
exports.appendThumbnail = function(fpath, thumbnail_id, thumbnail_config){
if(!fpath) return fpath;
var fname = path.basename(fpath);
var fext = path.extname(fname);
fpath = fpath.substr(0, fpath.length - fext.length) + '.' + thumbnail_id + fext;
if(thumbnail_config && thumbnail_config.format){
if(fext.length > 1) fpath = fpath.substr(0, fpath.length - fext.length) + '.' + thumbnail_config.format;
}
return fpath;
};
exports.media = function (req, res, next) {
var verb = req.method.toLowerCase();
var Q = req.query;
var P = req.body;
var appsrv = this;
var jsh = module.jsh;
var XValidate = jsh.XValidate;
var sql = '';
var sql_ptypes = [];
var sql_params = {};
var verrors = {};
var dbtypes = appsrv.DB.types;
var validate = null;
var model = jsh.getModel(req, module.namespace + 'Media_Tree');
if (!Helper.hasModelAction(req, model, 'B')) { Helper.GenError(req, res, -11, _t('Invalid Model Access')); return; }
funcs.getSiteConfig(req._DBContext, null, { continueOnConfigError: true }, function(err, siteConfig){
if(err) return Helper.GenError(req, res, -99999, err.toString());
if (verb == 'get'){ (function(){
if(!req.params || !req.params.media_key) return next();
var media_key = req.params.media_key;
var thumbnail_id = req.params.thumbnail;
var thumbnail_config = null;
if(thumbnail_id) thumbnail_config = siteConfig.media_thumbnails[thumbnail_id];
//Invalid Thumbnail
if(thumbnail_id && !thumbnail_config) return next();
//Check if media exists
sql_ptypes = [dbtypes.BigInt];
sql_params = { 'media_key': media_key };
validate = new XValidate();
verrors = {};
validate.AddValidator('_obj.media_key', 'Media Key', 'B', [XValidate._v_IsNumeric(), XValidate._v_Required()]);
sql = 'select media_key,media_file_id,media_filename,media_path,media_ext from {schema}.v_my_media where media_key=@media_key';
if(Q.media_id){
sql_ptypes.push(dbtypes.BigInt);
sql_params.media_id = Q.media_id;
validate.AddValidator('_obj.media_id', 'Media ID', 'B', [XValidate._v_IsNumeric()]);
sql = 'select media_key,media_file_id,media_filename,media_path,media_ext from {schema}.media where media_key=@media_key and media_id=@media_id and site_id={schema}.my_current_site_id()';
}
var fields = [];
var datalockstr = '';
appsrv.getDataLockSQL(req, model, fields, sql_ptypes, sql_params, verrors, function (datalockquery) { datalockstr += ' and ' + datalockquery; });
sql = Helper.ReplaceAll(sql, '%%%DATALOCKS%%%', datalockstr);
verrors = _.merge(verrors, validate.Validate('B', sql_params));
if (!_.isEmpty(verrors)) { Helper.GenError(req, res, -2, verrors[''].join('\n')); return; }
appsrv.ExecRecordset(req._DBContext, funcs.replaceSchema(sql), sql_ptypes, sql_params, function (err, rslt) {
if (err != null) { err.sql = sql; err.model = model; appsrv.AppDBError(req, res, err); return; }
if(!rslt || !rslt.length || !rslt[0] || (rslt[0].length != 1)){ return Helper.GenError(req, res, -4, 'Invalid Media ID'); }
var media = rslt[0][0];
if(Q.media_file_id){
if(Q.media_file_id.toString() != (media.media_file_id||'').toString()){
//Redirect to correct media_file_id
var query = req.query;
query.media_file_id = media.media_file_id;
var url = req.path + '?' + querystring.stringify(query);
return Helper.Redirect302(res, url);
}
}
//Validate parameters
if (!appsrv.ParamCheck('P', P, [])) { Helper.GenError(req, res, -4, 'Invalid Parameters'); return; }
if (!appsrv.ParamCheck('Q', Q, ['|width','|height','|download','|media_file_id','|media_id'])) { Helper.GenError(req, res, -4, 'Invalid Parameters'); return; }
//XValidate
if(!thumbnail_config && (Q.width || Q.height)){
validate = new XValidate();
verrors = {};
validate.AddValidator('_obj.width', 'Width', 'B', [ XValidate._v_IsNumeric(), XValidate._v_Required() ]);
validate.AddValidator('_obj.height', 'Height', 'B', [ XValidate._v_IsNumeric(), XValidate._v_Required() ]);
verrors = _.merge(verrors, validate.Validate('B', Q));
if (!_.isEmpty(verrors)) { Helper.GenError(req, res, -2, verrors[''].join('\n')); return; }
thumbnail_config = { resize: [ parseInt(Q.width), parseInt(Q.height) ] };
}
var serveoptions = { attachment: !!('download' in Q), mime_override: media.media_ext };
if(thumbnail_config && thumbnail_config.format) serveoptions.mime_override = '.' + thumbnail_config.format;
funcs.getMediaFile(media.media_file_id, media.media_filename, media.media_ext, thumbnail_id, thumbnail_config, function(err, fpath, fname, stat){
if(err){
if(err.message == 'Media file not found') return Helper.GenError(req, res, -33, 'Media file not found.');
jsh.Log.error(err.toString() + '\n' + (err.stack?err.stack:(new Error()).stack));
return Helper.GenError(req, res, -99999, 'Error occurred during media processing operation (' + err.toString() + ')');
}
var headers = {
'Content-Type': HelperFS.getMimeType(serveoptions.mime_override||fpath),
'Content-Length': stat.size,
'Content-Disposition': 'attachment; filename=' + encodeURIComponent(fname)
};
if(!serveoptions.attachment) delete headers['Content-Disposition'];
res.writeHead(200, headers);
fs.createReadStream(fpath).pipe(res);
});
return;
});
})(); }
else if (verb == 'put'){ (function(){
if (!Helper.hasModelAction(req, model, 'I')) { Helper.GenError(req, res, -11, _t('Invalid Model Access')); return; }
if(req.params && req.params.media_key) return next();
var public_folder = jsh.Config.datadir + 'temp/public/';
var form = new multiparty.Form({ maxFilesSize: jsh.Config.max_filesize, uploadDir: (public_folder) });
form.parse(req, function (err, fields, files) {
//Handle Error
if (err != null) {
if (('code' in err) && (err.code == 'ETOOBIG')) { return Helper.GenError(req, res, -31, 'Upload file exceeded maximum file size.'); }
jsh.Log.error(err);
return Helper.GenError(req, res, -30, 'Invalid file upload request.');
}
P = {};
for(var field_name in fields){
var field = fields[field_name];
if(_.isArray(field) && field.length) field = field[0];
P[field_name] = field;
}
if (files == null) { return Helper.GenError(req, res, -30, 'Invalid file upload request.'); }
if (!('media_file' in files)) { return Helper.GenError(req, res, -30, 'Invalid file upload request.'); }
if (files.media_file.length != 1) { return Helper.GenError(req, res, -30, 'Invalid file upload request.'); }
var media_file = files.media_file[0];
var media_size = media_file.size;
var media_file_orig_name = path.basename(media_file.originalFilename);
var tmp_file_path = media_file.path;
var media_ext = path.extname(path.basename(media_file_orig_name)).toLowerCase()||'.'; //Get extension
if (!_.includes(jsh.Config.valid_extensions, media_ext)) { return Helper.GenError(req, res, -32, 'File extension is not supported.'); }
//Validate parameters
if (!appsrv.ParamCheck('P', P, ['|media_desc', '&media_path', '|media_tags', '|media_type'])) { Helper.GenError(req, res, -4, 'Invalid Parameters'); return; }
if (!appsrv.ParamCheck('Q', Q, [])) { Helper.GenError(req, res, -4, 'Invalid Parameters'); return; }
validate = new XValidate();
verrors = {};
validate.AddValidator('_obj.media_desc', 'Description', 'B', [ XValidate._v_MaxLength(256) ]);
validate.AddValidator('_obj.media_path', 'Path', 'B', [ XValidate._v_MaxLength(2048), XValidate._v_Required() ]);
validate.AddValidator('_obj.media_tags', 'Tags', 'B', [ ]);
validate.AddValidator('_obj.media_type', 'Type', 'B', [ XValidate._v_MaxLength(32) ]);
verrors = _.merge(verrors, validate.Validate('B', P));
if (!_.isEmpty(verrors)) { Helper.GenError(req, res, -2, verrors[''].join('\n')); return; }
//Calculate extension, size, width, height
var media_key = null;
var media_desc = P.media_desc;
var media_path = P.media_path;
var media_tags = P.media_tags;
var media_type = P.media_type;
var media_height = null;
var media_width = null;
var base_path = path.dirname(media_path);
if(base_path[base_path.length - 1]!='/') base_path += '/';
var media_filename = HelperFS.cleanFileName(path.basename(media_path));
media_path = base_path + media_filename;
async.waterfall([
//Clear temp files
function(cb){
HelperFS.clearFiles(public_folder, jsh.Config.public_temp_expiration, -1, function(err){
if (_.isObject(err) && ('number' in err) && (err.number == -36)) return Helper.GenError(req, res, -36, 'User exceeded max temp folder size');
return cb();
});
},
//Rename duplicate path
function(cb){
var media_path_base = base_path + media_filename.substr(0, media_filename.length - media_ext.length) + '%' + media_ext;
sql_ptypes = [dbtypes.VarChar(2048)];
sql_params = {
'media_path_base': media_path_base
};
sql = 'select media_path from '+(module.schema?module.schema+'.':'')+'v_my_media where media_path like @media_path_base;';
appsrv.ExecRecordset(req._DBContext, sql, sql_ptypes, sql_params, function (err, rslt) {
if (err != null) { err.sql = sql; err.model = model; appsrv.AppDBError(req, res, err); return; }
if(!rslt || !rslt.length || !rslt[0] || !rslt[0].length) return cb();
var conflicts = rslt[0];
var curattempt = 0;
while(true){ // eslint-disable-line no-constant-condition
var found_conflict = false;
for(var i=0;i<conflicts.length;i++){
var conflict = conflicts[i];
if(conflict.media_path.toLowerCase()==media_path.toLowerCase()){ found_conflict = true; break; }
}
if(!found_conflict) break;
curattempt++;
media_path = base_path + media_filename.substr(0, media_filename.length - media_ext.length) + '('+curattempt.toString()+')' + media_ext;
}
return cb();
});
},
//Get image width / height
function(cb){
if(!_.includes(['.jpg','.jpeg','.tif','.tiff','.png','.gif','.svg'], media_ext)) return cb();
jsh.Extensions.image.size(tmp_file_path, function(err, size){
if(err || !size || !size.width || !size.height) return cb();
media_width = size.width;
media_height = size.height;
return cb();
});
},
//Apply maximum image size, if applicable
function(cb){
if(!media_width || !media_height) return cb();
if(siteConfig.media_thumbnails && siteConfig.media_thumbnails.maximum && siteConfig.media_thumbnails.maximum.resize){
jsh.Extensions.image.resize(tmp_file_path, tmp_file_path, siteConfig.media_thumbnails.maximum.resize, undefined, function(err){
if(err) return cb(err);
media_height = null;
media_width = null;
jsh.Extensions.image.size(tmp_file_path, function(err, size){
if(err || !size || !size.width || !size.height) return cb();
media_width = size.width;
media_height = size.height;
return cb();
});
});
}
else return cb();
},
//Insert into database
function(cb){
sql_ptypes = [dbtypes.VarChar(256),dbtypes.VarChar(2048),dbtypes.VarChar(-1),dbtypes.VarChar(32),dbtypes.VarChar(16),dbtypes.Int,dbtypes.Int,dbtypes.Int];
sql_params = {
'media_desc': media_desc,
'media_path': media_path,
'media_tags': media_tags,
'media_type': media_type,
'media_ext': media_ext,
'media_size': media_size,
'media_width': media_width,
'media_height': media_height
};
sql = (module.schema?module.schema+'.':'')+'insert_media(@media_desc, @media_path, @media_tags, @media_type, @media_ext, @media_size, @media_width, @media_height);';
var fields = [];
var datalockstr = '';
appsrv.getDataLockSQL(req, model, fields, sql_ptypes, sql_params, verrors, function (datalockquery) { datalockstr += ' and ' + datalockquery; });
sql = Helper.ReplaceAll(sql, '%%%DATALOCKS%%%', datalockstr);
appsrv.ExecRecordset(req._DBContext, sql, sql_ptypes, sql_params, function (err, rslt) {
if (err != null) { err.sql = sql; err.model = model; appsrv.AppDBError(req, res, err); return; }
if(!rslt || !rslt.length || !rslt[0] || (rslt[0].length != 1)){ return Helper.GenError(req, res, -4, 'Invalid Media ID'); }
media_key = rslt[0][0].media_key;
return cb();
});
},
//Copy file
function (cb) {
HelperFS.rename(tmp_file_path, funcs.getMediaFilename(media_key, media_ext), cb);
},
], function(err){
if(err){
jsh.Log.error(err.toString() + '\n' + (err.stack?err.stack:(new Error()).stack));
return Helper.GenError(req, res, -99999, 'Error occurred during media processing operation (' + err.toString() + ')');
}
res.end(JSON.stringify({ '_success': 1, 'media_key': media_key }));
});
});
})(); }
else if (verb == 'post'){ (function(){
if (!Helper.hasModelAction(req, model, 'U')) { Helper.GenError(req, res, -11, _t('Invalid Model Access')); return; }
if(!req.params || !req.params.media_key) return next();
var media_key = req.params.media_key;
//Check if media exists
sql_ptypes = [dbtypes.BigInt];
sql_params = { 'media_key': media_key };
validate = new XValidate();
verrors = {};
validate.AddValidator('_obj.media_key', 'Media Key', 'B', [XValidate._v_IsNumeric(), XValidate._v_Required()]);
sql = 'select media_key,media_file_id,media_filename,media_path,media_ext from '+(module.schema?module.schema+'.':'')+'v_my_media where media_key=@media_key';
var fields = [];
var datalockstr = '';
appsrv.getDataLockSQL(req, model, fields, sql_ptypes, sql_params, verrors, function (datalockquery) { datalockstr += ' and ' + datalockquery; });
sql = Helper.ReplaceAll(sql, '%%%DATALOCKS%%%', datalockstr);
verrors = _.merge(verrors, validate.Validate('B', sql_params));
if (!_.isEmpty(verrors)) { Helper.GenError(req, res, -2, verrors[''].join('\n')); return; }
appsrv.ExecRecordset(req._DBContext, sql, sql_ptypes, sql_params, function (err, rslt) {
if (err != null) { err.sql = sql; err.model = model; appsrv.AppDBError(req, res, err); return; }
if(!rslt || !rslt.length || !rslt[0] || (rslt[0].length != 1)){ return Helper.GenError(req, res, -4, 'Invalid Media ID'); }
var media = rslt[0][0];
var public_folder = jsh.Config.datadir + 'temp/public/';
var form = new multiparty.Form({ maxFilesSize: jsh.Config.max_filesize, uploadDir: (public_folder) });
form.parse(req, function (err, fields, files) {
//Handle Error
if (err != null) {
if (('code' in err) && (err.code == 'ETOOBIG')) { return Helper.GenError(req, res, -31, 'Upload file exceeded maximum file size.'); }
jsh.Log.error(err);
return Helper.GenError(req, res, -30, 'Invalid file upload request.');
}
P = {};
for(var field_name in fields){
var field = fields[field_name];
if(_.isArray(field) && field.length) field = field[0];
P[field_name] = field;
}
if (files == null) { return Helper.GenError(req, res, -30, 'Invalid file upload request.'); }
if (!('media_file' in files)) { return Helper.GenError(req, res, -30, 'Invalid file upload request.'); }
if (files.media_file.length != 1) { return Helper.GenError(req, res, -30, 'Invalid file upload request.'); }
var media_file = files.media_file[0];
var media_size = media_file.size;
var media_file_orig_name = path.basename(media_file.originalFilename);
var tmp_file_path = media_file.path;
var media_ext = path.extname(path.basename(media_file_orig_name)).toLowerCase()||'.'; //Get extension
if (!_.includes(jsh.Config.valid_extensions, media_ext)) { return Helper.GenError(req, res, -32, 'File extension is not supported.'); }
//Get new media_path base on media_ext
var media_path = media.media_path.substr(0, media.media_path.length - media.media_ext.length) + media_ext;
//Validate parameters
if (!appsrv.ParamCheck('P', P, [])) { Helper.GenError(req, res, -4, 'Invalid Parameters'); return; }
if (!appsrv.ParamCheck('Q', Q, [])) { Helper.GenError(req, res, -4, 'Invalid Parameters'); return; }
//Calculate extension, size, width, height
var media_file_id = null;
var media_height = null;
var media_width = null;
var media_data = null;
async.waterfall([
//Clear temp files
function(cb){
HelperFS.clearFiles(public_folder, jsh.Config.public_temp_expiration, -1, function(err){
if (_.isObject(err) && ('number' in err) && (err.number == -36)) return Helper.GenError(req, res, -36, 'User exceeded max temp folder size');
return cb();
});
},
//Get image width / height
function(cb){
jsh.Extensions.image.size(tmp_file_path, function(err, size){
if(err || !size || !size.width || !size.height) return cb();
media_width = size.width;
media_height = size.height;
return cb();
});
},
//Apply maximum image size, if applicable
function(cb){
if(!media_width || !media_height) return cb();
if(siteConfig.media_thumbnails && siteConfig.media_thumbnails.maximum && siteConfig.media_thumbnails.maximum.resize){
jsh.Extensions.image.resize(tmp_file_path, tmp_file_path, siteConfig.media_thumbnails.maximum.resize, undefined, function(err){
if(err) return cb(err);
media_height = null;
media_width = null;
jsh.Extensions.image.size(tmp_file_path, function(err, size){
if(err || !size || !size.width || !size.height) return cb();
media_width = size.width;
media_height = size.height;
return cb();
});
});
}
else return cb();
},
//Update database
function(cb){
sql_ptypes = [dbtypes.BigInt,dbtypes.VarChar(16),dbtypes.VarChar(2048),dbtypes.Int,dbtypes.Int,dbtypes.Int];
sql_params = {
media_key: media_key,
media_ext: media_ext,
media_path: media_path,
media_size: media_size,
media_width: media_width,
media_height: media_height
};
sql = 'update '+(module.schema?module.schema+'.':'')+'v_my_media set media_file_id=null,media_ext=@media_ext, media_path=@media_path, media_size=@media_size, media_width=@media_width, media_height=@media_height where media_key = @media_key; ';
sql += 'select media_id, media_filename, media_file_id, media_uptstmp, jsharmony.my_db_user_fmt(media_upuser) media_upuser_fmt, media_mtstmp, jsharmony.my_db_user_fmt(media_muser) media_muser_fmt from '+(module.schema?module.schema+'.':'')+'v_my_media where media_key=@media_key;';
var fields = [];
var datalockstr = '';
appsrv.getDataLockSQL(req, model, fields, sql_ptypes, sql_params, verrors, function (datalockquery) { datalockstr += ' and ' + datalockquery; });
sql = Helper.ReplaceAll(sql, '%%%DATALOCKS%%%', datalockstr);
appsrv.ExecRecordset(req._DBContext, sql, sql_ptypes, sql_params, function (err, rslt) {
if (err != null) { err.sql = sql; err.model = model; appsrv.AppDBError(req, res, err); return; }
if(!rslt || !rslt.length || !rslt[0] || (rslt[0].length != 1)){ return Helper.GenError(req, res, -4, 'Invalid Media ID'); }
media_file_id = rslt[0][0].media_file_id;
media_data = rslt[0][0];
return cb();
});
},
//Copy file
function (cb) {
HelperFS.rename(tmp_file_path, funcs.getMediaFilename(media_file_id, media_ext), cb);
},
], function(err){
if(err){
jsh.Log.error(err.toString() + '\n' + (err.stack?err.stack:(new Error()).stack));
return Helper.GenError(req, res, -99999, 'Error occurred during media processing operation (' + err.toString() + ')');
}
var rslt_data = {
'_success': 1,
media: {
media_ext: media_ext,
media_path: media_path,
media_width: media_width,
media_height: media_height,
media_size: media_size
}
};
rslt_data.media = _.extend(rslt_data.media, media_data);
res.end(JSON.stringify(rslt_data));
});
});
return;
});
})(); }
else if (verb == 'delete'){ (function(){
if (!Helper.hasModelAction(req, model, 'D')) { Helper.GenError(req, res, -11, _t('Invalid Model Access')); return; }
if(!req.params || !req.params.media_key) return next();
var media_key = req.params.media_key;
//Check if media exists
sql_ptypes = [dbtypes.BigInt];
sql_params = { 'media_key': media_key };
validate = new XValidate();
verrors = {};
validate.AddValidator('_obj.media_key', 'Media Key', 'B', [XValidate._v_IsNumeric(), XValidate._v_Required()]);
sql = 'select media_key,media_file_id,media_filename,media_path,media_ext from '+(module.schema?module.schema+'.':'')+'v_my_media where media_key=@media_key';
var fields = [];
var datalockstr = '';
appsrv.getDataLockSQL(req, model, fields, sql_ptypes, sql_params, verrors, function (datalockquery) { datalockstr += ' and ' + datalockquery; });
sql = Helper.ReplaceAll(sql, '%%%DATALOCKS%%%', datalockstr);
verrors = _.merge(verrors, validate.Validate('B', sql_params));
if (!_.isEmpty(verrors)) { Helper.GenError(req, res, -2, verrors[''].join('\n')); return; }
appsrv.ExecRecordset(req._DBContext, sql, sql_ptypes, sql_params, function (err, rslt) {
if (err != null) { err.sql = sql; err.model = model; appsrv.AppDBError(req, res, err); return; }
if(!rslt || !rslt.length || !rslt[0] || (rslt[0].length != 1)){ return Helper.GenError(req, res, -4, 'Invalid Media ID'); }
//Validate parameters
if (!appsrv.ParamCheck('P', P, [])) { Helper.GenError(req, res, -4, 'Invalid Parameters'); return; }
if (!appsrv.ParamCheck('Q', Q, [])) { Helper.GenError(req, res, -4, 'Invalid Parameters'); return; }
appsrv.ExecCommand(req._DBContext, 'delete from '+(module.schema?module.schema+'.':'')+'v_my_media where media_key=@media_key', sql_ptypes, sql_params, function (err, rslt) {
if(err) return Helper.GenError(req, res, -99999, 'Error deleting file (' + err.toString() + ')');
res.end(JSON.stringify({ '_success': 1 }));
});
return;
});
})(); }
else return next();
});
};
exports.replacePasteImages = function(dbcontext, db, dbtasks, dbtaskidFunc, baseurl, siteConfig, itemData, replaceContentFunc /* function(itemData, options, replaceFunc){ } */, options){
options = _.extend({
mediaFileNameFunc: function(media_ext){ return crypto.randomBytes(16).toString('hex')+'_'+(new Date().getTime()).toString(16)+media_ext; }
}, options);
var jsh = module.jsh;
var appsrv = jsh.AppSrv;
var dbtypes = appsrv.DB.types;
var inlineImages = [];
replaceContentFunc(itemData, { replaceHeaderFooterCSS: false }, function(content){
funcs.parsePasteImages(content, function(imageInfo){
var foundImage = false;
for(var i=0;i<inlineImages.length;i++){
if(inlineImages[i].url==imageInfo.url) foundImage = true;
}
if(!foundImage) inlineImages.push(imageInfo);
return imageInfo.url;
});
return content;
});
_.each(inlineImages, function(inlineImage){
dbtasks[dbtaskidFunc()] = function (dbtrans, db_callback, transtbl) {
var media_ext = mime.extension(inlineImage.type||'');
if(!media_ext) return db_callback(new Error('Media type "'+(inlineImage.type||'')+'" not supported'));
media_ext = '.' + media_ext;
if (!_.includes(jsh.Config.valid_extensions, media_ext)) { return db_callback(new Error('File extension "'+media_ext+'" is not supported.')); }
var media_data = Buffer.from(inlineImage.data||'', 'base64');
var media_size = media_data.length;
var media_path = siteConfig.savePastedImagesToMediaPath;
while(media_path && (media_path[0]=='/')) media_path = media_path.substr(1);
while(media_path[media_path.length-1]=='/') media_path = media_path.substr(0,media_path.length-1);
if(media_path) media_path = '/' + media_path;
var media_file_name = options.mediaFileNameFunc(media_ext);
media_path += '/'+media_file_name;
var media_key = null;
var user_folder = path.join(jsh.Config.datadir, 'temp', dbcontext);
var tmp_file_path = path.join(user_folder, media_file_name);
var media_width = null;
var media_height = null;
async.waterfall([
//Create temp folder if it does not exist
async.apply(HelperFS.createFolderIfNotExists, user_folder),
//Clear temp files
async.apply(HelperFS.clearFiles, user_folder, jsh.Config.user_temp_expiration, -1),
//Write image to disk
function(media_cb){
fs.writeFile(tmp_file_path, media_data, media_cb);
},
//Get image width / height
function(media_cb){
if(!_.includes(['.jpg','.jpeg','.tif','.tiff','.png','.gif','.svg'], media_ext)) return media_cb();
jsh.Extensions.image.size(tmp_file_path, function(err, size){
if(err || !size || !size.width || !size.height) return media_cb();
media_width = size.width;
media_height = size.height;
return media_cb();
});
},
//Apply maximum image size, if applicable
function(media_cb){
if(!media_width || !media_height) return media_cb();
if(siteConfig.media_thumbnails && siteConfig.media_thumbnails.maximum && siteConfig.media_thumbnails.maximum.resize){
jsh.Extensions.image.resize(tmp_file_path, tmp_file_path, siteConfig.media_thumbnails.maximum.resize, undefined, function(err){
if(err) return media_cb(err);
media_height = null;
media_width = null;
jsh.Extensions.image.size(tmp_file_path, function(err, size){
if(err || !size || !size.width || !size.height) return media_cb();
media_width = size.width;
media_height = size.height;
return media_cb();
});
});
}
else return media_cb();
},
//Save inline images to branch
function(media_cb){
var image_sql = (module.schema?module.schema+'.':'')+'insert_media(null, @media_path, null, null, @media_ext, @media_size, @media_width, @media_height);';
var image_sql_ptypes = [
dbtypes.VarChar(2048),
dbtypes.VarChar(16),
dbtypes.Int,
dbtypes.Int,
dbtypes.Int
];
var image_sql_params = {
media_path: media_path,
media_ext: media_ext,
media_size: media_size,
media_width: media_width,
media_height: media_height,
};
db.Recordset(dbcontext, image_sql, image_sql_ptypes, image_sql_params, dbtrans, function (err, rslt) {
if (err != null) { err.sql = image_sql; return media_cb(err); }
if(!rslt || !rslt.length || !rslt[0] || !rslt[0].media_key) return media_cb(new Error('Invalid database result'));
media_key = rslt[0].media_key;
return media_cb();
});
},
//Move file to target location
function (media_cb) {
HelperFS.rename(tmp_file_path, funcs.getMediaFilename(media_key, media_ext), media_cb);
},
function(media_cb){
inlineImage.mediaUrl = baseurl + '_funcs/media/'+media_key+'/#@JSHCMS';
replaceContentFunc(itemData, { replaceHeaderFooterCSS: false }, function(content){
return funcs.parsePasteImages(content, function(imageInfo){
if(inlineImage.url==imageInfo.url){
if(inlineImage.mediaUrl){
return inlineImage.mediaUrl;
}
}
return imageInfo.url;
});
});
return media_cb();
},
], db_callback);
};
});
};
exports.parsePasteImages = function (content, replaceFunc){
//Extract all inline images from the content
//<img src=\"data:image/png;jshcms=paste;base64,...
//Expand components
return funcs.replaceURLs(';jshcms=paste;', content, { replaceComponents: true }, function(rtag, url, getLinkContent){
var origUrl = url;
if(!Helper.beginsWith(url, 'data:')) return origUrl;
url = url.substr(5);
var idx = url.indexOf(';base64,');
if(idx<0) return origUrl;
var imageType = url.substr(0, idx);
var token = ';jshcms=paste';
if(imageType.indexOf(token)<0) token = token.substr(1);
imageType = Helper.ReplaceAll(imageType, token, '');
var base64data = url.substr(idx+8);
var imageInfo = {
url: origUrl,
type: imageType,
data: base64data,
};
if(replaceFunc) return replaceFunc(imageInfo);
return origUrl;
});
};
return exports;
};