Skip to content

Commit 6dc95ce

Browse files
committed
Merge branch 'develop' into epic/FOUR-25679
2 parents f44c728 + aeaabc1 commit 6dc95ce

43 files changed

Lines changed: 1557 additions & 759 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

ProcessMaker/Console/Commands/BuildScriptExecutors.php

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ class BuildScriptExecutors extends Command
1818
*
1919
* @var string
2020
*/
21-
protected $signature = 'processmaker:build-script-executor {lang} {user?} {--rebuild}';
21+
protected $signature = 'processmaker:build-script-executor
22+
{lang : The ID or language of the script executor}
23+
{user? : The user ID to send the broadcast event to}
24+
{--rebuild : Rebuild the docker image}
25+
{--build-args= : The build arguments for the docker build command}';
2226

2327
/**
2428
* The console command description.
@@ -159,6 +163,12 @@ public function buildExecutor()
159163
$command = Docker::command() .
160164
" build --build-arg SDK_DIR=./sdk -t {$image} -f {$packagePath}/Dockerfile.custom {$packagePath}";
161165

166+
$buildArgs = $this->getBuildArgs();
167+
168+
foreach ($buildArgs as $buildArg) {
169+
$command .= ' ' . $buildArg;
170+
}
171+
162172
$this->execCommand($command);
163173

164174
$isNayra = $scriptExecutor->language === Base::NAYRA_LANG;
@@ -167,6 +177,29 @@ public function buildExecutor()
167177
}
168178
}
169179

180+
/**
181+
* Get the build arguments for the docker build command.
182+
*
183+
* @return array
184+
* - '--build-arg <key>=<value>'
185+
*/
186+
public function getBuildArgs(): array
187+
{
188+
$args = $this->option('build-args');
189+
190+
if ($args) {
191+
$buildArgs = [];
192+
193+
foreach (explode(',', $args) as $arg) {
194+
$buildArgs[] = '--build-arg ' . $arg;
195+
}
196+
197+
return $buildArgs;
198+
}
199+
200+
return [];
201+
}
202+
170203
public function getDockerfileContent(ScriptExecutor $scriptExecutor): string
171204
{
172205
$lang = $scriptExecutor->language;

ProcessMaker/Helpers/DataTypeHelper.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ private static function isDate($value)
1010
{
1111
if (is_string($value)) {
1212
if (strlen($value) > 5) {
13+
if (!preg_match('/\d{4}-\d{2}-\d{2}/', $value)) {
14+
return false;
15+
}
1316
try {
1417
$parsed = Carbon::parse($value);
1518
if ($parsed->isMidnight()) {

ProcessMaker/Http/Controllers/Api/UserController.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -255,10 +255,13 @@ public function getUsersTaskCount(Request $request)
255255
$processRequestToken = ProcessRequestToken::findOrFail($request->input('assignable_for_task_id'));
256256
if (config('app.reassign_restrict_to_assignable_users')) {
257257
$include_ids = $processRequestToken->process->getAssignableUsersByAssignmentType($processRequestToken);
258-
}
259-
$assignmentRule = $processRequestToken->getAssignmentRule();
260-
if ($assignmentRule === 'rule_expression' && $request->has('form_data')) {
261-
$include_ids = $processRequestToken->getAssigneesFromExpression($request->input('form_data'));
258+
$assignmentRule = $processRequestToken->getAssignmentRule();
259+
if ($assignmentRule === 'rule_expression' && $request->has('form_data')) {
260+
$include_ids = $processRequestToken->getAssigneesFromExpression($request->input('form_data'));
261+
}
262+
if ($assignmentRule === 'process_variable' && $request->has('form_data')) {
263+
$include_ids = $processRequestToken->getUsersFromProcessVariable($request->input('form_data'));
264+
}
262265
}
263266
}
264267

ProcessMaker/Http/Controllers/TaskController.php

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ public function edit(ProcessRequestToken $task, string $preview = '')
140140
$element = $task->getDefinition(true);
141141
$screenFields = $screenVersion ? $screenVersion->screenFilteredFields() : [];
142142
$taskDraftsEnabled = TaskDraft::draftsEnabled();
143-
143+
$isSmartExtractTask = $task->element_name === 'Manual Document Review';
144144
// Remove screen parent to reduce the size of the response
145145
$screen = $task->screen;
146146
$screen['parent'] = null;
@@ -190,6 +190,25 @@ public function edit(ProcessRequestToken $task, string $preview = '')
190190
'datetime_format',
191191
]);
192192
$userConfiguration = (new UserConfigurationController())->index();
193+
$hitlEnabled = config('smart-extract.hitl_enabled', false) && $isSmartExtractTask;
194+
195+
// Build the iframe source
196+
$iframeSrc = null;
197+
if ($hitlEnabled) {
198+
$dashboardUrl = config('smart-extract.dashboard_url');
199+
$requestData = $task->processRequest->data ?? [];
200+
201+
$documentToken = $requestData['documentToken'] ?? null;
202+
$fileId = $requestData['fileId'] ?? null;
203+
204+
if ($documentToken && $fileId && !empty($dashboardUrl)) {
205+
$queryParams = http_build_query([
206+
'documentToken' => $documentToken,
207+
'fileId' => $fileId,
208+
]);
209+
$iframeSrc = $dashboardUrl . '?' . $queryParams;
210+
}
211+
}
193212

194213
return view('tasks.edit', [
195214
'task' => $task,
@@ -204,6 +223,8 @@ public function edit(ProcessRequestToken $task, string $preview = '')
204223
'screenFields' => $screenFields,
205224
'taskDraftsEnabled' => $taskDraftsEnabled,
206225
'userConfiguration' => $userConfiguration,
226+
'hitlEnabled' => $hitlEnabled,
227+
'iframeSrc' => $iframeSrc,
207228
]);
208229
}
209230
}

ProcessMaker/Http/Middleware/HideServerHeaders.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class HideServerHeaders
5555
/**
5656
* Handle an incoming request.
5757
*
58-
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
58+
* @param Closure(Request): (Response) $next
5959
*/
6060
public function handle(Request $request, Closure $next): Response
6161
{

ProcessMaker/Http/Middleware/IsManager.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class IsManager
1313
/**
1414
* Handle an incoming request.
1515
*
16-
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
16+
* @param Closure(Request): (Response) $next
1717
*/
1818
public function handle(Request $request, Closure $next): Response
1919
{

ProcessMaker/Http/Resources/Task.php

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ public function toArray($request)
7474

7575
$this->addAssignableUsers($array, $include);
7676

77+
$this->mergeHitlCaseNumber($array);
78+
7779
return $array;
7880
}
7981

@@ -114,6 +116,28 @@ private function addAssignableUsers(&$array, $include)
114116
}
115117
}
116118

119+
private function mergeHitlCaseNumber(array &$array): void
120+
{
121+
if (!config('smart-extract.hitl_enabled')) {
122+
return;
123+
}
124+
125+
if (!empty(data_get($array, 'process_request.case_number'))) {
126+
return;
127+
}
128+
129+
$this->processRequest->loadMissing('parentRequest');
130+
$parentCaseNumber = $this->processRequest->parentRequest?->case_number;
131+
if (!$parentCaseNumber) {
132+
return;
133+
}
134+
135+
data_set($array, 'process_request.case_number', $parentCaseNumber);
136+
if (empty($array['case_number'])) {
137+
$array['case_number'] = $parentCaseNumber;
138+
}
139+
}
140+
117141
/**
118142
* Add the active users to the list of assigned users
119143
*
@@ -131,7 +155,7 @@ private function addActiveAssignedUsers(array $users, array $assignedUsers)
131155
->whereNotIn('status', Process::NOT_ASSIGNABLE_USER_STATUS)
132156
->whereIn('id', $chunk)
133157
->pluck('id')->toArray();
134-
$assignedUsers = array_merge($assignedUsers,$activeUsers);
158+
$assignedUsers = array_merge($assignedUsers, $activeUsers);
135159
}
136160

137161
return $assignedUsers;

ProcessMaker/Jobs/ErrorHandling.php

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,10 +211,59 @@ public function setDefaultsFromDataSourceConfig(array $config)
211211
public static function convertResponseToException($result)
212212
{
213213
if ($result['status'] === 'error') {
214-
if (str_starts_with($result['message'], 'Command exceeded timeout of')) {
215-
throw new ScriptTimeoutException($result['message']);
214+
$rawMessage = $result['message'] ?? '';
215+
if (str_starts_with((string) $rawMessage, 'Command exceeded timeout of')) {
216+
throw new ScriptTimeoutException((string) $rawMessage);
217+
}
218+
219+
$message = self::extractScriptErrorMessage($result);
220+
221+
if (empty($message)) {
222+
$message = $rawMessage ?: 'Script execution failed with unknown error';
223+
}
224+
225+
throw new ScriptException($message);
226+
}
227+
}
228+
229+
/**
230+
* Extract a concise error message from the microservice response.
231+
*/
232+
private static function extractScriptErrorMessage(array $result): string
233+
{
234+
$candidates = [
235+
$result['output']['error'] ?? null,
236+
$result['output']['exception'] ?? null,
237+
$result['output']['stderr'] ?? null,
238+
$result['output']['stdout'] ?? null,
239+
$result['message'] ?? null,
240+
];
241+
242+
foreach ($candidates as $candidate) {
243+
if (is_string($candidate) || is_numeric($candidate)) {
244+
$short = self::shortenMessage((string) $candidate);
245+
if (!empty($short)) {
246+
return $short;
247+
}
216248
}
217-
throw new ScriptException(json_encode($result, JSON_PRETTY_PRINT));
218249
}
250+
251+
return '';
252+
}
253+
254+
/**
255+
* Keep only the first line of the error and limit its length to avoid noisy traces.
256+
*/
257+
private static function shortenMessage(string $message): string
258+
{
259+
$firstLine = strtok($message, "\n");
260+
$firstLine = $firstLine === false ? $message : $firstLine;
261+
$trimmed = trim($firstLine);
262+
263+
if (strlen($trimmed) > 400) {
264+
return substr($trimmed, 0, 400) . '';
265+
}
266+
267+
return $trimmed;
219268
}
220269
}

ProcessMaker/Models/ProcessRequestToken.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -991,6 +991,60 @@ public function getAssignmentRule()
991991
return $assignment;
992992
}
993993

994+
/**
995+
* Get user IDs from process variables for task assignment.
996+
*
997+
* Extracts user IDs and group IDs from form data based on the activity's
998+
* assignedUsers and assignedGroups properties. Retrieves all users from
999+
* specified groups (including subgroups recursively) and combines them
1000+
* with directly assigned users.
1001+
*
1002+
* Used when assignment rule is 'process_variable'.
1003+
*
1004+
* @param array $form_data Form data containing process variable values.
1005+
* Keys must match activity's assignedUsers and
1006+
* assignedGroups properties. Values must be arrays.
1007+
*
1008+
* @return array Unique numeric user IDs (direct users + users from groups).
1009+
*/
1010+
public function getUsersFromProcessVariable(array $form_data)
1011+
{
1012+
$activity = $this->getBpmnDefinition()->getBpmnElementInstance();
1013+
$assignedUsers = $activity->getProperty('assignedUsers', null);
1014+
$assignedGroups = $activity->getProperty('assignedGroups', null);
1015+
1016+
$usersIds = [];
1017+
$groupsIds = [];
1018+
1019+
// Validate and get user IDs from form_data
1020+
if ($assignedUsers && isset($form_data[$assignedUsers]) && is_array($form_data[$assignedUsers])) {
1021+
$usersIds = $form_data[$assignedUsers];
1022+
}
1023+
1024+
// Validate and get group IDs from form_data
1025+
if ($assignedGroups && isset($form_data[$assignedGroups]) && is_array($form_data[$assignedGroups])) {
1026+
$groupsIds = $form_data[$assignedGroups];
1027+
}
1028+
1029+
// Get users from groups using the Process model method
1030+
$usersFromGroups = [];
1031+
if (!empty($groupsIds) && $this->process) {
1032+
// Use the getConsolidatedUsers method from the Process model
1033+
// This method gets users from groups including subgroups recursively
1034+
$this->process->getConsolidatedUsers($groupsIds, $usersFromGroups);
1035+
}
1036+
1037+
// Combine direct users with users from groups
1038+
$allUserIds = array_unique(array_merge($usersIds, $usersFromGroups));
1039+
1040+
// Convert to numeric array and filter valid values
1041+
$allUserIds = array_values(array_filter($allUserIds, function ($id) {
1042+
return !empty($id) && is_numeric($id) && $id > 0;
1043+
}));
1044+
1045+
return $allUserIds;
1046+
}
1047+
9941048
/**
9951049
* Get the assignees for the token.
9961050
*

ProcessMaker/Models/ScriptDockerNayraTrait.php

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ trait ScriptDockerNayraTrait
2323
{
2424

2525
private $schema = 'http';
26-
public static $nayraPort = 8080;
2726

2827
/**
2928
* Execute the script task using Nayra Docker.
@@ -82,7 +81,7 @@ public function handleNayraDocker(string $code, array $data, array $config, $tim
8281
private function getNayraInstanceUrl()
8382
{
8483
$servers = self::getNayraAddresses();
85-
return $this->schema . '://' . $servers[0] . ':' . static::$nayraPort;
84+
return $this->schema . '://' . $servers[0] . ':' . $this->getNayraPort();
8685
}
8786

8887
private function getDockerLogs($instanceName)
@@ -131,11 +130,14 @@ private function bringUpNayra($restart = false)
131130
if ($status) {
132131
$this->bringUpNayraContainer();
133132
} else {
134-
133+
$isHost = config('app.nayra_docker_network') === 'host';
134+
$portMapping = $isHost ? '-e PORT=' . $this->getNayraPort() . ' ' : '-p ' . $this->getNayraPort() . ':8080 ';
135135
exec($docker . " stop {$instanceName}_nayra 2>&1 || true");
136136
exec($docker . " rm {$instanceName}_nayra 2>&1 || true");
137137
exec(
138-
$docker . ' run -d --name ' . $instanceName . '_nayra '
138+
$docker . ' run -d '
139+
. ($this->getNayraPort() !== 8080 ? $portMapping : '')
140+
. '--name ' . $instanceName . '_nayra '
139141
. (config('app.nayra_docker_network')
140142
? '--network=' . config('app.nayra_docker_network') . ' '
141143
: '')
@@ -322,4 +324,9 @@ public static function initNayraPhpUnitTest()
322324
}
323325
}
324326
}
327+
328+
private function getNayraPort()
329+
{
330+
return config('app.nayra_port', 8080);
331+
}
325332
}

0 commit comments

Comments
 (0)