diff --git a/dist/upload.d.ts.map b/dist/upload.d.ts.map index 473d3eb..17a5d9c 100644 --- a/dist/upload.d.ts.map +++ b/dist/upload.d.ts.map @@ -1 +1 @@ -{"version":3,"sources":["upload.ts"],"names":[],"mappings":"AAKA,OAAO,EAAC,IAAI,EAAC,MAAM,MAAM,CAAC;AAI1B,OAAO,MAAM,MAAM,UAAU,CAAC;AAC9B,OAAO,KAAK,MAAM,SAAS,CAAC;AAO5B,oBAAY,MAAM;IACjB,OAAO,IAAA;IACP,WAAW,IAAA;IACX,IAAI,IAAA;IACJ,IAAI,IAAA;IACJ,QAAQ,IAAA;IACR,QAAQ,IAAA;IACR,QAAQ,IAAA;IACR,QAAQ,IAAA;IACR,UAAU,IAAA;CAIV;AAED,MAAM,WAAW,KAAK;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,OAAO;IACvB,MAAM,EAAE,KAAK,CAAC;CACd;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,CAAC,OAAO,OAAO,MAAM;IAC1B,OAAc,EAAE,gBAAU;IAC1B,OAAc,MAAM,gBAAU;IAE9B,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,gBAAgB,CAAC;IACzB,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAC;IAClB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,WAAW,CAAM;IACzB,OAAO,CAAC,MAAM,CAAM;gBAMP,MAAM,GAAE,MAAM,GAAC,QAAe;IAU3C;;;;;;;;;;;;;;;;;OAiBG;IACI,MAAM,CAAE,MAAM,EAAE,MAAM,GAAC,QAAQ,GAAI,MAAM;IAMhD;;;;;;;;;;;;;;;;;OAiBG;IACI,EAAE,CAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAI,MAAM;IAQhE;;;;;;;;;;;OAWG;IACI,OAAO,CAAE,UAAU,EAAE,MAAM,GAAC,QAAQ,EAAE,QAAQ,GAAE,QAAe,GAAI,MAAM;IAchF;;;;;;;;;OASG;IACI,SAAS,CAAE,EAAE,KAAA,GAAI,MAAM;IAM9B;;;;;;;;OAQG;IACI,KAAK,CAAE,EAAE,KAAA,GAAI,MAAM;IAU1B;;OAEG;IACU,IAAI,CAAE,EAAE,EAAE,IAAI,EAAE,GAAG,GAAE,MAAM,EAAS,GAAI,OAAO,CAAC,MAAM,CAAC;IAqCpE;;OAEG;IACU,WAAW,CAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAI,OAAO,CAAC,IAAI,CAAC;IAOvE;;OAEG;IACI,KAAK;IAIZ;;OAEG;IACU,IAAI,CAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAI,OAAO,CAAE,MAAM,CAAC;IAgDtE;;OAEG;IACI,IAAI;IAIX;;OAEG;IACI,KAAK;YAOE,WAAW;YAuBX,QAAQ;YA4ER,OAAO;IA8FrB,OAAO,CAAC,WAAW;CAcnB","file":"upload.d.ts","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\n\nimport * as rootPath from 'app-root-path';\nimport * as knex from 'knex';\nimport {Knex} from 'knex';\n\nimport * as mv from 'mv';\n\nimport Editor from './editor';\nimport Field from './field';\nimport promisify from './promisify';\n\nlet stat = promisify( fs.stat );\nlet readFile = promisify( fs.readFile );\nlet rename = promisify( mv );\n\nexport enum DbOpts {\n\tContent,\n\tContentType,\n\tExtn,\n\tName,\n\tFileName, // Name + Extn\n\tFileSize,\n\tMimeType,\n\tReadOnly,\n\tSystemPath\n\t// Note that the PHP and .NET libraries have a WebPath, but that isn't\n\t// available here as there isn't a good and reliable way to get the web\n\t// root in node (it could be anywhere!).\n}\n\nexport interface IFile {\n\tuuid: string;\n\tfield: string;\n\tfile: string; // full path\n\tfilename: string; // name + extn\n\tencoding: string;\n\tmimetype: string;\n\ttruncated: boolean;\n\tdone: boolean;\n\tsize: number; // Added\n\textn: string; // Added\n\tname: string; // Added\n}\n\nexport interface IUpload {\n\tupload: IFile;\n}\n\n/**\n * Upload class for Editor. This class provides the ability to easily specify\n * file upload information, specifically how the file should be recorded on\n * the server (database and file system).\n *\n * An instance of this class is attached to a field using the {@link\n * Field.upload} method. When Editor detects a file upload for that file the\n * information provided for this instance is executed.\n *\n * The configuration is primarily driven through the {@link db} and {@link\n * action} methods:\n *\n * * {@link db} Describes how information about the uploaded file is to be\n * stored on the database.\n * * {@link action} Describes where the file should be stored on the file system\n * and provides the option of specifying a custom action when a file is\n * uploaded.\n *\n * Both methods are optional - you can store the file on the server using the\n * {@link db} method only if you want to store the file in the database, or if\n * you don't want to store relational data on the database us only {@link\n * action}. However, the majority of the time it is best to use both - store\n * information about the file on the database for fast retrieval (using a {@link\n * Editor.leftJoin()} for example) and the file on the file system for direct\n * web access.\n *\n * @export\n * @class Upload\n */\nexport default class Upload {\n\tpublic static Db = DbOpts; // legacy\n\tpublic static DbOpts = DbOpts;\n\n\tprivate _action: string|Function;\n\tprivate _dbCleanCallback; // async function\n\tprivate _dbCleanTableField: string;\n\tprivate _dbTable: string;\n\tprivate _dbPkey: string;\n\tprivate _dbFields;\n\tprivate _error: string;\n\tprivate _validators = [];\n\tprivate _where = [];\n\n\t/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n\t * Constructor\n\t */\n\n\tconstructor( action: string|Function = null ) {\n\t\tif ( action ) {\n\t\t\tthis.action( action );\n\t\t}\n\t}\n\n\t/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n\t * Public methods\n\t */\n\n\t/**\n\t * Set the action to take when a file is uploaded. This can be either of:\n\t *\n\t * * A string - the value given is the full system path to where the\n\t * uploaded file is written to. The value given can include three \"macros\"\n\t * which are replaced by the script dependent on the uploaded file:\n\t * * `__EXTN__` - the file extension\n\t * * `__NAME__` - the uploaded file's name (including the extension)\n\t * * `__ID__` - Database primary key value if the {@link db} method is\n\t * used.\n\t * * A closure - if a function is given the responsibility of what to do\n\t * with the uploaded file is transferred to this function. That will\n\t * typically involve writing it to the file system so it can be used\n\t * later.\n\t *\n\t * @param {(string|Function)} action Upload action\n\t * @returns {Upload} Self for chaining\n\t */\n\tpublic action( action: string|Function ): Upload {\n\t\tthis._action = action;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Database configuration method. When used, this method will tell Editor\n\t * what information you want written to a database on file upload, should\n\t * you wish to store relational information about your file on the database\n\t * (this is generally recommended).\n\t *\n\t * @param {string} table The name of the table where the file information\n\t * should be stored\n\t * @param {string} pkey Primary key column name. The `Upload` class\n\t * requires that the database table have a single primary key so each\n\t * row can be uniquely identified.\n\t * @param {object} fields A list of the fields to be written to on upload.\n\t * The property names are the database columns and the values can be\n\t * defined by the constants of this class. The value can also be a\n\t * string or a closure function if you wish to send custom information\n\t * to the database.\n\t * @returns {Upload} Self for chaining\n\t */\n\tpublic db( table: string, pkey: string, fields: object ): Upload {\n\t\tthis._dbTable = table;\n\t\tthis._dbPkey = pkey;\n\t\tthis._dbFields = fields;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set a callback function that is used to remove files which no longer have\n\t * a reference in a source table.\n\t *\n\t * @param {(string|Function)} tableField Table field to be used for the delete match\n\t * @param {Function} [callback=null] Function that will be executed on clean. It is\n\t * given an array of information from the database about the orphaned\n\t * rows, and can return true to indicate that the rows should be\n\t * removed from the database. Any other return value (including none)\n\t * will result in the records being retained.\n\t * @returns {Upload} Self for chaining\n\t */\n\tpublic dbClean( tableField: string|Function, callback: Function = null ): Upload {\n\t\t// Argument swapping\n\t\tif ( typeof tableField === 'function' ) {\n\t\t\tthis._dbCleanTableField = null;\n\t\t\tthis._dbCleanCallback = tableField;\n\t\t}\n\t\telse {\n\t\t\tthis._dbCleanTableField = tableField;\n\t\t\tthis._dbCleanCallback = callback;\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add a validation method to check file uploads. Multiple validators can be\n\t * added by calling this method multiple times - they will be executed in\n\t * sequence when a file has been uploaded.\n\t *\n\t * @param {any} fn Validation function. A files parameter is\n\t * passed in for the uploaded file and the return is either a string\n\t * (validation failed and error message), or `true` (validation passed).\n\t * @returns {Upload} Self for chaining\n\t */\n\tpublic validator( fn ): Upload {\n\t\tthis._validators.push( fn );\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add a condition to the data to be retrieved from the database. This\n\t * must be given as a function to be executed (usually anonymous) and\n\t * will be passed in a single argument, the `Query` object, to which\n\t * conditions can be added. Multiple calls to this method can be made.\n\t *\n\t * @param {any} fn Knex WHERE condition\n\t * @returns {Upload} Self for chaining\n\t */\n\tpublic where( fn ): Upload {\n\t\tthis._where.push( fn );\n\n\t\treturn this;\n\t}\n\n\t/* * * * * * * * * * * * * * * * * * * * * * * * *\n\t * Internal methods\n\t */\n\n\t/**\n\t * @ignore\n\t */\n\tpublic async data( db: Knex, ids: string[] = null ): Promise {\n\t\tif ( ! this._dbTable ) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// Select the details requested, for the columns requested\n\t\tlet query = db\n\t\t\t.select( this._dbPkey )\n\t\t\t.from( this._dbTable );\n\n\t\tlet keys = Object.keys( this._dbFields );\n\t\tfor ( let i = 0, ien = keys.length ; i < ien ; i++ ) {\n\t\t\tlet key = keys[i];\n\n\t\t\tif ( this._dbFields[ key ] !== DbOpts.Content ) {\n\t\t\t\tquery.select( key );\n\t\t\t}\n\t\t}\n\n\t\tif ( ids !== null ) {\n\t\t\tquery.whereIn( this._dbPkey, ids );\n\t\t}\n\n\t\tfor ( let i = 0, ien = this._where.length ; i < ien ; i++ ) {\n\t\t\tquery.where( this._where[i] );\n\t\t}\n\n\t\tlet result = await query;\n\t\tlet out = {};\n\n\t\tfor ( let i = 0, ien = result.length ; i < ien ; i++ ) {\n\t\t\tout[ result[i][ this._dbPkey] ] = result[i];\n\t\t}\n\n\t\treturn out;\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tpublic async dbCleanExec( editor: Editor, field: Field ): Promise {\n\t\t// Database and file system clean up BEFORE adding the new file to\n\t\t// the db, otherwise it will be removed immediately\n\t\tlet tables = editor.table();\n\t\tthis._dbClean( editor.db(), tables[0], field.dbField() );\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tpublic error() {\n\t\treturn this._error;\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tpublic async exec( editor: Editor, upload: IUpload ): Promise {\n\t\tlet id;\n\n\t\t// Add any extra information to the upload structure\n\t\tlet fileInfo = await stat( upload.upload.file );\n\t\tupload.upload.size = fileInfo.size;\n\n\t\tlet a = upload.upload.filename.split('.');\n\t\tupload.upload.extn = a.length > 1 ?\n\t\t\ta.pop() :\n\t\t\t'';\n\t\tupload.upload.name = a.join('.');\n\n\t\t// Validation\n\t\tfor ( let i = 0, ien = this._validators.length ; i < ien ; i++ ) {\n\t\t\tlet result = await this._validators[i]( upload.upload );\n\n\t\t\tif ( typeof result === 'string' ) {\n\t\t\t\tthis._error = result;\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\t// Database\n\t\tif ( this._dbTable ) {\n\t\t\tlet fields = Object.keys( this._dbFields );\n\n\t\t\tfor ( let i = 0, ien = fields.length ; i < ien ; i++ ) {\n\t\t\t\tlet prop = this._dbFields[ fields[i] ];\n\n\t\t\t\t// We can't know what the path is, if it has moved into place\n\t\t\t\t// by an external function - throw an error if this does happen\n\t\t\t\tif ( typeof this._action !== 'string' && prop === DbOpts.SystemPath ) {\n\t\t\t\t\tthis._error = 'Cannot set path information in the database ' +\n\t\t\t\t\t\t'if a custom method is used to save the file.';\n\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Commit to the database\n\t\t\tid = await this._dbExec( editor.db(), upload );\n\t\t}\n\n\t\tlet res = await this._actionExec( id, upload );\n\t\treturn res;\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tpublic pkey() {\n\t\treturn this._dbPkey;\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tpublic table() {\n\t\treturn this._dbTable;\n\t}\n\n\t/* * * * * * * * * * * * * * * * * * * * * * * * *\n\t * Private methods\n\t */\n\tprivate async _actionExec( id: string, files: IUpload ): Promise {\n\t\tif ( typeof this._action === 'function' ) {\n\t\t\tlet res = await this._action( files.upload, id );\n\t\t\treturn res;\n\t\t}\n\n\t\t// Default action - move the file to the location specified by the\n\t\t// action string\n\t\tlet to = this._substitute( this._action, files.upload.file, id );\n\t\tto = path.normalize( to );\n\n\t\ttry {\n\t\t\tawait( rename( files.upload.file, to, {mkdirp: true} ) );\n\t\t} catch (e) {\n\t\t\tthis._error = 'An error occurred while moving the uploaded file.';\n\t\t\treturn null;\n\t\t}\n\n\t\treturn id !== null ?\n\t\t\tid :\n\t\t\tto;\n\t}\n\n\tprivate async _dbClean( db: Knex, editorTable: string, fieldName: string ): Promise {\n\t\tlet callback = this._dbCleanCallback;\n\t\tlet that = this;\n\n\t\tif ( ! this._dbTable || ! callback ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// If there is a table / field that we should use to check if the value\n\t\t// is in use, then use that. Otherwise we'll try to use the information\n\t\t// from the Editor / Field instance.\n\t\tif ( this._dbCleanTableField ) {\n\t\t\tfieldName = this._dbCleanTableField;\n\t\t}\n\n\t\tlet table;\n\t\tlet field;\n\t\tlet a = fieldName.split('.');\n\n\t\tif ( a.length === 1 ) {\n\t\t\ttable = editorTable;\n\t\t\tfield = a[0];\n\t\t}\n\t\telse if ( a.length === 2 ) {\n\t\t\ttable = a[0];\n\t\t\tfield = a[1];\n\t\t}\n\t\telse {\n\t\t\ttable = a[1];\n\t\t\tfield = a[2];\n\t\t}\n\n\t\t// Select the details requested, for the columns requested\n\t\tlet fields = this._dbFields;\n\t\tlet columns = Object.keys( fields );\n\t\tlet query = db\n\t\t\t.select( this._dbPkey )\n\t\t\t.from( this._dbTable );\n\n\t\tfor ( let i = 0, ien = columns.length ; i < ien ; i++ ) {\n\t\t\tlet column = columns[i];\n\t\t\tlet prop = fields[ column ];\n\n\t\t\tif ( prop !== DbOpts.Content ) {\n\t\t\t\tquery.select( column );\n\t\t\t}\n\t\t}\n\n\t\tquery.whereNotIn( this._dbPkey, function() {\n\t\t\tthis.select( field ).from( table ).whereNotNull( field );\n\t\t} );\n\n\t\tlet rows = await query;\n\n\t\tif ( rows.length === 0 ) {\n\t\t\treturn;\n\t\t}\n\n\t\tlet result = await callback( rows );\n\n\t\t// Delete the selected rows, iff the developer says to do so with the\n\t\t// returned value (i.e. acknowledge that the files have be removed from\n\t\t// the file system)\n\t\tif ( result === true ) {\n\t\t\tlet queryDel = db\n\t\t\t\t.from( this._dbTable )\n\t\t\t\t.where( function() {\n\t\t\t\t\tfor ( let i = 0, ien = rows.length ; i < ien ; i++ ) {\n\t\t\t\t\t\tthis.orWhere( { [that._dbPkey]: rows[i][that._dbPkey] } );\n\t\t\t\t\t}\n\t\t\t\t} );\n\n\t\t\tawait queryDel.del();\n\t\t}\n\t}\n\n\tprivate async _dbExec( db: Knex, files: IUpload ): Promise {\n\t\tlet pathFields = {};\n\t\tlet fields = this._dbFields;\n\t\tlet columns = Object.keys( fields );\n\t\tlet set = {};\n\t\tlet upload = files.upload;\n\n\t\tfor ( let i = 0, ien = columns.length ; i < ien ; i++ ) {\n\t\t\tlet column = columns[i];\n\t\t\tlet prop = fields[ column ];\n\n\t\t\tswitch ( prop ) {\n\t\t\t\tcase DbOpts.ReadOnly:\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DbOpts.Content:\n\t\t\t\t\tset[ column ] = await readFile( upload.file );\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DbOpts.ContentType:\n\t\t\t\tcase DbOpts.MimeType:\n\t\t\t\t\tset[ column ] = upload.mimetype;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DbOpts.Extn:\n\t\t\t\t\tset[ column ] = upload.extn;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DbOpts.FileName:\n\t\t\t\t\tset[ column ] = upload.filename;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DbOpts.Name:\n\t\t\t\t\tset[ column ] = upload.name;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DbOpts.FileSize:\n\t\t\t\t\tset[ column ] = upload.size;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DbOpts.SystemPath:\n\t\t\t\t\tpathFields[ column ] = this._action;\n\t\t\t\t\tset[ column ] = '-'; // Use a temporary value to avoid cases\n\t\t\t\t\tbreak; // where the db will reject empty values\n\n\t\t\t\tdefault:\n\t\t\t\t\tlet val = typeof prop === 'function' ?\n\t\t\t\t\t\tprop( db, upload ) :\n\t\t\t\t\t\tprop;\n\n\t\t\t\t\tif ( typeof val === 'string' && val.match(/\\{.*\\}/) ) {\n\t\t\t\t\t\tpathFields[ column ] = val;\n\t\t\t\t\t\tset[ column ] = '-';\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tset[ column ] = val;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tlet res = await db\n\t\t\t.insert( set )\n\t\t\t.from( this._dbTable )\n\t\t\t.returning( this._dbPkey );\n\n\t\tlet id = res[0];\n\n\t\t// Update the newly inserted row with the path information. We have to\n\t\t// use a second statement here as we don't know in advance what the\n\t\t// database schema is and don't want to prescribe that certain triggers\n\t\t// etc be created. It makes it a bit less efficient but much more\n\t\t// compatible\n\t\tlet pathKeys = Object.keys( pathFields );\n\n\t\tif ( pathKeys.length ) {\n\t\t\t// For this to operate the action must be a string, which is\n\t\t\t// validated in the `exec` method\n\t\t\tlet toSet = {};\n\n\t\t\tfor ( let i = 0, ien = pathKeys.length ; i < ien ; i++ ) {\n\t\t\t\tlet key = pathKeys[i];\n\t\t\t\ttoSet[ key ] = this._substitute( pathFields[key], upload.file, id );\n\t\t\t}\n\n\t\t\tawait db\n\t\t\t\t.update( toSet )\n\t\t\t\t.from( this._dbTable )\n\t\t\t\t.where( { [this._dbPkey]: id } );\n\t\t}\n\n\t\treturn id;\n\t}\n\n\tprivate _substitute( convert: string, uploadPath: string, id: string ): string {\n\t\tlet a = uploadPath.toString().split( '/' );\n\t\tlet fileName = a.pop();\n\t\tlet fileParts = fileName.split('.');\n\t\tlet extn = fileParts.pop();\n\t\tlet namePart = fileParts.join('.');\n\n\t\tlet to = convert.toString();\n\t\tto = to.replace( '{name}', namePart );\n\t\tto = to.replace( '{id}', id );\n\t\tto = to.replace( '{extn}', extn );\n\n\t\treturn to;\n\t}\n}\n"]} \ No newline at end of file +{"version":3,"sources":["upload.ts"],"names":[],"mappings":"AAKA,OAAO,EAAC,IAAI,EAAC,MAAM,MAAM,CAAC;AAI1B,OAAO,MAAM,MAAM,UAAU,CAAC;AAC9B,OAAO,KAAK,MAAM,SAAS,CAAC;AAO5B,oBAAY,MAAM;IACjB,OAAO,IAAA;IACP,WAAW,IAAA;IACX,IAAI,IAAA;IACJ,IAAI,IAAA;IACJ,QAAQ,IAAA;IACR,QAAQ,IAAA;IACR,QAAQ,IAAA;IACR,QAAQ,IAAA;IACR,UAAU,IAAA;CAIV;AAED,MAAM,WAAW,KAAK;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,OAAO;IACvB,MAAM,EAAE,KAAK,CAAC;CACd;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,CAAC,OAAO,OAAO,MAAM;IAC1B,OAAc,EAAE,gBAAU;IAC1B,OAAc,MAAM,gBAAU;IAE9B,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,gBAAgB,CAAC;IACzB,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAC;IAClB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,WAAW,CAAM;IACzB,OAAO,CAAC,MAAM,CAAM;gBAMP,MAAM,GAAE,MAAM,GAAC,QAAe;IAU3C;;;;;;;;;;;;;;;;;OAiBG;IACI,MAAM,CAAE,MAAM,EAAE,MAAM,GAAC,QAAQ,GAAI,MAAM;IAMhD;;;;;;;;;;;;;;;;;OAiBG;IACI,EAAE,CAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAI,MAAM;IAQhE;;;;;;;;;;;OAWG;IACI,OAAO,CAAE,UAAU,EAAE,MAAM,GAAC,QAAQ,EAAE,QAAQ,GAAE,QAAe,GAAI,MAAM;IAchF;;;;;;;;;OASG;IACI,SAAS,CAAE,EAAE,KAAA,GAAI,MAAM;IAM9B;;;;;;;;OAQG;IACI,KAAK,CAAE,EAAE,KAAA,GAAI,MAAM;IAU1B;;OAEG;IACU,IAAI,CAAE,EAAE,EAAE,IAAI,EAAE,GAAG,GAAE,MAAM,EAAS,GAAI,OAAO,CAAC,MAAM,CAAC;IAqCpE;;OAEG;IACU,WAAW,CAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAI,OAAO,CAAC,IAAI,CAAC;IAOvE;;OAEG;IACI,KAAK;IAIZ;;OAEG;IACU,IAAI,CAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAI,OAAO,CAAE,MAAM,CAAC;IAgDtE;;OAEG;IACI,IAAI;IAIX;;OAEG;IACI,KAAK;YAOE,WAAW;YAuBX,QAAQ;YA4ER,OAAO;IAgGrB,OAAO,CAAC,WAAW;CAcnB","file":"upload.d.ts","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\n\nimport * as rootPath from 'app-root-path';\nimport * as knex from 'knex';\nimport {Knex} from 'knex';\n\nimport * as mv from 'mv';\n\nimport Editor from './editor';\nimport Field from './field';\nimport promisify from './promisify';\n\nlet stat = promisify( fs.stat );\nlet readFile = promisify( fs.readFile );\nlet rename = promisify( mv );\n\nexport enum DbOpts {\n\tContent,\n\tContentType,\n\tExtn,\n\tName,\n\tFileName, // Name + Extn\n\tFileSize,\n\tMimeType,\n\tReadOnly,\n\tSystemPath\n\t// Note that the PHP and .NET libraries have a WebPath, but that isn't\n\t// available here as there isn't a good and reliable way to get the web\n\t// root in node (it could be anywhere!).\n}\n\nexport interface IFile {\n\tuuid: string;\n\tfield: string;\n\tfile: string; // full path\n\tfilename: string; // name + extn\n\tencoding: string;\n\tmimetype: string;\n\ttruncated: boolean;\n\tdone: boolean;\n\tsize: number; // Added\n\textn: string; // Added\n\tname: string; // Added\n}\n\nexport interface IUpload {\n\tupload: IFile;\n}\n\n/**\n * Upload class for Editor. This class provides the ability to easily specify\n * file upload information, specifically how the file should be recorded on\n * the server (database and file system).\n *\n * An instance of this class is attached to a field using the {@link\n * Field.upload} method. When Editor detects a file upload for that file the\n * information provided for this instance is executed.\n *\n * The configuration is primarily driven through the {@link db} and {@link\n * action} methods:\n *\n * * {@link db} Describes how information about the uploaded file is to be\n * stored on the database.\n * * {@link action} Describes where the file should be stored on the file system\n * and provides the option of specifying a custom action when a file is\n * uploaded.\n *\n * Both methods are optional - you can store the file on the server using the\n * {@link db} method only if you want to store the file in the database, or if\n * you don't want to store relational data on the database us only {@link\n * action}. However, the majority of the time it is best to use both - store\n * information about the file on the database for fast retrieval (using a {@link\n * Editor.leftJoin()} for example) and the file on the file system for direct\n * web access.\n *\n * @export\n * @class Upload\n */\nexport default class Upload {\n\tpublic static Db = DbOpts; // legacy\n\tpublic static DbOpts = DbOpts;\n\n\tprivate _action: string|Function;\n\tprivate _dbCleanCallback; // async function\n\tprivate _dbCleanTableField: string;\n\tprivate _dbTable: string;\n\tprivate _dbPkey: string;\n\tprivate _dbFields;\n\tprivate _error: string;\n\tprivate _validators = [];\n\tprivate _where = [];\n\n\t/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n\t * Constructor\n\t */\n\n\tconstructor( action: string|Function = null ) {\n\t\tif ( action ) {\n\t\t\tthis.action( action );\n\t\t}\n\t}\n\n\t/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n\t * Public methods\n\t */\n\n\t/**\n\t * Set the action to take when a file is uploaded. This can be either of:\n\t *\n\t * * A string - the value given is the full system path to where the\n\t * uploaded file is written to. The value given can include three \"macros\"\n\t * which are replaced by the script dependent on the uploaded file:\n\t * * `__EXTN__` - the file extension\n\t * * `__NAME__` - the uploaded file's name (including the extension)\n\t * * `__ID__` - Database primary key value if the {@link db} method is\n\t * used.\n\t * * A closure - if a function is given the responsibility of what to do\n\t * with the uploaded file is transferred to this function. That will\n\t * typically involve writing it to the file system so it can be used\n\t * later.\n\t *\n\t * @param {(string|Function)} action Upload action\n\t * @returns {Upload} Self for chaining\n\t */\n\tpublic action( action: string|Function ): Upload {\n\t\tthis._action = action;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Database configuration method. When used, this method will tell Editor\n\t * what information you want written to a database on file upload, should\n\t * you wish to store relational information about your file on the database\n\t * (this is generally recommended).\n\t *\n\t * @param {string} table The name of the table where the file information\n\t * should be stored\n\t * @param {string} pkey Primary key column name. The `Upload` class\n\t * requires that the database table have a single primary key so each\n\t * row can be uniquely identified.\n\t * @param {object} fields A list of the fields to be written to on upload.\n\t * The property names are the database columns and the values can be\n\t * defined by the constants of this class. The value can also be a\n\t * string or a closure function if you wish to send custom information\n\t * to the database.\n\t * @returns {Upload} Self for chaining\n\t */\n\tpublic db( table: string, pkey: string, fields: object ): Upload {\n\t\tthis._dbTable = table;\n\t\tthis._dbPkey = pkey;\n\t\tthis._dbFields = fields;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set a callback function that is used to remove files which no longer have\n\t * a reference in a source table.\n\t *\n\t * @param {(string|Function)} tableField Table field to be used for the delete match\n\t * @param {Function} [callback=null] Function that will be executed on clean. It is\n\t * given an array of information from the database about the orphaned\n\t * rows, and can return true to indicate that the rows should be\n\t * removed from the database. Any other return value (including none)\n\t * will result in the records being retained.\n\t * @returns {Upload} Self for chaining\n\t */\n\tpublic dbClean( tableField: string|Function, callback: Function = null ): Upload {\n\t\t// Argument swapping\n\t\tif ( typeof tableField === 'function' ) {\n\t\t\tthis._dbCleanTableField = null;\n\t\t\tthis._dbCleanCallback = tableField;\n\t\t}\n\t\telse {\n\t\t\tthis._dbCleanTableField = tableField;\n\t\t\tthis._dbCleanCallback = callback;\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add a validation method to check file uploads. Multiple validators can be\n\t * added by calling this method multiple times - they will be executed in\n\t * sequence when a file has been uploaded.\n\t *\n\t * @param {any} fn Validation function. A files parameter is\n\t * passed in for the uploaded file and the return is either a string\n\t * (validation failed and error message), or `true` (validation passed).\n\t * @returns {Upload} Self for chaining\n\t */\n\tpublic validator( fn ): Upload {\n\t\tthis._validators.push( fn );\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add a condition to the data to be retrieved from the database. This\n\t * must be given as a function to be executed (usually anonymous) and\n\t * will be passed in a single argument, the `Query` object, to which\n\t * conditions can be added. Multiple calls to this method can be made.\n\t *\n\t * @param {any} fn Knex WHERE condition\n\t * @returns {Upload} Self for chaining\n\t */\n\tpublic where( fn ): Upload {\n\t\tthis._where.push( fn );\n\n\t\treturn this;\n\t}\n\n\t/* * * * * * * * * * * * * * * * * * * * * * * * *\n\t * Internal methods\n\t */\n\n\t/**\n\t * @ignore\n\t */\n\tpublic async data( db: Knex, ids: string[] = null ): Promise {\n\t\tif ( ! this._dbTable ) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// Select the details requested, for the columns requested\n\t\tlet query = db\n\t\t\t.select( this._dbPkey )\n\t\t\t.from( this._dbTable );\n\n\t\tlet keys = Object.keys( this._dbFields );\n\t\tfor ( let i = 0, ien = keys.length ; i < ien ; i++ ) {\n\t\t\tlet key = keys[i];\n\n\t\t\tif ( this._dbFields[ key ] !== DbOpts.Content ) {\n\t\t\t\tquery.select( key );\n\t\t\t}\n\t\t}\n\n\t\tif ( ids !== null ) {\n\t\t\tquery.whereIn( this._dbPkey, ids );\n\t\t}\n\n\t\tfor ( let i = 0, ien = this._where.length ; i < ien ; i++ ) {\n\t\t\tquery.where( this._where[i] );\n\t\t}\n\n\t\tlet result = await query;\n\t\tlet out = {};\n\n\t\tfor ( let i = 0, ien = result.length ; i < ien ; i++ ) {\n\t\t\tout[ result[i][ this._dbPkey] ] = result[i];\n\t\t}\n\n\t\treturn out;\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tpublic async dbCleanExec( editor: Editor, field: Field ): Promise {\n\t\t// Database and file system clean up BEFORE adding the new file to\n\t\t// the db, otherwise it will be removed immediately\n\t\tlet tables = editor.table();\n\t\tthis._dbClean( editor.db(), tables[0], field.dbField() );\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tpublic error() {\n\t\treturn this._error;\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tpublic async exec( editor: Editor, upload: IUpload ): Promise {\n\t\tlet id;\n\n\t\t// Add any extra information to the upload structure\n\t\tlet fileInfo = await stat( upload.upload.file );\n\t\tupload.upload.size = fileInfo.size;\n\n\t\tlet a = upload.upload.filename.split('.');\n\t\tupload.upload.extn = a.length > 1 ?\n\t\t\ta.pop() :\n\t\t\t'';\n\t\tupload.upload.name = a.join('.');\n\n\t\t// Validation\n\t\tfor ( let i = 0, ien = this._validators.length ; i < ien ; i++ ) {\n\t\t\tlet result = await this._validators[i]( upload.upload );\n\n\t\t\tif ( typeof result === 'string' ) {\n\t\t\t\tthis._error = result;\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\t// Database\n\t\tif ( this._dbTable ) {\n\t\t\tlet fields = Object.keys( this._dbFields );\n\n\t\t\tfor ( let i = 0, ien = fields.length ; i < ien ; i++ ) {\n\t\t\t\tlet prop = this._dbFields[ fields[i] ];\n\n\t\t\t\t// We can't know what the path is, if it has moved into place\n\t\t\t\t// by an external function - throw an error if this does happen\n\t\t\t\tif ( typeof this._action !== 'string' && prop === DbOpts.SystemPath ) {\n\t\t\t\t\tthis._error = 'Cannot set path information in the database ' +\n\t\t\t\t\t\t'if a custom method is used to save the file.';\n\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Commit to the database\n\t\t\tid = await this._dbExec( editor.db(), upload );\n\t\t}\n\n\t\tlet res = await this._actionExec( id, upload );\n\t\treturn res;\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tpublic pkey() {\n\t\treturn this._dbPkey;\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tpublic table() {\n\t\treturn this._dbTable;\n\t}\n\n\t/* * * * * * * * * * * * * * * * * * * * * * * * *\n\t * Private methods\n\t */\n\tprivate async _actionExec( id: string, files: IUpload ): Promise {\n\t\tif ( typeof this._action === 'function' ) {\n\t\t\tlet res = await this._action( files.upload, id );\n\t\t\treturn res;\n\t\t}\n\n\t\t// Default action - move the file to the location specified by the\n\t\t// action string\n\t\tlet to = this._substitute( this._action, files.upload.file, id );\n\t\tto = path.normalize( to );\n\n\t\ttry {\n\t\t\tawait( rename( files.upload.file, to, {mkdirp: true} ) );\n\t\t} catch (e) {\n\t\t\tthis._error = 'An error occurred while moving the uploaded file.';\n\t\t\treturn null;\n\t\t}\n\n\t\treturn id !== null ?\n\t\t\tid :\n\t\t\tto;\n\t}\n\n\tprivate async _dbClean( db: Knex, editorTable: string, fieldName: string ): Promise {\n\t\tlet callback = this._dbCleanCallback;\n\t\tlet that = this;\n\n\t\tif ( ! this._dbTable || ! callback ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// If there is a table / field that we should use to check if the value\n\t\t// is in use, then use that. Otherwise we'll try to use the information\n\t\t// from the Editor / Field instance.\n\t\tif ( this._dbCleanTableField ) {\n\t\t\tfieldName = this._dbCleanTableField;\n\t\t}\n\n\t\tlet table;\n\t\tlet field;\n\t\tlet a = fieldName.split('.');\n\n\t\tif ( a.length === 1 ) {\n\t\t\ttable = editorTable;\n\t\t\tfield = a[0];\n\t\t}\n\t\telse if ( a.length === 2 ) {\n\t\t\ttable = a[0];\n\t\t\tfield = a[1];\n\t\t}\n\t\telse {\n\t\t\ttable = a[1];\n\t\t\tfield = a[2];\n\t\t}\n\n\t\t// Select the details requested, for the columns requested\n\t\tlet fields = this._dbFields;\n\t\tlet columns = Object.keys( fields );\n\t\tlet query = db\n\t\t\t.select( this._dbPkey )\n\t\t\t.from( this._dbTable );\n\n\t\tfor ( let i = 0, ien = columns.length ; i < ien ; i++ ) {\n\t\t\tlet column = columns[i];\n\t\t\tlet prop = fields[ column ];\n\n\t\t\tif ( prop !== DbOpts.Content ) {\n\t\t\t\tquery.select( column );\n\t\t\t}\n\t\t}\n\n\t\tquery.whereNotIn( this._dbPkey, function() {\n\t\t\tthis.select( field ).from( table ).whereNotNull( field );\n\t\t} );\n\n\t\tlet rows = await query;\n\n\t\tif ( rows.length === 0 ) {\n\t\t\treturn;\n\t\t}\n\n\t\tlet result = await callback( rows );\n\n\t\t// Delete the selected rows, iff the developer says to do so with the\n\t\t// returned value (i.e. acknowledge that the files have be removed from\n\t\t// the file system)\n\t\tif ( result === true ) {\n\t\t\tlet queryDel = db\n\t\t\t\t.from( this._dbTable )\n\t\t\t\t.where( function() {\n\t\t\t\t\tfor ( let i = 0, ien = rows.length ; i < ien ; i++ ) {\n\t\t\t\t\t\tthis.orWhere( { [that._dbPkey]: rows[i][that._dbPkey] } );\n\t\t\t\t\t}\n\t\t\t\t} );\n\n\t\t\tawait queryDel.del();\n\t\t}\n\t}\n\n\tprivate async _dbExec( db: Knex, files: IUpload ): Promise {\n\t\tlet pathFields = {};\n\t\tlet fields = this._dbFields;\n\t\tlet columns = Object.keys( fields );\n\t\tlet set = {};\n\t\tlet upload = files.upload;\n\n\t\tfor ( let i = 0, ien = columns.length ; i < ien ; i++ ) {\n\t\t\tlet column = columns[i];\n\t\t\tlet prop = fields[ column ];\n\n\t\t\tswitch ( prop ) {\n\t\t\t\tcase DbOpts.ReadOnly:\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DbOpts.Content:\n\t\t\t\t\tset[ column ] = await readFile( upload.file );\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DbOpts.ContentType:\n\t\t\t\tcase DbOpts.MimeType:\n\t\t\t\t\tset[ column ] = upload.mimetype;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DbOpts.Extn:\n\t\t\t\t\tset[ column ] = upload.extn;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DbOpts.FileName:\n\t\t\t\t\tset[ column ] = upload.filename;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DbOpts.Name:\n\t\t\t\t\tset[ column ] = upload.name;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DbOpts.FileSize:\n\t\t\t\t\tset[ column ] = upload.size;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DbOpts.SystemPath:\n\t\t\t\t\tpathFields[ column ] = this._action;\n\t\t\t\t\tset[ column ] = '-'; // Use a temporary value to avoid cases\n\t\t\t\t\tbreak; // where the db will reject empty values\n\n\t\t\t\tdefault:\n\t\t\t\t\tlet val = typeof prop === 'function' ?\n\t\t\t\t\t\tprop( db, upload ) :\n\t\t\t\t\t\tprop;\n\n\t\t\t\t\tif ( typeof val === 'string' && val.match(/\\{.*\\}/) ) {\n\t\t\t\t\t\tpathFields[ column ] = val;\n\t\t\t\t\t\tset[ column ] = '-';\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tset[ column ] = val;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tlet res = await db\n\t\t\t.insert( set )\n\t\t\t.from( this._dbTable )\n\t\t\t.returning( this._dbPkey );\n\n\t\tlet id = typeof res[0] === 'object'\n\t\t\t? res[0][this._dbPkey] // Knex 1.0+\n\t\t\t: res[0]; // Knex 0.95 and earlier\n\n\t\t// Update the newly inserted row with the path information. We have to\n\t\t// use a second statement here as we don't know in advance what the\n\t\t// database schema is and don't want to prescribe that certain triggers\n\t\t// etc be created. It makes it a bit less efficient but much more\n\t\t// compatible\n\t\tlet pathKeys = Object.keys( pathFields );\n\n\t\tif ( pathKeys.length ) {\n\t\t\t// For this to operate the action must be a string, which is\n\t\t\t// validated in the `exec` method\n\t\t\tlet toSet = {};\n\n\t\t\tfor ( let i = 0, ien = pathKeys.length ; i < ien ; i++ ) {\n\t\t\t\tlet key = pathKeys[i];\n\t\t\t\ttoSet[ key ] = this._substitute( pathFields[key], upload.file, id );\n\t\t\t}\n\n\t\t\tawait db\n\t\t\t\t.update( toSet )\n\t\t\t\t.from( this._dbTable )\n\t\t\t\t.where( { [this._dbPkey]: id } );\n\t\t}\n\n\t\treturn id;\n\t}\n\n\tprivate _substitute( convert: string, uploadPath: string, id: string ): string {\n\t\tlet a = uploadPath.toString().split( '/' );\n\t\tlet fileName = a.pop();\n\t\tlet fileParts = fileName.split('.');\n\t\tlet extn = fileParts.pop();\n\t\tlet namePart = fileParts.join('.');\n\n\t\tlet to = convert.toString();\n\t\tto = to.replace( '{name}', namePart );\n\t\tto = to.replace( '{id}', id );\n\t\tto = to.replace( '{extn}', extn );\n\n\t\treturn to;\n\t}\n}\n"]} \ No newline at end of file diff --git a/dist/upload.js b/dist/upload.js index 21dd188..5a29fbd 100644 --- a/dist/upload.js +++ b/dist/upload.js @@ -520,7 +520,9 @@ var Upload = /** @class */ (function () { .returning(this._dbPkey)]; case 14: res = _e.sent(); - id = res[0]; + id = typeof res[0] === 'object' + ? res[0][this._dbPkey] // Knex 1.0+ + : res[0]; pathKeys = Object.keys(pathFields); if (!pathKeys.length) return [3 /*break*/, 16]; toSet = {}; diff --git a/dist/upload.js.map b/dist/upload.js.map index 4c33370..2b368e0 100644 --- a/dist/upload.js.map +++ b/dist/upload.js.map @@ -1 +1 @@ -{"version":3,"sources":["upload.ts"],"names":[],"mappings":"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","file":"upload.js","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\n\nimport * as rootPath from 'app-root-path';\nimport * as knex from 'knex';\nimport {Knex} from 'knex';\n\nimport * as mv from 'mv';\n\nimport Editor from './editor';\nimport Field from './field';\nimport promisify from './promisify';\n\nlet stat = promisify( fs.stat );\nlet readFile = promisify( fs.readFile );\nlet rename = promisify( mv );\n\nexport enum DbOpts {\n\tContent,\n\tContentType,\n\tExtn,\n\tName,\n\tFileName, // Name + Extn\n\tFileSize,\n\tMimeType,\n\tReadOnly,\n\tSystemPath\n\t// Note that the PHP and .NET libraries have a WebPath, but that isn't\n\t// available here as there isn't a good and reliable way to get the web\n\t// root in node (it could be anywhere!).\n}\n\nexport interface IFile {\n\tuuid: string;\n\tfield: string;\n\tfile: string; // full path\n\tfilename: string; // name + extn\n\tencoding: string;\n\tmimetype: string;\n\ttruncated: boolean;\n\tdone: boolean;\n\tsize: number; // Added\n\textn: string; // Added\n\tname: string; // Added\n}\n\nexport interface IUpload {\n\tupload: IFile;\n}\n\n/**\n * Upload class for Editor. This class provides the ability to easily specify\n * file upload information, specifically how the file should be recorded on\n * the server (database and file system).\n *\n * An instance of this class is attached to a field using the {@link\n * Field.upload} method. When Editor detects a file upload for that file the\n * information provided for this instance is executed.\n *\n * The configuration is primarily driven through the {@link db} and {@link\n * action} methods:\n *\n * * {@link db} Describes how information about the uploaded file is to be\n * stored on the database.\n * * {@link action} Describes where the file should be stored on the file system\n * and provides the option of specifying a custom action when a file is\n * uploaded.\n *\n * Both methods are optional - you can store the file on the server using the\n * {@link db} method only if you want to store the file in the database, or if\n * you don't want to store relational data on the database us only {@link\n * action}. However, the majority of the time it is best to use both - store\n * information about the file on the database for fast retrieval (using a {@link\n * Editor.leftJoin()} for example) and the file on the file system for direct\n * web access.\n *\n * @export\n * @class Upload\n */\nexport default class Upload {\n\tpublic static Db = DbOpts; // legacy\n\tpublic static DbOpts = DbOpts;\n\n\tprivate _action: string|Function;\n\tprivate _dbCleanCallback; // async function\n\tprivate _dbCleanTableField: string;\n\tprivate _dbTable: string;\n\tprivate _dbPkey: string;\n\tprivate _dbFields;\n\tprivate _error: string;\n\tprivate _validators = [];\n\tprivate _where = [];\n\n\t/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n\t * Constructor\n\t */\n\n\tconstructor( action: string|Function = null ) {\n\t\tif ( action ) {\n\t\t\tthis.action( action );\n\t\t}\n\t}\n\n\t/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n\t * Public methods\n\t */\n\n\t/**\n\t * Set the action to take when a file is uploaded. This can be either of:\n\t *\n\t * * A string - the value given is the full system path to where the\n\t * uploaded file is written to. The value given can include three \"macros\"\n\t * which are replaced by the script dependent on the uploaded file:\n\t * * `__EXTN__` - the file extension\n\t * * `__NAME__` - the uploaded file's name (including the extension)\n\t * * `__ID__` - Database primary key value if the {@link db} method is\n\t * used.\n\t * * A closure - if a function is given the responsibility of what to do\n\t * with the uploaded file is transferred to this function. That will\n\t * typically involve writing it to the file system so it can be used\n\t * later.\n\t *\n\t * @param {(string|Function)} action Upload action\n\t * @returns {Upload} Self for chaining\n\t */\n\tpublic action( action: string|Function ): Upload {\n\t\tthis._action = action;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Database configuration method. When used, this method will tell Editor\n\t * what information you want written to a database on file upload, should\n\t * you wish to store relational information about your file on the database\n\t * (this is generally recommended).\n\t *\n\t * @param {string} table The name of the table where the file information\n\t * should be stored\n\t * @param {string} pkey Primary key column name. The `Upload` class\n\t * requires that the database table have a single primary key so each\n\t * row can be uniquely identified.\n\t * @param {object} fields A list of the fields to be written to on upload.\n\t * The property names are the database columns and the values can be\n\t * defined by the constants of this class. The value can also be a\n\t * string or a closure function if you wish to send custom information\n\t * to the database.\n\t * @returns {Upload} Self for chaining\n\t */\n\tpublic db( table: string, pkey: string, fields: object ): Upload {\n\t\tthis._dbTable = table;\n\t\tthis._dbPkey = pkey;\n\t\tthis._dbFields = fields;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set a callback function that is used to remove files which no longer have\n\t * a reference in a source table.\n\t *\n\t * @param {(string|Function)} tableField Table field to be used for the delete match\n\t * @param {Function} [callback=null] Function that will be executed on clean. It is\n\t * given an array of information from the database about the orphaned\n\t * rows, and can return true to indicate that the rows should be\n\t * removed from the database. Any other return value (including none)\n\t * will result in the records being retained.\n\t * @returns {Upload} Self for chaining\n\t */\n\tpublic dbClean( tableField: string|Function, callback: Function = null ): Upload {\n\t\t// Argument swapping\n\t\tif ( typeof tableField === 'function' ) {\n\t\t\tthis._dbCleanTableField = null;\n\t\t\tthis._dbCleanCallback = tableField;\n\t\t}\n\t\telse {\n\t\t\tthis._dbCleanTableField = tableField;\n\t\t\tthis._dbCleanCallback = callback;\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add a validation method to check file uploads. Multiple validators can be\n\t * added by calling this method multiple times - they will be executed in\n\t * sequence when a file has been uploaded.\n\t *\n\t * @param {any} fn Validation function. A files parameter is\n\t * passed in for the uploaded file and the return is either a string\n\t * (validation failed and error message), or `true` (validation passed).\n\t * @returns {Upload} Self for chaining\n\t */\n\tpublic validator( fn ): Upload {\n\t\tthis._validators.push( fn );\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add a condition to the data to be retrieved from the database. This\n\t * must be given as a function to be executed (usually anonymous) and\n\t * will be passed in a single argument, the `Query` object, to which\n\t * conditions can be added. Multiple calls to this method can be made.\n\t *\n\t * @param {any} fn Knex WHERE condition\n\t * @returns {Upload} Self for chaining\n\t */\n\tpublic where( fn ): Upload {\n\t\tthis._where.push( fn );\n\n\t\treturn this;\n\t}\n\n\t/* * * * * * * * * * * * * * * * * * * * * * * * *\n\t * Internal methods\n\t */\n\n\t/**\n\t * @ignore\n\t */\n\tpublic async data( db: Knex, ids: string[] = null ): Promise {\n\t\tif ( ! this._dbTable ) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// Select the details requested, for the columns requested\n\t\tlet query = db\n\t\t\t.select( this._dbPkey )\n\t\t\t.from( this._dbTable );\n\n\t\tlet keys = Object.keys( this._dbFields );\n\t\tfor ( let i = 0, ien = keys.length ; i < ien ; i++ ) {\n\t\t\tlet key = keys[i];\n\n\t\t\tif ( this._dbFields[ key ] !== DbOpts.Content ) {\n\t\t\t\tquery.select( key );\n\t\t\t}\n\t\t}\n\n\t\tif ( ids !== null ) {\n\t\t\tquery.whereIn( this._dbPkey, ids );\n\t\t}\n\n\t\tfor ( let i = 0, ien = this._where.length ; i < ien ; i++ ) {\n\t\t\tquery.where( this._where[i] );\n\t\t}\n\n\t\tlet result = await query;\n\t\tlet out = {};\n\n\t\tfor ( let i = 0, ien = result.length ; i < ien ; i++ ) {\n\t\t\tout[ result[i][ this._dbPkey] ] = result[i];\n\t\t}\n\n\t\treturn out;\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tpublic async dbCleanExec( editor: Editor, field: Field ): Promise {\n\t\t// Database and file system clean up BEFORE adding the new file to\n\t\t// the db, otherwise it will be removed immediately\n\t\tlet tables = editor.table();\n\t\tthis._dbClean( editor.db(), tables[0], field.dbField() );\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tpublic error() {\n\t\treturn this._error;\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tpublic async exec( editor: Editor, upload: IUpload ): Promise {\n\t\tlet id;\n\n\t\t// Add any extra information to the upload structure\n\t\tlet fileInfo = await stat( upload.upload.file );\n\t\tupload.upload.size = fileInfo.size;\n\n\t\tlet a = upload.upload.filename.split('.');\n\t\tupload.upload.extn = a.length > 1 ?\n\t\t\ta.pop() :\n\t\t\t'';\n\t\tupload.upload.name = a.join('.');\n\n\t\t// Validation\n\t\tfor ( let i = 0, ien = this._validators.length ; i < ien ; i++ ) {\n\t\t\tlet result = await this._validators[i]( upload.upload );\n\n\t\t\tif ( typeof result === 'string' ) {\n\t\t\t\tthis._error = result;\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\t// Database\n\t\tif ( this._dbTable ) {\n\t\t\tlet fields = Object.keys( this._dbFields );\n\n\t\t\tfor ( let i = 0, ien = fields.length ; i < ien ; i++ ) {\n\t\t\t\tlet prop = this._dbFields[ fields[i] ];\n\n\t\t\t\t// We can't know what the path is, if it has moved into place\n\t\t\t\t// by an external function - throw an error if this does happen\n\t\t\t\tif ( typeof this._action !== 'string' && prop === DbOpts.SystemPath ) {\n\t\t\t\t\tthis._error = 'Cannot set path information in the database ' +\n\t\t\t\t\t\t'if a custom method is used to save the file.';\n\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Commit to the database\n\t\t\tid = await this._dbExec( editor.db(), upload );\n\t\t}\n\n\t\tlet res = await this._actionExec( id, upload );\n\t\treturn res;\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tpublic pkey() {\n\t\treturn this._dbPkey;\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tpublic table() {\n\t\treturn this._dbTable;\n\t}\n\n\t/* * * * * * * * * * * * * * * * * * * * * * * * *\n\t * Private methods\n\t */\n\tprivate async _actionExec( id: string, files: IUpload ): Promise {\n\t\tif ( typeof this._action === 'function' ) {\n\t\t\tlet res = await this._action( files.upload, id );\n\t\t\treturn res;\n\t\t}\n\n\t\t// Default action - move the file to the location specified by the\n\t\t// action string\n\t\tlet to = this._substitute( this._action, files.upload.file, id );\n\t\tto = path.normalize( to );\n\n\t\ttry {\n\t\t\tawait( rename( files.upload.file, to, {mkdirp: true} ) );\n\t\t} catch (e) {\n\t\t\tthis._error = 'An error occurred while moving the uploaded file.';\n\t\t\treturn null;\n\t\t}\n\n\t\treturn id !== null ?\n\t\t\tid :\n\t\t\tto;\n\t}\n\n\tprivate async _dbClean( db: Knex, editorTable: string, fieldName: string ): Promise {\n\t\tlet callback = this._dbCleanCallback;\n\t\tlet that = this;\n\n\t\tif ( ! this._dbTable || ! callback ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// If there is a table / field that we should use to check if the value\n\t\t// is in use, then use that. Otherwise we'll try to use the information\n\t\t// from the Editor / Field instance.\n\t\tif ( this._dbCleanTableField ) {\n\t\t\tfieldName = this._dbCleanTableField;\n\t\t}\n\n\t\tlet table;\n\t\tlet field;\n\t\tlet a = fieldName.split('.');\n\n\t\tif ( a.length === 1 ) {\n\t\t\ttable = editorTable;\n\t\t\tfield = a[0];\n\t\t}\n\t\telse if ( a.length === 2 ) {\n\t\t\ttable = a[0];\n\t\t\tfield = a[1];\n\t\t}\n\t\telse {\n\t\t\ttable = a[1];\n\t\t\tfield = a[2];\n\t\t}\n\n\t\t// Select the details requested, for the columns requested\n\t\tlet fields = this._dbFields;\n\t\tlet columns = Object.keys( fields );\n\t\tlet query = db\n\t\t\t.select( this._dbPkey )\n\t\t\t.from( this._dbTable );\n\n\t\tfor ( let i = 0, ien = columns.length ; i < ien ; i++ ) {\n\t\t\tlet column = columns[i];\n\t\t\tlet prop = fields[ column ];\n\n\t\t\tif ( prop !== DbOpts.Content ) {\n\t\t\t\tquery.select( column );\n\t\t\t}\n\t\t}\n\n\t\tquery.whereNotIn( this._dbPkey, function() {\n\t\t\tthis.select( field ).from( table ).whereNotNull( field );\n\t\t} );\n\n\t\tlet rows = await query;\n\n\t\tif ( rows.length === 0 ) {\n\t\t\treturn;\n\t\t}\n\n\t\tlet result = await callback( rows );\n\n\t\t// Delete the selected rows, iff the developer says to do so with the\n\t\t// returned value (i.e. acknowledge that the files have be removed from\n\t\t// the file system)\n\t\tif ( result === true ) {\n\t\t\tlet queryDel = db\n\t\t\t\t.from( this._dbTable )\n\t\t\t\t.where( function() {\n\t\t\t\t\tfor ( let i = 0, ien = rows.length ; i < ien ; i++ ) {\n\t\t\t\t\t\tthis.orWhere( { [that._dbPkey]: rows[i][that._dbPkey] } );\n\t\t\t\t\t}\n\t\t\t\t} );\n\n\t\t\tawait queryDel.del();\n\t\t}\n\t}\n\n\tprivate async _dbExec( db: Knex, files: IUpload ): Promise {\n\t\tlet pathFields = {};\n\t\tlet fields = this._dbFields;\n\t\tlet columns = Object.keys( fields );\n\t\tlet set = {};\n\t\tlet upload = files.upload;\n\n\t\tfor ( let i = 0, ien = columns.length ; i < ien ; i++ ) {\n\t\t\tlet column = columns[i];\n\t\t\tlet prop = fields[ column ];\n\n\t\t\tswitch ( prop ) {\n\t\t\t\tcase DbOpts.ReadOnly:\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DbOpts.Content:\n\t\t\t\t\tset[ column ] = await readFile( upload.file );\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DbOpts.ContentType:\n\t\t\t\tcase DbOpts.MimeType:\n\t\t\t\t\tset[ column ] = upload.mimetype;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DbOpts.Extn:\n\t\t\t\t\tset[ column ] = upload.extn;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DbOpts.FileName:\n\t\t\t\t\tset[ column ] = upload.filename;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DbOpts.Name:\n\t\t\t\t\tset[ column ] = upload.name;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DbOpts.FileSize:\n\t\t\t\t\tset[ column ] = upload.size;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DbOpts.SystemPath:\n\t\t\t\t\tpathFields[ column ] = this._action;\n\t\t\t\t\tset[ column ] = '-'; // Use a temporary value to avoid cases\n\t\t\t\t\tbreak; // where the db will reject empty values\n\n\t\t\t\tdefault:\n\t\t\t\t\tlet val = typeof prop === 'function' ?\n\t\t\t\t\t\tprop( db, upload ) :\n\t\t\t\t\t\tprop;\n\n\t\t\t\t\tif ( typeof val === 'string' && val.match(/\\{.*\\}/) ) {\n\t\t\t\t\t\tpathFields[ column ] = val;\n\t\t\t\t\t\tset[ column ] = '-';\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tset[ column ] = val;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tlet res = await db\n\t\t\t.insert( set )\n\t\t\t.from( this._dbTable )\n\t\t\t.returning( this._dbPkey );\n\n\t\tlet id = res[0];\n\n\t\t// Update the newly inserted row with the path information. We have to\n\t\t// use a second statement here as we don't know in advance what the\n\t\t// database schema is and don't want to prescribe that certain triggers\n\t\t// etc be created. It makes it a bit less efficient but much more\n\t\t// compatible\n\t\tlet pathKeys = Object.keys( pathFields );\n\n\t\tif ( pathKeys.length ) {\n\t\t\t// For this to operate the action must be a string, which is\n\t\t\t// validated in the `exec` method\n\t\t\tlet toSet = {};\n\n\t\t\tfor ( let i = 0, ien = pathKeys.length ; i < ien ; i++ ) {\n\t\t\t\tlet key = pathKeys[i];\n\t\t\t\ttoSet[ key ] = this._substitute( pathFields[key], upload.file, id );\n\t\t\t}\n\n\t\t\tawait db\n\t\t\t\t.update( toSet )\n\t\t\t\t.from( this._dbTable )\n\t\t\t\t.where( { [this._dbPkey]: id } );\n\t\t}\n\n\t\treturn id;\n\t}\n\n\tprivate _substitute( convert: string, uploadPath: string, id: string ): string {\n\t\tlet a = uploadPath.toString().split( '/' );\n\t\tlet fileName = a.pop();\n\t\tlet fileParts = fileName.split('.');\n\t\tlet extn = fileParts.pop();\n\t\tlet namePart = fileParts.join('.');\n\n\t\tlet to = convert.toString();\n\t\tto = to.replace( '{name}', namePart );\n\t\tto = to.replace( '{id}', id );\n\t\tto = to.replace( '{extn}', extn );\n\n\t\treturn to;\n\t}\n}\n"]} \ No newline at end of file +{"version":3,"sources":["upload.ts"],"names":[],"mappings":"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","file":"upload.js","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\n\nimport * as rootPath from 'app-root-path';\nimport * as knex from 'knex';\nimport {Knex} from 'knex';\n\nimport * as mv from 'mv';\n\nimport Editor from './editor';\nimport Field from './field';\nimport promisify from './promisify';\n\nlet stat = promisify( fs.stat );\nlet readFile = promisify( fs.readFile );\nlet rename = promisify( mv );\n\nexport enum DbOpts {\n\tContent,\n\tContentType,\n\tExtn,\n\tName,\n\tFileName, // Name + Extn\n\tFileSize,\n\tMimeType,\n\tReadOnly,\n\tSystemPath\n\t// Note that the PHP and .NET libraries have a WebPath, but that isn't\n\t// available here as there isn't a good and reliable way to get the web\n\t// root in node (it could be anywhere!).\n}\n\nexport interface IFile {\n\tuuid: string;\n\tfield: string;\n\tfile: string; // full path\n\tfilename: string; // name + extn\n\tencoding: string;\n\tmimetype: string;\n\ttruncated: boolean;\n\tdone: boolean;\n\tsize: number; // Added\n\textn: string; // Added\n\tname: string; // Added\n}\n\nexport interface IUpload {\n\tupload: IFile;\n}\n\n/**\n * Upload class for Editor. This class provides the ability to easily specify\n * file upload information, specifically how the file should be recorded on\n * the server (database and file system).\n *\n * An instance of this class is attached to a field using the {@link\n * Field.upload} method. When Editor detects a file upload for that file the\n * information provided for this instance is executed.\n *\n * The configuration is primarily driven through the {@link db} and {@link\n * action} methods:\n *\n * * {@link db} Describes how information about the uploaded file is to be\n * stored on the database.\n * * {@link action} Describes where the file should be stored on the file system\n * and provides the option of specifying a custom action when a file is\n * uploaded.\n *\n * Both methods are optional - you can store the file on the server using the\n * {@link db} method only if you want to store the file in the database, or if\n * you don't want to store relational data on the database us only {@link\n * action}. However, the majority of the time it is best to use both - store\n * information about the file on the database for fast retrieval (using a {@link\n * Editor.leftJoin()} for example) and the file on the file system for direct\n * web access.\n *\n * @export\n * @class Upload\n */\nexport default class Upload {\n\tpublic static Db = DbOpts; // legacy\n\tpublic static DbOpts = DbOpts;\n\n\tprivate _action: string|Function;\n\tprivate _dbCleanCallback; // async function\n\tprivate _dbCleanTableField: string;\n\tprivate _dbTable: string;\n\tprivate _dbPkey: string;\n\tprivate _dbFields;\n\tprivate _error: string;\n\tprivate _validators = [];\n\tprivate _where = [];\n\n\t/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n\t * Constructor\n\t */\n\n\tconstructor( action: string|Function = null ) {\n\t\tif ( action ) {\n\t\t\tthis.action( action );\n\t\t}\n\t}\n\n\t/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n\t * Public methods\n\t */\n\n\t/**\n\t * Set the action to take when a file is uploaded. This can be either of:\n\t *\n\t * * A string - the value given is the full system path to where the\n\t * uploaded file is written to. The value given can include three \"macros\"\n\t * which are replaced by the script dependent on the uploaded file:\n\t * * `__EXTN__` - the file extension\n\t * * `__NAME__` - the uploaded file's name (including the extension)\n\t * * `__ID__` - Database primary key value if the {@link db} method is\n\t * used.\n\t * * A closure - if a function is given the responsibility of what to do\n\t * with the uploaded file is transferred to this function. That will\n\t * typically involve writing it to the file system so it can be used\n\t * later.\n\t *\n\t * @param {(string|Function)} action Upload action\n\t * @returns {Upload} Self for chaining\n\t */\n\tpublic action( action: string|Function ): Upload {\n\t\tthis._action = action;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Database configuration method. When used, this method will tell Editor\n\t * what information you want written to a database on file upload, should\n\t * you wish to store relational information about your file on the database\n\t * (this is generally recommended).\n\t *\n\t * @param {string} table The name of the table where the file information\n\t * should be stored\n\t * @param {string} pkey Primary key column name. The `Upload` class\n\t * requires that the database table have a single primary key so each\n\t * row can be uniquely identified.\n\t * @param {object} fields A list of the fields to be written to on upload.\n\t * The property names are the database columns and the values can be\n\t * defined by the constants of this class. The value can also be a\n\t * string or a closure function if you wish to send custom information\n\t * to the database.\n\t * @returns {Upload} Self for chaining\n\t */\n\tpublic db( table: string, pkey: string, fields: object ): Upload {\n\t\tthis._dbTable = table;\n\t\tthis._dbPkey = pkey;\n\t\tthis._dbFields = fields;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set a callback function that is used to remove files which no longer have\n\t * a reference in a source table.\n\t *\n\t * @param {(string|Function)} tableField Table field to be used for the delete match\n\t * @param {Function} [callback=null] Function that will be executed on clean. It is\n\t * given an array of information from the database about the orphaned\n\t * rows, and can return true to indicate that the rows should be\n\t * removed from the database. Any other return value (including none)\n\t * will result in the records being retained.\n\t * @returns {Upload} Self for chaining\n\t */\n\tpublic dbClean( tableField: string|Function, callback: Function = null ): Upload {\n\t\t// Argument swapping\n\t\tif ( typeof tableField === 'function' ) {\n\t\t\tthis._dbCleanTableField = null;\n\t\t\tthis._dbCleanCallback = tableField;\n\t\t}\n\t\telse {\n\t\t\tthis._dbCleanTableField = tableField;\n\t\t\tthis._dbCleanCallback = callback;\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add a validation method to check file uploads. Multiple validators can be\n\t * added by calling this method multiple times - they will be executed in\n\t * sequence when a file has been uploaded.\n\t *\n\t * @param {any} fn Validation function. A files parameter is\n\t * passed in for the uploaded file and the return is either a string\n\t * (validation failed and error message), or `true` (validation passed).\n\t * @returns {Upload} Self for chaining\n\t */\n\tpublic validator( fn ): Upload {\n\t\tthis._validators.push( fn );\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add a condition to the data to be retrieved from the database. This\n\t * must be given as a function to be executed (usually anonymous) and\n\t * will be passed in a single argument, the `Query` object, to which\n\t * conditions can be added. Multiple calls to this method can be made.\n\t *\n\t * @param {any} fn Knex WHERE condition\n\t * @returns {Upload} Self for chaining\n\t */\n\tpublic where( fn ): Upload {\n\t\tthis._where.push( fn );\n\n\t\treturn this;\n\t}\n\n\t/* * * * * * * * * * * * * * * * * * * * * * * * *\n\t * Internal methods\n\t */\n\n\t/**\n\t * @ignore\n\t */\n\tpublic async data( db: Knex, ids: string[] = null ): Promise {\n\t\tif ( ! this._dbTable ) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// Select the details requested, for the columns requested\n\t\tlet query = db\n\t\t\t.select( this._dbPkey )\n\t\t\t.from( this._dbTable );\n\n\t\tlet keys = Object.keys( this._dbFields );\n\t\tfor ( let i = 0, ien = keys.length ; i < ien ; i++ ) {\n\t\t\tlet key = keys[i];\n\n\t\t\tif ( this._dbFields[ key ] !== DbOpts.Content ) {\n\t\t\t\tquery.select( key );\n\t\t\t}\n\t\t}\n\n\t\tif ( ids !== null ) {\n\t\t\tquery.whereIn( this._dbPkey, ids );\n\t\t}\n\n\t\tfor ( let i = 0, ien = this._where.length ; i < ien ; i++ ) {\n\t\t\tquery.where( this._where[i] );\n\t\t}\n\n\t\tlet result = await query;\n\t\tlet out = {};\n\n\t\tfor ( let i = 0, ien = result.length ; i < ien ; i++ ) {\n\t\t\tout[ result[i][ this._dbPkey] ] = result[i];\n\t\t}\n\n\t\treturn out;\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tpublic async dbCleanExec( editor: Editor, field: Field ): Promise {\n\t\t// Database and file system clean up BEFORE adding the new file to\n\t\t// the db, otherwise it will be removed immediately\n\t\tlet tables = editor.table();\n\t\tthis._dbClean( editor.db(), tables[0], field.dbField() );\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tpublic error() {\n\t\treturn this._error;\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tpublic async exec( editor: Editor, upload: IUpload ): Promise {\n\t\tlet id;\n\n\t\t// Add any extra information to the upload structure\n\t\tlet fileInfo = await stat( upload.upload.file );\n\t\tupload.upload.size = fileInfo.size;\n\n\t\tlet a = upload.upload.filename.split('.');\n\t\tupload.upload.extn = a.length > 1 ?\n\t\t\ta.pop() :\n\t\t\t'';\n\t\tupload.upload.name = a.join('.');\n\n\t\t// Validation\n\t\tfor ( let i = 0, ien = this._validators.length ; i < ien ; i++ ) {\n\t\t\tlet result = await this._validators[i]( upload.upload );\n\n\t\t\tif ( typeof result === 'string' ) {\n\t\t\t\tthis._error = result;\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\t// Database\n\t\tif ( this._dbTable ) {\n\t\t\tlet fields = Object.keys( this._dbFields );\n\n\t\t\tfor ( let i = 0, ien = fields.length ; i < ien ; i++ ) {\n\t\t\t\tlet prop = this._dbFields[ fields[i] ];\n\n\t\t\t\t// We can't know what the path is, if it has moved into place\n\t\t\t\t// by an external function - throw an error if this does happen\n\t\t\t\tif ( typeof this._action !== 'string' && prop === DbOpts.SystemPath ) {\n\t\t\t\t\tthis._error = 'Cannot set path information in the database ' +\n\t\t\t\t\t\t'if a custom method is used to save the file.';\n\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Commit to the database\n\t\t\tid = await this._dbExec( editor.db(), upload );\n\t\t}\n\n\t\tlet res = await this._actionExec( id, upload );\n\t\treturn res;\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tpublic pkey() {\n\t\treturn this._dbPkey;\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tpublic table() {\n\t\treturn this._dbTable;\n\t}\n\n\t/* * * * * * * * * * * * * * * * * * * * * * * * *\n\t * Private methods\n\t */\n\tprivate async _actionExec( id: string, files: IUpload ): Promise {\n\t\tif ( typeof this._action === 'function' ) {\n\t\t\tlet res = await this._action( files.upload, id );\n\t\t\treturn res;\n\t\t}\n\n\t\t// Default action - move the file to the location specified by the\n\t\t// action string\n\t\tlet to = this._substitute( this._action, files.upload.file, id );\n\t\tto = path.normalize( to );\n\n\t\ttry {\n\t\t\tawait( rename( files.upload.file, to, {mkdirp: true} ) );\n\t\t} catch (e) {\n\t\t\tthis._error = 'An error occurred while moving the uploaded file.';\n\t\t\treturn null;\n\t\t}\n\n\t\treturn id !== null ?\n\t\t\tid :\n\t\t\tto;\n\t}\n\n\tprivate async _dbClean( db: Knex, editorTable: string, fieldName: string ): Promise {\n\t\tlet callback = this._dbCleanCallback;\n\t\tlet that = this;\n\n\t\tif ( ! this._dbTable || ! callback ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// If there is a table / field that we should use to check if the value\n\t\t// is in use, then use that. Otherwise we'll try to use the information\n\t\t// from the Editor / Field instance.\n\t\tif ( this._dbCleanTableField ) {\n\t\t\tfieldName = this._dbCleanTableField;\n\t\t}\n\n\t\tlet table;\n\t\tlet field;\n\t\tlet a = fieldName.split('.');\n\n\t\tif ( a.length === 1 ) {\n\t\t\ttable = editorTable;\n\t\t\tfield = a[0];\n\t\t}\n\t\telse if ( a.length === 2 ) {\n\t\t\ttable = a[0];\n\t\t\tfield = a[1];\n\t\t}\n\t\telse {\n\t\t\ttable = a[1];\n\t\t\tfield = a[2];\n\t\t}\n\n\t\t// Select the details requested, for the columns requested\n\t\tlet fields = this._dbFields;\n\t\tlet columns = Object.keys( fields );\n\t\tlet query = db\n\t\t\t.select( this._dbPkey )\n\t\t\t.from( this._dbTable );\n\n\t\tfor ( let i = 0, ien = columns.length ; i < ien ; i++ ) {\n\t\t\tlet column = columns[i];\n\t\t\tlet prop = fields[ column ];\n\n\t\t\tif ( prop !== DbOpts.Content ) {\n\t\t\t\tquery.select( column );\n\t\t\t}\n\t\t}\n\n\t\tquery.whereNotIn( this._dbPkey, function() {\n\t\t\tthis.select( field ).from( table ).whereNotNull( field );\n\t\t} );\n\n\t\tlet rows = await query;\n\n\t\tif ( rows.length === 0 ) {\n\t\t\treturn;\n\t\t}\n\n\t\tlet result = await callback( rows );\n\n\t\t// Delete the selected rows, iff the developer says to do so with the\n\t\t// returned value (i.e. acknowledge that the files have be removed from\n\t\t// the file system)\n\t\tif ( result === true ) {\n\t\t\tlet queryDel = db\n\t\t\t\t.from( this._dbTable )\n\t\t\t\t.where( function() {\n\t\t\t\t\tfor ( let i = 0, ien = rows.length ; i < ien ; i++ ) {\n\t\t\t\t\t\tthis.orWhere( { [that._dbPkey]: rows[i][that._dbPkey] } );\n\t\t\t\t\t}\n\t\t\t\t} );\n\n\t\t\tawait queryDel.del();\n\t\t}\n\t}\n\n\tprivate async _dbExec( db: Knex, files: IUpload ): Promise {\n\t\tlet pathFields = {};\n\t\tlet fields = this._dbFields;\n\t\tlet columns = Object.keys( fields );\n\t\tlet set = {};\n\t\tlet upload = files.upload;\n\n\t\tfor ( let i = 0, ien = columns.length ; i < ien ; i++ ) {\n\t\t\tlet column = columns[i];\n\t\t\tlet prop = fields[ column ];\n\n\t\t\tswitch ( prop ) {\n\t\t\t\tcase DbOpts.ReadOnly:\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DbOpts.Content:\n\t\t\t\t\tset[ column ] = await readFile( upload.file );\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DbOpts.ContentType:\n\t\t\t\tcase DbOpts.MimeType:\n\t\t\t\t\tset[ column ] = upload.mimetype;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DbOpts.Extn:\n\t\t\t\t\tset[ column ] = upload.extn;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DbOpts.FileName:\n\t\t\t\t\tset[ column ] = upload.filename;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DbOpts.Name:\n\t\t\t\t\tset[ column ] = upload.name;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DbOpts.FileSize:\n\t\t\t\t\tset[ column ] = upload.size;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DbOpts.SystemPath:\n\t\t\t\t\tpathFields[ column ] = this._action;\n\t\t\t\t\tset[ column ] = '-'; // Use a temporary value to avoid cases\n\t\t\t\t\tbreak; // where the db will reject empty values\n\n\t\t\t\tdefault:\n\t\t\t\t\tlet val = typeof prop === 'function' ?\n\t\t\t\t\t\tprop( db, upload ) :\n\t\t\t\t\t\tprop;\n\n\t\t\t\t\tif ( typeof val === 'string' && val.match(/\\{.*\\}/) ) {\n\t\t\t\t\t\tpathFields[ column ] = val;\n\t\t\t\t\t\tset[ column ] = '-';\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tset[ column ] = val;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tlet res = await db\n\t\t\t.insert( set )\n\t\t\t.from( this._dbTable )\n\t\t\t.returning( this._dbPkey );\n\n\t\tlet id = typeof res[0] === 'object'\n\t\t\t? res[0][this._dbPkey] // Knex 1.0+\n\t\t\t: res[0]; // Knex 0.95 and earlier\n\n\t\t// Update the newly inserted row with the path information. We have to\n\t\t// use a second statement here as we don't know in advance what the\n\t\t// database schema is and don't want to prescribe that certain triggers\n\t\t// etc be created. It makes it a bit less efficient but much more\n\t\t// compatible\n\t\tlet pathKeys = Object.keys( pathFields );\n\n\t\tif ( pathKeys.length ) {\n\t\t\t// For this to operate the action must be a string, which is\n\t\t\t// validated in the `exec` method\n\t\t\tlet toSet = {};\n\n\t\t\tfor ( let i = 0, ien = pathKeys.length ; i < ien ; i++ ) {\n\t\t\t\tlet key = pathKeys[i];\n\t\t\t\ttoSet[ key ] = this._substitute( pathFields[key], upload.file, id );\n\t\t\t}\n\n\t\t\tawait db\n\t\t\t\t.update( toSet )\n\t\t\t\t.from( this._dbTable )\n\t\t\t\t.where( { [this._dbPkey]: id } );\n\t\t}\n\n\t\treturn id;\n\t}\n\n\tprivate _substitute( convert: string, uploadPath: string, id: string ): string {\n\t\tlet a = uploadPath.toString().split( '/' );\n\t\tlet fileName = a.pop();\n\t\tlet fileParts = fileName.split('.');\n\t\tlet extn = fileParts.pop();\n\t\tlet namePart = fileParts.join('.');\n\n\t\tlet to = convert.toString();\n\t\tto = to.replace( '{name}', namePart );\n\t\tto = to.replace( '{id}', id );\n\t\tto = to.replace( '{extn}', extn );\n\n\t\treturn to;\n\t}\n}\n"]} \ No newline at end of file diff --git a/src/upload.ts b/src/upload.ts index 6321a59..a812bf9 100644 --- a/src/upload.ts +++ b/src/upload.ts @@ -506,7 +506,9 @@ export default class Upload { .from( this._dbTable ) .returning( this._dbPkey ); - let id = res[0]; + let id = typeof res[0] === 'object' + ? res[0][this._dbPkey] // Knex 1.0+ + : res[0]; // Knex 0.95 and earlier // Update the newly inserted row with the path information. We have to // use a second statement here as we don't know in advance what the