UNPKG

@kwaeri/controller

Version:

The @kwaeri/controller component of the @kwaer/node-kit application platform.

792 lines (788 loc) 46.2 kB
/** * SPDX-PackageName: kwaeri/controller * SPDX-PackageVersion: 0.3.4 * SPDX-FileCopyrightText: © 2014 - 2022 Richard Winters <kirvedx@gmail.com> and contributors * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR MIT */ 'use strict'; // INCLUDES import * as events from 'events'; import { kdt } from '@kwaeri/developer-tools'; import debug from 'debug'; // DEFINES const _ = new kdt(), DEBUG = debug('nodekit:controller'); ; /** * The Controller is leveraged by the kwaeri/node-kit framework in order to * derive application controllers by composition - at runtime - from all supplied * action controllers, */ export class Controller { /** * @var configuration */ configuration; /** * @var derivationPath */ derivationPath; /** * @var model */ model; /** * @var renderer */ renderer; /** * @var promoter */ promoter; /** * @var extensions */ extensions; /** * @var modules */ modules = { positions: [""], rendered: {} }; /** * Class constructor */ constructor(configuration) { // Set the derived (action) controller path this.derivationPath = configuration.controllerPath; // Store the model provider this.model = configuration.model; // Store the rendrer this.renderer = configuration.renderer; // TODO: Evaluate // Initialize the layout system as early as possible (let this happen at // the point the controller is instantiated): //this.initLayouts( configuration ); // Initialize an event emitter that can effect (promote events to) the // listener stored in the router. this.promoter = new events.EventEmitter(); // Set extension path?:configuration this.extensions = configuration.extensionPath; DEBUG(`Extension path: [%s]`, this.extensions || `Not provided`); // Configuration is discarded and the controller receives an updated // configuration from the router } /** * Imports the configured layouts metadata and parses it. Should be called * where the controller is instantiated. * * @param { void } * * @returns { Promise<boolean> } The promise of a boolean */ async initLayouts() { // Determine if we are using layouts const themed = this.renderer.layout ? await import(`${this.renderer.layout}/theme.json`, { assert: { type: 'json' } }) : null; DEBUG(`Loading layout(s) '%s': [%o]`, `${this.renderer.layout}/theme.json`, themed); // Set available module positions accordingly this.modules.positions = (themed && themed.params && themed.params.module_positions) ? themed.params.module_positions : []; DEBUG(`Using module position(s): [%o]`, this.modules.positions.length ? this.modules.positions : `No`); return Promise.resolve(this.modules.positions.length ? true : false); } /** * Approaches the client's request * * @param request * @param response */ async approach(request, response) { // Get the request method let requestMethod; switch (request.method) { case 'POST': requestMethod = 'Post'; break; case 'PUT': requestMethod = 'Put'; break; case 'DELETE': requestMethod = 'Delete'; break; default: requestMethod = ''; break; } const method = (request.method === ("POST" || "PUT" || "DELETE")) ? `${request.method.charAt(0).toUpperCase()}${request.method.slice(1).toLowerCase()}` : ""; // Get the path let path = request.path, temporaryPath = path; // Remove leading slash if present, we check for one using the original path in the switch // below to handle default url processing by the controller if (temporaryPath.charAt(0) === '/') temporaryPath = temporaryPath.substr(1); // And get the path parts let parts = temporaryPath.split('/'); // Check that we got parts, and if so whether there wasn't more than one - ensuring // that an option was provided (specifically that more than 1 part exists) if (parts.length > 0) { if (!(parts.length > 1)) parts[1] = 'index'; } else { parts[0] = temporaryPath; parts[1] = 'index'; } // Here we attempt to load whatever resource is requested by controller name // if there is an error we display the 404 page. try { switch (path) { case '/': case '/home': case '/main': { DEBUG(`New '${this.derivationPath}/main'`); // Get the derived controller (our es6 implementation requires that // user-defined controllers export a default class): const controllerTypeImport = await import(this.derivationPath + '/main.mjs'), controllerType = controllerTypeImport.default, derived = new controllerType(); // And use composition to extend this base controller: derived.isExtension = false; derived.isModule = false; derived.modules = this.modules; derived.renderWithModuleContent = this.renderWithModuleContent; derived.renderModule = this.renderModule; derived.processModules = this.processModules; derived.derivationPath = this.derivationPath; derived.derivedExtensions = this.extensions; derived.configuration = this.configuration; derived.model = this.model; derived.renderer = this.renderer; derived.promoter = this.promoter; derived.setUserAuth = this.setUserAuth; if (this.configuration.xrm) derived.extensionData = false; // toString.call( derived[parts[1]] ) !== '[object Function]' DEBUG(`Use '${(_.type(derived[parts[1]]) !== "function") ? 'index' : parts[1]}'`); // The requested action determines the view, ensure the view action specified exists and that its a function, otherwise // we'll set Index as the action/view - and if that's not found then a great big 404 will display :) //if( toString.call( derived[parts[1]] ) !== '[object Function]' ) if (_.type(derived[parts[1]]) !== "function") parts[1] = 'index'; // Tac ?layout=0 on to the url in order to avoid wrapping the response with the layout/template if (Object.prototype.hasOwnProperty.call(request.query, 'layout')) { // Change from request..query.hasOwnProperty( 'layout' ) if (request.query.layout == 0) { derived.useModules = false; derived.klay = { controller: 'main', view: parts[1], layoutOverride: true, viewbag: { authenticated: request.isAuthenticated, user: request.session.get('user'), sysmsg: '' } }; } } else { // Let's fetch all the modules content that is to be used on this page derived.useModules = true; derived.klay = { controller: 'main', view: parts[1], links: [ /* '<link rel="stylesheet" type="text/css" href="/extensions/mmod/mmod_cms/css/mmod-cms.css" />' */], scripts: [ /* '<script type="text/javascript" charset="utf8" src="/extensions/mmod/mmod_cms/elements/menu.js"></script>' */], viewbag: { authenticated: request.isAuthenticated, user: request.session.get('user'), sysmsg: '' } }; } DEBUG(`Set 'UseModules' to '${derived.useModules}'`); derived.render = Controller.prototype.render; derived.respond = Controller.prototype.respond; // Require the controller, and use the action term within the parts array to invoke the proper controller method derived[parts[1] + requestMethod](request, response); } break; default: { /* We should first check if the first part of the query represents an applicatoin controller: If not then we'll check if it is an alias */ // Get the derived controller let checkalias = false, controllerTypeImport = null; try { // Ensure we call our es6 user-defined classes' default export: controllerTypeImport = await import(this.derivationPath + '/' + parts[0] + '.mjs'); DEBUG(`Use '${this.derivationPath}/${parts[0]}' default`); } catch (error) { checkalias = true; console.error(`Error: FILENOTFOUND: Could not find specified controller: '${this.derivationPath + '/' + parts[0]}`); DEBUG(`Check alias`); } if (checkalias) { try { controllerTypeImport = await import(this.derivationPath + '/extensions.mjs'); DEBUG(`Use '${this.derivationPath}/extensions' default`); } catch (error) { checkalias = false; console.error(`Error: FILENOTFOUND: Could not find extension controller: '${this.derivationPath + '/extensions'}`); console.error(`Error: BADURL: '${request.path}'`); DEBUG(`BADURL '${request.path}`); } // check to see if the first part of the query // represents an alternate route } // Get the derived controller: const controllerType = controllerTypeImport.default, derived = new controllerType(); // And use composition to extend this base controller: if (checkalias) derived.isExtension = true; else derived.isExtension = false; DEBUG(`Set 'isExtension' to '${derived.isExtension}'`); derived.isModule = false; derived.modules = this.modules; derived.renderWithModuleContent = this.renderWithModuleContent; derived.renderModule = this.renderModule; derived.processModules = this.processModules; derived.derivationPath = this.derivationPath; derived.derivedExtensions = this.extensions; derived.configuration = this.configuration; derived.model = this.model; derived.renderer = this.renderer; derived.promoter = this.promoter; derived.setUserAuth = this.setUserAuth; if (this.configuration.xrm && !checkalias) derived.extData = false; DEBUG(`Check '${parts[1]}' in '${this.derivationPath}/${parts[0]}'`); //DEBUG( `'${parts[1]}' as toString is '${toString.call(derived[parts[1]])}'` ); DEBUG(`'${parts[1]}' as toString is '${_.type(derived[parts[1]])}'`); //DEBUG( `Use '${( ( toString.call( derived[parts[1]] ) !== "[object Function]" ) ? 'index' : parts[1] )}'` ); DEBUG(`Use '${((_.type(derived[parts[1]]) !== "function") ? 'index' : parts[1])}'`); // The requested action determines the view, ensure the view action specified exists and that its a function, otherwise // we'll set Index as the action/view - and if that's not found then a great big 404 will display :) //if( toString.call( derived[parts[1]] ) !== '[object Function]' ) if (_.type(derived[parts[1]]) !== "function") parts[1] = 'index'; // Tac ?layout=0 on to the url in order to avoid wrapping the response with the layout/template if (Object.prototype.hasOwnProperty.call(request.query, 'layout')) { // Change from request..query.hasOwnProperty( 'layout' ) if (request.query.layout == 0) { derived.useModules = false; derived.klay = { controller: parts[0], view: parts[1], layoutOverride: true, viewbag: { authenticated: request.isAuthenticated, user: request.session.get('user'), sysmsg: '' } }; } } else { // Let's fetch all the modules content that is to be used on this page derived.useModules = true; derived.klay = { controller: parts[0], view: parts[1], viewbag: { authenticated: request.isAuthenticated, user: request.session.get('user'), sysmsg: '' } }; } DEBUG(`Set 'UseModules' to '${derived.useModules}'`); derived.render = Controller.prototype.render; derived.respond = Controller.prototype.respond; /* If a request is made to .../extensions/manage/<ns>/<ext>/<view><act> then we'll need to ensure the request can get there whether it is a get or post, without needlessly adding controller methods. This little switch will do fine since the extensions controller handles get/post manipulation for the extension system. */ if (parts[0] === 'extensions' && (parts[1] === 'manage' || parts[1] === 'index')) requestMethod = ''; // Require the controller, and use the action term within the parts array to invoke the proper controller method derived[parts[1] + requestMethod](request, response); } break; } } catch (error) { // And log the error ofc console.error(`Controller error: '${error} Derived path: ${path} line: ${/\(file:[\w\d/.-]+:([\d]+)/.exec(error.stack)}`); // If the controller can't be loaded for some reason, handle the exception by showing a 404 //require( './404' ).get( request, response ); // ToDo: Do something about this!! this.handle404(request, response); } } ; setUserAuth(sid, user, auth) { if (this.promoter) { DEBUG(`Set 'authenticated' to '${auth}' for '${sid}'`); this.promoter.emit('moderate', 'authenticated', sid, user, auth); } } ; respond(request, response, buffer) { // Ensure buffer is a string or buffer: if (_.type(buffer) !== "string") buffer = JSON.stringify(buffer); // Manage session state persistence: response.setSession(); // Response statusCode [by default] is 200, unless set differently: DEBUG(`Set 'status' [HTTP] to '${response.statusCode}' for '${request.path}'`); // It returns application/json content [by default]: if (!response.getHeader('Content-Type')) { DEBUG(`Set [DEFAULT] 'Content-Type' response header to 'application/json; charset=utf-8'`); response.setHeader('Content-Type', 'application/json; charset=utf-8'); } // We have a buffer of data: response.setHeader('Content-Length', buffer.length); DEBUG(`Set 'Content-Length' response header to '${buffer.length}'`); // That we write: response.write(buffer); DEBUG(`Write '{body}' for '${request.path}`); // We do not _render_ an HTML response of any kind: response.end(); DEBUG(`Response sent for '${request.path}'`); } /** * Renders a response for the client * * @param request * @param response * @param klay */ render(request, response, klay, overrideModules) { if (this.useModules && !overrideModules) { DEBUG(`Rendering with module content`); // We are rendering module content here: if (!klay) this.renderWithModuleContent(request, response, this.klay, this); else this.renderWithModuleContent(request, response, klay, this); /* console.log( 'Getting list of modules' ); // Time to fetch a list of all modules which are registered // for each available module position of the current theme this.modules.list = this.xrmModuleData( 'modules', { positions: this.modules.positions }, request, response, this ); console.log( 'Getting modules content' ); // And for each module to be displayed, we'll fetch its rendered // content this.modules.content = this.xrmModuleData( 'content', { modules: this.modules.list }, request, response ); */ } else { DEBUG(`Processing base request`); // We are not rendering modules here: if (!klay) if (this.isExtension) this.renderer.turn(request, response, this.klay, true, this.extData); else this.renderer.turn(request, response, this.klay, false); else if (this.isExtension) this.renderer.turn(request, response, klay, true, this.extData); else this.renderer.turn(request, response, klay, false); // Why is this here? //this.pottr.turn( request, response, klay ); } } ; /** * Renders a response for the client * * @param request * @param response * @param klay */ renderModule(request, response, klay) { //console.log( 'Processing module' ); if (!klay) if (this.isExtension) this.renderer.turn(request, response, this.klay, true, this.extData, this); else if (this.isExtension) this.renderer.turn(request, response, klay, true, this.extData, this); // Why is this here? //this.pottr.turn( request, response, klay ); } ; /** * Renders module content * * @param request * @param response * @param klay */ processModules(request, response, klay, future) { DEBUG(`Discovering modules`); if (klay.hasOwnProperty('modules') && !_.empty(klay.modules)) { DEBUG(`Checking discovered modules for content`); if (klay.modules.hasOwnProperty('content') && klay.modules.content.length > 0) { DEBUG(`Checking if this is the first module position`); if (klay.modules.content.length > 0 && klay.modules.content[0] === 0) { klay.modules.content[0] = 1; DEBUG(`Checking if this is the first module of position ${klay.modules.content[0]}`); if (klay.modules.content[klay.modules.content[0]].length > 0 && klay.modules.content[klay.modules.content[0]][0] === 0) klay.modules.content[klay.modules.content[0]][0] = 1; if (klay.modules.content[klay.modules.content[0]].length > 0 && klay.modules.content[klay.modules.content[0]][0] < klay.modules.content[klay.modules.content[0]].length) { DEBUG(`Discovered module ${klay.modules.content[klay.modules.content[0]][0]} in position ${klay.modules.content[0]}`); klay.modules.content[klay.modules.content[0]][klay.modules.content[klay.modules.content[0]][0]]._request = request; klay.modules.content[klay.modules.content[0]][klay.modules.content[klay.modules.content[0]][0]]._response = response; klay.modules.content[klay.modules.content[0]][klay.modules.content[klay.modules.content[0]][0]]._klay = klay; klay.modules.content[klay.modules.content[0]][klay.modules.content[klay.modules.content[0]][0]]._future = future; DEBUG(`Processing module ${klay.modules.content[klay.modules.content[0]][0]} in position ${klay.modules.content[0]}`); klay.modules.content[klay.modules.content[0]][klay.modules.content[klay.modules.content[0]][0]].renderModuleContent(request, response); } } else { DEBUG(`Checking for additional module positions`); if (klay.modules.content.length > 0 && klay.modules.content[0] >= klay.modules.content.length) { DEBUG(`Modules exhausted, processing main view`); future.klay.modules = klay.modules; future.render(request, response, klay, true); } else { DEBUG(`Checking if this is the first module of position ${klay.modules.content[0]}`); if (klay.modules.content[klay.modules.content[0]].length > 0 && klay.modules.content[klay.modules.content[0]][0] === 0) klay.modules.content[klay.modules.content[0]][0] = 1; if (klay.modules.content[klay.modules.content[0]].length > 0 && klay.modules.content[klay.modules.content[0]][0] < klay.modules.content[klay.modules.content[0]].length) { DEBUG(`Discovered module ${klay.modules.content[klay.modules.content[0]][0]} in position ${klay.modules.content[0]}`); klay.modules.content[klay.modules.content[0]][klay.modules.content[klay.modules.content[0]][0]]._request = request; klay.modules.content[klay.modules.content[0]][klay.modules.content[klay.modules.content[0]][0]]._response = response; klay.modules.content[klay.modules.content[0]][klay.modules.content[klay.modules.content[0]][0]]._klay = klay; klay.modules.content[klay.modules.content[0]][klay.modules.content[klay.modules.content[0]][0]]._future = future; DEBUG(`Processing module ${klay.modules.content[klay.modules.content[0]][0]} in position ${klay.modules.content[0]}`); klay.modules.content[klay.modules.content[0]][klay.modules.content[klay.modules.content[0]][0]].renderModuleContent(request, response); } else { DEBUG(`Modules exhausted for position ${klay.modules.content[0]}`); klay.modules.content[0]++; this.processModules(request, response, klay, future); } } } } else { DEBUG(`No module content found`); future.klay.modules = klay.modules; future.render(request, response, klay, true); } } else { DEBUG(`No modules discovered`); } } ; next(content) { // Uncomment this for a list of rendered module content: //DEBUG( `${( this as any )._klay.modules.rendered}` ); DEBUG(`STEP: Checking if there's rendered content to add to the rendered module content container`); if (this._klay.modules.rendered.hasOwnProperty(this._klay.modules.contentMap[this.modules.content[0]])) { if (!(_.type(this._klay.modules.rendered[this._klay.modules.contentMap[this._klay.modules.content[0]]]) == 'array')) { DEBUG(`STEP: Creating rendered module content container for ${this._klay.modules.contentMap[this.modules.content[0]]}`); this._klay.modules.rendered[this._klay.modules.contentMap[this._klay.modules.content[0]]] = []; } DEBUG(`STEP: Set processed module content for ${this._klay.modules.contentMap[this._klay.modules.content[0]]}`); let itsnew = true; for (let i = 0; i <= this._klay.modules.rendered[this._klay.modules.contentMap[this._klay.modules.content[0]]].length; i++) { if (this._klay.modules.rendered[this._klay.modules.contentMap[this._klay.modules.content[0]]][i] == content) itsnew = false; } if (itsnew) { DEBUG(`STEP: Adding module content for position ${this._klay.modules.content[0]} to contentMap`); this._klay.modules.rendered[this._klay.modules.contentMap[this._klay.modules.content[0]]].push(content); } } else { DEBUG(`STEP: Adding rendered module content container for position ${this._klay.modules.content[0]}:${this._klay.modules.contentMap[this._klay.modules.content[0]]}`); // Create the missing property, as an array: this._klay.modules.rendered[this._klay.modules.contentMap[this._klay.modules.content[0]]] = []; DEBUG(`STEP: Set ${this._klay.modules.content[0]}:${this._klay.modules.contentMap[this._klay.modules.content[0]]} content`); // And push the procesed content: this._klay.modules.rendered[this._klay.modules.contentMap[this._klay.modules.content[0]]].push(content); } // Let's see if there are still modules to be processed in this position: if (this._klay.modules.content[this._klay.modules.content[0]].length > 0 && this._klay.modules.content[this._klay.modules.content[0]][0] <= this._klay.modules.content[this._klay.modules.content[0]].length) { // Let's increment if we haven't surpassed the # of modules for this current position: this._klay.modules.content[this._klay.modules.content[0]][0]++; DEBUG(`STEP: Incremented current module to ${this._klay.modules.content[this._klay.modules.content[0]][0]}`); // Let's check again if we have surpassed the # of modules for this current position: if (this._klay.modules.content[this._klay.modules.content[0]][0] <= this._klay.modules.content[this._klay.modules.content[0]].length) { DEBUG(`STEP: Call processModules on module ${this._klay.modules.content[this._klay.modules.content[0]][0]} in position ${this._klay.modules.content[0]}`); // If not let's process another module: this.processModules(this._request, this._response, this._klay, this._future); } else { // If we have surpassed it, let's see if there is another position to process: if (this._klay.modules.content.length > 0 && this._klay.modules.content[0] <= this._klay.modules.content.length) { // If so let's increment the position to the next one: this._klay.modules.content[0]++; DEBUG(`STEP: Incrementing module position to ${this._klay.modules.content[0]}`); // Let's be sure the counter for this next position is less or equal to the number of modules for it: if (this._klay.modules.content[0] <= this._klay.modules.content.length) { // If so let's check if the counter for this next position is at 0: if (this._klay.modules.content[this._klay.modules.content[0]].length > 0 && this._klay.modules.content[this._klay.modules.content[0]][0] === 0) { DEBUG(`STEP: Set starting position for new module to 1`); // If this is true let's set it to 1 this._klay.modules.content[this._klay.modules.content[0]][0] = 1; } } DEBUG(`STEP: Call processModules on module ${this._klay.modules.content[this._klay.modules.content[0]][0]} in position ${this._klay.modules.content[0]}`); // And let's process the next module: this.processModules(this._request, this._response, this._klay, this._future); } else { DEBUG(`STEP: All modules processed - calling main 'render'`); // If not, let's wrap up this._future.klay.modules = this._klay.modules; this._future.render(this._request, this._response, this._klay, true); } } } else { // If there are no more modules to be process for this position, lets see if there is another position to process: if (this._klay.modules.content.length > 0 && this._klay.modules.content[0] <= this._klay.modules.content.length) { // If so let's increment the position to the next one this._klay.modules.content[0]++; DEBUG(`STEP: Incrementing module position to ${this._klay.modules.content[0]}`); // Let's be sure the counter for this next position is less or equal to the number of modules for it: if (this._klay.modules.content[0] <= this._klay.modules.content.length) { // If so let's check if the counter for this next position is at 0: if (this._klay.modules.content[this._klay.modules.content[0]].length > 0 && this._klay.modules.content[this._klay.modules.content[0]][0] === 0) { // If this is true let's set it to 1: this._klay.modules.content[this._klay.modules.content[0]][0] = 1; DEBUG(`STEP: Set starting position for new module to 1`); } } DEBUG(`STEP: Call processModules on module ${this._klay.modules.content[this._klay.modules.content[0]][0]} in position ${this._klay.modules.content[0]}`); // And let's process the next module: this.processModules(this._request, this._response, this._klay, this._future); } else { // If not, let's wrap up this._future.klay.modules = this._klay.modules; this._future.render(this._request, this._response, this._klay, true); } } } ; /** * Fetches data for modules * * @param fn * @param options */ async renderWithModuleContent(request, response, klay, forwarded) { let modulesList = {}; // Return a list of modules which are registered for // the available positions of the current theme // // The method requires a list of said positions, which // are populated within the controller when the xrm // application is originally initialized DEBUG(`Getting a list of modules per position`); let counter = 0; if (this.modules.hasOwnProperty('positions')) { if (this.modules.positions.length > 0) { // Only prep the database object if we're going to use it let db = this.model.dbo(); for (let position in this.modules.positions) { if (this.modules.positions[position] && this.modules.positions[position] !== "") { DEBUG(`Getting modules for '${this.modules.positions[position]}' position`); db.reset(); modulesList[this.modules.positions[position]] = db .query("select modules.name as name, `module-types`.name as type, extensions.name as provider, modules.params as params from modules " + "join `module-types` on `module-types`.id=modules.type " + "join extensions on extensions.id=`module-types`.provider " + "where modules.position='" + this.modules.positions[position] + "' and modules.enabled='1';") .execute(); if (_.type(modulesList[this.modules.positions[position]]) === 'array') { // Great, we have a list of modules that are set for the respective position, // along with their provider's id, and the module's options. } else { modulesList[this.modules.positions[position]] = false; } } } } } //console.log( 'Modules list: ' ); //console.log( modulesList ); this.modules.list = modulesList; let modulesContent = []; let modulesContentMap = []; /* Return an object in which each property references the content to be loaded for each module that is registered for each position of the current theme. The method requires a list of said modules, which can be fetched by invoking the 'modules' function of this method and by supplying a list of positions available with the current theme. We'll get the specific content by invoking the renderModule() function of each module's provider, and passing to said function the options associated with the respective module for the respective position. */ DEBUG(`Getting module content from provider extensions`); // Here we attempt to load whatever extension is requested by name // if there is an error we display the 404 page. if (this.modules.list && !_.empty(this.modules.list)) { modulesContent[0] = 0; let counter = 1; // Iterate through the different positions for which we polled for modules that were enabled for (let module in this.modules.list) { if (this.modules.list.hasOwnProperty(module) && this.modules.list[module].length > 0) { //modulesContent[counter] = []; modulesContentMap[counter] = module; let modulesByPosition = []; modulesByPosition[0] = 0; let counter2 = 1; // Iterate through each of the modules enabled for each position containing content for (let mcontent in this.modules.list[module]) { //modulesContentByPosition[counter2] = []; if (this.modules.list[module].hasOwnProperty(mcontent) && !_.empty(this.modules.list[module][mcontent])) { let eparts = this.modules.list[module][mcontent].provider.split('-'); // And get the path parts if (!(eparts.length > 1)) // Not a valid extension namespace console.error(`Error: '[${this.modules.list[module][mcontent].provider}]' is not a valid extension/module namespace`); try { DEBUG(`Use '${this.derivedExtensions + '/' + eparts[0] + '/' + eparts[0] + "_" + eparts[1] + '/controllers/' + this.modules.list[module][mcontent].type.toLowerCase()}' for '${this.modules.list[module]}'`); let extensionType = await import(this.derivedExtensions + '/' + eparts[0] + '/' + eparts[0] + "_" + eparts[1] + '/controllers/' + this.modules.list[module][mcontent].type.toLowerCase()), derivedExtension = new extensionType(); derivedExtension.isExtension = true; derivedExtension.isModule = true; derivedExtension.moduleParams = this.modules.list[module][mcontent].params; derivedExtension.extData = { vendor: eparts[0], extension: eparts[0] + '_' + eparts[1], isModuleContent: true }; derivedExtension.dextensions = this.derivedExtensions; derivedExtension.config = this.configuration; derivedExtension.modl = this.model; derivedExtension.pottr = this.renderer; derivedExtension.promoter = this.promoter; derivedExtension.setUserAuth = this.setUserAuth; // The requested action determines the view, ensure the view action specified exists and that its a function, otherwise // we'll set Index as the action/view - and if that's not found then a great big 404 will display :) //if( toString.call( derivedExtension.renderModuleContent ) !== '[object Function]' ) if (_.type(derivedExtension.renderModuleContent) !== "function") console.error(`Error: '${this.modules.list[module][mcontent].type.toLowerCase()} is not a valid extension/module`); derivedExtension.klay = { controller: this.modules.list[module][mcontent].type.toLowerCase(), view: this.modules.list[module][mcontent].type.toLowerCase(), layoutOverride: true, viewbag: { authenticated: request.isAuthenticated, user: request.session.get('user'), sysmsg: '' } }; derivedExtension.render = Controller.prototype.renderModule; derivedExtension.next = Controller.prototype.next; derivedExtension.processModules = Controller.prototype.processModules; modulesByPosition[counter2] = derivedExtension; //var tmc = dextension.renderModuleContent( request, response ); //console.log( 'returned from renderModuleContent: ' ); //console.log( tmc ); //modulesContent[module].push( tmc ); counter2++; } catch (error) { // And log the error ofc console.error(`Error: Could not load module content from extension controller. Derived path: '${this.derivedExtensions}/${eparts[0]}/${eparts[0]}_${eparts[1]}/controllers/${this.modules.list[module][mcontent].type.toLowerCase()}' Line: ${/\(file:[\w\d/.-]+:([\d]+)/.exec(error.stack)}`); // If the controller can't be loaded for some reason, handle the exception by showing a 404 //require( './../../../node_modules/nk/library/controller/404' ).get( request, response ); } } } modulesContent[counter] = modulesByPosition; counter++; } } } //console.log( 'Modules content: ' ); //console.log( modulesContent ); klay.modules = this.modules; this.modules.content = modulesContent; klay.modules.content = this.modules.content; this.modules.contentMap = modulesContentMap; klay.modules.contentMap = this.modules.contentMap; klay.modules.rendered = {}; DEBUG(`Call 'processModules'`); this.processModules(request, response, klay, this); } ; /** * Handles 404 error handling for our server * * @param request * @param response * * @return { void } */ handle404(request, response) { DEBUG(`Set 'status' [HTTP] to '404' for '${request.path}'`); // Set the request header status to 404: response.setStatus = 404; DEBUG(`Set 'Content-Type' to 'text/html'`); // Set the content-type header: response.setHeader('Content-Type', 'text/html'); DEBUG(`Write from controller.handle404()`); // Set the body of our response: response.write('<!doctype html>' + '<html lang="en">' + '<head>' + '<meta charset="utf-8">' + '<meta name="viewport" content="width=device-width, initial-scale=1.0">' + '<title>Whoopsie!</title>' + '<link rel="stylesheet" type="text/css" href="//ajax.googleapis.com/ajax/libs/jqueryui/1.11.0/themes/smoothness/jquery-ui.css" />' + '<link rel="stylesheet" type="text/css" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" />' + '<link rel="stylesheet" type="text/css" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap-theme.min.css" />' + '<link rel="stylesheet" type="text/css" href="/css/flaticon.css">' + '<link rel="stylesheet" type="text/css" href="//cdn.mmogp.com/mmod/kwaeri-ux/kwaeri-ux.min.css">' + '<link rel="stylesheet" type="text/css" href="/css/mmod-xrm.css">' + '</head>' + '<body>' + '<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">' + '<div class="container">' + '<div class="navbar-header">' + '<div class="icon-mmod pull-left"><span class="hidden"></span></div>' + '<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#nk-navbar-collapse-1">' + '<span class="sr-only">Toggle navigation</span>' + '<span class="icon-bar"></span>' + '<span class="icon-bar"></span>' + '<span class="icon-bar"></span>' + '</button>' + '<a href="/" class="navbar-brand" data-toggle="tooltip" title="Take me home!"><div class="icon-mmod-badge-small"></div></a>' + '</div>' + '<div class="collapse navbar-collapse" id="nk-navbar-collapse-1">' + '<ul class="nav navbar-nav navbar-right">' + '<li class="dropdown">' + '<a href="#" class="dropdown-toggle" data-toggle="dropdown">Options<span class="caret"></span></a>' + '<ul class="dropdown-menu" role="menu">' + '<li><a href="/about" class="main-nav-btn" data-toggle="tooltip" title="All about us">About</a></li>' + '</ul>' + '</li>' + '<li><a href="/about" class="mbif" title="Community support site"><span class="flaticon-help17"></span> Help</a></li>' + '</ul>' + '</div><!-- /.navbar-collapse -->' + '</div><!-- /.container -->' + '</nav>' + '<div class="container-fluid">' + '<div class="row topper">' + '<section id="showcase">' + '<div class="jumbotron errbla">' + '<center>' + '<h1>Too bad!</h1>' + '<p>The resource requested could not be processed...</p>' + '<p><p>' + '<h3>If you feel you received this message in error, do not hesitate to contact <a href="/about">Support</a>.</h3>' + '</center>' + '</div>' + '</section>' + '</div><!-- /.panel -->' + '</div><!-- container-fluid -->' + '<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>' + '<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.11.0/jquery-ui.min.js"></script>' + '<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>' + '<script type="text/javascript" charset="utf8" src="//cdn.mmogp.com/mmod/kwaeri-ux/kwaeri-ux.min.js"></script>' + '<script type="text/javascript" charset="utf8" src="/js/mmod-xrm.js"></script>' + '</body>' + '</html>'); // Send the response: response.end(); } } //# sourceMappingURL=controller.mjs.map