From 892bc2f8ca3bc2d992c01f12ff3aafee9a83964e Mon Sep 17 00:00:00 2001 From: Rodrigo Date: Thu, 25 Jun 2026 10:45:00 -0400 Subject: [PATCH] feat(FOUR-31979): merge develop into the performance feature branch --- .github/dependabot.yml | 63 + .github/workflows/nightly.yml | 11 + .../Controllers/Api/PermissionController.php | 31 +- .../Api/ProcessRequestController.php | 29 +- .../Http/Controllers/Api/TaskController.php | 20 +- .../Http/Controllers/HomeController.php | 10 + ProcessMaker/Http/Resources/ScreenVersion.php | 2 +- .../ImportExport/Exporters/ScriptExporter.php | 187 +- ProcessMaker/Jobs/RunServiceTask.php | 8 +- .../Managers/TaskSchedulerManager.php | 200 + ProcessMaker/Models/ProcessRequest.php | 3 +- ProcessMaker/Models/ProcessRequestToken.php | 20 +- .../Providers/ProcessMakerServiceProvider.php | 19 + ProcessMaker/ScriptRunners/Base.php | 4 + .../Traits/TaskControllerIndexMethods.php | 20 +- composer.json | 36 +- composer.lock | 1127 +++-- config/l5-swagger.php | 6 +- package-lock.json | 4 +- package.json | 2 +- resources/js/tasks/components/ListMixin.js | 8 +- .../casesDetail/components/CompletedForms.vue | 2 +- routes/api.php | 2 +- routes/web.php | 5 + storage/api-docs/api-docs.json | 4188 +++++++---------- tests/Feature/Api/PermissionsTest.php | 206 +- tests/Feature/Api/TimerStartEventTest.php | 21 + tests/Feature/HomeControllerTest.php | 88 +- .../Managers/TaskSchedulerManagerTest.php | 64 + ..._07_212653_set_user_id_on_oauth_client.php | 71 + 30 files changed, 3301 insertions(+), 3156 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/nightly.yml create mode 100644 upgrades/2026_05_07_212653_set_user_id_on_oauth_client.php diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..d0b7e745dd --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,63 @@ +# PM4 package: Laravel is provided by the host app; composer.json is only for this package's own PHP code/deps. +# JS build: Vue 2 + Laravel Mix (manifest: package.json). Built assets under public/js/ are not scanned by Dependabot. +# +# Policy: NO routine version-update PRs (open-pull-requests-limit: 0). +# Security/CVE PRs are handled by Dependabot security updates (org Settings → Code security). +# Security PRs are batched into one PR per ecosystem (patch/minor). +# Major security PRs will still open if no patch/minor fix exists — treat as manual review. +# +# Vue 2 pin: security fixes requiring Vue 3+ will be suppressed — accepted risk, +# migration not planned. Same applies to vue-loader, vue-template-compiler, @vue/cli. +# +# Webpack pin: develop lockfile pins 5.91.0; Dependabot security PRs may bump to 5.107+. +# 5.106.0 is the last release that still ships SizeFormatHelpers (Laravel Mix compat). +# Block webpack >= 5.107 so batched security PRs keep other bumps without breaking the build. +# +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file +version: 2 +updates: + - package-ecosystem: npm + directory: / + schedule: + interval: weekly + day: monday + open-pull-requests-limit: 0 + ignore: + # If you ever raise `open-pull-requests-limit`, this skips routine major bumps. + # Note: update-types has no effect on security updates. + - dependency-name: "*" + update-types: ["version-update:semver-major"] + - dependency-name: "vue" + versions: [">=3.0.0"] # stay on Vue 2.x — suppresses security PRs requiring v3+ too + - dependency-name: "@vue/cli*" + versions: [">=5.0.0"] # CLI v5+ is Vue 3 era + - dependency-name: "vue-loader" + versions: [">=17.0.0"] # vue-loader v17+ drops Vue 2 support + - dependency-name: "vue-template-compiler" + versions: [">=3.0.0"] # must stay in sync with Vue 2.x + - dependency-name: "webpack" + versions: [">=5.107.0"] # 5.106.0 last with SizeFormatHelpers; block 5.107+ security bumps + groups: + npm-security: + applies-to: security-updates # batches all JS security PRs into one + patterns: # note: update-types has no effect here for security + - "*" + # version suppressions (vue, webpack, etc.) live in top-level `ignore` above — groups do not support `ignore` + + - package-ecosystem: composer + directory: / + schedule: + interval: weekly + day: monday + open-pull-requests-limit: 0 + ignore: + # If you ever raise `open-pull-requests-limit`, this skips routine major bumps. + # Note: update-types has no effect on security updates. + - dependency-name: "*" + update-types: ["version-update:semver-major"] + groups: + composer-security: + applies-to: security-updates # batches all PHP security PRs into one + patterns: + - "*" + \ No newline at end of file diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml new file mode 100644 index 0000000000..8c0a3f2e3c --- /dev/null +++ b/.github/workflows/nightly.yml @@ -0,0 +1,11 @@ +name: Nightly Vulnerability Scan + +on: + schedule: + - cron: '0 0 * * *' # midnight UTC + workflow_dispatch: + +jobs: + nightly: + uses: processmaker/.github/.github/workflows/nightly.yml@main + secrets: inherit diff --git a/ProcessMaker/Http/Controllers/Api/PermissionController.php b/ProcessMaker/Http/Controllers/Api/PermissionController.php index 5fed46c80b..98d34cc722 100644 --- a/ProcessMaker/Http/Controllers/Api/PermissionController.php +++ b/ProcessMaker/Http/Controllers/Api/PermissionController.php @@ -4,11 +4,9 @@ use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; -use Illuminate\Support\Facades\Cache; -use ProcessMaker\Events\PermissionChanged; +use Illuminate\Validation\ValidationException; use ProcessMaker\Events\PermissionUpdated; use ProcessMaker\Http\Controllers\Controller; -use ProcessMaker\Http\Resources\ApiCollection; use ProcessMaker\Models\Group; use ProcessMaker\Models\Permission; use ProcessMaker\Models\User; @@ -30,7 +28,7 @@ class PermissionController extends Controller * * @param Request $request * - * @return Response + * @return \Illuminate\Support\Collection */ public function index(Request $request) { @@ -44,7 +42,7 @@ public function index(Request $request) * * @param Request $request * - * @return Response + * @return \Illuminate\Http\Response * * @OA\Put( * path="/permissions", @@ -82,8 +80,22 @@ public function index(Request $request) */ public function update(Request $request) { + $request->validate([ + 'user_id' => 'required_without:group_id|integer', + 'group_id' => 'required_without:user_id|integer', + 'permission_names' => 'nullable|array', + ]); + + if ($request->filled('user_id') && $request->filled('group_id')) { + throw ValidationException::withMessages([ + 'user_id' => [__('The user_id field cannot be present when group_id is present.')], + 'group_id' => [__('The group_id field cannot be present when user_id is present.')], + ]); + } + //Obtain the requested user or group - if ($request->input('user_id')) { + if ($request->filled('user_id')) { + $this->authorize('edit-users'); $entity = User::findOrFail($request->input('user_id')); // Obtain user old Permissions before save $originalPermissionNames = $entity->permissions()->pluck('name')->toArray(); @@ -98,14 +110,15 @@ public function update(Request $request) $entity->is_administrator = $isSettingToAdmin; $entity->save(); } - } elseif ($request->input('group_id')) { + } elseif ($request->filled('group_id')) { + $this->authorize('edit-groups'); $entity = Group::findOrFail($request->input('group_id')); // Obtain group old Permissions before save $originalPermissionNames = $entity->permissions()->pluck('name')->toArray(); } // Obtain the requested permission names for that entity - $requestPermissions = $request->input('permission_names'); + $requestPermissions = $request->input('permission_names') ?? []; // Convert permission names into a collection of Permission models $permissions = Permission::whereIn('name', $requestPermissions)->get(); @@ -114,7 +127,7 @@ public function update(Request $request) PermissionUpdated::dispatch( $requestPermissions, $originalPermissionNames, - $entity->is_administrator ?: false, + $entity instanceof User ? $entity->is_administrator : false, $request->input('user_id'), $request->input('group_id') ); diff --git a/ProcessMaker/Http/Controllers/Api/ProcessRequestController.php b/ProcessMaker/Http/Controllers/Api/ProcessRequestController.php index 54d50f1f1f..15746d08cd 100644 --- a/ProcessMaker/Http/Controllers/Api/ProcessRequestController.php +++ b/ProcessMaker/Http/Controllers/Api/ProcessRequestController.php @@ -31,6 +31,7 @@ use ProcessMaker\Models\Comment; use ProcessMaker\Models\ProcessRequest; use ProcessMaker\Models\ProcessRequestToken; +use ProcessMaker\Models\Screen; use ProcessMaker\Models\User; use ProcessMaker\Nayra\Contracts\Bpmn\ActivityInterface; use ProcessMaker\Nayra\Contracts\Bpmn\CatchEventInterface; @@ -793,8 +794,8 @@ public function screenRequested(Request $httpRequest, ProcessRequest $request) $query = ProcessRequestToken::query(); $query->select('id', 'element_id', 'process_id', 'process_request_id', 'data') ->where('process_request_id', $request->id) - ->whereNotIn('element_type', ['startEvent', 'end_event', 'scriptTask']) - ->where('status', 'CLOSED') + ->whereNotIn('element_type', ['end_event', 'scriptTask']) + ->whereIn('status', ['CLOSED', 'TRIGGERED']) ->orderBy('completed_at'); $response = @@ -806,19 +807,23 @@ public function screenRequested(Request $httpRequest, ProcessRequest $request) $collection = $response->getCollection() ->transform(function ($token): ?object { $definition = $token->getDefinition(); - if (array_key_exists('screenRef', $definition)) { - $screen = $token->getScreenVersion(); - if ($screen) { - $dataManager = new DataManager(); - $screen->data = $dataManager->getData($token, true); - $screen->screen_id = $screen->id; - $screen->id = $token->id; - - return $screen; + if (!array_key_exists('screenRef', $definition)) { + $config = isset($definition['config']) ? json_decode($definition['config']) : null; + $screenRef = (isset($config) && isset($config->web_entry)) ? $config->web_entry->screen_id ?? null : null; + if (!$screenRef) { + return null; } } + $screen = $token->getScreenVersion() ?? null; - return null; + if ($screen) { + $dataManager = new DataManager(); + $screen->data = $dataManager->getData($token, true); + $screen->screen_id = $screen->id; + $screen->id = $token->id; + } + + return $screen; }) ->reject(fn ($item) => $item === null) ->values(); diff --git a/ProcessMaker/Http/Controllers/Api/TaskController.php b/ProcessMaker/Http/Controllers/Api/TaskController.php index 0679b42853..28844fe451 100644 --- a/ProcessMaker/Http/Controllers/Api/TaskController.php +++ b/ProcessMaker/Http/Controllers/Api/TaskController.php @@ -208,9 +208,27 @@ public function getTasksByCase(Request $request, User $user = null) } // Validate the inputs, including optional ones + $allowedStatuses = ['ACTIVE', 'CLOSED', 'TRIGGERED']; $request->validate([ 'case_number' => 'required|integer', - 'status' => 'nullable|string|in:ACTIVE,CLOSED', + 'status' => [ + 'nullable', + 'string', + function ($attribute, $value, $fail) use ($allowedStatuses) { + $statuses = array_map(fn ($v) => mb_strtoupper(trim($v)), explode(',', $value)); + $statuses = array_filter($statuses); + foreach ($statuses as $status) { + if (!in_array($status, $allowedStatuses)) { + $fail(__('The :attribute must contain only valid statuses: :values. Multiple statuses can be comma-separated.', [ + 'attribute' => $attribute, + 'values' => implode(', ', $allowedStatuses), + ])); + + return; + } + } + }, + ], 'order_by' => 'nullable|string|in:id,element_name,due_at,user.lastname,process.name', 'order_direction' => 'nullable|string|in:asc,desc', 'page' => 'nullable|integer|min:1', diff --git a/ProcessMaker/Http/Controllers/HomeController.php b/ProcessMaker/Http/Controllers/HomeController.php index 201d8c6775..3b211b203c 100644 --- a/ProcessMaker/Http/Controllers/HomeController.php +++ b/ProcessMaker/Http/Controllers/HomeController.php @@ -60,6 +60,16 @@ public function redirectToIntended() return redirect($url)->withCookie(\Cookie::forget('processmaker_intended')); } + // No intended URL. Honor the tenant's package-dynamic-ui home page + // if it's installed, so admins'configured landing pages are still + // respected, before falling back to /requests. + if (Auth::check() && class_exists(\ProcessMaker\Package\PackageDynamicUI\Models\DynamicUI::class)) { + $homePage = \ProcessMaker\Package\PackageDynamicUI\Models\DynamicUI::getHomePage(Auth::user()); + if (!empty($homePage)) { + return redirect($homePage); + } + } + return redirect()->route('requests.index'); } } diff --git a/ProcessMaker/Http/Resources/ScreenVersion.php b/ProcessMaker/Http/Resources/ScreenVersion.php index 31b740aab7..ac288d6ee9 100644 --- a/ProcessMaker/Http/Resources/ScreenVersion.php +++ b/ProcessMaker/Http/Resources/ScreenVersion.php @@ -63,7 +63,7 @@ public function toArray($request) */ private function setDefaultScreenForNestedScreens(array &$screenVersion): void { - $configArray = $screenVersion['config']; + $configArray = $screenVersion['config'] ?? []; foreach ($configArray as $key => $config) { foreach ($config['items'] as $itemKey => $item) { if (isset($item['component']) && $item['component'] === 'FormNestedScreen') { diff --git a/ProcessMaker/ImportExport/Exporters/ScriptExporter.php b/ProcessMaker/ImportExport/Exporters/ScriptExporter.php index 48ef0fabd4..c1083bbca5 100644 --- a/ProcessMaker/ImportExport/Exporters/ScriptExporter.php +++ b/ProcessMaker/ImportExport/Exporters/ScriptExporter.php @@ -2,6 +2,8 @@ namespace ProcessMaker\ImportExport\Exporters; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Log; use ProcessMaker\ImportExport\DependentType; use ProcessMaker\Models\EnvironmentVariable; use ProcessMaker\Models\ScriptCategory; @@ -46,16 +48,19 @@ public function import() : bool $this->model->script_executor_id = $executor->id; } + // Update environment variable references in script code + $this->updateEnvironmentVariableReferences(); + // Pre-save cleanup for data source scripts to prevent constraint violations if ($this->mode === 'update' && $this->model->exists) { // Check if this script has any data source relationships that might cause conflicts - $hasDataSourceRelationships = \DB::table('data_source_scripts') + $hasDataSourceRelationships = DB::table('data_source_scripts') ->where('script_id', $this->model->id) ->exists(); if ($hasDataSourceRelationships) { // Clean up any conflicting records in data_source_scripts - \DB::table('data_source_scripts') + DB::table('data_source_scripts') ->where('script_id', $this->model->id) ->where('id', '!=', $this->model->id) ->delete(); @@ -72,11 +77,185 @@ private function getEnvironmentVariables() : array // Search for environment variable present in the code foreach ($environmentVariables as $variable) { - if (preg_match('/[^a-zA-Z0-9\s]' . $variable->name . '[^a-zA-Z0-9\s]?/', $this->model->code)) { - $environmentVariablesFound[] = $variable; + // Multiple patterns to catch different usage styles + $patterns = [ + // JavaScript patterns + '/\bprocess\.env\.' . preg_quote($variable->name, '/') . '\b/', // process.env.VAR + '/\bprocess\.env\[\s*[\'"]' . preg_quote($variable->name, '/') . '[\'"]\s*\]/', // process.env['VAR'] + + // PHP patterns + '/\bconfig\s*\(\s*[\'"]' . preg_quote($variable->name, '/') . '[\'"]\s*\)/', // config('VAR') + '/\benv\s*\(\s*[\'"]' . preg_quote($variable->name, '/') . '[\'"]\s*\)/', // env('VAR') + '/\$_ENV\s*\[\s*[\'"]' . preg_quote($variable->name, '/') . '[\'"]\s*\]/', // $_ENV['VAR'] + '/\$_SERVER\s*\[\s*[\'"]' . preg_quote($variable->name, '/') . '[\'"]\s*\]/', // $_SERVER['VAR'] + + // Java patterns + '/\bSystem\.getenv\s*\(\s*[\'"]' . preg_quote($variable->name, '/') . '[\'"]\s*\)/', // System.getenv("VAR") + '/\bSystem\.getProperty\s*\(\s*[\'"]' . preg_quote($variable->name, '/') . '[\'"]\s*\)/', // System.getProperty("VAR") + + // C# patterns + '/\bEnvironment\.GetEnvironmentVariable\s*\(\s*[\'"]' . preg_quote($variable->name, '/') . '[\'"]\s*\)/', // Environment.GetEnvironmentVariable("VAR") + '/\bConfigurationManager\.AppSettings\s*\[\s*[\'"]' . preg_quote($variable->name, '/') . '[\'"]\s*\]/', // ConfigurationManager.AppSettings["VAR"] + + // Python patterns + '/\bos\.environ\s*\[\s*[\'"]' . preg_quote($variable->name, '/') . '[\'"]\s*\]/', // os.environ['VAR'] + '/\bos\.environ\.get\s*\(\s*[\'"]' . preg_quote($variable->name, '/') . '[\'"]\s*\)/', // os.environ.get('VAR') + '/\bos\.getenv\s*\(\s*[\'"]' . preg_quote($variable->name, '/') . '[\'"]\s*\)/', // os.getenv('VAR') + + // Additional PHP patterns + '/\bgetenv\s*\(\s*[\'"]' . preg_quote($variable->name, '/') . '[\'"]\s*\)/', // getenv('VAR') + ]; + + foreach ($patterns as $pattern) { + if (preg_match($pattern, $this->model->code)) { + $environmentVariablesFound[] = $variable; + break; + } } } return $environmentVariablesFound; } + + /** + * Update environment variable references in script code when variables are renamed during import + * + * Note: This is a basic implementation. For a complete solution, you would need to: + * 1. Track original variable names during export phase + * 2. Map original names to new names after duplicate handling + * 3. Update all references in the script code + */ + private function updateEnvironmentVariableReferences(): void + { + // Only process in copy mode where variables might be renamed + if ($this->mode !== 'copy') { + return; + } + + // Get the original variables that were detected during export + $originalVariables = $this->getEnvironmentVariables(); + $variableMapping = []; + + // Compare with the imported variables to detect name changes + foreach ($this->getDependents(DependentType::ENVIRONMENT_VARIABLES, true) as $dependent) { + $importedVar = $dependent->model; + + // Find matching original variable by comparing the base name + foreach ($originalVariables as $originalVar) { + $mapped = false; + + // Check if this is a renamed version (has suffix like _1, _2, etc.) + if (preg_match('/^' . preg_quote($originalVar->name) . '_\d+$/', $importedVar->name)) { + $variableMapping[$originalVar->name] = $importedVar->name; + $mapped = true; + } + // Check if it's exactly the same name (no rename needed) + elseif ($originalVar->name === $importedVar->name) { + // Don't add to mapping since no replacement is needed + $mapped = true; + } + // Check if it has other suffixes like _copy, _duplicate, etc. + elseif (preg_match('/^' . preg_quote($originalVar->name) . '_(copy|duplicate|\d+)$/i', $importedVar->name)) { + $variableMapping[$originalVar->name] = $importedVar->name; + $mapped = true; + } + + if ($mapped) { + break; + } + } + } + + // Update script code with new variable names + if (!empty($variableMapping)) { + $updatedCode = $this->model->code; + + foreach ($variableMapping as $oldName => $newName) { + // Update common patterns for environment variable access + $patterns = [ + // JavaScript patterns - process.env.VARIABLE_NAME → process.env.VARIABLE_NAME_1 + '/\bprocess\.env\.' . preg_quote($oldName, '/') . '\b/', + '/\bprocess\.env\[\s*[\'"]' . preg_quote($oldName, '/') . '[\'"]\s*\]/', + '/\bprocess\.env\[\s*"' . preg_quote($oldName, '/') . '"\s*\]/', // Double quotes + + // PHP patterns + // config('VARIABLE_NAME') → config('VARIABLE_NAME_1') + '/\bconfig\s*\(\s*[\'"]' . preg_quote($oldName, '/') . '[\'"]\s*\)/', + // env('VARIABLE_NAME') → env('VARIABLE_NAME_1') + '/\benv\s*\(\s*[\'"]' . preg_quote($oldName, '/') . '[\'"]\s*\)/', + // $_ENV['VARIABLE_NAME'] → $_ENV['VARIABLE_NAME_1'] + '/\$_ENV\s*\[\s*[\'"]' . preg_quote($oldName, '/') . '[\'"]\s*\]/', + // $_SERVER['VARIABLE_NAME'] → $_SERVER['VARIABLE_NAME_1'] + '/\$_SERVER\s*\[\s*[\'"]' . preg_quote($oldName, '/') . '[\'"]\s*\]/', + // getenv('VARIABLE_NAME') → getenv('VARIABLE_NAME_1') + '/\bgetenv\s*\(\s*[\'"]' . preg_quote($oldName, '/') . '[\'"]\s*\)/', + // Config::get('VARIABLE_NAME') → Config::get('VARIABLE_NAME_1') + '/\bConfig\s*::\s*get\s*\(\s*[\'"]' . preg_quote($oldName, '/') . '[\'"]\s*\)/', + + // Java patterns + // System.getenv("VARIABLE_NAME") → System.getenv("VARIABLE_NAME_1") + '/\bSystem\.getenv\s*\(\s*[\'"]' . preg_quote($oldName, '/') . '[\'"]\s*\)/', + // System.getProperty("VARIABLE_NAME") → System.getProperty("VARIABLE_NAME_1") + '/\bSystem\.getProperty\s*\(\s*[\'"]' . preg_quote($oldName, '/') . '[\'"]\s*\)/', + + // C# patterns + // Environment.GetEnvironmentVariable("VARIABLE_NAME") → Environment.GetEnvironmentVariable("VARIABLE_NAME_1") + '/\bEnvironment\.GetEnvironmentVariable\s*\(\s*[\'"]' . preg_quote($oldName, '/') . '[\'"]\s*\)/', + // ConfigurationManager.AppSettings["VARIABLE_NAME"] → ConfigurationManager.AppSettings["VARIABLE_NAME_1"] + '/\bConfigurationManager\.AppSettings\s*\[\s*[\'"]' . preg_quote($oldName, '/') . '[\'"]\s*\]/', + + // Python patterns + // os.environ['VARIABLE_NAME'] → os.environ['VARIABLE_NAME_1'] + '/\bos\.environ\s*\[\s*[\'"]' . preg_quote($oldName, '/') . '[\'"]\s*\]/', + // os.environ.get('VARIABLE_NAME') → os.environ.get('VARIABLE_NAME_1') + '/\bos\.environ\.get\s*\(\s*[\'"]' . preg_quote($oldName, '/') . '[\'"]\s*\)/', + // os.getenv('VARIABLE_NAME') → os.getenv('VARIABLE_NAME_1') + '/\bos\.getenv\s*\(\s*[\'"]' . preg_quote($oldName, '/') . '[\'"]\s*\)/', + ]; + + foreach ($patterns as $patternIndex => $pattern) { + $updatedCode = preg_replace_callback($pattern, function ($matches) use ($oldName, $newName, $patternIndex) { + $original = $matches[0]; + + // Handle different replacement patterns + switch ($patternIndex) { + case 0: // process.env.VARIABLE_NAME + return str_replace('process.env.' . $oldName, 'process.env.' . $newName, $original); + case 1: // process.env['VARIABLE_NAME'] + case 2: // process.env["VARIABLE_NAME"] + return str_replace($oldName, $newName, $original); + case 6: // System.getenv for Java + return str_replace($oldName, $newName, $original); + case 7: // System.getProperty for Java + return str_replace($oldName, $newName, $original); + case 8: // Environment.GetEnvironmentVariable for C# + return str_replace($oldName, $newName, $original); + case 9: // ConfigurationManager.AppSettings for C# + return str_replace($oldName, $newName, $original); + case 10: // os.environ for Python + case 11: // os.environ.get for Python + case 12: // os.getenv for Python + return str_replace($oldName, $newName, $original); + default: // All other patterns (PHP) + return str_replace($oldName, $newName, $original); + } + }, $updatedCode); + } + } + + // Only update if changes were made + if ($updatedCode !== $this->model->code) { + $this->model->code = $updatedCode; + + // Log the variable name changes (summary) + Log::info('ScriptExporter: Updated variable references in script', [ + 'script_id' => $this->model->id, + 'script_title' => $this->model->title, + 'variables_updated' => count($variableMapping), + 'mappings' => $variableMapping, + 'mode' => $this->mode, + ]); + } + } + } } diff --git a/ProcessMaker/Jobs/RunServiceTask.php b/ProcessMaker/Jobs/RunServiceTask.php index 76ce0c9746..cfd4cf9729 100644 --- a/ProcessMaker/Jobs/RunServiceTask.php +++ b/ProcessMaker/Jobs/RunServiceTask.php @@ -36,9 +36,9 @@ class RunServiceTask extends BpmnAction implements ShouldQueue /** * Create a new job instance. * - * @param \ProcessMaker\Models\Process $definitions - * @param \ProcessMaker\Models\ProcessRequest $instance - * @param \ProcessMaker\Models\ProcessRequestToken $token + * @param Definitions $definitions + * @param ProcessRequest $instance + * @param ProcessRequestToken $token * @param array $data */ public function __construct(Definitions $definitions, ProcessRequest $instance, ProcessRequestToken $token, array $data, $attemptNum = 1) @@ -136,7 +136,7 @@ public function action(ProcessRequestToken $token = null, ServiceTaskInterface $ $token->logError($modifiedException, $element); Log::error('Service task failed: ' . $implementation . ' - ' . $message); - Log::error($exception->getTraceAsString()); + Log::debug($exception->getTraceAsString()); } } diff --git a/ProcessMaker/Managers/TaskSchedulerManager.php b/ProcessMaker/Managers/TaskSchedulerManager.php index b12c3049da..a8c4763ca0 100644 --- a/ProcessMaker/Managers/TaskSchedulerManager.php +++ b/ProcessMaker/Managers/TaskSchedulerManager.php @@ -17,10 +17,12 @@ use PDOException; use ProcessMaker\Facades\WorkflowManager; use ProcessMaker\Jobs\StartEventConditional; +use ProcessMaker\Models\EnvironmentVariable; use ProcessMaker\Models\Process; use ProcessMaker\Models\ProcessRequest; use ProcessMaker\Models\ProcessRequestLock; use ProcessMaker\Models\ScheduledTask; +use ProcessMaker\Models\Setting; use ProcessMaker\Models\TimerExpression; use ProcessMaker\Nayra\Bpmn\Models\BoundaryEvent; use ProcessMaker\Nayra\Bpmn\Models\DatePeriod; @@ -400,6 +402,16 @@ public function executeTimerStartEvent(ScheduledTask $task, $config) if (!$definitions->findElementById($config->element_id)) { return; } + + if ($this->shouldSkipHandleRepliesTimerStart($process)) { + Log::info('Skipping Actions By Email Handle Replies timer event because ABE inbound mail configuration is not adequate', [ + 'process_id' => $process->id, + 'process_name' => $process->name, + ]); + + return; + } + $event = $definitions->getEvent($config->element_id); $data = []; @@ -407,6 +419,194 @@ public function executeTimerStartEvent(ScheduledTask $task, $config) $processRequest = WorkflowManager::triggerStartEvent($process, $event, $data); } + /** + * Determine if the timer should be skipped for the Actions By Email handle replies process. + * + * @param Process $process + * @return bool + */ + private function shouldSkipHandleRepliesTimerStart(Process $process): bool + { + if (!$this->isHandleRepliesProcess($process)) { + return false; + } + + return !$this->hasAdequateAbeInboundConfiguration(); + } + + /** + * Whether Actions By Email has enough configuration to poll inbound mail (IMAP or OAuth). + * + * This is a heuristic in core: the connector may add more keys; we avoid starting the + * Handle Replies timer when the mailbox clearly is not set up (FOUR-30587). + * + * @return bool + */ + private function hasAdequateAbeInboundConfiguration(): bool + { + if (!Setting::readyToUseSettingsDatabase()) { + return false; + } + + $authMethodIndex = (int) ($this->getAbeInboundSettingValue(['abe_imap_auth_method']) ?? 0); + $username = $this->getAbeInboundSettingValue(['abe_imap_username', 'email_connector_mail_username']); + + if (!$this->hasValue($username)) { + return false; + } + + if ($authMethodIndex === 1) { + return $this->hasEnvironmentVariables([ + 'ABE_GMAIL_API_CLIENT_ID', + 'ABE_GMAIL_API_SECRET', + 'ABE_GMAIL_API_ACCESS_TOKEN', + 'ABE_GMAIL_API_REFRESH_TOKEN', + ]); + } + + if ($authMethodIndex === 2) { + return $this->hasEnvironmentVariables([ + 'ABE_OFFICE_365_CLIENT_ID', + 'ABE_OFFICE_365_TENANT_ID', + 'ABE_OFFICE_365_SECRET', + 'ABE_OFFICE_365_ACCESS_TOKEN', + 'ABE_OFFICE_365_REFRESH_TOKEN', + 'ABE_OFFICE_365_ACCESS_TOKEN_EXPIRE_DATE', + ]); + } + + return $this->hasStandardAbeInboundConfiguration(); + } + + /** + * IMAP configuration for standard authentication mode. + */ + private function hasStandardAbeInboundConfiguration(): bool + { + $password = $this->getAbeInboundSettingValue(['abe_imap_password', 'email_connector_mail_password']); + if (!$this->hasValue($password)) { + return false; + } + + $inboxUri = $this->getAbeInboundSettingValue(['abe_imap_inbox_uri']); + if ($this->hasValue($inboxUri)) { + return true; + } + + $server = $this->getAbeInboundSettingValue(['abe_imap_server', 'email_connector_mail_host']); + $port = $this->getAbeInboundSettingValue(['abe_imap_port', 'email_connector_mail_port']); + + return $this->hasValue($server) && $this->hasValue($port); + } + + /** + * Reads the first non-empty value from supported Actions By Email / mail settings keys. + * + * @param array $keys + * @return string|null + */ + private function getAbeInboundSettingValue(array $keys): ?string + { + $settings = Setting::query() + ->whereIn('key', $keys) + ->get() + ->keyBy('key'); + + if ($settings->isEmpty()) { + return null; + } + + foreach ($keys as $key) { + $setting = $settings->get($key); + if (!$setting) { + continue; + } + + $value = $this->extractSettingValue($setting->config); + if ($this->hasValue($value)) { + return (string) $value; + } + } + + return null; + } + + /** + * Normalize setting values from different possible setting formats. + * + * @param mixed $value + * @return mixed + */ + private function extractSettingValue($value) + { + if (is_object($value)) { + $value = (array) $value; + } + + if (is_array($value)) { + if (array_key_exists('value', $value)) { + return $value['value']; + } + + return $value; + } + + return $value; + } + + /** + * Check if a setting value is present and not empty. + * + * @param mixed $value + * @return bool + */ + private function hasValue($value): bool + { + if (is_array($value)) { + foreach ($value as $item) { + if ($this->hasValue($item)) { + return true; + } + } + + return false; + } + + return is_scalar($value) && trim((string) $value) !== ''; + } + + /** + * Identify the handle replies process by name. + * + * @param Process $process + * @return bool + */ + private function isHandleRepliesProcess(Process $process): bool + { + if ((string) $process->package_key === 'package-actions-by-email/handle-replies') { + return true; + } + + $name = (string) $process->name; + + return stripos($name, 'actions by email') !== false && stripos($name, 'handle replies') !== false; + } + + private function hasEnvironmentVariables(array $names): bool + { + $values = EnvironmentVariable::query() + ->whereIn('name', $names) + ->pluck('value', 'name'); + + foreach ($names as $name) { + if (!isset($values[$name]) || trim((string) $values[$name]) === '') { + return false; + } + } + + return true; + } + /** * Execute a timer start event * diff --git a/ProcessMaker/Models/ProcessRequest.php b/ProcessMaker/Models/ProcessRequest.php index 57509fe823..4ac0976872 100644 --- a/ProcessMaker/Models/ProcessRequest.php +++ b/ProcessMaker/Models/ProcessRequest.php @@ -586,7 +586,8 @@ public function logError(Throwable $exception, FlowElementInterface $element = n $errors[] = $error; $this->errors = $errors; $this->status = 'ERROR'; - Log::error($exception); + Log::error($exception->getMessage()); + Log::debug($exception->getTraceAsString()); if (!$this->isNonPersistent()) { $this->save(); // Update Case status diff --git a/ProcessMaker/Models/ProcessRequestToken.php b/ProcessMaker/Models/ProcessRequestToken.php index 01342a392b..b65c1b0a06 100644 --- a/ProcessMaker/Models/ProcessRequestToken.php +++ b/ProcessMaker/Models/ProcessRequestToken.php @@ -314,13 +314,20 @@ public function scopeFilterByCaseNumber($query, $request) } /** - * Scope to filter by status + * Scope to filter by status. + * Supports single value (e.g. ACTIVE) or comma-separated values (e.g. CLOSED,TRIGGERED). */ public function scopeFilterByStatus($query, $request) { $status = $request->input('status', 'ACTIVE'); + $statuses = array_map(fn ($v) => mb_strtoupper(trim($v)), explode(',', $status)); + $statuses = array_filter($statuses); - return $query->where('status', $status); + if (count($statuses) === 1) { + return $query->where('status', $statuses[0]); + } + + return $query->whereIn('status', $statuses); } /** @@ -443,7 +450,14 @@ public function getBpmnDefinition() public function getScreen(): ?Screen { $definition = $this->getDefinition(); - $screenRef = $definition['screenRef'] ?? null; + $screenRef = isset($definition['screenRef']) ? $definition['screenRef'] : null; + + if (!$screenRef) { + $config = isset($definition['config']) ? json_decode($definition['config']) : null; + + $screenRef = (isset($config) && isset($config->web_entry)) ? $config->web_entry->screen_id ?? null : null; + } + $screen = Screen::find($screenRef); if ($screen === null) { diff --git a/ProcessMaker/Providers/ProcessMakerServiceProvider.php b/ProcessMaker/Providers/ProcessMakerServiceProvider.php index 56d38de1d3..c19ded9d5f 100644 --- a/ProcessMaker/Providers/ProcessMakerServiceProvider.php +++ b/ProcessMaker/Providers/ProcessMakerServiceProvider.php @@ -23,6 +23,7 @@ use Laravel\Horizon\Horizon; use Laravel\Horizon\SystemProcessCounter; use Laravel\Horizon\WorkerCommandString; +use Laravel\Passport\Client as PassportClient; use Lavary\Menu\Menu; use OpenApi\Analysers\AttributeAnnotationFactory; use OpenApi\Analysers\DocBlockAnnotationFactory; @@ -354,6 +355,24 @@ protected static function bootObservers(): void Models\ProcessRequestToken::observe(Observers\ProcessRequestTokenObserver::class); Models\ProcessCollaboration::observe(Observers\ProcessCollaborationObserver::class); + + // Due to this change https://github.com/laravel/passport/blob/ea020190123953426a439f0267c6cfa478f6e6e7/src/Guards/TokenGuard.php#L146 + // user ID is now required for bearer tokens clients. Any user will work here, the token itself + // is what's associated with the real user. For now, we'll use the first administrator user. + PassportClient::creating(function (PassportClient $client): void { + if (!$client->personal_access_client || $client->user_id !== null) { + return; + } + + $adminUserId = Models\User::query() + ->where('is_administrator', true) + ->orderBy('id') + ->value('id'); + + if ($adminUserId !== null) { + $client->user_id = $adminUserId; + } + }); } /** diff --git a/ProcessMaker/ScriptRunners/Base.php b/ProcessMaker/ScriptRunners/Base.php index c609f3a0d6..d261245795 100644 --- a/ProcessMaker/ScriptRunners/Base.php +++ b/ProcessMaker/ScriptRunners/Base.php @@ -188,8 +188,12 @@ private function getEnvironmentVariables($useEscape = true) // Add the url to the host if ($useEscape) { $variablesParameter[] = 'HOST_URL=' . escapeshellarg(config('app.docker_host_url')); + $variablesParameter[] = 'SMART_EXTRACT_API_HOST=' . escapeshellarg(config('smart-extract.api_host')); + $variablesParameter[] = 'SMART_EXTRACT_REQUEST_TIMEOUT=' . escapeshellarg((string) config('smart-extract.request_timeout')); } else { $variablesParameter[] = 'HOST_URL=' . config('app.docker_host_url'); + $variablesParameter[] = 'SMART_EXTRACT_API_HOST=' . config('smart-extract.api_host'); + $variablesParameter[] = 'SMART_EXTRACT_REQUEST_TIMEOUT=' . config('smart-extract.request_timeout'); } return $variablesParameter; diff --git a/ProcessMaker/Traits/TaskControllerIndexMethods.php b/ProcessMaker/Traits/TaskControllerIndexMethods.php index b23a7a612a..dcfa97bea0 100644 --- a/ProcessMaker/Traits/TaskControllerIndexMethods.php +++ b/ProcessMaker/Traits/TaskControllerIndexMethods.php @@ -137,8 +137,12 @@ private function applyProcessRequestIdFilter($query, $column, $filterByFields, $ private function applyDefaultFiltering($query, $column, $filterByFields, $fieldFilter) { $key = array_search($column, $filterByFields); - $operator = is_numeric($fieldFilter) ? '=' : 'like'; - $query->where(is_string($key) ? $key : $column, $operator, $fieldFilter); + if (str_contains($fieldFilter, ',')) { + $query->whereIn(is_string($key) ? $key : $column, explode(',', $fieldFilter)); + } else { + $operator = is_numeric($fieldFilter) ? '=' : 'like'; + $query->where(is_string($key) ? $key : $column, $operator, $fieldFilter); + } } private function addTaskData($response) @@ -158,9 +162,15 @@ private function excludeNonVisibleTasks($query, $request) $nonSystem = filter_var($request->input('non_system'), FILTER_VALIDATE_BOOLEAN); $allTasks = filter_var($request->input('all_tasks'), FILTER_VALIDATE_BOOLEAN); $hitlEnabled = filter_var(config('smart-extract.hitl_enabled'), FILTER_VALIDATE_BOOLEAN); - $query->when(!$allTasks, function ($query) { - $query->where(function ($query) { - $query->where('element_type', '=', 'task'); + $includeScreen = filter_var($request->input('includeScreen'), FILTER_VALIDATE_BOOLEAN); + $query->when(!$allTasks, function ($query) use ($includeScreen) { + $query->where(function ($query) use ($includeScreen) { + if ($includeScreen) { + $query->orWhere('element_type', '=', 'task'); + $query->orWhere('element_type', '=', 'startEvent'); + } else { + $query->where('element_type', '=', 'task'); + } $query->orWhere(function ($query) { $query->where('element_type', '=', 'serviceTask'); $query->where('element_name', '=', 'AI Assistant'); diff --git a/composer.json b/composer.json index 0c89adfb08..4939e7b3a7 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "processmaker/processmaker", - "version": "2026.9.4-RC1", + "version": "2026.10.6", "description": "BPM PHP Software", "keywords": [ "php bpm processmaker" @@ -21,11 +21,12 @@ "fakerphp/faker": "^1.24", "google/apiclient": "^2.18", "google/protobuf": "^4.33.6", - "guzzlehttp/guzzle": "^7.9", + "guzzlehttp/guzzle": "^7.12.3", + "guzzlehttp/psr7": "^2.12.3", "igaster/laravel-theme": "^2.0", "jenssegers/agent": "^2.6", - "laravel/framework": "^13.0", - "laravel/horizon": "^5.45", + "laravel/framework": "^13.13", + "laravel/horizon": "^5.47", "laravel/pail": "^1.2", "laravel/passport": "^13.7", "laravel/scout": "^11.1", @@ -46,12 +47,12 @@ "paragonie/sodium_compat": "^2.5", "php-amqplib/php-amqplib": "^3.7", "php-http/promise": "~1.2.0", - "phpseclib/phpseclib": "^3.0", + "phpseclib/phpseclib": "^3.0.54", "pion/laravel-chunk-upload": "dev-master", "predis/predis": "^2.3", "processmaker/docker-executor-lua": "1.0.1", "processmaker/docker-executor-node": "1.1.0", - "processmaker/docker-executor-php": "1.4.3", + "processmaker/docker-executor-php": "1.4.5", "processmaker/laravel-i18next": "dev-master", "processmaker/nayra": "1.12.4", "processmaker/pmql": "1.13.3", @@ -69,10 +70,10 @@ "spatie/laravel-ignition": "^2.12", "spatie/laravel-medialibrary": "^11.21", "spatie/laravel-multitenancy": "^4.0.9", - "spomky-labs/otphp": "^11.3", + "spomky-labs/otphp": "^11.4.3", "swagger-api/swagger-ui": "^5.32.3", + "symfony/dependency-injection": "^8.1", "symfony/expression-language": "^7.2", - "symfony/http-foundation": "^7.3", "twilio/sdk": "^8.3", "typo3/class-alias-loader": "^1.2", "whichbrowser/parser": "^2.1" @@ -82,8 +83,7 @@ "laravel/boost": "^2.4", "mockery/mockery": "^1.6", "phpunit/phpunit": "^12.5.8", - "squizlabs/php_codesniffer": "^3.11", - "symfony/dom-crawler": "^7.2" + "squizlabs/php_codesniffer": "^3.11" }, "autoload": { "files": [ @@ -113,7 +113,7 @@ "Gmail" ], "processmaker": { - "build": "c44aadb4", + "build": "eb450f0e", "cicd-enabled": true, "custom": { "package-ellucian-ethos": "1.19.10", @@ -151,15 +151,15 @@ "connector-docusign": "1.11.2", "connector-idp": "1.14.2", "connector-pdf-print": "1.23.3", - "connector-send-email": "1.32.16", - "connector-slack": "1.9.6", + "connector-send-email": "1.32.17", + "connector-slack": "1.9.7", "docker-executor-node-ssr": "1.7.4", "package-ab-testing": "1.4.2", - "package-actions-by-email": "1.22.14", + "package-actions-by-email": "1.22.15", "package-advanced-user-manager": "1.13.3", - "package-ai": "1.16.18", + "package-ai": "1.16.21", "package-analytics-reporting": "1.11.5", - "package-auth": "1.24.15", + "package-auth": "1.24.17", "package-collections": "2.27.6", "package-comments": "1.16.4", "package-conversational-forms": "1.15.2", @@ -178,7 +178,7 @@ "package-rpa": "1.1.2", "package-savedsearch": "1.43.12-RC1", "package-slideshow": "1.4.3", - "package-smart-extract": "0.0.6", + "package-smart-extract": "1.0.0", "package-signature": "1.15.5", "package-testing": "1.8.2", "package-translations": "2.14.6", @@ -250,4 +250,4 @@ "ignore": [] } } -} \ No newline at end of file +} diff --git a/composer.lock b/composer.lock index 4923bcb064..db1809dfb8 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "cf13f025493260a0e8543c9d96181113", + "content-hash": "7be8d7f06a110182a85b4b60ec55e302", "packages": [ { "name": "babenkoivan/elastic-adapter", @@ -2132,25 +2132,26 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.10.0", + "version": "7.12.3", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4" + "reference": "9aa17bcdd777ee31df9fc83c337ca4ca2340def3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", - "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/9aa17bcdd777ee31df9fc83c337ca4ca2340def3", + "reference": "9aa17bcdd777ee31df9fc83c337ca4ca2340def3", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^2.3", - "guzzlehttp/psr7": "^2.8", + "guzzlehttp/promises": "^2.5", + "guzzlehttp/psr7": "^2.12.3", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", - "symfony/deprecation-contracts": "^2.2 || ^3.0" + "symfony/deprecation-contracts": "^2.5 || ^3.0", + "symfony/polyfill-php80": "^1.25" }, "provide": { "psr/http-client-implementation": "1.0" @@ -2159,8 +2160,9 @@ "bamarni/composer-bin-plugin": "^1.8.2", "ext-curl": "*", "guzzle/client-integration-tests": "3.0.2", + "guzzlehttp/test-server": "^0.5.1", "php-http/message-factory": "^1.1", - "phpunit/phpunit": "^8.5.39 || ^9.6.20", + "phpunit/phpunit": "^8.5.52 || ^9.6.34", "psr/log": "^1.1 || ^2.0 || ^3.0" }, "suggest": { @@ -2238,7 +2240,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.10.0" + "source": "https://github.com/guzzle/guzzle/tree/7.12.3" }, "funding": [ { @@ -2254,28 +2256,29 @@ "type": "tidelift" } ], - "time": "2025-08-23T22:36:01+00:00" + "time": "2026-06-23T15:29:02+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.3.0", + "version": "2.5.0", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "481557b130ef3790cf82b713667b43030dc9c957" + "reference": "4360e982f87f5f258bf872d094647791db2f4c8e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957", - "reference": "481557b130ef3790cf82b713667b43030dc9c957", + "url": "https://api.github.com/repos/guzzle/promises/zipball/4360e982f87f5f258bf872d094647791db2f4c8e", + "reference": "4360e982f87f5f258bf872d094647791db2f4c8e", "shasum": "" }, "require": { - "php": "^7.2.5 || ^8.0" + "php": "^7.2.5 || ^8.0", + "symfony/deprecation-contracts": "^2.5 || ^3.0" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.44 || ^9.6.25" + "phpunit/phpunit": "^8.5.52 || ^9.6.34" }, "type": "library", "extra": { @@ -2321,7 +2324,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.3.0" + "source": "https://github.com/guzzle/promises/tree/2.5.0" }, "funding": [ { @@ -2337,27 +2340,29 @@ "type": "tidelift" } ], - "time": "2025-08-22T14:34:08+00:00" + "time": "2026-06-02T12:23:43+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.9.0", + "version": "2.12.3", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884" + "reference": "7ec62dc3f44aa218487dbed81a9bf9bc647be55d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/7d0ed42f28e42d61352a7a79de682e5e67fec884", - "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/7ec62dc3f44aa218487dbed81a9bf9bc647be55d", + "reference": "7ec62dc3f44aa218487dbed81a9bf9bc647be55d", "shasum": "" }, "require": { "php": "^7.2.5 || ^8.0", "psr/http-factory": "^1.0", "psr/http-message": "^1.1 || ^2.0", - "ralouphie/getallheaders": "^3.0" + "ralouphie/getallheaders": "^3.0", + "symfony/deprecation-contracts": "^2.5 || ^3.0", + "symfony/polyfill-php80": "^1.25" }, "provide": { "psr/http-factory-implementation": "1.0", @@ -2365,9 +2370,9 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "http-interop/http-factory-tests": "0.9.0", + "http-interop/http-factory-tests": "1.1.0", "jshttp/mime-db": "1.54.0.1", - "phpunit/phpunit": "^8.5.44 || ^9.6.25" + "phpunit/phpunit": "^8.5.52 || ^9.6.34" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -2438,7 +2443,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.9.0" + "source": "https://github.com/guzzle/psr7/tree/2.12.3" }, "funding": [ { @@ -2454,20 +2459,20 @@ "type": "tidelift" } ], - "time": "2026-03-10T16:41:02+00:00" + "time": "2026-06-23T15:21:08+00:00" }, { "name": "guzzlehttp/uri-template", - "version": "v1.0.5", + "version": "v1.0.6", "source": { "type": "git", "url": "https://github.com/guzzle/uri-template.git", - "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1" + "reference": "eef7f87bab6f204eba3c39224d8075c70c637946" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/uri-template/zipball/4f4bbd4e7172148801e76e3decc1e559bdee34e1", - "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/eef7f87bab6f204eba3c39224d8075c70c637946", + "reference": "eef7f87bab6f204eba3c39224d8075c70c637946", "shasum": "" }, "require": { @@ -2476,7 +2481,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.44 || ^9.6.25", + "phpunit/phpunit": "^8.5.52 || ^9.6.34", "uri-template/tests": "1.0.0" }, "type": "library", @@ -2524,7 +2529,7 @@ ], "support": { "issues": "https://github.com/guzzle/uri-template/issues", - "source": "https://github.com/guzzle/uri-template/tree/v1.0.5" + "source": "https://github.com/guzzle/uri-template/tree/v1.0.6" }, "funding": [ { @@ -2540,7 +2545,7 @@ "type": "tidelift" } ], - "time": "2025-08-22T14:27:06+00:00" + "time": "2026-05-23T22:00:21+00:00" }, { "name": "igaster/laravel-theme", @@ -2742,16 +2747,16 @@ }, { "name": "laravel/framework", - "version": "v13.3.0", + "version": "v13.13.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "118b7063c44a2f3421d1646f5ddf08defcfd1db3" + "reference": "1daa6d3b4defe46976ccfa4fb0a7ab62717712a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/118b7063c44a2f3421d1646f5ddf08defcfd1db3", - "reference": "118b7063c44a2f3421d1646f5ddf08defcfd1db3", + "url": "https://api.github.com/repos/laravel/framework/zipball/1daa6d3b4defe46976ccfa4fb0a7ab62717712a2", + "reference": "1daa6d3b4defe46976ccfa4fb0a7ab62717712a2", "shasum": "" }, "require": { @@ -2792,8 +2797,9 @@ "symfony/http-kernel": "^7.4.0 || ^8.0.0", "symfony/mailer": "^7.4.0 || ^8.0.0", "symfony/mime": "^7.4.0 || ^8.0.0", - "symfony/polyfill-php84": "^1.33", - "symfony/polyfill-php85": "^1.33", + "symfony/polyfill-php84": "^1.36", + "symfony/polyfill-php85": "^1.36", + "symfony/polyfill-php86": "^1.36", "symfony/process": "^7.4.5 || ^8.0.5", "symfony/routing": "^7.4.0 || ^8.0.0", "symfony/uid": "^7.4.0 || ^8.0.0", @@ -2854,7 +2860,7 @@ "aws/aws-sdk-php": "^3.322.9", "ext-gmp": "*", "fakerphp/faker": "^1.24", - "guzzlehttp/psr7": "^2.4", + "guzzlehttp/psr7": "^2.9", "laravel/pint": "^1.18", "league/flysystem-aws-s3-v3": "^3.25.1", "league/flysystem-ftp": "^3.25.1", @@ -2905,6 +2911,7 @@ "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0 || ^7.0).", "resend/resend-php": "Required to enable support for the Resend mail transport (^0.10.0 || ^1.0).", + "spatie/fork": "Required to use the 'fork' concurrency driver (^1.2).", "symfony/cache": "Required to PSR-6 cache bridge (^7.4 || ^8.0).", "symfony/filesystem": "Required to enable support for relative symbolic links (^7.4 || ^8.0).", "symfony/http-client": "Required to enable support for the Symfony API mail transports (^7.4 || ^8.0).", @@ -2960,20 +2967,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2026-04-01T15:39:53+00:00" + "time": "2026-06-02T14:28:17+00:00" }, { "name": "laravel/horizon", - "version": "v5.45.5", + "version": "v5.47.2", "source": { "type": "git", "url": "https://github.com/laravel/horizon.git", - "reference": "683b6117ec0d0495a4d18106bc2c44b3461fd92b" + "reference": "a6ac142293ad02db4d7cccb961dd32f56ef1462d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/horizon/zipball/683b6117ec0d0495a4d18106bc2c44b3461fd92b", - "reference": "683b6117ec0d0495a4d18106bc2c44b3461fd92b", + "url": "https://api.github.com/repos/laravel/horizon/zipball/a6ac142293ad02db4d7cccb961dd32f56ef1462d", + "reference": "a6ac142293ad02db4d7cccb961dd32f56ef1462d", "shasum": "" }, "require": { @@ -3038,9 +3045,9 @@ ], "support": { "issues": "https://github.com/laravel/horizon/issues", - "source": "https://github.com/laravel/horizon/tree/v5.45.5" + "source": "https://github.com/laravel/horizon/tree/v5.47.2" }, - "time": "2026-04-01T07:28:03+00:00" + "time": "2026-06-03T15:11:37+00:00" }, { "name": "laravel/pail", @@ -3199,16 +3206,16 @@ }, { "name": "laravel/prompts", - "version": "v0.3.16", + "version": "v0.3.18", "source": { "type": "git", "url": "https://github.com/laravel/prompts.git", - "reference": "11e7d5f93803a2190b00e145142cb00a33d17ad2" + "reference": "a19af51bb144bf87f08397921fa619f85c7d4e72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/prompts/zipball/11e7d5f93803a2190b00e145142cb00a33d17ad2", - "reference": "11e7d5f93803a2190b00e145142cb00a33d17ad2", + "url": "https://api.github.com/repos/laravel/prompts/zipball/a19af51bb144bf87f08397921fa619f85c7d4e72", + "reference": "a19af51bb144bf87f08397921fa619f85c7d4e72", "shasum": "" }, "require": { @@ -3252,9 +3259,9 @@ "description": "Add beautiful and user-friendly forms to your command-line applications.", "support": { "issues": "https://github.com/laravel/prompts/issues", - "source": "https://github.com/laravel/prompts/tree/v0.3.16" + "source": "https://github.com/laravel/prompts/tree/v0.3.18" }, - "time": "2026-03-23T14:35:33+00:00" + "time": "2026-05-19T00:47:18+00:00" }, { "name": "laravel/scout", @@ -3394,16 +3401,16 @@ }, { "name": "laravel/serializable-closure", - "version": "v2.0.10", + "version": "v2.0.13", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "870fc81d2f879903dfc5b60bf8a0f94a1609e669" + "reference": "b566ee0dd251f3c4078bed003a7ce015f5ea6dce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/870fc81d2f879903dfc5b60bf8a0f94a1609e669", - "reference": "870fc81d2f879903dfc5b60bf8a0f94a1609e669", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/b566ee0dd251f3c4078bed003a7ce015f5ea6dce", + "reference": "b566ee0dd251f3c4078bed003a7ce015f5ea6dce", "shasum": "" }, "require": { @@ -3451,7 +3458,7 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2026-02-20T19:59:49+00:00" + "time": "2026-04-16T14:03:50+00:00" }, { "name": "laravel/telescope", @@ -4098,16 +4105,16 @@ }, { "name": "league/flysystem", - "version": "3.33.0", + "version": "3.34.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "570b8871e0ce693764434b29154c54b434905350" + "reference": "2daaac3b0d4c83ea7ed5d8586e786f5d00f3540e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/570b8871e0ce693764434b29154c54b434905350", - "reference": "570b8871e0ce693764434b29154c54b434905350", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/2daaac3b0d4c83ea7ed5d8586e786f5d00f3540e", + "reference": "2daaac3b0d4c83ea7ed5d8586e786f5d00f3540e", "shasum": "" }, "require": { @@ -4175,9 +4182,9 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/3.33.0" + "source": "https://github.com/thephpleague/flysystem/tree/3.34.0" }, - "time": "2026-03-25T07:59:30+00:00" + "time": "2026-05-14T10:28:08+00:00" }, { "name": "league/flysystem-local", @@ -5310,16 +5317,16 @@ }, { "name": "nesbot/carbon", - "version": "3.11.3", + "version": "3.11.4", "source": { "type": "git", "url": "https://github.com/CarbonPHP/carbon.git", - "reference": "6a7e652845bb018c668220c2a545aded8594fbbf" + "reference": "e890471a3494740f7d9326d72ce6a8c559ffee60" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/6a7e652845bb018c668220c2a545aded8594fbbf", - "reference": "6a7e652845bb018c668220c2a545aded8594fbbf", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/e890471a3494740f7d9326d72ce6a8c559ffee60", + "reference": "e890471a3494740f7d9326d72ce6a8c559ffee60", "shasum": "" }, "require": { @@ -5411,7 +5418,7 @@ "type": "tidelift" } ], - "time": "2026-03-11T17:23:39+00:00" + "time": "2026-04-07T09:57:54+00:00" }, { "name": "nette/schema", @@ -5482,16 +5489,16 @@ }, { "name": "nette/utils", - "version": "v4.1.3", + "version": "v4.1.4", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "bb3ea637e3d131d72acc033cfc2746ee893349fe" + "reference": "7da6c396d7ebe142bc857c20479d5e70a5e1aac7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/bb3ea637e3d131d72acc033cfc2746ee893349fe", - "reference": "bb3ea637e3d131d72acc033cfc2746ee893349fe", + "url": "https://api.github.com/repos/nette/utils/zipball/7da6c396d7ebe142bc857c20479d5e70a5e1aac7", + "reference": "7da6c396d7ebe142bc857c20479d5e70a5e1aac7", "shasum": "" }, "require": { @@ -5567,9 +5574,9 @@ ], "support": { "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v4.1.3" + "source": "https://github.com/nette/utils/tree/v4.1.4" }, - "time": "2026-02-13T03:05:33+00:00" + "time": "2026-05-11T20:49:54+00:00" }, { "name": "nikic/php-parser", @@ -7125,16 +7132,16 @@ }, { "name": "phpseclib/phpseclib", - "version": "3.0.52", + "version": "3.0.55", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "2adaefc83df2ec548558307690f376dd7d4f4fce" + "reference": "db9744e6d47e742b1f974e965ad49bdd041105af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/2adaefc83df2ec548558307690f376dd7d4f4fce", - "reference": "2adaefc83df2ec548558307690f376dd7d4f4fce", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/db9744e6d47e742b1f974e965ad49bdd041105af", + "reference": "db9744e6d47e742b1f974e965ad49bdd041105af", "shasum": "" }, "require": { @@ -7215,7 +7222,7 @@ ], "support": { "issues": "https://github.com/phpseclib/phpseclib/issues", - "source": "https://github.com/phpseclib/phpseclib/tree/3.0.52" + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.55" }, "funding": [ { @@ -7231,7 +7238,7 @@ "type": "tidelift" } ], - "time": "2026-04-27T07:02:15+00:00" + "time": "2026-06-14T23:24:10+00:00" }, { "name": "phpstan/phpdoc-parser", @@ -7496,16 +7503,16 @@ }, { "name": "processmaker/docker-executor-php", - "version": "1.4.3", + "version": "1.4.5", "source": { "type": "git", "url": "https://github.com/ProcessMaker/docker-executor-php.git", - "reference": "c26a6be594e95e46459c4f45ad8b926e7c90fb32" + "reference": "5b9fc7a848106c46abe3c535a0f797bce7e6c8bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ProcessMaker/docker-executor-php/zipball/c26a6be594e95e46459c4f45ad8b926e7c90fb32", - "reference": "c26a6be594e95e46459c4f45ad8b926e7c90fb32", + "url": "https://api.github.com/repos/ProcessMaker/docker-executor-php/zipball/5b9fc7a848106c46abe3c535a0f797bce7e6c8bf", + "reference": "5b9fc7a848106c46abe3c535a0f797bce7e6c8bf", "shasum": "" }, "type": "library", @@ -7529,9 +7536,9 @@ "description": "PHP script executor for processmaker 4", "support": { "issues": "https://github.com/ProcessMaker/docker-executor-php/issues", - "source": "https://github.com/ProcessMaker/docker-executor-php/tree/v1.4.3" + "source": "https://github.com/ProcessMaker/docker-executor-php/tree/v1.4.5" }, - "time": "2026-04-23T22:02:29+00:00" + "time": "2026-06-23T21:44:58+00:00" }, { "name": "processmaker/laravel-i18next", @@ -9860,16 +9867,16 @@ }, { "name": "spomky-labs/otphp", - "version": "11.4.1", + "version": "11.5.0", "source": { "type": "git", "url": "https://github.com/Spomky-Labs/otphp.git", - "reference": "126c99b6cbbc18992cf3fba3b87931ba4e312482" + "reference": "877683d6352b80cdc7020fd43a725629c2524435" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Spomky-Labs/otphp/zipball/126c99b6cbbc18992cf3fba3b87931ba4e312482", - "reference": "126c99b6cbbc18992cf3fba3b87931ba4e312482", + "url": "https://api.github.com/repos/Spomky-Labs/otphp/zipball/877683d6352b80cdc7020fd43a725629c2524435", + "reference": "877683d6352b80cdc7020fd43a725629c2524435", "shasum": "" }, "require": { @@ -9914,7 +9921,7 @@ ], "support": { "issues": "https://github.com/Spomky-Labs/otphp/issues", - "source": "https://github.com/Spomky-Labs/otphp/tree/11.4.1" + "source": "https://github.com/Spomky-Labs/otphp/tree/11.5.0" }, "funding": [ { @@ -9926,7 +9933,7 @@ "type": "patreon" } ], - "time": "2026-01-05T13:20:36+00:00" + "time": "2026-06-06T23:41:24+00:00" }, { "name": "swagger-api/swagger-ui", @@ -9991,16 +9998,16 @@ }, { "name": "symfony/cache", - "version": "v8.0.3", + "version": "v8.0.13", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "ef8c7dbfe613d2773d0b5e68b2ef2db72c8b025f" + "reference": "75f92239836ce08bce4d19f2734737dae4276fe9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/ef8c7dbfe613d2773d0b5e68b2ef2db72c8b025f", - "reference": "ef8c7dbfe613d2773d0b5e68b2ef2db72c8b025f", + "url": "https://api.github.com/repos/symfony/cache/zipball/75f92239836ce08bce4d19f2734737dae4276fe9", + "reference": "75f92239836ce08bce4d19f2734737dae4276fe9", "shasum": "" }, "require": { @@ -10012,7 +10019,6 @@ "symfony/var-exporter": "^7.4|^8.0" }, "conflict": { - "doctrine/dbal": "<4.3", "ext-redis": "<6.1", "ext-relay": "<0.12.1" }, @@ -10067,7 +10073,7 @@ "psr6" ], "support": { - "source": "https://github.com/symfony/cache/tree/v8.0.3" + "source": "https://github.com/symfony/cache/tree/v8.0.13" }, "funding": [ { @@ -10087,20 +10093,20 @@ "type": "tidelift" } ], - "time": "2025-12-28T10:45:32+00:00" + "time": "2026-05-24T08:44:11+00:00" }, { "name": "symfony/cache-contracts", - "version": "v3.6.0", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/cache-contracts.git", - "reference": "5d68a57d66910405e5c0b63d6f0af941e66fc868" + "reference": "225e8a254166bd3442e370c6f50145465db63831" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/5d68a57d66910405e5c0b63d6f0af941e66fc868", - "reference": "5d68a57d66910405e5c0b63d6f0af941e66fc868", + "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/225e8a254166bd3442e370c6f50145465db63831", + "reference": "225e8a254166bd3442e370c6f50145465db63831", "shasum": "" }, "require": { @@ -10114,7 +10120,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -10147,7 +10153,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/cache-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/cache-contracts/tree/v3.7.0" }, "funding": [ { @@ -10158,29 +10164,33 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-03-13T15:25:07+00:00" + "time": "2026-05-05T15:33:14+00:00" }, { "name": "symfony/clock", - "version": "v8.0.8", + "version": "v8.1.0", "source": { "type": "git", "url": "https://github.com/symfony/clock.git", - "reference": "b55a638b189a6faa875e0ccdb00908fb87af95b3" + "reference": "701ef4de9705d6c32292ebee5e8044094a09fbf6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/clock/zipball/b55a638b189a6faa875e0ccdb00908fb87af95b3", - "reference": "b55a638b189a6faa875e0ccdb00908fb87af95b3", + "url": "https://api.github.com/repos/symfony/clock/zipball/701ef4de9705d6c32292ebee5e8044094a09fbf6", + "reference": "701ef4de9705d6c32292ebee5e8044094a09fbf6", "shasum": "" }, "require": { - "php": ">=8.4", + "php": ">=8.4.1", "psr/clock": "^1.0" }, "provide": { @@ -10220,7 +10230,7 @@ "time" ], "support": { - "source": "https://github.com/symfony/clock/tree/v8.0.8" + "source": "https://github.com/symfony/clock/tree/v8.1.0" }, "funding": [ { @@ -10240,27 +10250,33 @@ "type": "tidelift" } ], - "time": "2026-03-30T15:14:47+00:00" + "time": "2026-05-29T05:06:50+00:00" }, { "name": "symfony/console", - "version": "v8.0.8", + "version": "v8.1.0", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "5b66d385dc58f69652e56f78a4184615e3f2b7f7" + "reference": "f5a856c6ecb56b3c21ed94a5b7bf940d857d110a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/5b66d385dc58f69652e56f78a4184615e3f2b7f7", - "reference": "5b66d385dc58f69652e56f78a4184615e3f2b7f7", + "url": "https://api.github.com/repos/symfony/console/zipball/f5a856c6ecb56b3c21ed94a5b7bf940d857d110a", + "reference": "f5a856c6ecb56b3c21ed94a5b7bf940d857d110a", "shasum": "" }, "require": { - "php": ">=8.4", + "php": ">=8.4.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "^1.0", + "symfony/polyfill-php85": "^1.32", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^7.4|^8.0" + "symfony/string": "^7.4.6|^8.0.6" + }, + "conflict": { + "symfony/dependency-injection": "<8.1", + "symfony/event-dispatcher": "<8.1" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" @@ -10268,14 +10284,18 @@ "require-dev": { "psr/log": "^1|^2|^3", "symfony/config": "^7.4|^8.0", - "symfony/dependency-injection": "^7.4|^8.0", - "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/dependency-injection": "^8.1", + "symfony/event-dispatcher": "^8.1", + "symfony/filesystem": "^7.4|^8.0", "symfony/http-foundation": "^7.4|^8.0", "symfony/http-kernel": "^7.4|^8.0", "symfony/lock": "^7.4|^8.0", "symfony/messenger": "^7.4|^8.0", + "symfony/mime": "^7.4|^8.0", "symfony/process": "^7.4|^8.0", "symfony/stopwatch": "^7.4|^8.0", + "symfony/uid": "^7.4|^8.0", + "symfony/validator": "^7.4|^8.0", "symfony/var-dumper": "^7.4|^8.0" }, "type": "library", @@ -10310,7 +10330,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v8.0.8" + "source": "https://github.com/symfony/console/tree/v8.1.0" }, "funding": [ { @@ -10330,24 +10350,24 @@ "type": "tidelift" } ], - "time": "2026-03-30T15:14:47+00:00" + "time": "2026-05-29T05:06:50+00:00" }, { "name": "symfony/css-selector", - "version": "v8.0.8", + "version": "v8.1.0", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "8db1c00226a94d8ab6aa89d9224eeee91e2ea2ed" + "reference": "dc0e2be45c9b5588c82414f02ac574b4b986abcd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/8db1c00226a94d8ab6aa89d9224eeee91e2ea2ed", - "reference": "8db1c00226a94d8ab6aa89d9224eeee91e2ea2ed", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/dc0e2be45c9b5588c82414f02ac574b4b986abcd", + "reference": "dc0e2be45c9b5588c82414f02ac574b4b986abcd", "shasum": "" }, "require": { - "php": ">=8.4" + "php": ">=8.4.1" }, "type": "library", "autoload": { @@ -10379,7 +10399,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v8.0.8" + "source": "https://github.com/symfony/css-selector/tree/v8.1.0" }, "funding": [ { @@ -10399,20 +10419,101 @@ "type": "tidelift" } ], - "time": "2026-03-30T15:14:47+00:00" + "time": "2026-05-29T05:06:50+00:00" + }, + { + "name": "symfony/dependency-injection", + "version": "v8.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/dependency-injection.git", + "reference": "b6ba1f45127106885de4b77558c5ecca8feb1e1b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/b6ba1f45127106885de4b77558c5ecca8feb1e1b", + "reference": "b6ba1f45127106885de4b77558c5ecca8feb1e1b", + "shasum": "" + }, + "require": { + "php": ">=8.4.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/service-contracts": "^3.6", + "symfony/var-exporter": "^8.1" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "provide": { + "psr/container-implementation": "1.1|2.0", + "symfony/service-implementation": "1.1|2.0|3.0" + }, + "require-dev": { + "symfony/config": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/yaml": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\DependencyInjection\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows you to standardize and centralize the way objects are constructed in your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/dependency-injection/tree/v8.1.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-29T05:06:50+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.6.0", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/50f59d1f3ca46d41ac911f97a78626b6756af35b", + "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b", "shasum": "" }, "require": { @@ -10425,7 +10526,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -10450,7 +10551,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.0" }, "funding": [ { @@ -10461,29 +10562,33 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2026-04-13T15:52:40+00:00" }, { "name": "symfony/error-handler", - "version": "v8.0.8", + "version": "v8.1.0", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "c1119fe8dcfc3825ec74ec061b96ef0c8f281517" + "reference": "d8aeb1abd3fef84795567850d3a567bdb5945ee5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/c1119fe8dcfc3825ec74ec061b96ef0c8f281517", - "reference": "c1119fe8dcfc3825ec74ec061b96ef0c8f281517", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/d8aeb1abd3fef84795567850d3a567bdb5945ee5", + "reference": "d8aeb1abd3fef84795567850d3a567bdb5945ee5", "shasum": "" }, "require": { - "php": ">=8.4", + "php": ">=8.4.1", "psr/log": "^1|^2|^3", "symfony/polyfill-php85": "^1.32", "symfony/var-dumper": "^7.4|^8.0" @@ -10527,7 +10632,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v8.0.8" + "source": "https://github.com/symfony/error-handler/tree/v8.1.0" }, "funding": [ { @@ -10547,24 +10652,25 @@ "type": "tidelift" } ], - "time": "2026-03-30T15:14:47+00:00" + "time": "2026-05-29T05:06:50+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v8.0.8", + "version": "v8.1.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "f662acc6ab22a3d6d716dcb44c381c6002940df6" + "reference": "f249ae3f680958b6f1f9dd76e5747cf0695b4102" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/f662acc6ab22a3d6d716dcb44c381c6002940df6", - "reference": "f662acc6ab22a3d6d716dcb44c381c6002940df6", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/f249ae3f680958b6f1f9dd76e5747cf0695b4102", + "reference": "f249ae3f680958b6f1f9dd76e5747cf0695b4102", "shasum": "" }, "require": { - "php": ">=8.4", + "php": ">=8.4.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/event-dispatcher-contracts": "^2.5|^3" }, "conflict": { @@ -10612,7 +10718,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v8.0.8" + "source": "https://github.com/symfony/event-dispatcher/tree/v8.1.0" }, "funding": [ { @@ -10632,20 +10738,20 @@ "type": "tidelift" } ], - "time": "2026-03-30T15:14:47+00:00" + "time": "2026-05-29T05:06:50+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.6.0", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "59eb412e93815df44f05f342958efa9f46b1e586" + "reference": "ccba7060602b7fed0b03c85bf025257f76d9ef32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", - "reference": "59eb412e93815df44f05f342958efa9f46b1e586", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/ccba7060602b7fed0b03c85bf025257f76d9ef32", + "reference": "ccba7060602b7fed0b03c85bf025257f76d9ef32", "shasum": "" }, "require": { @@ -10659,7 +10765,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -10692,7 +10798,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.7.0" }, "funding": [ { @@ -10703,25 +10809,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2026-01-05T13:30:16+00:00" }, { "name": "symfony/expression-language", - "version": "v7.4.0", + "version": "v7.4.8", "source": { "type": "git", "url": "https://github.com/symfony/expression-language.git", - "reference": "8b9bbbb8c71f79a09638f6ea77c531e511139efa" + "reference": "87ff95687748f4af65e4d5a6e917d448ec52aa83" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/expression-language/zipball/8b9bbbb8c71f79a09638f6ea77c531e511139efa", - "reference": "8b9bbbb8c71f79a09638f6ea77c531e511139efa", + "url": "https://api.github.com/repos/symfony/expression-language/zipball/87ff95687748f4af65e4d5a6e917d448ec52aa83", + "reference": "87ff95687748f4af65e4d5a6e917d448ec52aa83", "shasum": "" }, "require": { @@ -10756,7 +10866,7 @@ "description": "Provides an engine that can compile and evaluate expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/expression-language/tree/v7.4.0" + "source": "https://github.com/symfony/expression-language/tree/v7.4.8" }, "funding": [ { @@ -10776,24 +10886,24 @@ "type": "tidelift" } ], - "time": "2025-11-12T15:39:26+00:00" + "time": "2026-03-24T13:12:05+00:00" }, { "name": "symfony/finder", - "version": "v8.0.8", + "version": "v8.1.0", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "8da41214757b87d97f181e3d14a4179286151007" + "reference": "58d2e767a66052c1487356f953445634a8194c64" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/8da41214757b87d97f181e3d14a4179286151007", - "reference": "8da41214757b87d97f181e3d14a4179286151007", + "url": "https://api.github.com/repos/symfony/finder/zipball/58d2e767a66052c1487356f953445634a8194c64", + "reference": "58d2e767a66052c1487356f953445634a8194c64", "shasum": "" }, "require": { - "php": ">=8.4" + "php": ">=8.4.1" }, "require-dev": { "symfony/filesystem": "^7.4|^8.0" @@ -10824,7 +10934,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v8.0.8" + "source": "https://github.com/symfony/finder/tree/v8.1.0" }, "funding": [ { @@ -10844,41 +10954,40 @@ "type": "tidelift" } ], - "time": "2026-03-30T15:14:47+00:00" + "time": "2026-05-29T05:06:50+00:00" }, { "name": "symfony/http-foundation", - "version": "v7.4.8", + "version": "v8.1.0", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "9381209597ec66c25be154cbf2289076e64d1eab" + "reference": "af11474600f06718086c2cda4fa6fa8d0a672e7e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/9381209597ec66c25be154cbf2289076e64d1eab", - "reference": "9381209597ec66c25be154cbf2289076e64d1eab", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/af11474600f06718086c2cda4fa6fa8d0a672e7e", + "reference": "af11474600f06718086c2cda4fa6fa8d0a672e7e", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4.1", "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "^1.1" }, "conflict": { - "doctrine/dbal": "<3.6", - "symfony/cache": "<6.4.12|>=7.0,<7.1.5" + "doctrine/dbal": "<4.3" }, "require-dev": { - "doctrine/dbal": "^3.6|^4", + "doctrine/dbal": "^4.3", "predis/predis": "^1.1|^2.0", - "symfony/cache": "^6.4.12|^7.1.5|^8.0", - "symfony/clock": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/expression-language": "^6.4|^7.0|^8.0", - "symfony/http-kernel": "^6.4|^7.0|^8.0", - "symfony/mime": "^6.4|^7.0|^8.0", - "symfony/rate-limiter": "^6.4|^7.0|^8.0" + "symfony/cache": "^7.4|^8.0", + "symfony/clock": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/mime": "^7.4|^8.0", + "symfony/rate-limiter": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -10906,7 +11015,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.4.8" + "source": "https://github.com/symfony/http-foundation/tree/v8.1.0" }, "funding": [ { @@ -10926,34 +11035,38 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-05-29T05:06:50+00:00" }, { "name": "symfony/http-kernel", - "version": "v8.0.8", + "version": "v8.1.0", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "1770f6818d83b2fddc12185025b93f39a90cb628" + "reference": "cefeb37c82eed3e0c42fa25ba64cd3a908d90f39" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/1770f6818d83b2fddc12185025b93f39a90cb628", - "reference": "1770f6818d83b2fddc12185025b93f39a90cb628", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/cefeb37c82eed3e0c42fa25ba64cd3a908d90f39", + "reference": "cefeb37c82eed3e0c42fa25ba64cd3a908d90f39", "shasum": "" }, "require": { - "php": ">=8.4", + "php": ">=8.4.1", "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/error-handler": "^7.4|^8.0", "symfony/event-dispatcher": "^7.4|^8.0", "symfony/http-foundation": "^7.4|^8.0", "symfony/polyfill-ctype": "^1.8" }, "conflict": { + "symfony/dependency-injection": "<8.1", "symfony/flex": "<2.10", "symfony/http-client-contracts": "<2.5", "symfony/translation-contracts": "<2.5", + "symfony/var-dumper": "<8.1", + "symfony/web-profiler-bundle": "<8.1", "twig/twig": "<3.21" }, "provide": { @@ -10966,13 +11079,14 @@ "symfony/config": "^7.4|^8.0", "symfony/console": "^7.4|^8.0", "symfony/css-selector": "^7.4|^8.0", - "symfony/dependency-injection": "^7.4|^8.0", + "symfony/dependency-injection": "^8.1", "symfony/dom-crawler": "^7.4|^8.0", "symfony/expression-language": "^7.4|^8.0", "symfony/finder": "^7.4|^8.0", "symfony/http-client-contracts": "^2.5|^3", "symfony/process": "^7.4|^8.0", "symfony/property-access": "^7.4|^8.0", + "symfony/rate-limiter": "^7.4|^8.0", "symfony/routing": "^7.4|^8.0", "symfony/serializer": "^7.4|^8.0", "symfony/stopwatch": "^7.4|^8.0", @@ -10980,7 +11094,7 @@ "symfony/translation-contracts": "^2.5|^3", "symfony/uid": "^7.4|^8.0", "symfony/validator": "^7.4|^8.0", - "symfony/var-dumper": "^7.4|^8.0", + "symfony/var-dumper": "^8.1", "symfony/var-exporter": "^7.4|^8.0", "twig/twig": "^3.21" }, @@ -11010,7 +11124,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v8.0.8" + "source": "https://github.com/symfony/http-kernel/tree/v8.1.0" }, "funding": [ { @@ -11030,25 +11144,25 @@ "type": "tidelift" } ], - "time": "2026-03-31T21:14:05+00:00" + "time": "2026-05-29T08:46:08+00:00" }, { "name": "symfony/mailer", - "version": "v8.0.8", + "version": "v8.1.0", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "ca5f6edaf8780ece814404b58a4482b22b509c56" + "reference": "9418d772df3a03a142e3bc06f602adb2b8724877" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/ca5f6edaf8780ece814404b58a4482b22b509c56", - "reference": "ca5f6edaf8780ece814404b58a4482b22b509c56", + "url": "https://api.github.com/repos/symfony/mailer/zipball/9418d772df3a03a142e3bc06f602adb2b8724877", + "reference": "9418d772df3a03a142e3bc06f602adb2b8724877", "shasum": "" }, "require": { "egulias/email-validator": "^2.1.10|^3|^4", - "php": ">=8.4", + "php": ">=8.4.1", "psr/event-dispatcher": "^1", "psr/log": "^1|^2|^3", "symfony/event-dispatcher": "^7.4|^8.0", @@ -11090,7 +11204,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v8.0.8" + "source": "https://github.com/symfony/mailer/tree/v8.1.0" }, "funding": [ { @@ -11110,24 +11224,24 @@ "type": "tidelift" } ], - "time": "2026-03-30T15:14:47+00:00" + "time": "2026-05-29T05:06:50+00:00" }, { "name": "symfony/mime", - "version": "v8.0.8", + "version": "v8.1.0", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "ddff21f14c7ce04b98101b399a9463dce8b0ce66" + "reference": "b164ae7e3f7915aacfe9ee155f2f358502440664" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/ddff21f14c7ce04b98101b399a9463dce8b0ce66", - "reference": "ddff21f14c7ce04b98101b399a9463dce8b0ce66", + "url": "https://api.github.com/repos/symfony/mime/zipball/b164ae7e3f7915aacfe9ee155f2f358502440664", + "reference": "b164ae7e3f7915aacfe9ee155f2f358502440664", "shasum": "" }, "require": { - "php": ">=8.4", + "php": ">=8.4.1", "symfony/polyfill-intl-idn": "^1.10", "symfony/polyfill-mbstring": "^1.0" }, @@ -11176,7 +11290,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v8.0.8" + "source": "https://github.com/symfony/mime/tree/v8.1.0" }, "funding": [ { @@ -11196,20 +11310,20 @@ "type": "tidelift" } ], - "time": "2026-03-30T15:14:47+00:00" + "time": "2026-05-29T05:06:50+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.33.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + "reference": "141046a8f9477948ff284fa65be2095baafb94f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2", "shasum": "" }, "require": { @@ -11259,7 +11373,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0" }, "funding": [ { @@ -11279,20 +11393,107 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-04-10T16:19:22+00:00" + }, + { + "name": "symfony/polyfill-deepclone", + "version": "v1.38.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-deepclone.git", + "reference": "c1b95c370cb2ee4ee221f0a317f5ae5dfae9a42e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-deepclone/zipball/c1b95c370cb2ee4ee221f0a317f5ae5dfae9a42e", + "reference": "c1b95c370cb2ee4ee221f0a317f5ae5dfae9a42e", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "provide": { + "ext-deepclone": "*" + }, + "suggest": { + "ext-deepclone": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\DeepClone\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the deepclone extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "deepclone", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-deepclone/tree/v1.38.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-06-08T20:10:26+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.33.0", + "version": "v1.38.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" + "reference": "e9247d281d694a5120554d9afaf54e070e88a603" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/e9247d281d694a5120554d9afaf54e070e88a603", + "reference": "e9247d281d694a5120554d9afaf54e070e88a603", "shasum": "" }, "require": { @@ -11341,7 +11542,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.38.1" }, "funding": [ { @@ -11361,20 +11562,20 @@ "type": "tidelift" } ], - "time": "2025-06-27T09:58:17+00:00" + "time": "2026-05-26T05:58:03+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.33.0", + "version": "v1.38.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3" + "reference": "dc21118016c039a66235cf93d96b435ffb282412" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3", - "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/dc21118016c039a66235cf93d96b435ffb282412", + "reference": "dc21118016c039a66235cf93d96b435ffb282412", "shasum": "" }, "require": { @@ -11428,7 +11629,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.38.1" }, "funding": [ { @@ -11448,20 +11649,20 @@ "type": "tidelift" } ], - "time": "2024-09-10T14:38:51+00:00" + "time": "2026-05-25T15:22:23+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.33.0", + "version": "v1.38.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "3833d7255cc303546435cb650316bff708a1c75c" + "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", - "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/2d446c214bdbe5b71bde5011b060a05fece3ae6b", + "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b", "shasum": "" }, "require": { @@ -11513,7 +11714,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.38.0" }, "funding": [ { @@ -11533,20 +11734,20 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-05-25T13:48:31+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.33.0", + "version": "v1.38.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + "reference": "14c5439eec4ccff081ac14eca2dc57feb2a66d92" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/14c5439eec4ccff081ac14eca2dc57feb2a66d92", + "reference": "14c5439eec4ccff081ac14eca2dc57feb2a66d92", "shasum": "" }, "require": { @@ -11598,7 +11799,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.1" }, "funding": [ { @@ -11618,20 +11819,20 @@ "type": "tidelift" } ], - "time": "2024-12-23T08:48:59+00:00" + "time": "2026-05-26T12:51:13+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.33.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", - "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dfb55726c3a76ea3b6459fcfda1ec2d80a682411", + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411", "shasum": "" }, "require": { @@ -11682,7 +11883,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.37.0" }, "funding": [ { @@ -11702,7 +11903,7 @@ "type": "tidelift" } ], - "time": "2025-01-02T08:10:11+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { "name": "symfony/polyfill-php82", @@ -11786,16 +11987,16 @@ }, { "name": "symfony/polyfill-php83", - "version": "v1.33.0", + "version": "v1.38.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5" + "reference": "8339098cae28673c15cce00d80734af0453054e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5", - "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/8339098cae28673c15cce00d80734af0453054e2", + "reference": "8339098cae28673c15cce00d80734af0453054e2", "shasum": "" }, "require": { @@ -11842,7 +12043,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.38.1" }, "funding": [ { @@ -11862,20 +12063,20 @@ "type": "tidelift" } ], - "time": "2025-07-08T02:45:35+00:00" + "time": "2026-05-26T12:51:13+00:00" }, { "name": "symfony/polyfill-php84", - "version": "v1.33.0", + "version": "v1.38.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php84.git", - "reference": "d8ced4d875142b6a7426000426b8abc631d6b191" + "reference": "f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191", - "reference": "d8ced4d875142b6a7426000426b8abc631d6b191", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa", + "reference": "f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa", "shasum": "" }, "require": { @@ -11922,7 +12123,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-php84/tree/v1.38.1" }, "funding": [ { @@ -11942,20 +12143,20 @@ "type": "tidelift" } ], - "time": "2025-06-24T13:30:11+00:00" + "time": "2026-05-26T12:51:13+00:00" }, { "name": "symfony/polyfill-php85", - "version": "v1.33.0", + "version": "v1.38.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php85.git", - "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91" + "reference": "ba2ba04f3352cfa2dcbbcb90aee13ed967f505b1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", - "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/ba2ba04f3352cfa2dcbbcb90aee13ed967f505b1", + "reference": "ba2ba04f3352cfa2dcbbcb90aee13ed967f505b1", "shasum": "" }, "require": { @@ -12002,7 +12203,87 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php85/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-php85/tree/v1.38.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-26T02:25:22+00:00" + }, + { + "name": "symfony/polyfill-php86", + "version": "v1.38.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php86.git", + "reference": "fcec68d64f46dc84e1f6ffcf2c6dda40ff3143ad" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php86/zipball/fcec68d64f46dc84e1f6ffcf2c6dda40ff3143ad", + "reference": "fcec68d64f46dc84e1f6ffcf2c6dda40ff3143ad", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php86\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.6+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php86/tree/v1.38.0" }, "funding": [ { @@ -12022,20 +12303,20 @@ "type": "tidelift" } ], - "time": "2025-06-23T16:12:55+00:00" + "time": "2026-05-25T11:52:35+00:00" }, { "name": "symfony/polyfill-uuid", - "version": "v1.33.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-uuid.git", - "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2" + "reference": "26dfec253c4cf3e51b541b52ddf7e42cb0908e94" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/21533be36c24be3f4b1669c4725c7d1d2bab4ae2", - "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/26dfec253c4cf3e51b541b52ddf7e42cb0908e94", + "reference": "26dfec253c4cf3e51b541b52ddf7e42cb0908e94", "shasum": "" }, "require": { @@ -12085,7 +12366,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/polyfill-uuid/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.37.0" }, "funding": [ { @@ -12105,24 +12386,24 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { "name": "symfony/process", - "version": "v8.0.8", + "version": "v8.1.0", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc" + "reference": "c4a9e58f235a6bf7f97ffbfedae2687353ac79e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc", - "reference": "cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc", + "url": "https://api.github.com/repos/symfony/process/zipball/c4a9e58f235a6bf7f97ffbfedae2687353ac79e5", + "reference": "c4a9e58f235a6bf7f97ffbfedae2687353ac79e5", "shasum": "" }, "require": { - "php": ">=8.4" + "php": ">=8.4.1" }, "type": "library", "autoload": { @@ -12150,7 +12431,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v8.0.8" + "source": "https://github.com/symfony/process/tree/v8.1.0" }, "funding": [ { @@ -12170,7 +12451,7 @@ "type": "tidelift" } ], - "time": "2026-03-30T15:14:47+00:00" + "time": "2026-05-29T05:06:50+00:00" }, { "name": "symfony/psr-http-message-bridge", @@ -12261,20 +12542,20 @@ }, { "name": "symfony/routing", - "version": "v8.0.8", + "version": "v8.1.0", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "0de330ec2ea922a7b08ec45615bd51179de7fda4" + "reference": "fe0bfec72c8a806109fb9c3a5f2b898fe0c76eb3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/0de330ec2ea922a7b08ec45615bd51179de7fda4", - "reference": "0de330ec2ea922a7b08ec45615bd51179de7fda4", + "url": "https://api.github.com/repos/symfony/routing/zipball/fe0bfec72c8a806109fb9c3a5f2b898fe0c76eb3", + "reference": "fe0bfec72c8a806109fb9c3a5f2b898fe0c76eb3", "shasum": "" }, "require": { - "php": ">=8.4", + "php": ">=8.4.1", "symfony/deprecation-contracts": "^2.5|^3" }, "require-dev": { @@ -12317,7 +12598,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v8.0.8" + "source": "https://github.com/symfony/routing/tree/v8.1.0" }, "funding": [ { @@ -12337,20 +12618,20 @@ "type": "tidelift" } ], - "time": "2026-03-30T15:14:47+00:00" + "time": "2026-05-29T05:06:50+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.6.1", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" + "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", - "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d25d82433a80eba6aa0e6c24b61d7370d99e444a", + "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a", "shasum": "" }, "require": { @@ -12368,7 +12649,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -12404,7 +12685,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.6.1" + "source": "https://github.com/symfony/service-contracts/tree/v3.7.0" }, "funding": [ { @@ -12424,24 +12705,24 @@ "type": "tidelift" } ], - "time": "2025-07-15T11:30:57+00:00" + "time": "2026-03-28T09:44:51+00:00" }, { "name": "symfony/string", - "version": "v8.0.8", + "version": "v8.1.0", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "ae9488f874d7603f9d2dfbf120203882b645d963" + "reference": "afd5944f4005862d961efb85c8bbd5c523c4e3c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/ae9488f874d7603f9d2dfbf120203882b645d963", - "reference": "ae9488f874d7603f9d2dfbf120203882b645d963", + "url": "https://api.github.com/repos/symfony/string/zipball/afd5944f4005862d961efb85c8bbd5c523c4e3c9", + "reference": "afd5944f4005862d961efb85c8bbd5c523c4e3c9", "shasum": "" }, "require": { - "php": ">=8.4", + "php": ">=8.4.1", "symfony/polyfill-ctype": "^1.8", "symfony/polyfill-intl-grapheme": "^1.33", "symfony/polyfill-intl-normalizer": "^1.0", @@ -12494,7 +12775,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v8.0.8" + "source": "https://github.com/symfony/string/tree/v8.1.0" }, "funding": [ { @@ -12514,24 +12795,24 @@ "type": "tidelift" } ], - "time": "2026-03-30T15:14:47+00:00" + "time": "2026-05-29T05:06:50+00:00" }, { "name": "symfony/translation", - "version": "v8.0.8", + "version": "v8.1.0", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "27c03ae3940de24ba2f71cfdbac824f2aa1fdf2f" + "reference": "b2bd012ca28c4acae830ee1206a5b6e35dd99693" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/27c03ae3940de24ba2f71cfdbac824f2aa1fdf2f", - "reference": "27c03ae3940de24ba2f71cfdbac824f2aa1fdf2f", + "url": "https://api.github.com/repos/symfony/translation/zipball/b2bd012ca28c4acae830ee1206a5b6e35dd99693", + "reference": "b2bd012ca28c4acae830ee1206a5b6e35dd99693", "shasum": "" }, "require": { - "php": ">=8.4", + "php": ">=8.4.1", "symfony/polyfill-mbstring": "^1.0", "symfony/translation-contracts": "^3.6.1" }, @@ -12587,7 +12868,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v8.0.8" + "source": "https://github.com/symfony/translation/tree/v8.1.0" }, "funding": [ { @@ -12607,20 +12888,20 @@ "type": "tidelift" } ], - "time": "2026-03-30T15:14:47+00:00" + "time": "2026-05-29T05:06:50+00:00" }, { "name": "symfony/translation-contracts", - "version": "v3.6.1", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "65a8bc82080447fae78373aa10f8d13b38338977" + "reference": "0ab302977a952b42fd51475c4ebac81f8da0a95d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/65a8bc82080447fae78373aa10f8d13b38338977", - "reference": "65a8bc82080447fae78373aa10f8d13b38338977", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/0ab302977a952b42fd51475c4ebac81f8da0a95d", + "reference": "0ab302977a952b42fd51475c4ebac81f8da0a95d", "shasum": "" }, "require": { @@ -12633,7 +12914,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -12669,7 +12950,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.6.1" + "source": "https://github.com/symfony/translation-contracts/tree/v3.7.0" }, "funding": [ { @@ -12689,7 +12970,7 @@ "type": "tidelift" } ], - "time": "2025-07-15T13:41:35+00:00" + "time": "2026-01-05T13:30:16+00:00" }, { "name": "symfony/type-info", @@ -12775,20 +13056,20 @@ }, { "name": "symfony/uid", - "version": "v8.0.8", + "version": "v8.1.0", "source": { "type": "git", "url": "https://github.com/symfony/uid.git", - "reference": "f63fa6096a24147283bce4d29327d285326438e0" + "reference": "7393f157a55f7e70a4de0334435c55a5a8fe749a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/f63fa6096a24147283bce4d29327d285326438e0", - "reference": "f63fa6096a24147283bce4d29327d285326438e0", + "url": "https://api.github.com/repos/symfony/uid/zipball/7393f157a55f7e70a4de0334435c55a5a8fe749a", + "reference": "7393f157a55f7e70a4de0334435c55a5a8fe749a", "shasum": "" }, "require": { - "php": ">=8.4", + "php": ">=8.4.1", "symfony/polyfill-uuid": "^1.15" }, "require-dev": { @@ -12829,7 +13110,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/uid/tree/v8.0.8" + "source": "https://github.com/symfony/uid/tree/v8.1.0" }, "funding": [ { @@ -12849,24 +13130,24 @@ "type": "tidelift" } ], - "time": "2026-03-30T15:14:47+00:00" + "time": "2026-05-29T05:06:50+00:00" }, { "name": "symfony/var-dumper", - "version": "v8.0.8", + "version": "v8.1.0", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "cfb7badd53bf4177f6e9416cfbbccc13c0e773a1" + "reference": "c2c4df1d21477cc21c9f6dc1b14d07c3abc4963e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/cfb7badd53bf4177f6e9416cfbbccc13c0e773a1", - "reference": "cfb7badd53bf4177f6e9416cfbbccc13c0e773a1", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/c2c4df1d21477cc21c9f6dc1b14d07c3abc4963e", + "reference": "c2c4df1d21477cc21c9f6dc1b14d07c3abc4963e", "shasum": "" }, "require": { - "php": ">=8.4", + "php": ">=8.4.1", "symfony/polyfill-mbstring": "^1.0" }, "conflict": { @@ -12916,7 +13197,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v8.0.8" + "source": "https://github.com/symfony/var-dumper/tree/v8.1.0" }, "funding": [ { @@ -12936,24 +13217,26 @@ "type": "tidelift" } ], - "time": "2026-03-31T07:15:36+00:00" + "time": "2026-05-29T05:06:50+00:00" }, { "name": "symfony/var-exporter", - "version": "v8.0.0", + "version": "v8.1.0", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "7345f46c251f2eb27c7b3ebdb5bb076b3ffcae04" + "reference": "2dd18582c5f6c024db9fc0ff9c76d873af726f34" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/7345f46c251f2eb27c7b3ebdb5bb076b3ffcae04", - "reference": "7345f46c251f2eb27c7b3ebdb5bb076b3ffcae04", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/2dd18582c5f6c024db9fc0ff9c76d873af726f34", + "reference": "2dd18582c5f6c024db9fc0ff9c76d873af726f34", "shasum": "" }, "require": { - "php": ">=8.4" + "php": ">=8.4.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-deepclone": "^1.37" }, "require-dev": { "symfony/property-access": "^7.4|^8.0", @@ -12983,11 +13266,12 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Allows exporting any serializable PHP data structure to plain PHP code", + "description": "Provides tools to export, instantiate, hydrate, clone and lazy-load PHP objects", "homepage": "https://symfony.com", "keywords": [ "clone", "construct", + "deep-clone", "export", "hydrate", "instantiate", @@ -12996,7 +13280,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v8.0.0" + "source": "https://github.com/symfony/var-exporter/tree/v8.1.0" }, "funding": [ { @@ -13016,20 +13300,20 @@ "type": "tidelift" } ], - "time": "2025-11-05T18:53:00+00:00" + "time": "2026-05-29T05:06:50+00:00" }, { "name": "symfony/yaml", - "version": "v7.4.8", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "c58fdf7b3d6c2995368264c49e4e8b05bcff2883" + "reference": "a7ec3b1156faf8815db7683ec7c1e7338e6f977c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/c58fdf7b3d6c2995368264c49e4e8b05bcff2883", - "reference": "c58fdf7b3d6c2995368264c49e4e8b05bcff2883", + "url": "https://api.github.com/repos/symfony/yaml/zipball/a7ec3b1156faf8815db7683ec7c1e7338e6f977c", + "reference": "a7ec3b1156faf8815db7683ec7c1e7338e6f977c", "shasum": "" }, "require": { @@ -13072,7 +13356,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.4.8" + "source": "https://github.com/symfony/yaml/tree/v7.4.13" }, "funding": [ { @@ -13092,7 +13376,7 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-05-25T06:06:12+00:00" }, { "name": "tbachert/spi", @@ -13399,23 +13683,23 @@ }, { "name": "voku/portable-ascii", - "version": "2.0.3", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/voku/portable-ascii.git", - "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d" + "reference": "8e1051fe39379367aecf014f41744ce7539a856f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", - "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/8e1051fe39379367aecf014f41744ce7539a856f", + "reference": "8e1051fe39379367aecf014f41744ce7539a856f", "shasum": "" }, "require": { - "php": ">=7.0.0" + "php": ">=7.1.0" }, "require-dev": { - "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" + "phpunit/phpunit": "~8.5 || ~9.6 || ~10.5 || ~11.5" }, "suggest": { "ext-intl": "Use Intl for transliterator_transliterate() support" @@ -13445,7 +13729,7 @@ ], "support": { "issues": "https://github.com/voku/portable-ascii/issues", - "source": "https://github.com/voku/portable-ascii/tree/2.0.3" + "source": "https://github.com/voku/portable-ascii/tree/2.1.1" }, "funding": [ { @@ -13469,7 +13753,7 @@ "type": "tidelift" } ], - "time": "2024-11-21T01:49:47+00:00" + "time": "2026-04-26T05:33:54+00:00" }, { "name": "whichbrowser/parser", @@ -14018,73 +14302,6 @@ }, "time": "2026-03-05T07:58:43+00:00" }, - { - "name": "masterminds/html5", - "version": "2.10.0", - "source": { - "type": "git", - "url": "https://github.com/Masterminds/html5-php.git", - "reference": "fcf91eb64359852f00d921887b219479b4f21251" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/fcf91eb64359852f00d921887b219479b4f21251", - "reference": "fcf91eb64359852f00d921887b219479b4f21251", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.7-dev" - } - }, - "autoload": { - "psr-4": { - "Masterminds\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matt Butcher", - "email": "technosophos@gmail.com" - }, - { - "name": "Matt Farina", - "email": "matt@mattfarina.com" - }, - { - "name": "Asmir Mustafic", - "email": "goetas@gmail.com" - } - ], - "description": "An HTML5 parser and serializer.", - "homepage": "http://masterminds.github.io/html5-php", - "keywords": [ - "HTML5", - "dom", - "html", - "parser", - "querypath", - "serializer", - "xml" - ], - "support": { - "issues": "https://github.com/Masterminds/html5-php/issues", - "source": "https://github.com/Masterminds/html5-php/tree/2.10.0" - }, - "time": "2025-07-25T09:04:22+00:00" - }, { "name": "mockery/mockery", "version": "1.6.12", @@ -15813,78 +16030,6 @@ ], "time": "2024-10-20T05:08:20+00:00" }, - { - "name": "symfony/dom-crawler", - "version": "v7.4.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/dom-crawler.git", - "reference": "0c5e8f20c74c78172a8ee72b125909b505033597" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/0c5e8f20c74c78172a8ee72b125909b505033597", - "reference": "0c5e8f20c74c78172a8ee72b125909b505033597", - "shasum": "" - }, - "require": { - "masterminds/html5": "^2.6", - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.0" - }, - "require-dev": { - "symfony/css-selector": "^6.4|^7.0|^8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\DomCrawler\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Eases DOM navigation for HTML and XML documents", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/dom-crawler/tree/v7.4.1" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-12-06T15:47:47+00:00" - }, { "name": "theseer/tokenizer", "version": "2.0.1", diff --git a/config/l5-swagger.php b/config/l5-swagger.php index 2ef80f985e..ee6f509a2e 100644 --- a/config/l5-swagger.php +++ b/config/l5-swagger.php @@ -166,9 +166,9 @@ 'description' => 'Laravel passport oauth2 security.', 'flows' => [ 'authorizationCode' => [ - 'authorizationUrl' => config('app.url') . '/oauth/authorize', - 'tokenUrl' => config('app.url') . '/oauth/token', - 'refreshUrl' => config('app.url') . '/token/refresh', + 'authorizationUrl' => '/oauth/authorize', + 'tokenUrl' => '/oauth/token', + 'refreshUrl' => '/token/refresh', 'scopes' => (object) [], ], ], diff --git a/package-lock.json b/package-lock.json index f95f9faf2d..87c6cdb6c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@processmaker/processmaker", - "version": "2026.9.4-RC1", + "version": "2026.10.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@processmaker/processmaker", - "version": "2026.9.4-RC1", + "version": "2026.10.6", "hasInstallScript": true, "license": "ISC", "dependencies": { diff --git a/package.json b/package.json index abc5983b1c..5808531b06 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@processmaker/processmaker", - "version": "2026.9.4-RC1", + "version": "2026.10.6", "description": "ProcessMaker 4", "author": "DevOps ", "license": "ISC", diff --git a/resources/js/tasks/components/ListMixin.js b/resources/js/tasks/components/ListMixin.js index 7f35e23d7e..d39e4e332b 100644 --- a/resources/js/tasks/components/ListMixin.js +++ b/resources/js/tasks/components/ListMixin.js @@ -83,10 +83,12 @@ const ListMixin = { } this.previousAdvancedFilter = advancedFilter; let includeString = "process,processRequest,processRequest.user,user,data"; - // If columns are default (isDefaultColumns = true), don't include data - // If columns are NOT default (isDefaultColumns = false), include data + // Omit data only for default columns without form fields (FOUR-24946 payload optimization) const isDefaultColumns = window.ProcessMaker?.isDefaultColumns ?? false; - if (isDefaultColumns) { + const hasDataColumns = (this.columns || []).some( + (column) => String(column.field || "").startsWith("data."), + ); + if (isDefaultColumns && !hasDataColumns) { includeString = "process,processRequest,processRequest.user,user"; } const include = includeString.split(","); diff --git a/resources/jscomposition/cases/casesDetail/components/CompletedForms.vue b/resources/jscomposition/cases/casesDetail/components/CompletedForms.vue index ce222a3a22..dd0422e804 100644 --- a/resources/jscomposition/cases/casesDetail/components/CompletedForms.vue +++ b/resources/jscomposition/cases/casesDetail/components/CompletedForms.vue @@ -76,7 +76,7 @@ const getData = async () => { const response = await getDataTask({ params: { case_number: getCaseNumber(), - status: "CLOSED", + status: "CLOSED,TRIGGERED", includeScreen: 1, order_by: filter.value?.field, order_direction: filter.value?.filter, diff --git a/routes/api.php b/routes/api.php index 011ca9d41d..b3e7891063 100644 --- a/routes/api.php +++ b/routes/api.php @@ -210,7 +210,7 @@ // Permissions Route::get('permissions', [PermissionController::class, 'index'])->name('permissions.index'); - Route::put('permissions', [PermissionController::class, 'update'])->name('permissions.update')->middleware('can:edit-users'); + Route::put('permissions', [PermissionController::class, 'update'])->name('permissions.update'); // Tenant Jobs Dashboard API Route::get('tenant-queues/tenants', [TenantQueueController::class, 'getTenants'])->name('tenant-queue.tenants'); diff --git a/routes/web.php b/routes/web.php index 6ce99b4904..c0cf1141bf 100644 --- a/routes/web.php +++ b/routes/web.php @@ -292,3 +292,8 @@ 'Content-Type' => 'text/plain; version=0.0.4', ]); }); + +Route::get('api/oauth2-redirect.js', function () { + // Fix l5-swagger and swagger-ui mismatch. See https://github.com/DarkaOnLine/L5-Swagger/issues/648 + return response()->file(base_path('vendor/swagger-api/swagger-ui/dist/oauth2-redirect.js')); +})->name('oauth2-redirect.js'); diff --git a/storage/api-docs/api-docs.json b/storage/api-docs/api-docs.json index 3755004f8f..3b01de8d45 100644 --- a/storage/api-docs/api-docs.json +++ b/storage/api-docs/api-docs.json @@ -18,14 +18,92 @@ } ], "paths": { - "/collections": { + "/cases/{case_number}": { + "delete": { + "tags": [ + "Cases" + ], + "summary": "Delete a case and its related requests", + "description": "Delete a case and its related requests.", + "operationId": "deleteCase", + "parameters": [ + { + "name": "case_number", + "in": "path", + "description": "Case number to delete", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "success" + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "$ref": "#/components/responses/404" + }, + "409": { + "description": "Conflict" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/customize-ui": { + "post": { + "tags": [ + "CssSettings" + ], + "summary": "Create or update a new setting", + "description": "Create a new Settings css-override", + "operationId": "updateCssSetting", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "properties": { + "variables": { + "type": "string" + }, + "sansSerifFont": { + "type": "string" + } + }, + "type": "object" + } + } + } + }, + "responses": { + "201": { + "description": "success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/settings" + } + } + } + } + } + } + }, + "/environment_variables": { "get": { "tags": [ - "Collections" + "Environment Variables" ], - "summary": "Returns all collections that the user has access to", - "description": "Get a list of Collections.", - "operationId": "getCollections", + "summary": "Returns all environmentVariables that the user has access to. For security, values are not included.", + "description": "Fetch a collection of variables based on paged request and filter if provided", + "operationId": "getEnvironmentVariables", "parameters": [ { "$ref": "#/components/parameters/filter" @@ -45,7 +123,7 @@ ], "responses": { "200": { - "description": "list of collections", + "description": "list of environmentVariables", "content": { "application/json": { "schema": { @@ -53,11 +131,11 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/collections" + "$ref": "#/components/schemas/EnvironmentVariable" } }, "meta": { - "$ref": "#/components/schemas/metadata" + "type": "object" } }, "type": "object" @@ -69,17 +147,17 @@ }, "post": { "tags": [ - "Collections" + "Environment Variables" ], - "summary": "Save a new collections", - "description": "Create a new Collection.", - "operationId": "createCollection", + "summary": "Create a new environment variable", + "description": "Creates a new global Environment Variable in the system", + "operationId": "createEnvironmentVariable", "requestBody": { "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/collectionsEditable" + "$ref": "#/components/schemas/EnvironmentVariableEditable" } } } @@ -90,7 +168,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/collections" + "$ref": "#/components/schemas/EnvironmentVariable" } } } @@ -98,32 +176,32 @@ } } }, - "/collections/{collection_id}": { + "/environment_variables/{environment_variable_id}": { "get": { "tags": [ - "Collections" + "Environment Variables" ], - "summary": "Get single collections by ID", - "description": "Get a single Collection.", - "operationId": "getCollectionById", + "summary": "Get an environment variable by id. For security, the value is not included.", + "description": "Return an environment variable instance\nUsing implicit model binding, will automatically return 404 if variable now found", + "operationId": "getEnvironmentVariableById", "parameters": [ { - "name": "collection_id", + "name": "environment_variable_id", "in": "path", - "description": "ID of collection to return", + "description": "ID of environment_variables to return", "required": true, "schema": { - "type": "string" + "type": "integer" } } ], "responses": { - "200": { - "description": "Successfully found the collections", + "201": { + "description": "success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/collections" + "$ref": "#/components/schemas/EnvironmentVariable" } } } @@ -132,19 +210,19 @@ }, "put": { "tags": [ - "Collections" + "Environment Variables" ], - "summary": "Update a collection", - "description": "Update a Collection.", - "operationId": "updateCollection", + "summary": "Update an environment variable", + "description": "Update an environment variable", + "operationId": "updateEnvironmentVariable", "parameters": [ { - "name": "collection_id", + "name": "environment_variable_id", "in": "path", - "description": "ID of collection to update", + "description": "ID of environment variables to update", "required": true, "schema": { - "type": "string" + "type": "integer" } } ], @@ -153,167 +231,57 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/collectionsEditable" - } - } - } - }, - "responses": { - "204": { - "description": "success" - } - } - }, - "delete": { - "tags": [ - "Collections" - ], - "summary": "Delete a collection", - "description": "Delete a Collection.", - "operationId": "deleteCollection", - "parameters": [ - { - "name": "collection_id", - "in": "path", - "description": "ID of collection to return", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "success" - } - } - } - }, - "/collections/{collection_id}/export": { - "post": { - "tags": [ - "Screens" - ], - "summary": "Trigger export collections job", - "description": "Export the specified collection.", - "operationId": "exportCollection", - "parameters": [ - { - "name": "collection_id", - "in": "path", - "description": "ID of the collection to export", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "202": { - "description": "success" - } - } - } - }, - "/collections/import": { - "post": { - "tags": [ - "Collections" - ], - "summary": "Import a new collection", - "description": "Import the specified collection.", - "operationId": "importCollection", - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "required": [ - "file" - ], - "properties": { - "file": { - "description": "file to upload", - "type": "file", - "format": "file" - } - }, - "type": "object" + "$ref": "#/components/schemas/EnvironmentVariableEditable" } } } }, "responses": { - "201": { + "200": { "description": "success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/collections" + "$ref": "#/components/schemas/EnvironmentVariable" } } } - }, - "200": { - "description": "success" } } - } - }, - "/collections/{collection_id}/truncate": { + }, "delete": { "tags": [ - "Collections" + "Environment Variables" ], - "summary": "Deletes all records in a collection", - "description": "Truncate a Collection.", - "operationId": "truncateCollection", + "summary": "Delete an environment variable", + "operationId": "deleteEnvironmentVariable", "parameters": [ { - "name": "collection_id", + "name": "environment_variable_id", "in": "path", - "description": "ID of collection to truncate", + "description": "ID of environment_variables to return", "required": true, "schema": { - "type": "string" + "type": "integer" } } ], "responses": { - "204": { + "200": { "description": "success" } } } }, - "/collections/{collection_id}/records": { + "/files": { "get": { "tags": [ - "Collections" + "Files" ], - "summary": "Returns all records", - "description": "Get the list of records of a collection.", - "operationId": "getRecords", + "summary": "Returns the list of files", + "description": "Display a listing of the resource.", + "operationId": "getFiles", "parameters": [ - { - "name": "collection_id", - "in": "path", - "description": "ID of collection to get records for", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "pmql", - "in": "query", - "schema": { - "type": "string" - } - }, - { - "$ref": "#/components/parameters/per_page" - }, { "$ref": "#/components/parameters/filter" }, @@ -324,12 +292,12 @@ "$ref": "#/components/parameters/order_direction" }, { - "$ref": "#/components/parameters/include" + "$ref": "#/components/parameters/per_page" } ], "responses": { "200": { - "description": "list of records of a collection", + "description": "list of files", "content": { "application/json": { "schema": { @@ -337,7 +305,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/records" + "$ref": "#/components/schemas/media" } }, "meta": { @@ -353,232 +321,208 @@ }, "post": { "tags": [ - "Collections" + "Files" ], - "summary": "Save a new record in a collection", - "description": "Create a new record in a Collection.", - "operationId": "createRecord", + "summary": "Save a new media file. Note: To upload files to a request, use createRequestFile in the RequestFile API", + "description": "Store a newly created resource in storage.", + "operationId": "createFile", "parameters": [ { - "name": "collection_id", - "in": "path", - "description": "ID of the collection", + "name": "model_id", + "in": "query", + "description": "ID of the model to which the file will be associated", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "model", + "in": "query", + "description": "Full namespaced class of the model to associate", "required": true, "schema": { "type": "string" } + }, + { + "name": "data_name", + "in": "query", + "description": "Name of the variable used in a request", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "collection", + "in": "query", + "description": "Media collection name. For requests, use 'default'", + "required": false, + "schema": { + "type": "string" + } } ], "requestBody": { "required": true, "content": { - "application/json": { + "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/recordsEditable" + "properties": { + "file": { + "description": "save a new media file", + "type": "string", + "format": "binary" + } + }, + "type": "object" } } } }, "responses": { - "201": { + "200": { "description": "success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/records" - } + "properties": { + "id": { + "type": "string" + }, + "model_id": { + "type": "string" + }, + "file_name": { + "type": "string" + }, + "mime_type": { + "type": "string" + } + }, + "type": "object" + } } } } } } }, - "/collections/{collection_id}/records/{record_id}": { + "/files/{file_id}": { "get": { "tags": [ - "Collections" + "Files" ], - "summary": "Get single record of a collection", - "description": "Get a single record of a Collection.", - "operationId": "getRecordById", + "summary": "Get the metadata of a file. To actually fetch the file see Get File Contents", + "description": "Get a single media file.", + "operationId": "getFileById", "parameters": [ { - "name": "collection_id", - "in": "path", - "description": "ID of the collection", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "record_id", + "name": "file_id", "in": "path", - "description": "ID of the record to return", + "description": "ID of the file to return", "required": true, "schema": { - "type": "string" + "type": "integer" } } ], "responses": { "200": { - "description": "Successfully found the record", + "description": "Successfully found the file", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/records" + "$ref": "#/components/schemas/media" } } } - } - } - }, - "put": { - "tags": [ - "Collections" - ], - "summary": "Update a record", - "description": "Update a record in a Collection.", - "operationId": "updateRecord", - "parameters": [ - { - "name": "collection_id", - "in": "path", - "description": "ID of collection", - "required": true, - "schema": { - "type": "string" - } }, - { - "name": "record_id", - "in": "path", - "description": "ID of the record ", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/recordsEditable" - } - } - } - }, - "responses": { - "204": { - "description": "success" + "404": { + "$ref": "#/components/responses/404" } } }, "delete": { "tags": [ - "Collections" + "Files" ], - "summary": "Delete a collection record", - "description": "Delete a record of a Collection.", - "operationId": "deleteRecord", + "summary": "Delete a media file", + "description": "Remove the specified resource from storage.", + "operationId": "deleteFile", "parameters": [ { - "name": "collection_id", - "in": "path", - "description": "ID of collection", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "record_id", + "name": "file_id", "in": "path", - "description": "ID of record in collection", + "description": "ID of the file", "required": true, "schema": { - "type": "string" + "type": "integer" } } ], "responses": { "204": { "description": "success" + }, + "404": { + "$ref": "#/components/responses/404" } } - }, - "patch": { + } + }, + "/files/{file_id}/contents": { + "get": { "tags": [ - "Collections" + "Files" ], - "summary": "Partial update of a record", - "description": "Implements a partial update of a record in a Collection.", - "operationId": "patchRecord", + "summary": "Get the contents of a file", + "description": "Display the specified resource.", + "operationId": "getFileContentsById", "parameters": [ { - "name": "collection_id", - "in": "path", - "description": "ID of collection ", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "record_id", + "name": "file_id", "in": "path", - "description": "ID of the record ", + "description": "ID of the file to return", "required": true, "schema": { - "type": "string" + "type": "integer" } } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/collectionsEditable" - } - } - } - }, "responses": { "200": { - "description": "success" + "description": "File stream", + "content": { + "application/octet-stream": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "404": { + "$ref": "#/components/responses/404" } } } }, - "/saved-searches/{saved_search_id}/charts": { + "/groups": { "get": { "tags": [ - "SavedSearchCharts" + "Groups" ], - "summary": "Returns all saved search charts that the user has access to", - "description": "Get a list of SavedSearchCharts.", - "operationId": "getSavedSearchCharts", + "summary": "Returns all groups that the user has access to", + "description": "Display a listing of the resource.", + "operationId": "getGroups", "parameters": [ { - "$ref": "#/components/parameters/filter" + "$ref": "#/components/parameters/status" }, { - "name": "type", - "in": "query", - "description": "Only return saved searches by type", - "required": false, - "schema": { - "type": "string", - "enum": [ - "request", - "task", - "collection" - ] - } + "$ref": "#/components/parameters/filter" }, { "$ref": "#/components/parameters/order_by" @@ -595,7 +539,7 @@ ], "responses": { "200": { - "description": "list of saved search charts", + "description": "list of groups", "content": { "application/json": { "schema": { @@ -603,16 +547,11 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/SavedSearchChart" + "$ref": "#/components/schemas/groups" } }, "meta": { - "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/metadata" - } - ] + "$ref": "#/components/schemas/metadata" } }, "type": "object" @@ -622,67 +561,19 @@ } } }, - "put": { - "tags": [ - "SavedSearchCharts" - ], - "summary": "Update several saved search charts at once", - "description": "Batch update several SavedSearchCharts.", - "operationId": "batchUpdateSavedSearchCharts", - "parameters": [ - { - "name": "saved_search_id", - "in": "path", - "description": "ID of saved search to which these charts will be saved", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SavedSearchChart" - } - } - } - } - }, - "responses": { - "204": { - "description": "success" - } - } - }, "post": { "tags": [ - "SavedSearchCharts" - ], - "summary": "Save a new saved search chart", - "description": "Create a new SavedSearchChart.", - "operationId": "createSavedSearchChart", - "parameters": [ - { - "name": "saved_search_id", - "in": "path", - "description": "ID of saved search to which this chart will be saved", - "required": true, - "schema": { - "type": "string" - } - } + "Groups" ], + "summary": "Save a new group", + "description": "Store a newly created resource in storage.", + "operationId": "createGroup", "requestBody": { "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SavedSearchChartEditable" + "$ref": "#/components/schemas/groupsEditable" } } } @@ -693,61 +584,67 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SavedSearchChart" + "$ref": "#/components/schemas/groups" } } } + }, + "422": { + "$ref": "#/components/responses/422" } } } }, - "/saved-searches/charts/{chart_id}": { + "/groups/{group_id}": { "get": { "tags": [ - "SavedSearchCharts" + "Groups" ], - "summary": "Get single saved search chart by ID", - "description": "Get a single SavedSearchChart.", - "operationId": "getSavedSearchChartById", + "summary": "Get single group by ID", + "description": "Display the specified resource.", + "operationId": "getGroupById", "parameters": [ { - "name": "chart_id", + "name": "group_id", "in": "path", - "description": "ID of chart to return", + "description": "ID of group to return", "required": true, "schema": { - "type": "string" + "type": "integer" } } ], "responses": { "200": { - "description": "Successfully found the saved search chart", + "description": "Successfully found the group", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SavedSearchChart" + "$ref": "#/components/schemas/groups" } } } + }, + "404": { + "$ref": "#/components/responses/404" } } }, "put": { "tags": [ - "SavedSearchCharts" + "Groups" ], - "summary": "Update a saved search chart", - "description": "Update a SavedSearchChart.", - "operationId": "updateSavedSearchChart", + "summary": "Update a group", + "description": "Update a user", + "operationId": "updateGroup", "parameters": [ { - "name": "chart_id", + "name": "group_id", "in": "path", - "description": "ID of chart to return", + "description": "ID of group to return", "required": true, "schema": { - "type": "string" + "type": "integer" } } ], @@ -756,106 +653,94 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SavedSearchChartEditable" + "$ref": "#/components/schemas/groupsEditable" } } } }, "responses": { - "200": { - "description": "success", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SavedSearchChart" - } - } - } + "204": { + "description": "success" + }, + "404": { + "$ref": "#/components/responses/404" } } }, "delete": { "tags": [ - "SavedSearchCharts" + "Groups" ], - "summary": "Delete a saved search chart", - "description": "Delete a SavedSearchChart.", - "operationId": "deleteSavedSearchChart", + "summary": "Delete a group", + "description": "Delete a user", + "operationId": "deleteGroup", "parameters": [ { - "name": "chart_id", + "name": "group_id", "in": "path", - "description": "ID of chart to return", + "description": "ID of group to return", "required": true, "schema": { - "type": "string" + "type": "integer" } } ], "responses": { "204": { "description": "success" + }, + "404": { + "$ref": "#/components/responses/404" } } } }, - "/saved-searches/charts/{chart_id}/fields": { + "/groups/{group_id}/users": { "get": { "tags": [ - "SavedSearchCharts" + "Groups" ], - "summary": "Get available chart fields for a Saved Search by ID", - "description": "Get available chart fields for a Saved Search.", - "operationId": "getSavedSearchFieldsById", + "summary": "Returns all users of a group", + "description": "Display the list of users in a group", + "operationId": "getGroupUsers", "parameters": [ { - "name": "chart_id", + "name": "group_id", "in": "path", - "description": "ID of Saved Search to return", + "description": "ID of group", "required": true, "schema": { - "type": "string" + "type": "integer" } + }, + { + "$ref": "#/components/parameters/filter" + }, + { + "$ref": "#/components/parameters/order_direction" + }, + { + "$ref": "#/components/parameters/per_page" } ], "responses": { "200": { - "description": "Successfully found the saved search", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SavedSearch" - } - } - } - } - } - } - }, - "/saved-searches/reports": { - "post": { - "tags": [ - "Reports" - ], - "summary": "Save a new report", - "operationId": "createReport", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportEditable" - } - } - } - }, - "responses": { - "201": { - "description": "success", + "description": "list of members of a group", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Report" + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/users" + } + }, + "meta": { + "$ref": "#/components/schemas/metadata" + } + }, + "type": "object" } } } @@ -863,42 +748,52 @@ } } }, - "/saved-searches/reports/{reportId}": { - "put": { + "/groups/{group_id}/groups": { + "get": { "tags": [ - "SavedSearches" + "Groups" ], - "summary": "Update a saved search", - "description": "Update a Report", - "operationId": "updateReport", + "summary": "Returns all users of a group", + "description": "Display the list of groups in a group", + "operationId": "getGroupGroupss", "parameters": [ { - "name": "reportId", + "name": "group_id", "in": "path", - "description": "ID of report", + "description": "ID of group", "required": true, "schema": { - "type": "string" + "type": "integer" } + }, + { + "$ref": "#/components/parameters/filter" + }, + { + "$ref": "#/components/parameters/order_direction" + }, + { + "$ref": "#/components/parameters/per_page" } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SavedSearchEditable" - } - } - } - }, "responses": { "200": { - "description": "success", + "description": "list of members of a group", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SavedSearch" + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/groups" + } + }, + "meta": { + "$ref": "#/components/schemas/metadata" + } + }, + "type": "object" } } } @@ -906,44 +801,17 @@ } } }, - "/saved-searches": { + "/group_members": { "get": { "tags": [ - "SavedSearches" + "Group Members" ], - "summary": "Returns all saved searches that the user has access to", - "description": "Get a list of SavedSearches.", - "operationId": "getSavedSearches", + "summary": "Returns all groups for a given member", + "description": "Display a listing of the resource.", + "operationId": "getGroupMembers", "parameters": [ { - "$ref": "#/components/parameters/filter" - }, - { - "name": "type", - "in": "query", - "description": "Only return saved searches by type", - "required": false, - "schema": { - "type": "string", - "enum": [ - "request", - "task", - "collection" - ] - } - }, - { - "name": "subset", - "in": "query", - "description": "Only return saved searches that are yours or those that have been shared with you", - "required": false, - "schema": { - "type": "string", - "enum": [ - "mine", - "shared" - ] - } + "$ref": "#/components/parameters/member_id" }, { "$ref": "#/components/parameters/order_by" @@ -953,14 +821,11 @@ }, { "$ref": "#/components/parameters/per_page" - }, - { - "$ref": "#/components/parameters/include" } ], "responses": { "200": { - "description": "list of saved searches", + "description": "list of group_members", "content": { "application/json": { "schema": { @@ -968,16 +833,11 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/SavedSearch" + "$ref": "#/components/schemas/groupMembers" } }, "meta": { - "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/metadata" - } - ] + "$ref": "#/components/schemas/metadata" } }, "type": "object" @@ -989,17 +849,17 @@ }, "post": { "tags": [ - "SavedSearches" + "Group Members" ], - "summary": "Save a new saved search", - "description": "Create a new SavedSearch.", - "operationId": "createSavedSearch", + "summary": "Save a new group member", + "description": "Store a newly created resource in storage.", + "operationId": "createGroupMember", "requestBody": { "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SavedSearchEditable" + "$ref": "#/components/schemas/groupMembersEditable" } } } @@ -1010,7 +870,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SavedSearch" + "$ref": "#/components/schemas/createGroupMembers" } } } @@ -1018,19 +878,19 @@ } } }, - "/saved-searches/{savedSearchId}": { + "/group_members/{group_member_id}": { "get": { "tags": [ - "SavedSearches" + "Group Members" ], - "summary": "Get single saved searches by ID", - "description": "Get a single SavedSearch.", - "operationId": "getSavedSearchById", + "summary": "Get single group member by ID", + "description": "Display the specified resource.", + "operationId": "getGroupMemberById", "parameters": [ { - "name": "savedSearchId", + "name": "group_member_id", "in": "path", - "description": "ID of saved search to return", + "description": "ID of group members to return", "required": true, "schema": { "type": "string" @@ -1039,126 +899,97 @@ ], "responses": { "200": { - "description": "Successfully found the saved search", + "description": "Successfully found the group members", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SavedSearch" + "$ref": "#/components/schemas/getGroupMembersById" } } } } } }, - "put": { + "delete": { "tags": [ - "SavedSearches" + "Group Members" ], - "summary": "Update a saved search", - "description": "Update a SavedSearch.", - "operationId": "updateSavedSearch", + "summary": "Delete a group member", + "description": "Delete a group membership", + "operationId": "deleteGroupMember", "parameters": [ { - "name": "savedSearchId", + "name": "group_member_id", "in": "path", - "description": "ID of saved search to return", + "description": "ID of group_members to return", "required": true, "schema": { "type": "string" } } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SavedSearchEditable" - } - } - } - }, "responses": { - "200": { - "description": "success", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SavedSearch" - } - } - } + "204": { + "description": "success" } } } }, - "/saved-searches/{savedSearchId}/columns": { + "/group_members_available": { "get": { "tags": [ - "SavedSearches" + "Group Members" ], - "summary": "Returns all columns associated with a Saved Search", - "description": "Display a listing of columns.", - "operationId": "getSavedSearchColumns", + "summary": "Returns all groups available for a given member", + "description": "Display a listing of groups available", + "operationId": "getGroupMembersAvailable", "parameters": [ { - "name": "savedSearchId", + "name": "member_id", "in": "path", - "description": "ID of saved search to return", + "description": "ID of group member to return", "required": true, "schema": { "type": "string" } }, { - "name": "include", - "in": "query", - "description": "Include specific categories. Comma separated list.", + "name": "member_type", + "in": "path", + "description": "type of group member to return", + "required": true, "schema": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "current", - "default", - "available", - "data" - ] - }, - "uniqueItems": false + "type": "string" } + }, + { + "$ref": "#/components/parameters/filter" + }, + { + "$ref": "#/components/parameters/order_by" + }, + { + "$ref": "#/components/parameters/order_direction" + }, + { + "$ref": "#/components/parameters/per_page" } ], "responses": { "200": { - "description": "Categorized list of columns", + "description": "list of groups available to be assigned as member", "content": { "application/json": { "schema": { "properties": { - "current": { - "type": "array", - "items": { - "$ref": "#/components/schemas/columns" - } - }, - "default": { - "type": "array", - "items": { - "$ref": "#/components/schemas/columns" - } - }, - "available": { - "type": "array", - "items": { - "$ref": "#/components/schemas/columns" - } - }, "data": { "type": "array", "items": { - "$ref": "#/components/schemas/columns" + "$ref": "#/components/schemas/availableGroupMembers" } + }, + "meta": { + "$ref": "#/components/schemas/metadata" } }, "type": "object" @@ -1169,19 +1000,19 @@ } } }, - "/saved-searches/{savedSearchId}/users": { + "/user_members_available": { "get": { "tags": [ - "Users" + "Group Members" ], - "summary": "Returns all users", - "description": "Display a listing of the resource.", - "operationId": "getSavedSearchUsers", + "summary": "Returns all users available for a given group", + "description": "Display a listing of users available", + "operationId": "getUserMembersAvailable", "parameters": [ { - "name": "savedSearchId", + "name": "group_id", "in": "path", - "description": "ID of saved search to return", + "description": "ID of group to return", "required": true, "schema": { "type": "string" @@ -1190,7 +1021,7 @@ { "name": "filter", "in": "query", - "description": "Filter results by string. Searches First Name, Last Name, Email and Username.", + "description": "Filter results by string. Searches Name. Can be a substring.", "schema": { "type": "string" } @@ -1203,14 +1034,11 @@ }, { "$ref": "#/components/parameters/per_page" - }, - { - "$ref": "#/components/parameters/include" } ], "responses": { "200": { - "description": "list of users", + "description": "list of users available to be assigned as member", "content": { "application/json": { "schema": { @@ -1233,15 +1061,27 @@ } } }, - "/saved-searches/{savedSearchId}/groups": { + "/notifications": { "get": { "tags": [ - "Groups" + "Notifications" ], - "summary": "Returns all groups that the user has access to", + "summary": "Returns all notifications that the user has access to", "description": "Display a listing of the resource.", - "operationId": "getSavedSearchGroups", + "operationId": "getNotifications", "parameters": [ + { + "name": "status", + "in": "query", + "description": "Only return notifications by status (unread, all, etc.)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/filter" + }, { "$ref": "#/components/parameters/order_by" }, @@ -1257,126 +1097,7 @@ ], "responses": { "200": { - "description": "list of groups", - "content": { - "application/json": { - "schema": { - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/groups" - } - }, - "meta": { - "$ref": "#/components/schemas/metadata" - } - }, - "type": "object" - } - } - } - } - } - } - }, - "/saved-searches/{saved_search_id}": { - "delete": { - "tags": [ - "SavedSearches" - ], - "summary": "Delete a saved search", - "description": "Delete a SavedSearch.", - "operationId": "deleteSavedSearch", - "parameters": [ - { - "name": "saved_search_id", - "in": "path", - "description": "ID of saved search to return", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "success" - } - } - } - }, - "/saved-searches/icons": { - "get": { - "tags": [ - "SavedSearches" - ], - "summary": "Returns all icons for saved searches", - "description": "Get a list of icons available for SavedSearches.", - "operationId": "getSavedSearchesIcons", - "parameters": [ - { - "$ref": "#/components/parameters/per_page" - } - ], - "responses": { - "200": { - "description": "list of icons for saved searches", - "content": { - "application/json": { - "schema": { - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SavedSearchIcon" - } - }, - "meta": { - "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/metadata" - } - ] - } - }, - "type": "object" - } - } - } - } - } - } - }, - "/version_histories": { - "get": { - "tags": [ - "Version History" - ], - "summary": "Return all version History according to the model", - "description": "Get the list of records of Version History", - "operationId": "getVersionHistories", - "parameters": [ - { - "$ref": "#/components/parameters/filter" - }, - { - "$ref": "#/components/parameters/order_by" - }, - { - "$ref": "#/components/parameters/order_direction" - }, - { - "$ref": "#/components/parameters/per_page" - }, - { - "$ref": "#/components/parameters/include" - } - ], - "responses": { - "200": { - "description": "list of Version History", + "description": "list of notifications", "content": { "application/json": { "schema": { @@ -1384,17 +1105,10 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/versionHistory" + "$ref": "#/components/schemas/Notification" } }, - "meta": { - "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/metadata" - } - ] - } + "meta": {} }, "type": "object" } @@ -1405,17 +1119,17 @@ }, "post": { "tags": [ - "Version History" + "Notifications" ], - "summary": "Save a new Version History", - "description": "Create a new Version History.", - "operationId": "createVersion", + "summary": "Save a new notifications", + "description": "Store a newly created resource in storage.", + "operationId": "createNotification", "requestBody": { "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/versionHistoryEditable" + "$ref": "#/components/schemas/NotificationEditable" } } } @@ -1426,7 +1140,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/versionHistory" + "$ref": "#/components/schemas/Notification" } } } @@ -1434,19 +1148,19 @@ } } }, - "/version_histories/{version_history_id}": { + "/notifications/{notification_id}": { "get": { "tags": [ - "Version History" + "Notifications" ], - "summary": "Get single Version History by ID", - "description": "Get a single Version History.", - "operationId": "getVersionHistoryById", + "summary": "Get single notification by ID", + "description": "Display the specified resource.", + "operationId": "getNotificationById", "parameters": [ { - "name": "version_history_id", + "name": "notification_id", "in": "path", - "description": "ID of Version History to return", + "description": "ID of notification to return", "required": true, "schema": { "type": "string" @@ -1455,11 +1169,11 @@ ], "responses": { "200": { - "description": "Successfully found the Version History", + "description": "Successfully found the notification", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/versionHistory" + "$ref": "#/components/schemas/Notification" } } } @@ -1468,16 +1182,16 @@ }, "put": { "tags": [ - "Version History" + "Notifications" ], - "summary": "Update a Version History", - "description": "Update a Version History.", - "operationId": "updateVersion", + "summary": "Update a notification", + "description": "Update a user", + "operationId": "updateNotification", "parameters": [ { - "name": "version_history_id", + "name": "notification_id", "in": "path", - "description": "ID of Version History to return", + "description": "ID of notification to return", "required": true, "schema": { "type": "string" @@ -1489,36 +1203,29 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/versionHistoryEditable" + "$ref": "#/components/schemas/NotificationEditable" } } } }, "responses": { "204": { - "description": "success", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/versionHistory" - } - } - } + "description": "success" } } }, "delete": { "tags": [ - "Version History" + "Notifications" ], - "summary": "Delete a Version History", - "description": "Delete a Version History.", - "operationId": "deleteVersion", + "summary": "Delete a notification", + "description": "Delete a notification", + "operationId": "deleteNotification", "parameters": [ { - "name": "version_history_id", + "name": "notification_id", "in": "path", - "description": "ID of Version History to return", + "description": "ID of notification to return", "required": true, "schema": { "type": "string" @@ -1527,68 +1234,113 @@ ], "responses": { "204": { - "description": "success", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/versionHistory" - } - } - } + "description": "success" } } } }, - "/version_histories/clone": { - "post": { + "/read_notifications": { + "put": { "tags": [ - "Version History" + "Notifications" ], - "summary": "Clone a new Version History", - "description": "Clone a new Version History.", - "operationId": "cloneVersion", + "summary": "Mark notifications as read by the user", + "description": "Update notification as read", + "operationId": "markNotificationAsRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/versionHistoryEditable" + "properties": { + "message_ids": { + "description": "list of message ids that will be marked as read", + "type": "array", + "items": { + "type": "string" + } + }, + "routes": { + "description": "all messages that has an url that is in this list will be marked as read", + "type": "array", + "items": { + "type": "string" + } + } + }, + "type": "object" } } } }, "responses": { "201": { - "description": "success", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/versionHistory" - } + "description": "success" + } + } + } + }, + "/unread_notifications": { + "put": { + "tags": [ + "Notifications" + ], + "summary": "Mark notifications as unread by the user", + "description": "Update notifications as unread", + "operationId": "markNotificationAsUnread", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "properties": { + "message_ids": { + "description": "list of message ids that will be marked as read", + "type": "array", + "items": { + "type": "string" + } + }, + "routes": { + "description": "all messages that has an url that is in this list will be marked as read", + "type": "array", + "items": { + "type": "string" + } + } + }, + "type": "object" } } } + }, + "responses": { + "201": { + "description": "success" + } } } }, - "/customize-ui": { - "post": { + "/read_all_notifications": { + "put": { "tags": [ - "CssSettings" + "Notifications" ], - "summary": "Create or update a new setting", - "description": "Create a new Settings css-override", - "operationId": "updateCssSetting", + "summary": "Mark notifications as read by id and type", + "description": "Update all notification as read.", + "operationId": "markAllAsRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "properties": { - "variables": { - "type": "string" + "id": { + "description": "Polymorphic relation id", + "type": "integer" }, - "sansSerifFont": { + "type": { + "description": "Polymorphic relation type", "type": "string" } }, @@ -1599,29 +1351,73 @@ }, "responses": { "201": { - "description": "success", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/settings" - } - } - } + "description": "success" } } } }, - "/environment_variables": { - "get": { + "/permissions": { + "put": { "tags": [ - "Environment Variables" + "Permissions" ], - "summary": "Returns all environmentVariables that the user has access to. For security, values are not included.", - "description": "Fetch a collection of variables based on paged request and filter if provided", - "operationId": "getEnvironmentVariables", + "summary": "Update the permissions of a user", + "description": "Update permissions", + "operationId": "51b3555fb753f44324bf5c3880e01454", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "properties": { + "user_id": { + "description": "ID of the user whose permissions are configured", + "type": "integer" + }, + "group_id": { + "description": "ID of the group whose permissions are configured", + "type": "integer" + }, + "is_administrator": { + "description": "Whether the user should have Super Admin privileges", + "type": "boolean", + "default": false + }, + "permission_names": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "type": "object" + } + } + } + }, + "responses": { + "204": { + "description": "success" + } + } + } + }, + "/process_categories": { + "get": { + "tags": [ + "Process Categories" + ], + "summary": "Returns all processes categories that the user has access to", + "description": "Display a listing of the Process Categories.", + "operationId": "getProcessCategories", "parameters": [ { - "$ref": "#/components/parameters/filter" + "name": "filter", + "in": "query", + "description": "Filter results by string. Searches Name and Status. All fields must match exactly.", + "schema": { + "type": "string" + } }, { "$ref": "#/components/parameters/order_by" @@ -1631,14 +1427,11 @@ }, { "$ref": "#/components/parameters/per_page" - }, - { - "$ref": "#/components/parameters/include" } ], "responses": { "200": { - "description": "list of environmentVariables", + "description": "list of processes categories", "content": { "application/json": { "schema": { @@ -1646,7 +1439,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/EnvironmentVariable" + "$ref": "#/components/schemas/ProcessCategory" } }, "meta": { @@ -1662,17 +1455,17 @@ }, "post": { "tags": [ - "Environment Variables" + "Process Categories" ], - "summary": "Create a new environment variable", - "description": "Creates a new global Environment Variable in the system", - "operationId": "createEnvironmentVariable", + "summary": "Save a new process Category", + "description": "Store a newly created Process Category in storage", + "operationId": "createProcessCategory", "requestBody": { "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EnvironmentVariableEditable" + "$ref": "#/components/schemas/ProcessCategoryEditable" } } } @@ -1683,7 +1476,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EnvironmentVariable" + "$ref": "#/components/schemas/ProcessCategory" } } } @@ -1691,19 +1484,19 @@ } } }, - "/environment_variables/{environment_variable_id}": { + "/process_categories/{process_category_id}": { "get": { "tags": [ - "Environment Variables" + "Process Categories" ], - "summary": "Get an environment variable by id. For security, the value is not included.", - "description": "Return an environment variable instance\nUsing implicit model binding, will automatically return 404 if variable now found", - "operationId": "getEnvironmentVariableById", + "summary": "Get single process category by ID", + "description": "Display the specified Process category.", + "operationId": "getProcessCategoryById", "parameters": [ { - "name": "environment_variable_id", + "name": "process_category_id", "in": "path", - "description": "ID of environment_variables to return", + "description": "ID of process category to return", "required": true, "schema": { "type": "integer" @@ -1711,12 +1504,12 @@ } ], "responses": { - "201": { - "description": "success", + "200": { + "description": "Successfully found the process", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EnvironmentVariable" + "$ref": "#/components/schemas/ProcessCategory" } } } @@ -1725,16 +1518,16 @@ }, "put": { "tags": [ - "Environment Variables" + "Process Categories" ], - "summary": "Update an environment variable", - "description": "Update an environment variable", - "operationId": "updateEnvironmentVariable", + "summary": "Update a process Category", + "description": "Updates the current element", + "operationId": "updateProcessCategory", "parameters": [ { - "name": "environment_variable_id", + "name": "process_category_id", "in": "path", - "description": "ID of environment variables to update", + "description": "ID of process category to return", "required": true, "schema": { "type": "integer" @@ -1746,7 +1539,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EnvironmentVariableEditable" + "$ref": "#/components/schemas/ProcessCategoryEditable" } } } @@ -1757,7 +1550,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EnvironmentVariable" + "$ref": "#/components/schemas/ProcessCategory" } } } @@ -1766,15 +1559,16 @@ }, "delete": { "tags": [ - "Environment Variables" + "Process Categories" ], - "summary": "Delete an environment variable", - "operationId": "deleteEnvironmentVariable", + "summary": "Delete a process category", + "description": "Remove the specified resource from storage.", + "operationId": "deleteProcessCategory", "parameters": [ { - "name": "environment_variable_id", + "name": "process_category_id", "in": "path", - "description": "ID of environment_variables to return", + "description": "ID of process category to return", "required": true, "schema": { "type": "integer" @@ -1782,20 +1576,27 @@ } ], "responses": { - "200": { - "description": "success" + "204": { + "description": "success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Process" + } + } + } } } } }, - "/files": { + "/processes": { "get": { "tags": [ - "Files" + "Processes" ], - "summary": "Returns the list of files", - "description": "Display a listing of the resource.", - "operationId": "getFiles", + "summary": "Returns all processes that the user has access to", + "description": "Get list Process", + "operationId": "getProcesses", "parameters": [ { "$ref": "#/components/parameters/filter" @@ -1808,11 +1609,26 @@ }, { "$ref": "#/components/parameters/per_page" + }, + { + "$ref": "#/components/parameters/status" + }, + { + "$ref": "#/components/parameters/include" + }, + { + "name": "simplified_data_for_selector", + "in": "query", + "description": "Comma separated list of fields to include in the response", + "schema": { + "type": "string", + "default": "" + } } ], "responses": { "200": { - "description": "list of files", + "description": "list of processes", "content": { "application/json": { "schema": { @@ -1820,7 +1636,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/media" + "$ref": "#/components/schemas/Process" } }, "meta": { @@ -1836,87 +1652,28 @@ }, "post": { "tags": [ - "Files" + "Processes" ], - "summary": "Save a new media file. Note: To upload files to a request, use createRequestFile in the RequestFile API", + "summary": "Save a new process", "description": "Store a newly created resource in storage.", - "operationId": "createFile", - "parameters": [ - { - "name": "model_id", - "in": "query", - "description": "ID of the model to which the file will be associated", - "required": true, - "schema": { - "type": "integer" - } - }, - { - "name": "model", - "in": "query", - "description": "Full namespaced class of the model to associate", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "data_name", - "in": "query", - "description": "Name of the variable used in a request", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "collection", - "in": "query", - "description": "Media collection name. For requests, use 'default'", - "required": false, - "schema": { - "type": "string" - } - } - ], + "operationId": "createProcess", "requestBody": { "required": true, "content": { - "multipart/form-data": { + "application/json": { "schema": { - "properties": { - "file": { - "description": "save a new media file", - "type": "string", - "format": "binary" - } - }, - "type": "object" + "$ref": "#/components/schemas/ProcessEditable" } } } }, "responses": { - "200": { + "201": { "description": "success", "content": { "application/json": { "schema": { - "properties": { - "id": { - "type": "string" - }, - "model_id": { - "type": "string" - }, - "file_name": { - "type": "string" - }, - "mime_type": { - "type": "string" - } - }, - "type": "object" + "$ref": "#/components/schemas/Process" } } } @@ -1924,1041 +1681,48 @@ } } }, - "/files/{file_id}": { + "/processes/{processId}": { "get": { "tags": [ - "Files" + "Processes" ], - "summary": "Get the metadata of a file. To actually fetch the file see Get File Contents", - "description": "Get a single media file.", - "operationId": "getFileById", + "summary": "Get single process by ID", + "description": "Display the specified resource.", + "operationId": "getProcessById", "parameters": [ { - "name": "file_id", + "name": "processId", "in": "path", - "description": "ID of the file to return", + "description": "ID of process to return", "required": true, "schema": { "type": "integer" } - } - ], - "responses": { - "200": { - "description": "Successfully found the file", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/media" - } - } - } - }, - "404": { - "$ref": "#/components/responses/404" - } - } - }, - "delete": { - "tags": [ - "Files" - ], - "summary": "Delete a media file", - "description": "Remove the specified resource from storage.", - "operationId": "deleteFile", - "parameters": [ - { - "name": "file_id", - "in": "path", - "description": "ID of the file", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "responses": { - "204": { - "description": "success" - }, - "404": { - "$ref": "#/components/responses/404" - } - } - } - }, - "/files/{file_id}/contents": { - "get": { - "tags": [ - "Files" - ], - "summary": "Get the contents of a file", - "description": "Display the specified resource.", - "operationId": "getFileContentsById", - "parameters": [ - { - "name": "file_id", - "in": "path", - "description": "ID of the file to return", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "responses": { - "200": { - "description": "File stream", - "content": { - "application/octet-stream": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "404": { - "$ref": "#/components/responses/404" - } - } - } - }, - "/groups": { - "get": { - "tags": [ - "Groups" - ], - "summary": "Returns all groups that the user has access to", - "description": "Display a listing of the resource.", - "operationId": "getGroups", - "parameters": [ - { - "$ref": "#/components/parameters/status" - }, - { - "$ref": "#/components/parameters/filter" - }, - { - "$ref": "#/components/parameters/order_by" - }, - { - "$ref": "#/components/parameters/order_direction" - }, - { - "$ref": "#/components/parameters/per_page" - }, - { - "$ref": "#/components/parameters/include" - } - ], - "responses": { - "200": { - "description": "list of groups", - "content": { - "application/json": { - "schema": { - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/groups" - } - }, - "meta": { - "$ref": "#/components/schemas/metadata" - } - }, - "type": "object" - } - } - } - } - } - }, - "post": { - "tags": [ - "Groups" - ], - "summary": "Save a new group", - "description": "Store a newly created resource in storage.", - "operationId": "createGroup", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/groupsEditable" - } - } - } - }, - "responses": { - "201": { - "description": "success", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/groups" - } - } - } - }, - "422": { - "$ref": "#/components/responses/422" - } - } - } - }, - "/groups/{group_id}": { - "get": { - "tags": [ - "Groups" - ], - "summary": "Get single group by ID", - "description": "Display the specified resource.", - "operationId": "getGroupById", - "parameters": [ - { - "name": "group_id", - "in": "path", - "description": "ID of group to return", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "responses": { - "200": { - "description": "Successfully found the group", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/groups" - } - } - } - }, - "404": { - "$ref": "#/components/responses/404" - } - } - }, - "put": { - "tags": [ - "Groups" - ], - "summary": "Update a group", - "description": "Update a user", - "operationId": "updateGroup", - "parameters": [ - { - "name": "group_id", - "in": "path", - "description": "ID of group to return", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/groupsEditable" - } - } - } - }, - "responses": { - "204": { - "description": "success" - }, - "404": { - "$ref": "#/components/responses/404" - } - } - }, - "delete": { - "tags": [ - "Groups" - ], - "summary": "Delete a group", - "description": "Delete a user", - "operationId": "deleteGroup", - "parameters": [ - { - "name": "group_id", - "in": "path", - "description": "ID of group to return", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "responses": { - "204": { - "description": "success" - }, - "404": { - "$ref": "#/components/responses/404" - } - } - } - }, - "/groups/{group_id}/users": { - "get": { - "tags": [ - "Groups" - ], - "summary": "Returns all users of a group", - "description": "Display the list of users in a group", - "operationId": "getGroupUsers", - "parameters": [ - { - "name": "group_id", - "in": "path", - "description": "ID of group", - "required": true, - "schema": { - "type": "integer" - } - }, - { - "$ref": "#/components/parameters/filter" - }, - { - "$ref": "#/components/parameters/order_direction" - }, - { - "$ref": "#/components/parameters/per_page" - } - ], - "responses": { - "200": { - "description": "list of members of a group", - "content": { - "application/json": { - "schema": { - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/users" - } - }, - "meta": { - "$ref": "#/components/schemas/metadata" - } - }, - "type": "object" - } - } - } - } - } - } - }, - "/groups/{group_id}/groups": { - "get": { - "tags": [ - "Groups" - ], - "summary": "Returns all users of a group", - "description": "Display the list of groups in a group", - "operationId": "getGroupGroupss", - "parameters": [ - { - "name": "group_id", - "in": "path", - "description": "ID of group", - "required": true, - "schema": { - "type": "integer" - } - }, - { - "$ref": "#/components/parameters/filter" - }, - { - "$ref": "#/components/parameters/order_direction" - }, - { - "$ref": "#/components/parameters/per_page" - } - ], - "responses": { - "200": { - "description": "list of members of a group", - "content": { - "application/json": { - "schema": { - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/groups" - } - }, - "meta": { - "$ref": "#/components/schemas/metadata" - } - }, - "type": "object" - } - } - } - } - } - } - }, - "/group_members": { - "get": { - "tags": [ - "Group Members" - ], - "summary": "Returns all groups for a given member", - "description": "Display a listing of the resource.", - "operationId": "getGroupMembers", - "parameters": [ - { - "$ref": "#/components/parameters/member_id" - }, - { - "$ref": "#/components/parameters/order_by" - }, - { - "$ref": "#/components/parameters/order_direction" - }, - { - "$ref": "#/components/parameters/per_page" - } - ], - "responses": { - "200": { - "description": "list of group_members", - "content": { - "application/json": { - "schema": { - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/groupMembers" - } - }, - "meta": { - "$ref": "#/components/schemas/metadata" - } - }, - "type": "object" - } - } - } - } - } - }, - "post": { - "tags": [ - "Group Members" - ], - "summary": "Save a new group member", - "description": "Store a newly created resource in storage.", - "operationId": "createGroupMember", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/groupMembersEditable" - } - } - } - }, - "responses": { - "201": { - "description": "success", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/createGroupMembers" - } - } - } - } - } - } - }, - "/group_members/{group_member_id}": { - "get": { - "tags": [ - "Group Members" - ], - "summary": "Get single group member by ID", - "description": "Display the specified resource.", - "operationId": "getGroupMemberById", - "parameters": [ - { - "name": "group_member_id", - "in": "path", - "description": "ID of group members to return", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Successfully found the group members", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/getGroupMembersById" - } - } - } - } - } - }, - "delete": { - "tags": [ - "Group Members" - ], - "summary": "Delete a group member", - "description": "Delete a group membership", - "operationId": "deleteGroupMember", - "parameters": [ - { - "name": "group_member_id", - "in": "path", - "description": "ID of group_members to return", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "success" - } - } - } - }, - "/group_members_available": { - "get": { - "tags": [ - "Group Members" - ], - "summary": "Returns all groups available for a given member", - "description": "Display a listing of groups available", - "operationId": "getGroupMembersAvailable", - "parameters": [ - { - "name": "member_id", - "in": "path", - "description": "ID of group member to return", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "member_type", - "in": "path", - "description": "type of group member to return", - "required": true, - "schema": { - "type": "string" - } - }, - { - "$ref": "#/components/parameters/filter" - }, - { - "$ref": "#/components/parameters/order_by" - }, - { - "$ref": "#/components/parameters/order_direction" - }, - { - "$ref": "#/components/parameters/per_page" - } - ], - "responses": { - "200": { - "description": "list of groups available to be assigned as member", - "content": { - "application/json": { - "schema": { - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/availableGroupMembers" - } - }, - "meta": { - "$ref": "#/components/schemas/metadata" - } - }, - "type": "object" - } - } - } - } - } - } - }, - "/user_members_available": { - "get": { - "tags": [ - "Group Members" - ], - "summary": "Returns all users available for a given group", - "description": "Display a listing of users available", - "operationId": "getUserMembersAvailable", - "parameters": [ - { - "name": "group_id", - "in": "path", - "description": "ID of group to return", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "filter", - "in": "query", - "description": "Filter results by string. Searches Name. Can be a substring.", - "schema": { - "type": "string" - } - }, - { - "$ref": "#/components/parameters/order_by" - }, - { - "$ref": "#/components/parameters/order_direction" - }, - { - "$ref": "#/components/parameters/per_page" - } - ], - "responses": { - "200": { - "description": "list of users available to be assigned as member", - "content": { - "application/json": { - "schema": { - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/users" - } - }, - "meta": { - "$ref": "#/components/schemas/metadata" - } - }, - "type": "object" - } - } - } - } - } - } - }, - "/notifications": { - "get": { - "tags": [ - "Notifications" - ], - "summary": "Returns all notifications that the user has access to", - "description": "Display a listing of the resource.", - "operationId": "getNotifications", - "parameters": [ - { - "name": "status", - "in": "query", - "description": "Only return notifications by status (unread, all, etc.)", - "required": false, - "schema": { - "type": "string" - } - }, - { - "$ref": "#/components/parameters/filter" - }, - { - "$ref": "#/components/parameters/order_by" - }, - { - "$ref": "#/components/parameters/order_direction" - }, - { - "$ref": "#/components/parameters/per_page" - }, - { - "$ref": "#/components/parameters/include" - } - ], - "responses": { - "200": { - "description": "list of notifications", - "content": { - "application/json": { - "schema": { - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Notification" - } - }, - "meta": {} - }, - "type": "object" - } - } - } - } - } - }, - "post": { - "tags": [ - "Notifications" - ], - "summary": "Save a new notifications", - "description": "Store a newly created resource in storage.", - "operationId": "createNotification", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/NotificationEditable" - } - } - } - }, - "responses": { - "201": { - "description": "success", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Notification" - } - } - } - } - } - } - }, - "/notifications/{notification_id}": { - "get": { - "tags": [ - "Notifications" - ], - "summary": "Get single notification by ID", - "description": "Display the specified resource.", - "operationId": "getNotificationById", - "parameters": [ - { - "name": "notification_id", - "in": "path", - "description": "ID of notification to return", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Successfully found the notification", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Notification" - } - } - } - } - } - }, - "put": { - "tags": [ - "Notifications" - ], - "summary": "Update a notification", - "description": "Update a user", - "operationId": "updateNotification", - "parameters": [ - { - "name": "notification_id", - "in": "path", - "description": "ID of notification to return", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/NotificationEditable" - } - } - } - }, - "responses": { - "204": { - "description": "success" - } - } - }, - "delete": { - "tags": [ - "Notifications" - ], - "summary": "Delete a notification", - "description": "Delete a notification", - "operationId": "deleteNotification", - "parameters": [ - { - "name": "notification_id", - "in": "path", - "description": "ID of notification to return", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "success" - } - } - } - }, - "/read_notifications": { - "put": { - "tags": [ - "Notifications" - ], - "summary": "Mark notifications as read by the user", - "description": "Update notification as read", - "operationId": "markNotificationAsRead", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "properties": { - "message_ids": { - "description": "list of message ids that will be marked as read", - "type": "array", - "items": { - "type": "string" - } - }, - "routes": { - "description": "all messages that has an url that is in this list will be marked as read", - "type": "array", - "items": { - "type": "string" - } - } - }, - "type": "object" - } - } - } - }, - "responses": { - "201": { - "description": "success" - } - } - } - }, - "/unread_notifications": { - "put": { - "tags": [ - "Notifications" - ], - "summary": "Mark notifications as unread by the user", - "description": "Update notifications as unread", - "operationId": "markNotificationAsUnread", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "properties": { - "message_ids": { - "description": "list of message ids that will be marked as read", - "type": "array", - "items": { - "type": "string" - } - }, - "routes": { - "description": "all messages that has an url that is in this list will be marked as read", - "type": "array", - "items": { - "type": "string" - } - } - }, - "type": "object" - } - } - } - }, - "responses": { - "201": { - "description": "success" - } - } - } - }, - "/read_all_notifications": { - "put": { - "tags": [ - "Notifications" - ], - "summary": "Mark notifications as read by id and type", - "description": "Update all notification as read.", - "operationId": "markAllAsRead", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "properties": { - "id": { - "description": "Polymorphic relation id", - "type": "integer" - }, - "type": { - "description": "Polymorphic relation type", - "type": "string" - } - }, - "type": "object" - } - } - } - }, - "responses": { - "201": { - "description": "success" - } - } - } - }, - "/permissions": { - "put": { - "tags": [ - "Permissions" - ], - "summary": "Update the permissions of a user", - "description": "Update permissions", - "operationId": "51b3555fb753f44324bf5c3880e01454", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "properties": { - "user_id": { - "description": "ID of the user whose permissions are configured", - "type": "integer" - }, - "group_id": { - "description": "ID of the group whose permissions are configured", - "type": "integer" - }, - "is_administrator": { - "description": "Whether the user should have Super Admin privileges", - "type": "boolean", - "default": false - }, - "permission_names": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "type": "object" - } - } - } - }, - "responses": { - "204": { - "description": "success" - } - } - } - }, - "/process_categories": { - "get": { - "tags": [ - "Process Categories" - ], - "summary": "Returns all processes categories that the user has access to", - "description": "Display a listing of the Process Categories.", - "operationId": "getProcessCategories", - "parameters": [ - { - "name": "filter", - "in": "query", - "description": "Filter results by string. Searches Name and Status. All fields must match exactly.", - "schema": { - "type": "string" - } - }, - { - "$ref": "#/components/parameters/order_by" - }, - { - "$ref": "#/components/parameters/order_direction" }, { - "$ref": "#/components/parameters/per_page" + "$ref": "#/components/parameters/include" } ], "responses": { "200": { - "description": "list of processes categories", + "description": "Successfully found the process", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Process" + } + } + } + }, + "204": { + "description": "Process not found", "content": { "application/json": { "schema": { "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ProcessCategory" - } - }, - "meta": { - "type": "object" + "message": { + "type": "string", + "example": "The requested process was not found" } }, "type": "object" @@ -2968,50 +1732,59 @@ } } }, - "post": { + "put": { "tags": [ - "Process Categories" + "Processes" + ], + "summary": "Update a process", + "description": "Updates the current element.", + "operationId": "updateProcess", + "parameters": [ + { + "name": "processId", + "in": "path", + "description": "ID of process to return", + "required": true, + "schema": { + "type": "integer" + } + } ], - "summary": "Save a new process Category", - "description": "Store a newly created Process Category in storage", - "operationId": "createProcessCategory", "requestBody": { "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProcessCategoryEditable" + "$ref": "#/components/schemas/ProcessEditable" } } } }, "responses": { - "201": { + "200": { "description": "success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProcessCategory" + "$ref": "#/components/schemas/Process" } } } } } - } - }, - "/process_categories/{process_category_id}": { - "get": { + }, + "delete": { "tags": [ - "Process Categories" + "Processes" ], - "summary": "Get single process category by ID", - "description": "Display the specified Process category.", - "operationId": "getProcessCategoryById", + "summary": "Delete a process", + "description": "Remove the specified resource from storage.", + "operationId": "deleteProcess", "parameters": [ { - "name": "process_category_id", + "name": "processId", "in": "path", - "description": "ID of process category to return", + "description": "ID of process to return", "required": true, "schema": { "type": "integer" @@ -3019,79 +1792,90 @@ } ], "responses": { - "200": { - "description": "Successfully found the process", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProcessCategory" - } - } - } + "204": { + "description": "success" } } - }, - "put": { + } + }, + "/processes/{processId}/start_events": { + "get": { "tags": [ - "Process Categories" + "Processes" ], - "summary": "Update a process Category", - "description": "Updates the current element", - "operationId": "updateProcessCategory", + "summary": "Get start events of a process by Id", + "description": "Display the specified resource.", + "operationId": "getStartEventsProcessById", "parameters": [ { - "name": "process_category_id", + "name": "processId", "in": "path", - "description": "ID of process category to return", + "description": "ID of process to return", "required": true, "schema": { "type": "integer" } + }, + { + "$ref": "#/components/parameters/include" } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProcessCategoryEditable" - } - } - } - }, "responses": { "200": { - "description": "success", + "description": "Successfully found the start events process", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProcessCategory" + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProcessStartEvents" + } + }, + "meta": { + "$ref": "#/components/schemas/metadata" + } + }, + "type": "object" } } } } } - }, - "delete": { + } + }, + "/processes/{processId}/draft": { + "put": { "tags": [ - "Process Categories" + "Processes" ], - "summary": "Delete a process category", - "description": "Remove the specified resource from storage.", - "operationId": "deleteProcessCategory", + "summary": "Update a draft process", + "description": "Update draft process.", + "operationId": "updateDraftProcess", "parameters": [ { - "name": "process_category_id", + "name": "processId", "in": "path", - "description": "ID of process category to return", + "description": "ID of process to return", "required": true, "schema": { "type": "integer" } } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProcessEditable" + } + } + } + }, "responses": { - "204": { + "200": { "description": "success", "content": { "application/json": { @@ -3104,14 +1888,14 @@ } } }, - "/processes": { + "/start_processes": { "get": { "tags": [ "Processes" ], - "summary": "Returns all processes that the user has access to", - "description": "Get list Process", - "operationId": "getProcesses", + "summary": "Returns the list of processes that the user can start", + "description": "Returns the list of processes that the user can start.", + "operationId": "startProcesses", "parameters": [ { "$ref": "#/components/parameters/filter" @@ -3125,25 +1909,22 @@ { "$ref": "#/components/parameters/per_page" }, - { - "$ref": "#/components/parameters/status" - }, { "$ref": "#/components/parameters/include" }, { - "name": "simplified_data_for_selector", - "in": "query", - "description": "Comma separated list of fields to include in the response", + "name": "without_event_definitions", + "in": "path", + "description": "If true return only processes that haven't start event definitions", + "required": false, "schema": { - "type": "string", - "default": "" + "type": "boolean" } } ], "responses": { "200": { - "description": "list of processes", + "description": "list of processes that the user can start", "content": { "application/json": { "schema": { @@ -3151,7 +1932,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/Process" + "$ref": "#/components/schemas/ProcessWithStartEvents" } }, "meta": { @@ -3164,26 +1945,29 @@ } } } - }, - "post": { + } + }, + "/processes/{processId}/restore": { + "put": { "tags": [ "Processes" ], - "summary": "Save a new process", - "description": "Store a newly created resource in storage.", - "operationId": "createProcess", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProcessEditable" - } + "summary": "Restore an inactive process", + "description": "Reverses the soft delete of the element.", + "operationId": "restoreProcess", + "parameters": [ + { + "name": "processId", + "in": "path", + "description": "ID of process to return", + "required": true, + "schema": { + "type": "integer" } } - }, + ], "responses": { - "201": { + "200": { "description": "success", "content": { "application/json": { @@ -3196,65 +1980,104 @@ } } }, - "/processes/{processId}": { - "get": { + "/processes/{processId}/export": { + "post": { "tags": [ "Processes" ], - "summary": "Get single process by ID", - "description": "Display the specified resource.", - "operationId": "getProcessById", + "summary": "Export a single process by ID and return a URL to download it", + "description": "Export the specified process.", + "operationId": "exportProcess", "parameters": [ { "name": "processId", "in": "path", - "description": "ID of process to return", + "description": "ID of process to export", "required": true, "schema": { "type": "integer" } - }, - { - "$ref": "#/components/parameters/include" } ], "responses": { "200": { - "description": "Successfully found the process", + "description": "Successfully built the process for export", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Process" + "properties": { + "url": { + "type": "string" + } + }, + "type": "object" } } } } } - }, - "put": { + } + }, + "/processes/import/validation": { + "post": { "tags": [ "Processes" ], - "summary": "Update a process", - "description": "Updates the current element.", - "operationId": "updateProcess", - "parameters": [ - { - "name": "processId", - "in": "path", - "description": "ID of process to return", - "required": true, - "schema": { - "type": "integer" + "summary": "Validate a import", + "description": "Validate the specified process before importing.", + "operationId": "validateImport", + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "properties": { + "file": { + "description": "file to import", + "type": "string", + "format": "binary" + } + }, + "type": "object" + } + } + } + }, + "responses": { + "200": { + "description": "success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProcessImport" + } + } } } + } + } + }, + "/processes/import": { + "post": { + "tags": [ + "Processes" ], + "summary": "Import a new process", + "description": "Import the specified process.", + "operationId": "importProcess", "requestBody": { "required": true, "content": { - "application/json": { + "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/ProcessEditable" + "properties": { + "file": { + "description": "file to import", + "type": "string", + "format": "binary" + } + }, + "type": "object" } } } @@ -3265,25 +2088,27 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Process" + "$ref": "#/components/schemas/ProcessImport" } } } } } - }, - "delete": { + } + }, + "/processes/{processId}/bpmn": { + "get": { "tags": [ "Processes" ], - "summary": "Delete a process", - "description": "Remove the specified resource from storage.", - "operationId": "deleteProcess", + "summary": "Download the BPMN definition of a process", + "description": "Download the BPMN definition of a process", + "operationId": "processBpmn", "parameters": [ { "name": "processId", "in": "path", - "description": "ID of process to return", + "description": "ID of process", "required": true, "schema": { "type": "integer" @@ -3291,49 +2116,52 @@ } ], "responses": { - "204": { - "description": "success" + "200": { + "description": "Successfully built the process for export", + "content": { + "application/json": { + "schema": { + "properties": { + "url": { + "type": "string" + } + }, + "type": "object" + } + } + } } } } }, - "/processes/{processId}/start_events": { - "get": { + "/processes/import/{code}/is_ready": { + "head": { "tags": [ "Processes" ], - "summary": "Get start events of a process by Id", - "description": "Display the specified resource.", - "operationId": "getStartEventsProcessById", + "summary": "Check if the import is ready", + "description": "Check if the import is ready", + "operationId": "6a131993b7c879ddcd3d3a291dd8380f", "parameters": [ { - "name": "processId", + "name": "code", "in": "path", - "description": "ID of process to return", + "description": "Import code", "required": true, "schema": { - "type": "integer" + "type": "string" } - }, - { - "$ref": "#/components/parameters/include" } ], "responses": { "200": { - "description": "Successfully found the start events process", + "description": "check is import is ready", "content": { "application/json": { "schema": { "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ProcessStartEvents" - } - }, - "meta": { - "$ref": "#/components/schemas/metadata" + "ready": { + "type": "boolean" } }, "type": "object" @@ -3344,17 +2172,17 @@ } } }, - "/processes/{processId}/draft": { - "put": { + "/processes/{process_id}/import/assignments": { + "post": { "tags": [ "Processes" ], - "summary": "Update a draft process", - "description": "Update draft process.", - "operationId": "updateDraftProcess", + "summary": "Update assignments after import", + "description": "Import Assignments of process.", + "operationId": "assignmentProcess", "parameters": [ { - "name": "processId", + "name": "process_id", "in": "path", "description": "ID of process to return", "required": true, @@ -3368,77 +2196,64 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProcessEditable" + "$ref": "#/components/schemas/ProcessAssignments" } } } }, "responses": { - "200": { - "description": "success", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Process" - } - } - } + "204": { + "description": "success" } } } }, - "/start_processes": { - "get": { + "/process_events/{process_id}": { + "post": { "tags": [ "Processes" ], - "summary": "Returns the list of processes that the user can start", - "description": "Returns the list of processes that the user can start.", - "operationId": "startProcesses", + "summary": "Start a new process", + "description": "Trigger an start event within a process.", + "operationId": "triggerStartEvent", "parameters": [ { - "$ref": "#/components/parameters/filter" - }, - { - "$ref": "#/components/parameters/order_by" - }, - { - "$ref": "#/components/parameters/order_direction" - }, - { - "$ref": "#/components/parameters/per_page" - }, - { - "$ref": "#/components/parameters/include" + "name": "process_id", + "in": "path", + "description": "ID of process to return", + "required": true, + "schema": { + "type": "integer" + } }, { - "name": "without_event_definitions", - "in": "path", - "description": "If true return only processes that haven't start event definitions", - "required": false, + "name": "event", + "in": "query", + "description": "Node ID of the start event", + "required": true, "schema": { - "type": "boolean" + "type": "string" + } + } + ], + "requestBody": { + "description": "data that will be stored as part of the created request", + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object" + } } } - ], + }, "responses": { "200": { - "description": "list of processes that the user can start", + "description": "success", "content": { "application/json": { "schema": { - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ProcessWithStartEvents" - } - }, - "meta": { - "$ref": "#/components/schemas/metadata" - } - }, - "type": "object" + "$ref": "#/components/schemas/processRequest" } } } @@ -3446,67 +2261,110 @@ } } }, - "/processes/{processId}/restore": { - "put": { + "/processes/{process}/stages": { + "get": { "tags": [ "Processes" ], - "summary": "Restore an inactive process", - "description": "Reverses the soft delete of the element.", - "operationId": "restoreProcess", + "summary": "Get the list of stages for a process", + "description": "Get stages of a process", + "operationId": "b40606bf1f4f04479be88823f470626f", "parameters": [ { - "name": "processId", + "name": "process", "in": "path", - "description": "ID of process to return", + "description": "ID of the process", "required": true, "schema": { "type": "integer" } + }, + { + "name": "alternative", + "in": "query", + "description": "Alternative version (A or B)", + "required": false, + "schema": { + "type": "string" + } } ], "responses": { "200": { - "description": "success", + "description": "List of stages", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Process" + "type": "array", + "items": { + "properties": { + "id": { + "type": "integer" + }, + "order": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "selected": { + "type": "boolean" + } + }, + "type": "object" + } } } } } } - } - }, - "/processes/{processId}/export": { + }, "post": { "tags": [ "Processes" ], - "summary": "Export a single process by ID and return a URL to download it", - "description": "Export the specified process.", - "operationId": "exportProcess", + "summary": "Save or update the list of stages for a process", + "description": "Save stages for a process", + "operationId": "eb23a62117463592164d70f29aecdf9b", "parameters": [ { - "name": "processId", + "name": "process", "in": "path", - "description": "ID of process to export", + "description": "ID of the process", "required": true, "schema": { "type": "integer" } + }, + { + "name": "alternative", + "in": "query", + "description": "Alternative version (A or B)", + "required": false, + "schema": { + "type": "string" + } } ], - "responses": { - "200": { - "description": "Successfully built the process for export", - "content": { - "application/json": { - "schema": { + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { "properties": { - "url": { + "id": { + "type": "integer" + }, + "order": { + "type": "integer" + }, + "label": { "type": "string" + }, + "selected": { + "type": "boolean" } }, "type": "object" @@ -3514,41 +2372,31 @@ } } } - } - } - }, - "/processes/import/validation": { - "post": { - "tags": [ - "Processes" - ], - "summary": "Validate a import", - "description": "Validate the specified process before importing.", - "operationId": "validateImport", - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "properties": { - "file": { - "description": "file to import", - "type": "string", - "format": "binary" - } - }, - "type": "object" - } - } - } }, "responses": { "200": { - "description": "success", + "description": "Updated stages", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProcessImport" + "type": "array", + "items": { + "properties": { + "id": { + "type": "integer" + }, + "order": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "selected": { + "type": "boolean" + } + }, + "type": "object" + } } } } @@ -3556,111 +2404,197 @@ } } }, - "/processes/import": { - "post": { + "/processes/{process}/aggregation": { + "get": { "tags": [ "Processes" ], - "summary": "Import a new process", - "description": "Import the specified process.", - "operationId": "importProcess", - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "properties": { - "file": { - "description": "file to import", - "type": "string", - "format": "binary" - } - }, - "type": "object" - } + "summary": "Get the aggregation configuration for a process", + "description": "Get aggregation for a process", + "operationId": "ecf07de0e8ce9b5876c72ea0133d83a4", + "parameters": [ + { + "name": "process", + "in": "path", + "description": "ID of the process", + "required": true, + "schema": { + "type": "integer" } } - }, + ], "responses": { "200": { - "description": "success", + "description": "Aggregation configuration", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProcessImport" + "properties": { + "aggregation": { + "description": "string containing var aggregation", + "type": "string" + } + }, + "type": "object" } } } } } - } - }, - "/processes/{processId}/bpmn": { - "get": { + }, + "post": { "tags": [ "Processes" ], - "summary": "Download the BPMN definition of a process", - "description": "Download the BPMN definition of a process", - "operationId": "processBpmn", + "summary": "Save or update the aggregation field for a process", + "description": "Updates the aggregation field of a process. If no aggregation is provided, defaults to 'amount'.", + "operationId": "8b6d8704835d8571bc50e31938fd7ded", "parameters": [ { - "name": "processId", + "name": "process", "in": "path", - "description": "ID of process", + "description": "ID of the process", "required": true, "schema": { "type": "integer" } } ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "properties": { + "aggregation": { + "description": "Field name to use for aggregation (defaults to 'amount' if not provided)", + "type": "string", + "example": "amount" + } + }, + "type": "object" + } + } + } + }, "responses": { "200": { - "description": "Successfully built the process for export", + "description": "Updated aggregation field", "content": { "application/json": { "schema": { "properties": { - "url": { - "type": "string" + "data": { + "description": "The saved aggregation field value", + "type": "string", + "example": "amount" } }, "type": "object" } } } + }, + "404": { + "description": "Process not found" } } } }, - "/processes/import/{code}/is_ready": { - "head": { + "/processes/{process}/stage-mapping": { + "get": { "tags": [ "Processes" ], - "summary": "Check if the import is ready", - "description": "Check if the import is ready", - "operationId": "6a131993b7c879ddcd3d3a291dd8380f", + "summary": "Get process stages configuration", + "description": "Retrieves and formats the stages configuration for a specific process, including total counts and individual stages.", + "operationId": "getStageMapping", "parameters": [ { - "name": "code", + "name": "process", "in": "path", - "description": "Import code", + "description": "ID of the process", "required": true, "schema": { - "type": "string" + "type": "integer" } } ], "responses": { "200": { - "description": "check is import is ready", + "description": "Successful operation", "content": { "application/json": { "schema": { "properties": { - "ready": { - "type": "boolean" + "data": { + "properties": { + "total": { + "properties": { + "stage_id": { + "type": "number", + "example": "0" + }, + "stage_name": { + "type": "string", + "example": "Total Cases" + }, + "percentage": { + "type": "number", + "example": 100 + }, + "percentage_format": { + "type": "string", + "example": "100%" + }, + "agregation_sum": { + "type": "number", + "example": 50000 + }, + "agregation_count": { + "type": "number", + "example": 150 + } + }, + "type": "object" + }, + "stages": { + "type": "array", + "items": { + "properties": { + "stage_id": { + "type": "number", + "example": "1" + }, + "stage_name": { + "type": "string", + "example": "In progress" + }, + "percentage": { + "type": "number", + "example": 60, + "nullable": true + }, + "percentage_format": { + "type": "string", + "example": "60%" + }, + "agregation_sum": { + "type": "number", + "example": 28678, + "nullable": true + }, + "agregation_count": { + "type": "number", + "example": 100, + "nullable": true + } + }, + "type": "object" + } + } + }, + "type": "object" } }, "type": "object" @@ -3671,88 +2605,157 @@ } } }, - "/processes/{process_id}/import/assignments": { - "post": { + "/api/processes/{process}/default-stages": { + "get": { "tags": [ "Processes" ], - "summary": "Update assignments after import", - "description": "Import Assignments of process.", - "operationId": "assignmentProcess", + "summary": "Get default process stages configuration", + "description": "Retrieves and formats the default stages configuration for a process.", + "operationId": "getDefaultStagesPerProcess", "parameters": [ { - "name": "process_id", + "name": "process", "in": "path", - "description": "ID of process to return", + "description": "ID of the process", "required": true, "schema": { "type": "integer" } } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProcessAssignments" + "responses": { + "200": { + "description": "Successful operation", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "properties": { + "stage_id": { + "type": "number", + "example": "1" + }, + "stage_name": { + "type": "string", + "example": "In progress" + }, + "percentage": { + "type": "number", + "example": 60, + "nullable": true + }, + "percentage_format": { + "type": "string", + "example": "60%" + }, + "agregation_sum": { + "type": "number", + "example": 28678, + "nullable": true + }, + "agregation_count": { + "type": "number", + "example": 100, + "nullable": true + } + }, + "type": "object" + } + } } } } - }, - "responses": { - "204": { - "description": "success" - } } } }, - "/process_events/{process_id}": { - "post": { + "/api/processes/{process}/metrics": { + "get": { "tags": [ "Processes" ], - "summary": "Start a new process", - "description": "Trigger an start event within a process.", - "operationId": "triggerStartEvent", + "summary": "Get process metrics configuration", + "description": "Retrieves and formats the metrics configuration for a specific process. Supports different formats like 'student' or default metrics.", + "operationId": "getMetricsPerProcess", "parameters": [ { - "name": "process_id", + "name": "process", "in": "path", - "description": "ID of process to return", + "description": "ID of the process", "required": true, "schema": { "type": "integer" } }, { - "name": "event", + "name": "format", "in": "query", - "description": "Node ID of the start event", - "required": true, + "description": "Format type of the metrics (e.g., 'student')", + "required": false, "schema": { - "type": "string" + "type": "string", + "default": "student", + "enum": [ + "student", + "default" + ] } } ], - "requestBody": { - "description": "data that will be stored as part of the created request", - "required": false, - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, "responses": { "200": { - "description": "success", + "description": "Successful operation", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/processRequest" + "type": "array", + "items": { + "properties": { + "id": { + "type": "integer", + "example": 1 + }, + "metric_description": { + "type": "string", + "example": "Max amount available" + }, + "metric_count": { + "type": "integer", + "example": 10, + "nullable": true + }, + "metric_count_description": { + "type": "string", + "example": "Across 10 applicants" + }, + "metric_value": { + "type": "number", + "example": 84000 + }, + "metric_value_unit": { + "type": "string", + "example": "k" + } + }, + "type": "object" + } + } + } + } + }, + "400": { + "description": "Invalid format parameter", + "content": { + "application/json": { + "schema": { + "properties": { + "error": { + "type": "string", + "example": "Invalid format parameter" + } + }, + "type": "object" } } } @@ -5130,6 +4133,9 @@ "items": { "type": "object" } + }, + "sync": { + "type": "boolean" } }, "type": "object" @@ -6441,6 +5447,64 @@ } } } + }, + "post": { + "tags": [ + "Users" + ], + "summary": "Returns all users and their total tasks (POST version for large form_data)", + "operationId": "postUsersTaskCount", + "requestBody": { + "description": "Request body for filtering users", + "content": { + "application/json": { + "schema": { + "properties": { + "filter": { + "description": "Filter results by string. Searches First Name, Last Name, Email, or Username.", + "type": "string" + }, + "include_ids": { + "description": "Comma separated list of user IDs to include in the response. Eg. 1,2,3", + "type": "string" + }, + "assignable_for_task_id": { + "description": "Task ID to get assignable users for", + "type": "integer" + }, + "form_data": { + "description": "Form data used to evaluate rule expressions for task assignment", + "type": "object" + } + }, + "type": "object" + } + } + } + }, + "responses": { + "200": { + "description": "List of users with task counts", + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/users" + } + }, + "meta": { + "$ref": "#/components/schemas/metadata" + } + }, + "type": "object" + } + } + } + } + } } }, "/users/{user_id}": { @@ -6922,378 +5986,233 @@ } } } - } - }, - "components": { - "schemas": { - "DateTime": { - "properties": { - "date": { - "type": "string" - } - }, - "type": "object" - }, - "collectionsEditable": { - "properties": { - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "custom_title": { - "type": "string" - }, - "create_screen_id": { - "type": "string", - "format": "id" - }, - "read_screen_id": { - "type": "string", - "format": "id" - }, - "update_screen_id": { - "type": "string", - "format": "id" - }, - "signal_create": { - "type": "boolean" - }, - "signal_update": { - "type": "boolean" - }, - "signal_delete": { - "type": "boolean" - } - }, - "type": "object" - }, - "collections": { - "allOf": [ + }, + "/processes/variables": { + "get": { + "tags": [ + "Processes Variables" + ], + "summary": "Get variables for multiple processes with pagination", + "operationId": "660c9459febd17c58400be4b4d49a152", + "parameters": [ { - "$ref": "#/components/schemas/collectionsEditable" + "name": "processIds", + "in": "query", + "description": "Comma-separated list of process IDs", + "required": false, + "schema": { + "type": "string", + "example": "1,2,3", + "nullable": true + } }, { - "properties": { - "id": { - "type": "integer" - }, - "created_at": { - "type": "string", - "format": "date-time" - }, - "updated_at": { - "type": "string", - "format": "date-time" - }, - "created_by_id": { - "type": "string", - "format": "id" - }, - "updated_by_id": { - "type": "string", - "format": "id" - }, - "columns": { - "type": "array", - "items": { - "type": "object" - } - } - }, - "type": "object" - } - ] - }, - "recordsEditable": { - "properties": { - "data": { - "type": "object" - } - }, - "type": "object" - }, - "records": { - "allOf": [ - { - "$ref": "#/components/schemas/recordsEditable" + "name": "page", + "in": "query", + "description": "Page number", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } }, { - "properties": { - "id": { - "type": "integer" - }, - "collection_id": { - "type": "string", - "format": "id" - } - }, - "type": "object" - } - ] - }, - "SavedSearchEditable": { - "properties": { - "meta": { - "description": "Represents an Eloquent model of a Saved Search.", - "type": "object", - "additionalProperties": "true" - }, - "pmql": { - "type": "string" - }, - "title": { - "type": "string" - }, - "type": { - "type": "string", - "enum": [ - "task", - "request" - ] - }, - "advanced_filter": { - "type": "object", - "additionalProperties": "true" + "name": "per_page", + "in": "query", + "description": "Items per page", + "required": false, + "schema": { + "type": "integer", + "default": 20 + } } - }, - "type": "object" - }, - "SavedSearch": { - "allOf": [ - { - "properties": { - "id": { - "type": "string", - "format": "id" - }, - "user_id": { - "type": "string", - "format": "id" - }, - "created_at": { - "type": "string", - "format": "date-time" - }, - "updated_at": { - "type": "string", - "format": "date-time" - } - }, - "type": "object" - }, + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Variable" + } + }, + "meta": { + "$ref": "#/components/schemas/PaginationMeta" + } + }, + "type": "object" + } + } + } + } + }, + "servers": [ { - "$ref": "#/components/schemas/SavedSearchEditable" + "url": "http://landlord.test/api/1.1", + "description": "API v1.1 Server" } ] - }, - "SavedSearchIcon": { + } + } + }, + "components": { + "schemas": { + "DateTime": { "properties": { - "name": { - "type": "string" - }, - "value": { + "date": { "type": "string" } }, "type": "object" }, - "SavedSearchChartEditable": { + "updateUserGroups": { "properties": { - "title": { - "description": "Represents an Eloquent model of a Saved Search Chart.", - "type": "string" - }, - "type": { - "type": "string", - "enum": [ - "bar", - "bar-vertical", - "line", - "pie", - "doughnut" - ] - }, - "config": { - "type": "object", - "additionalProperties": "true" - }, - "sort": { - "type": "integer" + "groups": { + "type": "array", + "items": { + "type": "integer", + "example": 1 + } } }, "type": "object" }, - "SavedSearchChart": { - "allOf": [ - { - "properties": { - "id": { - "type": "string", - "format": "id" - }, - "saved_search_id": { - "type": "string", - "format": "id" - }, - "user_id": { - "type": "string", - "format": "id" - }, - "created_at": { - "type": "string", - "format": "date-time" - }, - "updated_at": { - "type": "string", - "format": "date-time" - }, - "deleted_at": { - "type": "string", - "format": "date-time" - } - }, - "type": "object" - }, - { - "$ref": "#/components/schemas/SavedSearchChartEditable" + "restoreUser": { + "properties": { + "username": { + "description": "Username to restore", + "type": "string" } - ] + }, + "type": "object" }, - "ReportEditable": { + "Variable": { "properties": { - "type": { + "id": { + "type": "integer", + "example": 1 + }, + "process_id": { + "type": "integer", + "example": 1 + }, + "uuid": { "type": "string", - "enum": [ - "adhoc", - "scheduled" - ] + "format": "uuid", + "example": "550e8400-e29b-41d4-a716-446655440000" }, - "format": { + "field": { "type": "string", + "example": "string", "enum": [ - "csv", - "xlsx" + "string", + "number", + "boolean", + "array" ] }, - "saved_search_id": { - "type": "integer" - }, - "config": { - "type": "object", - "additionalProperties": "true" - }, - "to": { - "type": "array", - "items": { - "type": "string" - } + "label": { + "type": "string", + "example": "Variable 1 for Process 1" }, - "subject": { - "type": "string" + "name": { + "type": "string", + "example": "var_1_1" }, - "body": { - "type": "string" - } - }, - "type": "object" - }, - "Report": { - "allOf": [ - { + "asset": { "properties": { "id": { "type": "string", - "format": "id" + "example": "asset_1_1" }, - "user_id": { + "type": { "type": "string", - "format": "id" + "example": "sensor", + "enum": [ + "sensor", + "actuator", + "controller", + "device" + ] }, - "created_at": { + "name": { "type": "string", - "format": "date-time" + "example": "Asset 1 for Process 1" }, - "updated_at": { + "uuid": { "type": "string", - "format": "date-time" + "format": "uuid", + "example": "550e8400-e29b-41d4-a716-446655440000" } }, "type": "object" }, - { - "$ref": "#/components/schemas/SavedSearchEditable" + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" } - ] + }, + "type": "object" }, - "versionHistoryEditable": { + "PaginationMeta": { "properties": { - "versionable_id": { - "description": "Class VersionHistoryCollection", - "type": "integer" + "current_page": { + "type": "integer", + "example": 1 }, - "versionable_type": { - "type": "string" + "from": { + "type": "integer", + "example": 1 }, - "name": { - "type": "string" + "last_page": { + "type": "integer", + "example": 5 }, - "subject": { - "type": "string" + "path": { + "type": "string", + "example": "http://processmaker.com/processes/variables" }, - "description": { - "type": "string" + "per_page": { + "type": "integer", + "example": 20 }, - "status": { - "type": "string", - "enum": [ - "ACTIVE", - "INACTIVE" - ] - } - }, - "type": "object" - }, - "versionHistory": { - "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/versionHistoryEditable" + "to": { + "type": "integer", + "example": 20 }, - { + "total": { + "type": "integer", + "example": 100 + }, + "links": { "properties": { - "created_at": { + "first": { "type": "string", - "format": "date-time" + "example": "http://processmaker.com/processes/variables?page=1" }, - "updated_at": { + "last": { "type": "string", - "format": "date-time" + "example": "http://processmaker.com/processes/variables?page=5" + }, + "prev": { + "type": "string", + "nullable": true + }, + "next": { + "type": "string", + "example": "http://processmaker.com/processes/variables?page=2" } }, "type": "object" } - ] - }, - "updateUserGroups": { - "properties": { - "groups": { - "type": "array", - "items": { - "type": "integer", - "example": 1 - } - } - }, - "type": "object" - }, - "restoreUser": { - "properties": { - "username": { - "description": "Username to restore", - "type": "string" - } }, "type": "object" }, @@ -7982,8 +6901,11 @@ "type": "object" }, "manager_id": { - "type": "integer", - "format": "id" + "type": "array", + "items": { + "type": "integer", + "format": "id" + } } }, "type": "object" @@ -8632,6 +7554,9 @@ }, "key": { "type": "string" + }, + "output": { + "type": "object" } }, "type": "object" @@ -9002,6 +7927,9 @@ }, "force_change_password": { "type": "boolean" + }, + "email_task_notification": { + "type": "boolean" } }, "type": "object" @@ -9194,9 +8122,9 @@ "description": "Laravel passport oauth2 security.", "flows": { "authorizationCode": { - "authorizationUrl": "http://processmaker.test/oauth/authorize", - "tokenUrl": "http://processmaker.test/oauth/token", - "refreshUrl": "http://processmaker.test/token/refresh", + "authorizationUrl": "/oauth/authorize", + "tokenUrl": "/oauth/token", + "refreshUrl": "/token/refresh", "scopes": {} } } @@ -9208,6 +8136,108 @@ } } }, + "tags": [ + { + "name": "Cases", + "description": "Cases" + }, + { + "name": "CssSettings", + "description": "CssSettings" + }, + { + "name": "Environment Variables", + "description": "Environment Variables" + }, + { + "name": "Files", + "description": "Files" + }, + { + "name": "Groups", + "description": "Groups" + }, + { + "name": "Group Members", + "description": "Group Members" + }, + { + "name": "Notifications", + "description": "Notifications" + }, + { + "name": "Permissions", + "description": "Permissions" + }, + { + "name": "Process Categories", + "description": "Process Categories" + }, + { + "name": "Processes", + "description": "Processes" + }, + { + "name": "Process Requests", + "description": "Process Requests" + }, + { + "name": "Request Files", + "description": "Request Files" + }, + { + "name": "Screen Categories", + "description": "Screen Categories" + }, + { + "name": "Screens", + "description": "Screens" + }, + { + "name": "Script Categories", + "description": "Script Categories" + }, + { + "name": "Scripts", + "description": "Scripts" + }, + { + "name": "Rebuild Script Executors", + "description": "Rebuild Script Executors" + }, + { + "name": "Security Logs", + "description": "Security Logs" + }, + { + "name": "Settings", + "description": "Settings" + }, + { + "name": "Signals", + "description": "Signals" + }, + { + "name": "Task Assignments", + "description": "Task Assignments" + }, + { + "name": "Tasks", + "description": "Tasks" + }, + { + "name": "Users", + "description": "Users" + }, + { + "name": "Personal Tokens", + "description": "Personal Tokens" + }, + { + "name": "Processes Variables", + "description": "Processes Variables" + } + ], "security": [ { "passport": [], diff --git a/tests/Feature/Api/PermissionsTest.php b/tests/Feature/Api/PermissionsTest.php index 196ed18f3d..0ce4d4a1ba 100644 --- a/tests/Feature/Api/PermissionsTest.php +++ b/tests/Feature/Api/PermissionsTest.php @@ -22,6 +22,10 @@ class PermissionsTest extends TestCase { use RequestHelper; + private const PERMISSIONS_URL = '/permissions'; + + private const PROCESSES_URL = '/processes'; + protected function withUserSetup() { $this->user->is_administrator = false; @@ -59,10 +63,10 @@ public function testApiPermissions() 'status' => 'ACTIVE', ]); - $response = $this->apiCall('GET', '/processes'); + $response = $this->apiCall('GET', self::PROCESSES_URL); $response->assertStatus(200); - $response = $this->apiCall('GET', '/processes/' . $process->id); + $response = $this->apiCall('GET', self::PROCESSES_URL . '/' . $process->id); $response->assertStatus(200); $permission = Permission::byName('archive-processes'); @@ -74,7 +78,7 @@ public function testApiPermissions() // Invalidate permission cache to ensure changes take effect $this->user->invalidatePermissionCache(); - $response = $this->apiCall('DELETE', '/processes/' . $process->id); + $response = $this->apiCall('DELETE', self::PROCESSES_URL . '/' . $process->id); $response->assertStatus(403); $this->user->permissions()->attach($permission->id); @@ -84,7 +88,7 @@ public function testApiPermissions() // Invalidate permission cache to ensure the new permission takes effect $this->user->invalidatePermissionCache(); - $response = $this->apiCall('DELETE', '/processes/' . $process->id); + $response = $this->apiCall('DELETE', self::PROCESSES_URL . '/' . $process->id); $response->assertStatus(204); } @@ -97,7 +101,7 @@ public function testSetPermissionsForUser() $testUser = User::factory()->create(); $testPermission = Permission::factory()->create(); - $response = $this->apiCall('PUT', '/permissions', [ + $response = $this->apiCall('PUT', self::PERMISSIONS_URL, [ 'user_id' => $testUser->id, 'permission_names' => [$testPermission->name], ]); @@ -109,6 +113,184 @@ public function testSetPermissionsForUser() $this->assertEquals($testUser->permissions->first()->id, $testPermission->id); } + public function testSetPermissionsForGroupWithInheritedEditGroupsPermission() + { + $this->user = User::factory()->create([ + 'password' => Hash::make('password'), + 'is_administrator' => false, + ]); + $this->initializePermissions(false); + + $adminGroup = Group::factory()->create(); + $adminGroup->permissions()->attach(Permission::whereIn('name', [ + 'view-groups', + 'create-groups', + 'edit-groups', + 'delete-groups', + ])->pluck('id')); + + GroupMember::factory()->create([ + 'group_id' => $adminGroup->id, + 'member_type' => User::class, + 'member_id' => $this->user->id, + ]); + + $this->user->invalidatePermissionCache(); + + $targetGroup = Group::factory()->create(); + + $response = $this->apiCall('PUT', self::PERMISSIONS_URL, [ + 'group_id' => $targetGroup->id, + 'permission_names' => ['view-groups', 'edit-groups'], + ]); + + $response->assertStatus(204); + $this->assertEqualsCanonicalizing( + ['view-groups', 'edit-groups'], + $targetGroup->refresh()->permissions()->pluck('name')->toArray() + ); + } + + public function testSetPermissionsForUserWithInheritedEditUsersPermission() + { + $this->user = User::factory()->create([ + 'password' => Hash::make('password'), + 'is_administrator' => false, + ]); + $this->initializePermissions(false); + + $adminGroup = Group::factory()->create(); + $adminGroup->permissions()->attach(Permission::byName('edit-users')->id); + + GroupMember::factory()->create([ + 'group_id' => $adminGroup->id, + 'member_type' => User::class, + 'member_id' => $this->user->id, + ]); + + $this->user->invalidatePermissionCache(); + + $targetUser = User::factory()->create(); + + $response = $this->apiCall('PUT', self::PERMISSIONS_URL, [ + 'user_id' => $targetUser->id, + 'permission_names' => ['view-groups'], + ]); + + $response->assertStatus(204); + $this->assertEqualsCanonicalizing( + ['view-groups'], + $targetUser->refresh()->permissions()->pluck('name')->toArray() + ); + } + + public function testSetPermissionsForUserRequiresEditUsersPermission() + { + $this->user = User::factory()->create([ + 'password' => Hash::make('password'), + 'is_administrator' => false, + ]); + $this->initializePermissions(false); + + $adminGroup = Group::factory()->create(); + $adminGroup->permissions()->attach(Permission::byName('edit-groups')->id); + + GroupMember::factory()->create([ + 'group_id' => $adminGroup->id, + 'member_type' => User::class, + 'member_id' => $this->user->id, + ]); + + $this->user->invalidatePermissionCache(); + + $targetUser = User::factory()->create(); + + $response = $this->apiCall('PUT', self::PERMISSIONS_URL, [ + 'user_id' => $targetUser->id, + 'permission_names' => ['view-groups'], + ]); + + $response->assertStatus(403); + $this->assertFalse($targetUser->refresh()->permissions()->where('name', 'view-groups')->exists()); + } + + public function testSetPermissionsForGroupRequiresEditGroupsPermission() + { + $this->user = User::factory()->create([ + 'password' => Hash::make('password'), + 'is_administrator' => false, + ]); + $this->initializePermissions(false); + + $targetGroup = Group::factory()->create(); + + $response = $this->apiCall('PUT', self::PERMISSIONS_URL, [ + 'group_id' => $targetGroup->id, + 'permission_names' => ['view-groups'], + ]); + + $response->assertStatus(403); + $this->assertCount(0, $targetGroup->refresh()->permissions); + } + + public function testUnauthorizedPermissionUpdatesDoNotExposeTargetExistence() + { + $this->user = User::factory()->create([ + 'password' => Hash::make('password'), + 'is_administrator' => false, + ]); + $this->initializePermissions(false); + + $targetUser = User::factory()->create(); + $targetGroup = Group::factory()->create(); + $missingUserId = User::max('id') + 1; + $missingGroupId = Group::max('id') + 1; + + $response = $this->apiCall('PUT', self::PERMISSIONS_URL, [ + 'user_id' => $targetUser->id, + 'permission_names' => ['view-groups'], + ]); + $response->assertStatus(403); + + $response = $this->apiCall('PUT', self::PERMISSIONS_URL, [ + 'user_id' => $missingUserId, + 'permission_names' => ['view-groups'], + ]); + $response->assertStatus(403); + + $response = $this->apiCall('PUT', self::PERMISSIONS_URL, [ + 'group_id' => $targetGroup->id, + 'permission_names' => ['view-groups'], + ]); + $response->assertStatus(403); + + $response = $this->apiCall('PUT', self::PERMISSIONS_URL, [ + 'group_id' => $missingGroupId, + 'permission_names' => ['view-groups'], + ]); + $response->assertStatus(403); + } + + public function testSetPermissionsRequiresExactlyOneTarget() + { + $targetUser = User::factory()->create(); + $targetGroup = Group::factory()->create(); + + $response = $this->apiCall('PUT', self::PERMISSIONS_URL, [ + 'permission_names' => ['view-groups'], + ]); + + $response->assertStatus(422); + + $response = $this->apiCall('PUT', self::PERMISSIONS_URL, [ + 'user_id' => $targetUser->id, + 'group_id' => $targetGroup->id, + 'permission_names' => ['view-groups'], + ]); + + $response->assertStatus(422); + } + public function testSetPermissionsViewProcessCatalogForUser() { $faker = Faker::create(); @@ -180,7 +362,7 @@ public function testCategoryPermission() // Invalidate permission cache to ensure the new permission takes effect $this->user->invalidatePermissionCache(); - $response = $this->apiCall('PUT', $url, $attrs); + $this->apiCall('PUT', $url, $attrs); $this->assertEquals('Test Category Update', $class::find($id)->name); // test view permission @@ -250,8 +432,8 @@ public function testSetPermissionsViewMyRequestForUser() public function testSetPermissionsViewMyRequestForUsersAndGroupCreated() { // Set up the users and groups - $users = User::factory()->count(5)->create(); - $groups = Group::factory()->count(3)->create(); + User::factory()->count(5)->create(); + Group::factory()->count(3)->create(); // Run the seeder $this->seed(PermissionSeeder::class); @@ -279,7 +461,7 @@ public function testAdministratorRoleAssignment() $this->user = $regularUser; $this->user->save(); - $response = $this->apiCall('PUT', '/permissions', [ + $response = $this->apiCall('PUT', self::PERMISSIONS_URL, [ 'user_id' => $targetUser->id, 'is_administrator' => true, 'permission_names' => [], @@ -292,7 +474,7 @@ public function testAdministratorRoleAssignment() $targetUser->is_administrator = true; $targetUser->save(); - $response = $this->apiCall('PUT', '/permissions', [ + $response = $this->apiCall('PUT', self::PERMISSIONS_URL, [ 'user_id' => $targetUser->id, 'is_administrator' => false, 'permission_names' => [], @@ -306,7 +488,7 @@ public function testAdministratorRoleAssignment() $this->user = $adminUser; $this->user->save(); - $response = $this->apiCall('PUT', '/permissions', [ + $response = $this->apiCall('PUT', self::PERMISSIONS_URL, [ 'user_id' => $targetUser->id, 'is_administrator' => false, 'permission_names' => [], @@ -317,7 +499,7 @@ public function testAdministratorRoleAssignment() $this->assertFalse($targetUser->is_administrator); // Test 4: Admin can grant admin role - $response = $this->apiCall('PUT', '/permissions', [ + $response = $this->apiCall('PUT', self::PERMISSIONS_URL, [ 'user_id' => $targetUser->id, 'is_administrator' => true, 'permission_names' => [], diff --git a/tests/Feature/Api/TimerStartEventTest.php b/tests/Feature/Api/TimerStartEventTest.php index 88b483e7be..27635609ab 100644 --- a/tests/Feature/Api/TimerStartEventTest.php +++ b/tests/Feature/Api/TimerStartEventTest.php @@ -228,4 +228,25 @@ public function testScheduleMustNotStartTimerEventWhenProcessInactive() $task->type = 'TIMER_START_EVENT'; $manager->executeTimerStartEvent($task, json_decode($task->configuration)); } + + public function testScheduleMustNotStartHandleRepliesTimerWhenAbeInboundConfigInadequate() + { + // triggerStartEvent must not run when ABE inbound mail settings are missing/inadequate + WorkflowManager::shouldReceive('triggerStartEvent') + ->never() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any()); + + $data = []; + $data['name'] = 'Actions By Email - Handle Replies'; + $data['bpmn'] = Process::getProcessTemplate('TimerStartEvent.bpmn'); + + $process = Process::factory()->create($data); + + $manager = new TaskSchedulerManager(); + $task = new ScheduledTask(); + $task->process_id = $process->id; + $task->configuration = '{"type":"TimeCycle","interval":"R4\/2019-02-13T13:08:00Z\/PT1M", "element_id" : "_9"}'; + $task->type = 'TIMER_START_EVENT'; + $manager->executeTimerStartEvent($task, json_decode($task->configuration)); + } } diff --git a/tests/Feature/HomeControllerTest.php b/tests/Feature/HomeControllerTest.php index ebd1527464..32e8abbb95 100644 --- a/tests/Feature/HomeControllerTest.php +++ b/tests/Feature/HomeControllerTest.php @@ -2,16 +2,22 @@ namespace Tests\Feature; -use Tests\TestCase; -use ProcessMaker\Models\User; use ProcessMaker\Models\Group; +use ProcessMaker\Models\User; use ProcessMaker\Package\PackageDynamicUI\Models\DynamicUI; use Tests\Feature\Shared\RequestHelper; +use Tests\TestCase; class HomeControllerTest extends TestCase { use RequestHelper; + /** + * Sample deep-link URL used as the "originally requested" page for the + * redirect-to-intended tests. + */ + private const INTENDED_DEEP_LINK = '/designer/scripts/123/edit'; + protected function setUp(): void { parent::setUp(); @@ -32,7 +38,7 @@ public function testRedirectsToLoginWhenNotAuthenticated() public function testRedirectsToCustomDashboardWhenUserHasDashboard() { $user = User::factory()->create(); - + // Create a custom dashboard for the user DynamicUI::create([ 'type' => 'DASHBOARD', @@ -68,7 +74,7 @@ public function testRedirectsToCustomDashboardWhenGroupHasDashboard() public function testRedirectsToTasksOnMobileWithoutCustomDashboard() { $user = User::factory()->create(); - + // Mock MobileHelper to return true $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Mobile/15E148 Safari/604.1'; $_COOKIE['isMobile'] = 'true'; @@ -84,7 +90,7 @@ public function testRedirectsToTasksOnMobileWithoutCustomDashboard() public function testRedirectsToInboxOnDesktopWithoutCustomDashboard() { $user = User::factory()->create(); - + // Mock MobileHelper to return false $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36'; $_COOKIE['isMobile'] = 'false'; @@ -131,4 +137,76 @@ public function testRedirectsToGroupUrlRedirect() $response = $this->actingAs($user)->get('/'); $response->assertRedirect('https://processmaker.com/home'); } + + /** @test */ + public function testRedirectToIntendedHonorsCookie() + { + $user = User::factory()->create(); + $response = $this->actingAs($user) + ->withCookie('processmaker_intended', self::INTENDED_DEEP_LINK) + ->get(route('redirect_to_intended')); + $response->assertRedirect(self::INTENDED_DEEP_LINK); + } + + /** + * Regression test for the SAML "land on home instead of intended URL" + * bug: when the user comes back from the SSO callback through the + * /redirect-to-intended recovery hop with a valid `processmaker_intended` + * cookie, the cookie wins -- the configured Dynamic UI home page does + * not preempt it. + * + * @test + */ + public function testRedirectToIntendedCookieTakesPrecedenceOverDynamicUiHome() + { + $user = User::factory()->create(); + + DynamicUI::create([ + 'type' => 'URL', + 'assignable_id' => $user->id, + 'assignable_type' => User::class, + 'homepage' => 'https://processmaker.com/configured-landing', + ]); + + $response = $this->actingAs($user) + ->withCookie('processmaker_intended', self::INTENDED_DEEP_LINK) + ->get(route('redirect_to_intended')); + + $response->assertRedirect(self::INTENDED_DEEP_LINK); + } + + /** + * The intended-URL cookie is single-use: once we've consumed it we must + * tell the browser to drop it, otherwise it would keep deflecting every + * subsequent SSO callback to the same stale URL. + * + * @test + */ + public function testRedirectToIntendedClearsCookieAfterUse() + { + $user = User::factory()->create(); + + $response = $this->actingAs($user) + ->withCookie('processmaker_intended', self::INTENDED_DEEP_LINK) + ->get(route('redirect_to_intended')); + + $response->assertRedirect(self::INTENDED_DEEP_LINK); + $response->assertCookieExpired('processmaker_intended'); + } + + /** + * When there's no intended URL at all we still need to send the user somewhere. + * With no dashboard configured, DynamicUI::getHomePage() falls through to route('home') + * which HomeController::index() will then re-route to /inbox (or /tasks on mobile) on the next request. + * + * @test + */ + public function testRedirectToIntendedFallsBackToHomeWhenNoCookieAndNoDashboard() + { + $user = User::factory()->create(); + + $response = $this->actingAs($user)->get(route('redirect_to_intended')); + + $response->assertRedirect(route('home')); + } } diff --git a/tests/unit/ProcessMaker/Managers/TaskSchedulerManagerTest.php b/tests/unit/ProcessMaker/Managers/TaskSchedulerManagerTest.php index 7937ed4211..3e3d1c5adc 100644 --- a/tests/unit/ProcessMaker/Managers/TaskSchedulerManagerTest.php +++ b/tests/unit/ProcessMaker/Managers/TaskSchedulerManagerTest.php @@ -4,6 +4,10 @@ use Carbon\Carbon; use DateTime; +use ProcessMaker\Models\EnvironmentVariable; +use ProcessMaker\Models\Process; +use ProcessMaker\Models\Setting; +use ReflectionMethod; use Tests\TestCase; class TaskSchedulerManagerTest extends TestCase @@ -74,4 +78,64 @@ public function testTruncateDates() $rounded = $this->manager->truncateDateTime($date); $this->assertEquals('00:01:00', $rounded->format('H:i:s')); } + + public function testHasAdequateAbeInboundConfigurationForStandardAuth() + { + Setting::updateOrCreate(['key' => 'abe_imap_auth_method'], ['config' => '0']); + Setting::updateOrCreate(['key' => 'abe_imap_username'], ['config' => 'abe@test.com']); + Setting::updateOrCreate(['key' => 'abe_imap_password'], ['config' => '123Test']); + Setting::updateOrCreate(['key' => 'abe_imap_server'], ['config' => 'imap.example.com']); + Setting::updateOrCreate(['key' => 'abe_imap_port'], ['config' => '993']); + + $this->assertTrue((bool) $this->invokePrivateMethod('hasAdequateAbeInboundConfiguration')); + } + + public function testHasAdequateAbeInboundConfigurationForGoogleOauth() + { + Setting::updateOrCreate(['key' => 'abe_imap_auth_method'], ['config' => '1']); + Setting::updateOrCreate(['key' => 'abe_imap_username'], ['config' => 'abe@test.com']); + + foreach ([ + 'ABE_GMAIL_API_CLIENT_ID', + 'ABE_GMAIL_API_SECRET', + 'ABE_GMAIL_API_ACCESS_TOKEN', + 'ABE_GMAIL_API_REFRESH_TOKEN', + ] as $name) { + EnvironmentVariable::factory()->create([ + 'name' => $name, + 'value' => 'value-' . strtolower($name), + ]); + } + + $this->assertTrue((bool) $this->invokePrivateMethod('hasAdequateAbeInboundConfiguration')); + } + + public function testHasAdequateAbeInboundConfigurationReturnsFalseWhenUsernameIsMissing() + { + Setting::updateOrCreate(['key' => 'abe_imap_auth_method'], ['config' => '0']); + Setting::updateOrCreate(['key' => 'abe_imap_username'], ['config' => '']); + Setting::updateOrCreate(['key' => 'abe_imap_password'], ['config' => '123Test']); + Setting::updateOrCreate(['key' => 'abe_imap_server'], ['config' => 'imap.example.com']); + Setting::updateOrCreate(['key' => 'abe_imap_port'], ['config' => '993']); + + $this->assertFalse((bool) $this->invokePrivateMethod('hasAdequateAbeInboundConfiguration')); + } + + public function testIsHandleRepliesProcessUsesPackageKey() + { + $process = new Process([ + 'package_key' => 'package-actions-by-email/handle-replies', + 'name' => 'Anything', + ]); + + $this->assertTrue((bool) $this->invokePrivateMethod('isHandleRepliesProcess', [$process])); + } + + private function invokePrivateMethod(string $method, array $args = []) + { + $reflection = new ReflectionMethod($this->manager, $method); + $reflection->setAccessible(true); + + return $reflection->invokeArgs($this->manager, $args); + } } diff --git a/upgrades/2026_05_07_212653_set_user_id_on_oauth_client.php b/upgrades/2026_05_07_212653_set_user_id_on_oauth_client.php new file mode 100644 index 0000000000..2b9f183677 --- /dev/null +++ b/upgrades/2026_05_07_212653_set_user_id_on_oauth_client.php @@ -0,0 +1,71 @@ +where('is_administrator', true) + ->orderBy('id') + ->value('id'); + + if ($adminUserId === null) { + return; + } + + DB::table('oauth_clients') + ->where('personal_access_client', true) + ->whereNull('user_id') + ->update(['user_id' => $adminUserId]); + } + + /** + * Reverse the upgrade migration. + * + * @return void + */ + public function down() + { + $adminUserId = DB::table('users') + ->where('is_administrator', true) + ->orderBy('id') + ->value('id'); + + if ($adminUserId === null) { + return; + } + + DB::table('oauth_clients') + ->where('personal_access_client', true) + ->where('user_id', $adminUserId) + ->update(['user_id' => null]); + } +}