I use this method for cross-compiling fresh version of Windows software package for specific device on the linux server and add this file to the current Stock entity .
Building is a long process, so I use Job feature to start process from cron, as described at
https://github.com/espocrm/documenta...opment/jobs.md
You could use this solution to generate anything you want on your linux server running EspoCRM using any linux software.
In example I use a custom entity named Stock with some fields and a relation to Documents.
Of course this example could be easily modified for any other entity, existing or new, just replace Stock with your entity.
Controller for Entity for Api calls. It creates a Job with current Entity data.
File custom/Espo/Custom/Controllers/Stock.php
This is a Job. Job will be run as crond child.
File custom/Espo/Custom/Services/BuildService.php
Adding a button to Entity detail view
File custom/Espo/Custom/Resources/metadata/clientDefs/Stock.json
This will run on frontend when user press a button
File client/custom/src/build-action-handler.js
You are free to use this code as you wish.
Building is a long process, so I use Job feature to start process from cron, as described at
https://github.com/espocrm/documenta...opment/jobs.md
You could use this solution to generate anything you want on your linux server running EspoCRM using any linux software.
In example I use a custom entity named Stock with some fields and a relation to Documents.
Of course this example could be easily modified for any other entity, existing or new, just replace Stock with your entity.
Controller for Entity for Api calls. It creates a Job with current Entity data.
File custom/Espo/Custom/Controllers/Stock.php
Code:
<?php namespace Espo\Custom\Controllers; class Stock extends \Espo\Core\Templates\Controllers\Base { public function getActionBuild($params, $data, $request) { $this->getEntityManager()->createEntity('Job', [ 'serviceName' => 'BuildService', 'methodName' => 'buildProg', 'data' => (object) [ // Get fields from current entity 'stock' => $request->get('id'), 'device' => $request->get('device'), 'serial' => $request->get('serial'), ], 'executeTime' => date('Y-m-d H:i:s'), // you can delay execution by setting a later time 'queue' => 'q0', // available queues are listed below ]); return true; // can be true, false, array or object. } }
File custom/Espo/Custom/Services/BuildService.php
Code:
<?php namespace Espo\Custom\Services; class BuildService extends \Espo\Core\Services\Base { protected function init() { $this->addDependencyList([ 'entityManager', ]); } public function buildProg($data) { $stockId = $data->stock; $device = $data->device; $serial = $data->serial; // Creating configuration file for build $fd = fopen( "/path_to_your_EspoCRM//build/config.mk", "w" ); { fwrite( $fd, "HW_DEVICES = " . $device . "\n" ); fwrite( $fd, $device . "_SERIAL = " . $serial . "\n" ); fwrite( $fd, $device . "_BACKEND = libusb0\n" ); fclose( $fd ); } // Running build process. This is a shell script with build commands with output redirected to log file. // To return filename to PHP we just echo resulting executable name at last line of www-build script $fileName = exec( "/path_to_your_EspoCRM/build/www-build", $foo, $err ); if( $err || !$fileName ) return false; $fileSize = filesize( $fileName ); if( !$fileSize ) return false; // // Parse program output if resulting filename returned in build script exhaust // $param = array(); // foreach( $foo => $s ) // { // $a = explode( '=', $s ); // $param[ trim( $a[0] ) ] = trim( $a[1] ); // } $em = $this->getInjection('entityManager'); // Create new document $document = $em->createEntity( 'Document', [ 'name' => basename( $fileName ), ]); // Create empty attachment with real file size $attachment = $em->createEntity( 'Attachment', [ 'name' => basename( $fileName ), 'size' => $fileSize, 'type' => 'application/x-ms-dos-executable', 'role' => 'Attachment', 'contents' => '', 'relatedId' => $document->id, 'relatedType' => 'Document', ]); // Small hack - replace empty attachment file with our fresh executable $tmp = realpath( $em->getRepository( 'Attachment' )->getFilePath( $attachment ) ); unlink( $tmp ); rename( $fileName, $tmp ); // Relate to Document $em->getRepository( 'Document' )->relate( $document, 'file', $attachment ); // Relate to Folder $fr = $em->getRepository( 'DocumentFolder' ); $folder = $fr->where([ 'name' => 'GetSpectrum' ])->findOne(); $fr->relate( $folder, 'documents', $document ); // Relate to Stock $sr = $em->getRepository( 'Stock' ); $stock = $sr->get( $stockId ); $sr->relate( $stock, 'documents', $document ); return true; } }
File custom/Espo/Custom/Resources/metadata/clientDefs/Stock.json
Code:
{ "controller": "controllers/record", "boolFilterList": [ "onlyMy" ], "kanbanViewMode": false, "color": "#c7ed55", "iconClass": "fas fa-warehouse", "menu": { "detail": { "buttons": [ "__APPEND__", { "action": "buildProg", "label": "Build Program", "style": "default", "acl": "edit", "aclScope": "Stock", "data": { "handler": "custom:build-action-handler" } } ] } } }
File client/custom/src/build-action-handler.js
Code:
define('custom:build-action-handler', ['action-handler'], function (Dep) { return Dep.extend({ actionBuildProg: function (data, e) { var that = this; Espo.Ajax.getRequest('Stock/' + this.view.model.id).then(function (response) { var req = 'Stock/action/build' + '?id=' + response.id + '&device=' + response.name + '&serial=' + response.serialNumber; Espo.Ajax.getRequest(req).then(function (res) { // show notification that build in process Espo.Ui.notify('Building program... ', 'dismissable', 0, true); setTimeout(that.timerBuildCheck, 30000, that, res.jobId); }); }); }, // check job status periodically to show notification and update entity page to make new file visible timerBuildCheck: function (that, jobId) { Espo.Ajax.getRequest('Job/' + jobId).then(function (response) { if(response.status == 'Success') { Espo.Ui.notify('Program built', 'success', 3000); that.view.model.trigger('update-all'); return; } if(response.status == 'Failed') { Espo.Ui.notify('Build error', 'warning', 3000); return; } setTimeout(that.timerBuildCheck, 5000, that, jobId); }); }, controlButtonVisibility: function () { if (~['Converted', 'Dead', 'Recycled'].indexOf(this.view.model.get('status'))) { this.view.hideHeaderActionItem('buildGS'); } else { this.view.showHeaderActionItem('buildGS'); } } }); });
Comment