Skip to content

Commit d1f4334

Browse files
committed
Merge branch 'develop' into FOUR-28426
2 parents c3bc43c + 01ad702 commit d1f4334

File tree

8 files changed

+253
-103
lines changed

8 files changed

+253
-103
lines changed

ProcessMaker/Jobs/RefreshArtisanCaches.php

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -23,30 +23,7 @@ class RefreshArtisanCaches implements ShouldQueue
2323
*/
2424
public function handle()
2525
{
26-
// Skip in testing environment because this reconnects the database
27-
// meaning we loose transactions, and sets the console output verbosity
28-
// to quiet so we loose expectsOutput assertions.
29-
if (app()->environment('testing')) {
30-
return;
31-
}
32-
33-
$options = [
34-
'--no-interaction' => true,
35-
'--quiet' => true,
36-
];
37-
38-
if (app()->configurationIsCached()) {
39-
// Run in a separate process to avoid the tenant being set.
40-
// We do not use a tenant-specific config cache file.
41-
Process::path(base_path())
42-
->env(['TENANT' => false, 'APP_URL' => false])
43-
->run(Application::formatCommandString('config:cache'))->throw();
44-
} else {
45-
Artisan::call('queue:restart', $options);
46-
47-
// We call this manually here since this job is dispatched
48-
// automatically when the config *is* cached
49-
RestartMessageConsumers::dispatchSync();
50-
}
26+
// Do not rebuild the cache and restart the queue any more.
27+
// This is no longer needed.
5128
}
5229
}

ProcessMaker/Models/CallActivity.php

Lines changed: 60 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -97,22 +97,10 @@ protected function completeSubprocess(TokenInterface $token, ExecutionInstanceIn
9797
$store = $closedInstance->getDataStore();
9898
$allData = $store->getData();
9999

100-
// Determine which data should be merged back from the subprocess.
101-
$updatedKeys = method_exists($store, 'getUpdated')
102-
? $store->getUpdated()
103-
: null;
104-
105-
if ($updatedKeys === null) {
106-
// Legacy behavior or no tracking available: copy all data.
107-
$data = $allData;
108-
} elseif ($updatedKeys === []) {
109-
// Nothing was updated in the subprocess: do not merge anything.
110-
$data = [];
111-
} else {
112-
// Merge only the explicitly updated keys.
113-
$updatedKeys = array_values((array) $updatedKeys);
114-
$data = array_intersect_key($allData, array_flip($updatedKeys));
115-
}
100+
$data = $this->resolveUpdatedData($store, $allData);
101+
$parentData = $token->getInstance()->getDataStore()->getData();
102+
$data = $this->mergeNewKeys($data, $allData, $parentData);
103+
$data = $this->mergeChangedKeys($data, $allData, $parentData);
116104

117105
$dataManager = new DataManager();
118106
$dataManager->updateData($token, $data);
@@ -125,6 +113,62 @@ protected function completeSubprocess(TokenInterface $token, ExecutionInstanceIn
125113
return $this;
126114
}
127115

116+
/**
117+
* Decide which data from the subprocess should be merged based on updated keys.
118+
*/
119+
protected function resolveUpdatedData($store, array $allData): array
120+
{
121+
$updatedKeys = method_exists($store, 'getUpdated')
122+
? $store->getUpdated()
123+
: null;
124+
125+
if ($updatedKeys === null) {
126+
return $allData;
127+
}
128+
129+
if ($updatedKeys === []) {
130+
return [];
131+
}
132+
133+
$updatedKeys = array_values((array) $updatedKeys);
134+
135+
return array_intersect_key($allData, array_flip($updatedKeys));
136+
}
137+
138+
/**
139+
* Merge keys that exist only in the subprocess data.
140+
*/
141+
protected function mergeNewKeys(array $data, array $allData, array $parentData): array
142+
{
143+
$newKeys = array_diff(array_keys($allData), array_keys($parentData));
144+
if (empty($newKeys)) {
145+
return $data;
146+
}
147+
148+
return $data + array_intersect_key($allData, array_flip($newKeys));
149+
}
150+
151+
/**
152+
* Merge keys that changed in the subprocess but may not have been tracked.
153+
*/
154+
protected function mergeChangedKeys(array $data, array $allData, array $parentData): array
155+
{
156+
$changedKeys = [];
157+
foreach ($allData as $key => $value) {
158+
if (array_key_exists($key, $parentData) && $parentData[$key] !== $value) {
159+
$changedKeys[] = $key;
160+
}
161+
}
162+
163+
if (empty($changedKeys)) {
164+
return $data;
165+
}
166+
167+
$pendingKeys = array_diff($changedKeys, array_keys($data));
168+
169+
return $data + array_intersect_key($allData, array_flip($pendingKeys));
170+
}
171+
128172
/**
129173
* Catch a subprocess error
130174
*

ProcessMaker/Models/Setting.php

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
use Carbon\Carbon;
66
use Illuminate\Database\Eloquent\Model;
77
use Illuminate\Support\Facades\DB;
8+
use Illuminate\Support\Facades\Redis;
9+
use Illuminate\Support\Facades\Schema;
810
use Illuminate\Validation\Rule;
911
use Log;
1012
use ProcessMaker\Cache\Settings\SettingCacheFactory;
@@ -73,6 +75,12 @@ class Setting extends ProcessMakerModel implements HasMedia, PrometheusMetricInt
7375

7476
public const SESSION_CONTROL_GROUP = 'Session Control';
7577

78+
private static bool $redisAvailable = false;
79+
80+
private static bool $settingsTableExists = false;
81+
82+
private static bool $readyToUseSettingsDatabase = false;
83+
7684
/**
7785
* The attributes that aren't mass assignable.
7886
*
@@ -150,6 +158,10 @@ public static function messages()
150158
*/
151159
public static function byKey(string $key)
152160
{
161+
if (!self::readyToUseSettingsDatabase()) {
162+
return null;
163+
}
164+
153165
$settingCache = SettingCacheFactory::getSettingsCache();
154166
$settingKey = $settingCache->createKey([
155167
'key' => $key,
@@ -520,4 +532,51 @@ public function getPrometheusMetricLabel(): string
520532
{
521533
return 'settings.' . $this->key;
522534
}
535+
536+
public static function readyToUseSettingsDatabase()
537+
{
538+
if (!self::$readyToUseSettingsDatabase) {
539+
self::$readyToUseSettingsDatabase =
540+
app('tenant-resolved') &&
541+
self::databaseAvailable() &&
542+
self::redisAvailable() &&
543+
self::settingsTableExists();
544+
}
545+
546+
return self::$readyToUseSettingsDatabase;
547+
}
548+
549+
private static function databaseAvailable()
550+
{
551+
try {
552+
DB::connection()->getPdo();
553+
554+
return true;
555+
} catch (\PDOException $e) {
556+
return false;
557+
}
558+
}
559+
560+
private static function redisAvailable()
561+
{
562+
if (!self::$redisAvailable) {
563+
try {
564+
Redis::connection()->ping();
565+
self::$redisAvailable = true;
566+
} catch (\Exception $e) {
567+
self::$redisAvailable = false;
568+
}
569+
}
570+
571+
return self::$redisAvailable;
572+
}
573+
574+
private static function settingsTableExists()
575+
{
576+
if (!self::$settingsTableExists) {
577+
self::$settingsTableExists = Schema::hasTable('settings');
578+
}
579+
580+
return self::$settingsTableExists;
581+
}
523582
}

ProcessMaker/Providers/ProcessMakerServiceProvider.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Illuminate\Support\Arr;
1717
use Illuminate\Support\Env;
1818
use Illuminate\Support\Facades;
19+
use Illuminate\Support\Facades\Artisan;
1920
use Illuminate\Support\Facades\Context;
2021
use Illuminate\Support\Facades\DB;
2122
use Illuminate\Support\Facades\Log;
@@ -99,6 +100,9 @@ public function boot(): void
99100
parent::boot();
100101

101102
Route::pushMiddlewareToGroup('api', HandleEtag::class);
103+
104+
$this->checkConfigCache();
105+
102106
// Hook after service providers boot
103107
self::$bootTime = (microtime(true) - self::$bootStart) * 1000; // Convert to milliseconds
104108
}
@@ -577,4 +581,22 @@ private static function actuallyRunningInConsole(): bool
577581
{
578582
return PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg';
579583
}
584+
585+
/**
586+
* Ensure that config:cache is not run for tenant instances.
587+
*/
588+
private function checkConfigCache(): void
589+
{
590+
// Only if app is running in console
591+
if (!app()->runningInConsole()) {
592+
return;
593+
}
594+
595+
// Safety check to prevent config:cache from being run for tenant instances.
596+
if (config('app.multitenancy') && app('currentTenant')) {
597+
Artisan::command('config:cache', function () {
598+
throw new \Exception('Cannot cache config for tenant instance. Must be run from landlord instance.');
599+
});
600+
}
601+
}
580602
}

ProcessMaker/Repositories/SettingsConfigRepository.php

Lines changed: 1 addition & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,10 @@
44

55
use Illuminate\Config\Repository;
66
use Illuminate\Support\Arr;
7-
use Illuminate\Support\Facades\DB;
8-
use Illuminate\Support\Facades\Redis;
9-
use Illuminate\Support\Facades\Schema;
107
use ProcessMaker\Models\Setting;
118

129
class SettingsConfigRepository extends Repository
1310
{
14-
private bool $redisAvailable = false;
15-
16-
private bool $settingsTableExists = false;
17-
18-
private bool $readyToUseSettingsDatabase = false;
19-
2011
/**
2112
* Determine if the given configuration value exists.
2213
*
@@ -86,7 +77,7 @@ public function getMany($keys)
8677

8778
private function getFromSettings($key)
8879
{
89-
if (!$this->readyToUseSettingsDatabase()) {
80+
if (!Setting::readyToUseSettingsDatabase()) {
9081
return null;
9182
}
9283

@@ -113,51 +104,4 @@ private function getFromSettings($key)
113104

114105
return null;
115106
}
116-
117-
private function readyToUseSettingsDatabase()
118-
{
119-
if (!$this->readyToUseSettingsDatabase) {
120-
$this->readyToUseSettingsDatabase =
121-
app('tenant-resolved') &&
122-
$this->databaseAvailable() &&
123-
$this->redisAvailable() &&
124-
$this->settingsTableExists();
125-
}
126-
127-
return $this->readyToUseSettingsDatabase;
128-
}
129-
130-
private function databaseAvailable()
131-
{
132-
try {
133-
DB::connection()->getPdo();
134-
135-
return true;
136-
} catch (\PDOException $e) {
137-
return false;
138-
}
139-
}
140-
141-
private function redisAvailable()
142-
{
143-
if (!$this->redisAvailable) {
144-
try {
145-
Redis::connection()->ping();
146-
$this->redisAvailable = true;
147-
} catch (\Exception $e) {
148-
$this->redisAvailable = false;
149-
}
150-
}
151-
152-
return $this->redisAvailable;
153-
}
154-
155-
private function settingsTableExists()
156-
{
157-
if (!$this->settingsTableExists) {
158-
$this->settingsTableExists = Schema::hasTable('settings');
159-
}
160-
161-
return $this->settingsTableExists;
162-
}
163107
}

composer.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "processmaker/processmaker",
3-
"version": "4.15.11",
3+
"version": "4.15.11+mt",
44
"description": "BPM PHP Software",
55
"keywords": [
66
"php bpm processmaker"
@@ -109,7 +109,7 @@
109109
"Gmail"
110110
],
111111
"processmaker": {
112-
"build": "0d7c4cbe",
112+
"build": "1a91bb61",
113113
"cicd-enabled": true,
114114
"custom": {
115115
"package-ellucian-ethos": "1.19.7",
@@ -149,7 +149,7 @@
149149
"connector-docusign": "1.11.0",
150150
"connector-idp": "1.14.0",
151151
"connector-pdf-print": "1.23.1",
152-
"connector-send-email": "1.32.10",
152+
"connector-send-email": "1.32.13",
153153
"connector-slack": "1.9.3",
154154
"docker-executor-node-ssr": "1.7.2",
155155
"package-ab-testing": "1.4.0",

composer.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)