Skip to content

Commit 892bc2f

Browse files
committed
feat(FOUR-31979): merge develop into the performance feature branch
1 parent 1444838 commit 892bc2f

30 files changed

Lines changed: 3301 additions & 3156 deletions

.github/dependabot.yml

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# PM4 package: Laravel is provided by the host app; composer.json is only for this package's own PHP code/deps.
2+
# JS build: Vue 2 + Laravel Mix (manifest: package.json). Built assets under public/js/ are not scanned by Dependabot.
3+
#
4+
# Policy: NO routine version-update PRs (open-pull-requests-limit: 0).
5+
# Security/CVE PRs are handled by Dependabot security updates (org Settings → Code security).
6+
# Security PRs are batched into one PR per ecosystem (patch/minor).
7+
# Major security PRs will still open if no patch/minor fix exists — treat as manual review.
8+
#
9+
# Vue 2 pin: security fixes requiring Vue 3+ will be suppressed — accepted risk,
10+
# migration not planned. Same applies to vue-loader, vue-template-compiler, @vue/cli.
11+
#
12+
# Webpack pin: develop lockfile pins 5.91.0; Dependabot security PRs may bump to 5.107+.
13+
# 5.106.0 is the last release that still ships SizeFormatHelpers (Laravel Mix compat).
14+
# Block webpack >= 5.107 so batched security PRs keep other bumps without breaking the build.
15+
#
16+
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
17+
version: 2
18+
updates:
19+
- package-ecosystem: npm
20+
directory: /
21+
schedule:
22+
interval: weekly
23+
day: monday
24+
open-pull-requests-limit: 0
25+
ignore:
26+
# If you ever raise `open-pull-requests-limit`, this skips routine major bumps.
27+
# Note: update-types has no effect on security updates.
28+
- dependency-name: "*"
29+
update-types: ["version-update:semver-major"]
30+
- dependency-name: "vue"
31+
versions: [">=3.0.0"] # stay on Vue 2.x — suppresses security PRs requiring v3+ too
32+
- dependency-name: "@vue/cli*"
33+
versions: [">=5.0.0"] # CLI v5+ is Vue 3 era
34+
- dependency-name: "vue-loader"
35+
versions: [">=17.0.0"] # vue-loader v17+ drops Vue 2 support
36+
- dependency-name: "vue-template-compiler"
37+
versions: [">=3.0.0"] # must stay in sync with Vue 2.x
38+
- dependency-name: "webpack"
39+
versions: [">=5.107.0"] # 5.106.0 last with SizeFormatHelpers; block 5.107+ security bumps
40+
groups:
41+
npm-security:
42+
applies-to: security-updates # batches all JS security PRs into one
43+
patterns: # note: update-types has no effect here for security
44+
- "*"
45+
# version suppressions (vue, webpack, etc.) live in top-level `ignore` above — groups do not support `ignore`
46+
47+
- package-ecosystem: composer
48+
directory: /
49+
schedule:
50+
interval: weekly
51+
day: monday
52+
open-pull-requests-limit: 0
53+
ignore:
54+
# If you ever raise `open-pull-requests-limit`, this skips routine major bumps.
55+
# Note: update-types has no effect on security updates.
56+
- dependency-name: "*"
57+
update-types: ["version-update:semver-major"]
58+
groups:
59+
composer-security:
60+
applies-to: security-updates # batches all PHP security PRs into one
61+
patterns:
62+
- "*"
63+

.github/workflows/nightly.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
name: Nightly Vulnerability Scan
2+
3+
on:
4+
schedule:
5+
- cron: '0 0 * * *' # midnight UTC
6+
workflow_dispatch:
7+
8+
jobs:
9+
nightly:
10+
uses: processmaker/.github/.github/workflows/nightly.yml@main
11+
secrets: inherit

ProcessMaker/Http/Controllers/Api/PermissionController.php

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,9 @@
44

55
use Illuminate\Http\Request;
66
use Illuminate\Support\Facades\Auth;
7-
use Illuminate\Support\Facades\Cache;
8-
use ProcessMaker\Events\PermissionChanged;
7+
use Illuminate\Validation\ValidationException;
98
use ProcessMaker\Events\PermissionUpdated;
109
use ProcessMaker\Http\Controllers\Controller;
11-
use ProcessMaker\Http\Resources\ApiCollection;
1210
use ProcessMaker\Models\Group;
1311
use ProcessMaker\Models\Permission;
1412
use ProcessMaker\Models\User;
@@ -30,7 +28,7 @@ class PermissionController extends Controller
3028
*
3129
* @param Request $request
3230
*
33-
* @return Response
31+
* @return \Illuminate\Support\Collection
3432
*/
3533
public function index(Request $request)
3634
{
@@ -44,7 +42,7 @@ public function index(Request $request)
4442
*
4543
* @param Request $request
4644
*
47-
* @return Response
45+
* @return \Illuminate\Http\Response
4846
*
4947
* @OA\Put(
5048
* path="/permissions",
@@ -82,8 +80,22 @@ public function index(Request $request)
8280
*/
8381
public function update(Request $request)
8482
{
83+
$request->validate([
84+
'user_id' => 'required_without:group_id|integer',
85+
'group_id' => 'required_without:user_id|integer',
86+
'permission_names' => 'nullable|array',
87+
]);
88+
89+
if ($request->filled('user_id') && $request->filled('group_id')) {
90+
throw ValidationException::withMessages([
91+
'user_id' => [__('The user_id field cannot be present when group_id is present.')],
92+
'group_id' => [__('The group_id field cannot be present when user_id is present.')],
93+
]);
94+
}
95+
8596
//Obtain the requested user or group
86-
if ($request->input('user_id')) {
97+
if ($request->filled('user_id')) {
98+
$this->authorize('edit-users');
8799
$entity = User::findOrFail($request->input('user_id'));
88100
// Obtain user old Permissions before save
89101
$originalPermissionNames = $entity->permissions()->pluck('name')->toArray();
@@ -98,14 +110,15 @@ public function update(Request $request)
98110
$entity->is_administrator = $isSettingToAdmin;
99111
$entity->save();
100112
}
101-
} elseif ($request->input('group_id')) {
113+
} elseif ($request->filled('group_id')) {
114+
$this->authorize('edit-groups');
102115
$entity = Group::findOrFail($request->input('group_id'));
103116
// Obtain group old Permissions before save
104117
$originalPermissionNames = $entity->permissions()->pluck('name')->toArray();
105118
}
106119

107120
// Obtain the requested permission names for that entity
108-
$requestPermissions = $request->input('permission_names');
121+
$requestPermissions = $request->input('permission_names') ?? [];
109122

110123
// Convert permission names into a collection of Permission models
111124
$permissions = Permission::whereIn('name', $requestPermissions)->get();
@@ -114,7 +127,7 @@ public function update(Request $request)
114127
PermissionUpdated::dispatch(
115128
$requestPermissions,
116129
$originalPermissionNames,
117-
$entity->is_administrator ?: false,
130+
$entity instanceof User ? $entity->is_administrator : false,
118131
$request->input('user_id'),
119132
$request->input('group_id')
120133
);

ProcessMaker/Http/Controllers/Api/ProcessRequestController.php

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
use ProcessMaker\Models\Comment;
3232
use ProcessMaker\Models\ProcessRequest;
3333
use ProcessMaker\Models\ProcessRequestToken;
34+
use ProcessMaker\Models\Screen;
3435
use ProcessMaker\Models\User;
3536
use ProcessMaker\Nayra\Contracts\Bpmn\ActivityInterface;
3637
use ProcessMaker\Nayra\Contracts\Bpmn\CatchEventInterface;
@@ -793,8 +794,8 @@ public function screenRequested(Request $httpRequest, ProcessRequest $request)
793794
$query = ProcessRequestToken::query();
794795
$query->select('id', 'element_id', 'process_id', 'process_request_id', 'data')
795796
->where('process_request_id', $request->id)
796-
->whereNotIn('element_type', ['startEvent', 'end_event', 'scriptTask'])
797-
->where('status', 'CLOSED')
797+
->whereNotIn('element_type', ['end_event', 'scriptTask'])
798+
->whereIn('status', ['CLOSED', 'TRIGGERED'])
798799
->orderBy('completed_at');
799800

800801
$response =
@@ -806,19 +807,23 @@ public function screenRequested(Request $httpRequest, ProcessRequest $request)
806807
$collection = $response->getCollection()
807808
->transform(function ($token): ?object {
808809
$definition = $token->getDefinition();
809-
if (array_key_exists('screenRef', $definition)) {
810-
$screen = $token->getScreenVersion();
811-
if ($screen) {
812-
$dataManager = new DataManager();
813-
$screen->data = $dataManager->getData($token, true);
814-
$screen->screen_id = $screen->id;
815-
$screen->id = $token->id;
816-
817-
return $screen;
810+
if (!array_key_exists('screenRef', $definition)) {
811+
$config = isset($definition['config']) ? json_decode($definition['config']) : null;
812+
$screenRef = (isset($config) && isset($config->web_entry)) ? $config->web_entry->screen_id ?? null : null;
813+
if (!$screenRef) {
814+
return null;
818815
}
819816
}
817+
$screen = $token->getScreenVersion() ?? null;
820818

821-
return null;
819+
if ($screen) {
820+
$dataManager = new DataManager();
821+
$screen->data = $dataManager->getData($token, true);
822+
$screen->screen_id = $screen->id;
823+
$screen->id = $token->id;
824+
}
825+
826+
return $screen;
822827
})
823828
->reject(fn ($item) => $item === null)
824829
->values();

ProcessMaker/Http/Controllers/Api/TaskController.php

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,9 +208,27 @@ public function getTasksByCase(Request $request, User $user = null)
208208
}
209209

210210
// Validate the inputs, including optional ones
211+
$allowedStatuses = ['ACTIVE', 'CLOSED', 'TRIGGERED'];
211212
$request->validate([
212213
'case_number' => 'required|integer',
213-
'status' => 'nullable|string|in:ACTIVE,CLOSED',
214+
'status' => [
215+
'nullable',
216+
'string',
217+
function ($attribute, $value, $fail) use ($allowedStatuses) {
218+
$statuses = array_map(fn ($v) => mb_strtoupper(trim($v)), explode(',', $value));
219+
$statuses = array_filter($statuses);
220+
foreach ($statuses as $status) {
221+
if (!in_array($status, $allowedStatuses)) {
222+
$fail(__('The :attribute must contain only valid statuses: :values. Multiple statuses can be comma-separated.', [
223+
'attribute' => $attribute,
224+
'values' => implode(', ', $allowedStatuses),
225+
]));
226+
227+
return;
228+
}
229+
}
230+
},
231+
],
214232
'order_by' => 'nullable|string|in:id,element_name,due_at,user.lastname,process.name',
215233
'order_direction' => 'nullable|string|in:asc,desc',
216234
'page' => 'nullable|integer|min:1',

ProcessMaker/Http/Controllers/HomeController.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,16 @@ public function redirectToIntended()
6060
return redirect($url)->withCookie(\Cookie::forget('processmaker_intended'));
6161
}
6262

63+
// No intended URL. Honor the tenant's package-dynamic-ui home page
64+
// if it's installed, so admins'configured landing pages are still
65+
// respected, before falling back to /requests.
66+
if (Auth::check() && class_exists(\ProcessMaker\Package\PackageDynamicUI\Models\DynamicUI::class)) {
67+
$homePage = \ProcessMaker\Package\PackageDynamicUI\Models\DynamicUI::getHomePage(Auth::user());
68+
if (!empty($homePage)) {
69+
return redirect($homePage);
70+
}
71+
}
72+
6373
return redirect()->route('requests.index');
6474
}
6575
}

ProcessMaker/Http/Resources/ScreenVersion.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public function toArray($request)
6363
*/
6464
private function setDefaultScreenForNestedScreens(array &$screenVersion): void
6565
{
66-
$configArray = $screenVersion['config'];
66+
$configArray = $screenVersion['config'] ?? [];
6767
foreach ($configArray as $key => $config) {
6868
foreach ($config['items'] as $itemKey => $item) {
6969
if (isset($item['component']) && $item['component'] === 'FormNestedScreen') {

0 commit comments

Comments
 (0)