Skip to content

Commit 4d51989

Browse files
committed
Merge remote-tracking branch 'origin/release-2024-fall' into bugfix/FOUR-19638-fall
2 parents ce9c655 + 6ed946d commit 4d51989

36 files changed

+3291
-2487
lines changed

ProcessMaker/Http/Controllers/Api/DevLinkController.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Illuminate\Validation\Rule;
77
use ProcessMaker\Http\Controllers\Controller;
88
use ProcessMaker\Http\Resources\ApiCollection;
9+
use ProcessMaker\Jobs\DevLinkInstall;
910
use ProcessMaker\Models\Bundle;
1011
use ProcessMaker\Models\DevLink;
1112
use ProcessMaker\Models\Setting;
@@ -153,9 +154,14 @@ public function deleteBundle(Bundle $bundle)
153154
$bundle->delete();
154155
}
155156

156-
public function installRemoteBundle(DevLink $devLink, $remoteBundleId)
157+
public function installRemoteBundle(Request $request, DevLink $devLink, $remoteBundleId)
157158
{
158-
return $devLink->installRemoteBundle($remoteBundleId);
159+
DevLinkInstall::dispatch(
160+
$request->user()->id,
161+
$devLink->id,
162+
$remoteBundleId,
163+
'update'
164+
);
159165
}
160166

161167
public function exportLocalBundle(Bundle $bundle)

ProcessMaker/Http/Controllers/Api/UserConfigurationController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class UserConfigurationController extends Controller
1717
'isMenuCollapse' => true,
1818
],
1919
'cases' => [
20-
'isMenuCollapse' => true,
20+
'isMenuCollapse' => false,
2121
],
2222
'requests' => [
2323
'isMenuCollapse' => true,

ProcessMaker/ImportExport/Importer.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ public function doImport($existingAssetInDatabase = null, $importingFromTemplate
4848

4949
$count = count(Arr::where($this->manifest->all(), fn ($exporter) => $exporter->mode !== 'discard'));
5050
$this->logger->log("Importing {$count} assets");
51-
foreach ($this->manifest->all() as $exporter) {
51+
foreach ($this->manifest->all() as $uuid => $exporter) {
52+
$this->logger->setStatus('saving', $uuid);
5253
if ($exporter->mode !== 'discard') {
5354
$this->logger->log('Importing ' . get_class($exporter->model));
5455
if ($exporter->disableEventsWhenImporting) {
@@ -66,7 +67,8 @@ public function doImport($existingAssetInDatabase = null, $importingFromTemplate
6667
Schema::enableForeignKeyConstraints();
6768

6869
// Now, run the import method in each Exporter class
69-
foreach ($this->manifest->all() as $exporter) {
70+
foreach ($this->manifest->all() as $uuid => $exporter) {
71+
$this->logger->setStatus('associating', $uuid);
7072
if ($exporter->mode !== 'discard') {
7173
$this->logger->log('Associating ' . get_class($exporter->model));
7274
$exporter->runImport($existingAssetInDatabase, $importingFromTemplate);

ProcessMaker/ImportExport/Logger.php

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ class Logger
1616

1717
private $warnings = [];
1818

19+
private int $totalSteps = 1;
20+
21+
private int $currentStep = 1;
22+
1923
public function __construct($userId = null)
2024
{
2125
$this->pid = getmypid();
@@ -51,9 +55,14 @@ public function error($message)
5155
$this->dispatch('error', $message);
5256
}
5357

58+
public function status($message)
59+
{
60+
$this->dispatch('status', $message);
61+
}
62+
5463
public function exception($e)
5564
{
56-
$this->dispatch('error', get_class($e) . ': ' . $e->getMessage());
65+
$this->dispatch('error', get_class($e) . ': ' . $e->getMessage() . ' ' . $e->getTraceAsString());
5766
}
5867

5968
private function dispatch($type, $message, $additionalParams = [])
@@ -71,11 +80,6 @@ public function addWarning($message)
7180
$this->warnings[] = $message;
7281
}
7382

74-
public function getWarnings()
75-
{
76-
return $this->warnings;
77-
}
78-
7983
private function logToFile($type, $message, $additionalParams = [])
8084
{
8185
$params = '';
@@ -85,4 +89,29 @@ private function logToFile($type, $message, $additionalParams = [])
8589
$datetime = date('Y-m-d H:i:s');
8690
Storage::append(ImportV2::LOG_PATH, "[$this->pid] [$this->userId] [$datetime] [$type] $message" . $params);
8791
}
92+
93+
public function setSteps($payloads)
94+
{
95+
$this->currentStep = 0;
96+
$this->totalSteps = 0;
97+
98+
foreach ($payloads as $payload) {
99+
// Multiply by 2 since each has a "saving" and an "associating" stage.
100+
$this->totalSteps += count($payload['export']) * 2;
101+
}
102+
}
103+
104+
public function setStatus($type, $item = '')
105+
{
106+
if ($type === 'done') {
107+
$this->dispatch('status', 'done', $this->warnings);
108+
109+
return;
110+
}
111+
112+
$this->status(ucfirst($type) . ' ' . $item);
113+
$this->currentStep++;
114+
115+
$this->dispatch('progress', round(($this->currentStep / $this->totalSteps) * 100));
116+
}
88117
}

ProcessMaker/Jobs/DevLinkInstall.php

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
namespace ProcessMaker\Jobs;
4+
5+
use Illuminate\Bus\Queueable;
6+
use Illuminate\Contracts\Queue\ShouldQueue;
7+
use Illuminate\Foundation\Bus\Dispatchable;
8+
use Illuminate\Queue\InteractsWithQueue;
9+
use Illuminate\Queue\SerializesModels;
10+
use Illuminate\Support\Facades\Cache;
11+
use ProcessMaker\ImportExport\Logger;
12+
use ProcessMaker\Jobs\ImportV2;
13+
use ProcessMaker\Models\DevLink;
14+
use Throwable;
15+
16+
class DevLinkInstall implements ShouldQueue
17+
{
18+
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
19+
20+
public $tries = 1;
21+
22+
public $maxExceptions = 1;
23+
24+
public $devLink = null;
25+
26+
public function __construct(
27+
public int $userId,
28+
public int $devLinkId,
29+
public int $bundleId,
30+
public string $importMode = 'update',
31+
) {
32+
}
33+
34+
/**
35+
* Execute the job.
36+
*/
37+
public function handle(): void
38+
{
39+
$this->devLink = DevLink::findOrFail($this->devLinkId);
40+
$this->devLink->logger = new Logger($this->userId);
41+
42+
$lock = Cache::lock(ImportV2::CACHE_LOCK_KEY, ImportV2::RELEASE_LOCK_AFTER);
43+
44+
if ($lock->get()) {
45+
$this->devLink->logger->clear();
46+
$this->devLink->installRemoteBundle($this->bundleId);
47+
$lock->release();
48+
} else {
49+
// Don't throw exception because that will unlock the running job
50+
$this->devLink->logger->error('Already running!');
51+
}
52+
}
53+
54+
public function failed(Throwable $exception): void
55+
{
56+
(new Logger($this->userId))->exception($exception);
57+
58+
// Unlock the job
59+
// We can't use $this->lock->release() here because this is run in a new instance
60+
Cache::lock(ImportV2::CACHE_LOCK_KEY)->forceRelease();
61+
}
62+
}

ProcessMaker/Managers/PackageManager.php

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -54,19 +54,29 @@ public function remove($name)
5454
unset($this->packages[$name]);
5555
}
5656

57+
/**
58+
* Get all json translations registered from packages
59+
*
60+
* @return array
61+
*/
62+
public function getJsonTranslationsRegistered() : array
63+
{
64+
return app()->translator->getLoader()->jsonPaths();
65+
}
66+
5767
/**
5868
* Look into current packages and create a language file for each one
59-
*
69+
*
6070
* @param $code The language code to create a language file of
6171
*/
6272
public function createLanguageFile($code)
6373
{
6474
// Get current packages
65-
$packages = app()->translator->getLoader()->jsonPaths();
75+
$packages = $this->getJsonTranslationsRegistered();
6676

6777
foreach ($packages as $package) {
6878
if (!file_exists("{$package}/en.json")) {
69-
return ;
79+
return;
7080
} else {
7181
// If language does not exist, clone en.json without values
7282
if (!file_exists("{$package}/{$code}.json")) {
@@ -78,8 +88,8 @@ public function createLanguageFile($code)
7888
$value = '';
7989
}
8090

81-
// Get empty file with only keys, empty values
82-
$baseFile = json_encode($data);
91+
// Get empty file with only keys, empty values
92+
$baseFile = json_encode($data, JSON_PRETTY_PRINT);
8393

8494
// Create file in package
8595
file_put_contents("{$package}/{$code}.json", $baseFile);
@@ -90,20 +100,20 @@ public function createLanguageFile($code)
90100

91101
/**
92102
* Delete a language file form all currently installed packages
93-
*
94-
* @param $code The language code of which to delete the language file
103+
*
104+
* @param $code The language code of which to delete the language file
95105
*/
96106
public function deleteLanguageFile($code)
97107
{
98-
// Get current packages
99-
$packages = app()->translator->getLoader()->jsonPaths();
108+
// Get current packages
109+
$packages = $this->getJsonTranslationsRegistered();
100110

101-
foreach ($packages as $package) {
111+
foreach ($packages as $package) {
102112
// Check if file exists in package
103113
if (File::exists("{$package}/{$code}.json")) {
104114
// Delete file
105115
File::delete("{$package}/{$code}.json");
106116
}
107117
}
108118
}
109-
}
119+
}

ProcessMaker/Models/DevLink.php

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,12 @@ public function remoteAssetsListing($request)
102102

103103
public function installRemoteBundle($bundleId)
104104
{
105+
if (!$this->logger) {
106+
$this->logger = new Logger();
107+
}
108+
109+
$this->logger->status(__('Downloading bundle from remote instance'));
110+
105111
$bundleInfo = $this->client()->get(
106112
route('api.devlink.local-bundle', ['bundle' => $bundleId], false)
107113
)->json();
@@ -118,34 +124,29 @@ public function installRemoteBundle($bundleId)
118124
[
119125
'name' => $bundleInfo['name'],
120126
'published' => $bundleInfo['published'],
121-
'locked' => $bundleInfo['locked'],
122127
'version' => $bundleInfo['version'],
123128
]
124129
);
125130

131+
$this->logger->status('Installing bundle on the this instance');
132+
$this->logger->setSteps($bundleExport['payloads']);
133+
126134
$assets = [];
127135
foreach ($bundleExport['payloads'] as $payload) {
128136
$assets[] = $this->import($payload);
129137
}
130138

139+
$this->logger->status('Syncing bundle assets');
131140
$bundle->syncAssets($assets);
132141

133-
return [
134-
'warnings_devlink' => $this->logger->getWarnings(),
135-
];
142+
$this->logger->setStatus('done');
136143
}
137144

138145
private function import(array $payload)
139146
{
140-
if (!$this->logger) {
141-
$this->logger = new Logger();
142-
}
143-
144147
$importer = new Importer($payload, new Options([]), $this->logger);
145148
$manifest = $importer->doImport();
146149

147-
$manifest[$payload['root']]->model['warnings_devlink'] = $importer->logger->getWarnings();
148-
149150
return $manifest[$payload['root']]->model;
150151
}
151152

ProcessMaker/Repositories/CaseRepository.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ public function update(ExecutionInstanceInterface $instance, TokenInterface $tok
117117
]);
118118

119119
$this->case->case_title = $instance->case_title;
120+
$this->case->case_title_formatted = $instance->case_title_formatted;
120121
$this->case->case_status = $instance->status === CaseStatusConstants::ACTIVE ? CaseStatusConstants::IN_PROGRESS : $instance->status;
121122
$this->case->request_tokens = CaseUtils::storeRequestTokens($this->case->request_tokens, $token->getKey());
122123
$this->case->tasks = CaseUtils::storeTasks($this->case->tasks, $taskData);

ProcessMaker/Repositories/CaseUtils.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,12 @@ public static function storeRequestTokens(Collection $requestTokens, ?int $token
123123
*/
124124
public static function storeTasks(Collection $tasks, ?array $taskData = []): Collection
125125
{
126-
if (in_array($taskData['element_type'], self::ALLOWED_ELEMENT_TYPES) && !empty($taskData) && array_key_exists('id', $taskData) && array_key_exists('element_id', $taskData) && array_key_exists('name', $taskData) && array_key_exists('process_id', $taskData)) {
126+
$requiredKeys = ['id', 'element_id', 'name', 'process_id', 'element_type'];
127+
128+
if (
129+
!empty($taskData) && !array_diff($requiredKeys, array_keys($taskData))
130+
&& in_array($taskData['element_type'], self::ALLOWED_ELEMENT_TYPES)
131+
) {
127132
unset($taskData['element_type']);
128133
$tasks->push($taskData);
129134
}

resources/js/admin/devlink/components/AssetListing.vue

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,20 @@ const dateFormatter = (value) => {
2323
const fields = [
2424
{
2525
key: 'id',
26-
label: 'ID'
26+
label: vue.$t('ID'),
2727
},
2828
{
2929
key: typeConfig?.nameField || 'name',
30-
label: 'Name'
30+
label: vue.$t('Name'),
3131
},
3232
{
3333
key: 'created_at',
34-
label: 'Created',
34+
label: vue.$t('Created'),
3535
formatter: dateFormatter,
3636
},
3737
{
3838
key: 'updated_at',
39-
label: 'Last Modified',
39+
label: vue.$t('Last Modified'),
4040
formatter: dateFormatter,
4141
},
4242
{
@@ -54,7 +54,10 @@ const closeModal = () => {
5454
};
5555
5656
const install = (asset) => {
57-
vue.$bvModal.msgBoxConfirm('Are you sure you want to install this asset onto this instance?').then((confirm) => {
57+
vue.$bvModal.msgBoxConfirm(vue.$t('Are you sure you want to install this asset onto this instance?'), {
58+
okTitle: vue.$t('Ok'),
59+
cancelTitle: vue.$t('Cancel')
60+
}).then((confirm) => {
5861
if (confirm) {
5962
const params = {
6063
class: typeConfig.class,
@@ -63,7 +66,7 @@ const install = (asset) => {
6366
ProcessMaker.apiClient
6467
.post(`/devlink/${route.params.id}/install-remote-asset`, params)
6568
.then((response) => {
66-
window.ProcessMaker.alert('Asset successfully installed', "success");
69+
window.ProcessMaker.alert(vue.$t('Asset successfully installed'), "success");
6770
warnings.value = response.data.warnings_devlink;
6871
if (warnings.value.length > 0) {
6972
showModal();

0 commit comments

Comments
 (0)