diff --git a/ProcessMaker/Http/Controllers/Api/ScreenController.php b/ProcessMaker/Http/Controllers/Api/ScreenController.php index c7459c9fbf..943618b81b 100644 --- a/ProcessMaker/Http/Controllers/Api/ScreenController.php +++ b/ProcessMaker/Http/Controllers/Api/ScreenController.php @@ -482,6 +482,14 @@ public function duplicate(Screen $screen, Request $request) */ public function destroy(Screen $screen) { + // Check if the screen is a default screen + if ($screen->is_default == 1) { + return response([ + 'message' => 'Cannot delete a default screen', + 'errors' => ['is_default' => 'Default screens cannot be deleted'], + ], 422); + } + $screen->delete(); // Call new event to store changes in LOG ScreenDeleted::dispatch($screen); diff --git a/ProcessMaker/Http/Controllers/Process/ModelerController.php b/ProcessMaker/Http/Controllers/Process/ModelerController.php index 6e7ac436cb..6f665518ec 100644 --- a/ProcessMaker/Http/Controllers/Process/ModelerController.php +++ b/ProcessMaker/Http/Controllers/Process/ModelerController.php @@ -12,6 +12,7 @@ use ProcessMaker\Models\ProcessCategory; use ProcessMaker\Models\ProcessLaunchpad; use ProcessMaker\Models\ProcessRequest; +use ProcessMaker\Models\Screen; use ProcessMaker\Models\ScreenCategory; use ProcessMaker\Models\ScreenType; use ProcessMaker\Models\ScriptCategory; @@ -125,6 +126,8 @@ public function prepareModelerData( $process->load('alternativeInfo'); } + $defaultEmailNotification = $this->getDefaultEmailNotification(); + return [ 'process' => $process, 'manager' => $manager, @@ -147,6 +150,34 @@ public function prepareModelerData( 'alternative' => $alternative, 'abPublish' => PackageHelper::isPackageInstalled('ProcessMaker\Package\PackageABTesting\PackageServiceProvider'), 'launchpad' => ProcessLaunchpad::getLaunchpad(true, $process->id), + 'defaultEmailNotification' => $defaultEmailNotification, + ]; + } + + /** + * Get the default email notification configuration for tasks + * + * Returns an array containing the default email notification settings including: + * - subject: The default email subject template + * - type: The notification type (screen) + * - screenRef: The ID of the default email notification screen + * - toRecipients: Array of default recipients (assigned user) + * @return array{screenRef: mixed, subject: string, toRecipients: array, type: string} + */ + private function getDefaultEmailNotification(): array + { + $screen = Screen::getScreenByKey('default-email-task-notification'); + + return [ + 'subject' => 'RE: {{_user.firstname}} assigned you in "{{_task_name}}"', + 'type' => 'screen', + 'screenRef' => $screen->id, + 'toRecipients' => [ + [ + 'type' => 'assignedUser', + 'value' => null, + ], + ], ]; } diff --git a/ProcessMaker/Models/Screen.php b/ProcessMaker/Models/Screen.php index d8d16a8987..71c1953127 100644 --- a/ProcessMaker/Models/Screen.php +++ b/ProcessMaker/Models/Screen.php @@ -305,7 +305,17 @@ public static function getScreenByKey(string $key) : ?self return $screen; } - private static function createScreenByKey(string $key, bool $isSystem = true, string $path = null): self + public static function getScreenByKeyPerDefault(string $key) : ?self + { + $screen = self::firstWhere('key', $key); + if (!$screen) { + $screen = self::createScreenByKey($key, false, null, 1); + } + + return $screen; + } + + private static function createScreenByKey(string $key, bool $isSystem = true, string $path = null, $isDefault = 0): self { // If no path is provided, use the default path if (!$path) { @@ -339,6 +349,7 @@ private static function createScreenByKey(string $key, bool $isSystem = true, st // Create new screen unset($screen['categories']); $screen['screen_category_id'] = null; + $screen['is_default'] = $isDefault; if ($newScreen) { $newScreen->fill($screen); diff --git a/ProcessMaker/Repositories/TokenRepository.php b/ProcessMaker/Repositories/TokenRepository.php index 25ad6eeabf..e98a967622 100644 --- a/ProcessMaker/Repositories/TokenRepository.php +++ b/ProcessMaker/Repositories/TokenRepository.php @@ -176,10 +176,6 @@ public function persistActivityActivated(ActivityInterface $activity, TokenInter if (!is_null($user)) { // Review if the task has enable the action by email $this->validateAndSendActionByEmail($activity, $token, $user->email); - // Review if the user has enable the email notification - $isEmailTaskValid = $this->validateEmailUserNotification($token, $user); - // Define the flag if the email needs to sent - $token->is_emailsent = $isEmailTaskValid ? 1 : 0; } $this->instanceRepository->persistInstanceUpdated($token->getInstance()); } @@ -231,71 +227,6 @@ private function validateAndSendActionByEmail(ActivityInterface $activity, Token } } - /** - * Validates the user's email notification settings and sends an email if enabled. - * - * @param TokenInterface $token The token containing task information. - * @param User $user The user to whom the email notification will be sent. - * @return mixed|null Returns the result of the email sending operation or null if not sent. - */ - private function validateEmailUserNotification(TokenInterface $token, User $user) - { - try { - Log::Info('User isEmailTaskEnable: ' . $user->email_task_notification); - // Return if email task notification is not enabled or email is empty - if ($user->email_task_notification === 0 || empty($user->email)) { - return null; - } - // Prepare data for the email - $data = $this->prepareEmailData($token, $user); - - // Send Email - return (new TaskActionByEmail())->sendAbeEmail($data['configEmail'], $user->email, $data['emailData']); - } catch (\Exception $e) { - // Catch and log the error - Log::error('Failed to validate and send email task notification', [ - 'error' => $e->getMessage(), - ]); - } - } - - /** - * Prepares the email data and configuration for sending an email notification. - * - * @param TokenInterface $token The token containing task information. - * @param User $user The user for whom the email data is being prepared. - * @return array An associative array containing 'emailData' and 'configEmail'. - */ - private function prepareEmailData(TokenInterface $token, User $user) - { - // Get the case - $caseTitle = ProcessRequest::where('id', $token->process_request_id)->value('case_title'); - // Prepare the email data - $taskName = $token->element_name ?? ''; - $emailData = [ - 'firstname' => $user->firstname ?? '', - 'assigned_by' => Auth::user()->fullname ?? __('System'), - 'element_name' => $taskName, - 'case_title' => $caseTitle, // Populate this if needed - 'due_date' => $token->due_at ?? '', - 'link_review_task' => config('app.url') . '/' . 'tasks/' . $token->id . '/edit', - 'imgHeader' => config('app.url') . '/img/processmaker_login.png', - ]; - // Get the screen by key - $screen = Screen::getScreenByKey('default-email-task-notification'); - // Prepare the email configuration - $configEmail = [ - 'emailServer' => 0, // Use the default email server - 'subject' => "{$user->firstname} assigned you in '{$taskName}'", - 'screenEmailRef' => $screen->id ?? 0, // Define here the screen to use - ]; - - return [ - 'emailData' => $emailData, - 'configEmail' => $configEmail, - ]; - } - /** * Get due Variable * diff --git a/database/migrations/2025_07_09_215813_add_is_default_column_for_screens.php b/database/migrations/2025_07_09_215813_add_is_default_column_for_screens.php new file mode 100644 index 0000000000..3a58d8f0aa --- /dev/null +++ b/database/migrations/2025_07_09_215813_add_is_default_column_for_screens.php @@ -0,0 +1,33 @@ +boolean('is_default')->default(false); + }); + Schema::table('screen_versions', function (Blueprint $table) { + $table->boolean('is_default')->default(false); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('screens', function (Blueprint $table) { + $table->dropColumn('is_default'); + }); + Schema::table('screen_versions', function (Blueprint $table) { + $table->dropColumn('is_default'); + }); + } +}; diff --git a/database/processes/screens/default-email-task-notification.json b/database/processes/screens/default-email-task-notification.json index b3eb5e2400..d6f3944b20 100644 --- a/database/processes/screens/default-email-task-notification.json +++ b/database/processes/screens/default-email-task-notification.json @@ -1,448 +1,544 @@ { - "type": "screen_package", - "version": "2", - "screens": [ + "type": "screen_package", + "version": "2", + "screens": [ + { + "screen_category_id": null, + "title": "Default Email Task Notification", + "description": "Screen for the email task notification", + "type": "EMAIL", + "config": [ { - "screen_category_id": null, - "title": "DEFAULT_EMAIL_TASK_NOTIFICATION", - "description": "Screen for the email task notification", - "type": "EMAIL", - "config": [ - { - "name": "DEFAULT_EMAIL_TASK_NOTIFICATION", - "items": [ - { - "uuid": "c331f828-3b0f-47a3-bf6e-9037717a7690", - "label": "Rich Text", - "config": { - "icon": "fas fa-pencil-ruler", - "label": null, - "content": "

", - "interactive": true, - "renderVarHtml": false - }, - "component": "FormHtmlViewer", - "inspector": [ - { - "type": "FormTextArea", - "field": "content", - "config": { - "rows": 5, - "label": "Content", - "value": null, - "helper": "The HTML text to display" - } - }, - { - "type": "FormCheckbox", - "field": "renderVarHtml", - "config": { - "label": "Render HTML from a Variable", - "value": null, - "helper": null - } - }, - { - "type": "FormInput", - "field": "conditionalHide", - "config": { - "label": "Visibility Rule", - "helper": "This control is hidden until this expression is true" - } - }, - { - "type": "DeviceVisibility", - "field": "deviceVisibility", - "config": { - "label": "Device Visibility", - "helper": "This control is hidden until this expression is true" - } - }, - { - "type": "FormInput", - "field": "customFormatter", - "config": { - "label": "Custom Format String", - "helper": "Use the Mask Pattern format
Date ##/##/####
SSN ###-##-####
Phone (###) ###-####", - "validation": null - } - }, - { - "type": "FormInput", - "field": "customCssSelector", - "config": { - "label": "CSS Selector Name", - "helper": "Use this in your custom css rules", - "validation": "regex: [-?[_a-zA-Z]+[_-a-zA-Z0-9]*]" - } - }, - { - "type": "FormInput", - "field": "ariaLabel", - "config": { - "label": "Aria Label", - "helper": "Attribute designed to help assistive technology (e.g. screen readers) attach a label" - } - }, - { - "type": "FormInput", - "field": "tabindex", - "config": { - "label": "Tab Order", - "helper": "Order in which a user will move focus from one control to another by pressing the Tab key", - "validation": "regex: [0-9]*" - } - }, - { - "type": "EncryptedConfig", - "field": "encryptedConfig", - "config": { - "label": "Encrypted", - "helper": null - } - } - ], - "editor-control": "FormHtmlEditor", - "editor-component": "FormHtmlEditor" - }, - { - "uuid": "64801c0f-3b96-4261-a1ab-76f521a537ba", - "label": "Rich Text", - "config": { - "icon": "fas fa-pencil-ruler", - "label": null, - "content": "

Hello {{ _firstname }} You just been assigned in a task by {{assigned_by}}.

", - "interactive": true, - "renderVarHtml": false - }, - "component": "FormHtmlViewer", - "inspector": [ - { - "type": "FormTextArea", - "field": "content", - "config": { - "rows": 5, - "label": "Content", - "value": null, - "helper": "The HTML text to display" - } - }, - { - "type": "FormCheckbox", - "field": "renderVarHtml", - "config": { - "label": "Render HTML from a Variable", - "value": null, - "helper": null - } - }, - { - "type": "FormInput", - "field": "conditionalHide", - "config": { - "label": "Visibility Rule", - "helper": "This control is hidden until this expression is true" - } - }, - { - "type": "DeviceVisibility", - "field": "deviceVisibility", - "config": { - "label": "Device Visibility", - "helper": "This control is hidden until this expression is true" - } - }, - { - "type": "FormInput", - "field": "customFormatter", - "config": { - "label": "Custom Format String", - "helper": "Use the Mask Pattern format
Date ##/##/####
SSN ###-##-####
Phone (###) ###-####", - "validation": null - } - }, - { - "type": "FormInput", - "field": "customCssSelector", - "config": { - "label": "CSS Selector Name", - "helper": "Use this in your custom css rules", - "validation": "regex: [-?[_a-zA-Z]+[_-a-zA-Z0-9]*]" - } - }, - { - "type": "FormInput", - "field": "ariaLabel", - "config": { - "label": "Aria Label", - "helper": "Attribute designed to help assistive technology (e.g. screen readers) attach a label" - } - }, - { - "type": "FormInput", - "field": "tabindex", - "config": { - "label": "Tab Order", - "helper": "Order in which a user will move focus from one control to another by pressing the Tab key", - "validation": "regex: [0-9]*" - } - }, - { - "type": "EncryptedConfig", - "field": "encryptedConfig", - "config": { - "label": "Encrypted", - "helper": null - } - } - ], - "editor-control": "FormHtmlEditor", - "editor-component": "FormHtmlEditor" - }, - { - "uuid": "1329abf7-3427-49cf-98f8-31f64fcbdd1b", - "label": "Link URL", - "config": { - "icon": "fas fa-link", - "event": "link", - "label": "REVIEW TASK", - "linkUrl": "{{link_review_task}}", - "variant": "primary" - }, - "component": "LinkButton", - "inspector": [ - { - "type": "FormInput", - "field": "label", - "config": { - "label": "Label", - "helper": "The label describes the button's text" - } - }, - { - "type": "FormInput", - "field": "linkUrl", - "config": { - "label": "Link URL", - "helper": "Type here the URL link. Mustache syntax is supported." - } - }, - { - "type": "FormMultiselect", - "field": "variant", - "config": { - "label": "Button Variant Style", - "helper": "The variant determines the appearance of the button", - "options": [ - { - "value": "primary", - "content": "Primary" - }, - { - "value": "secondary", - "content": "Secondary" - }, - { - "value": "success", - "content": "Success" - }, - { - "value": "danger", - "content": "Danger" - }, - { - "value": "warning", - "content": "Warning" - }, - { - "value": "info", - "content": "Info" - }, - { - "value": "light", - "content": "Light" - }, - { - "value": "dark", - "content": "Dark" - }, - { - "value": "link", - "content": "Link" - } - ] - } - }, - { - "type": "FormInput", - "field": "conditionalHide", - "config": { - "label": "Visibility Rule", - "helper": "This control is hidden until this expression is true" - } - }, - { - "type": "DeviceVisibility", - "field": "deviceVisibility", - "config": { - "label": "Device Visibility", - "helper": "This control is hidden until this expression is true" - } - }, - { - "type": "FormInput", - "field": "customFormatter", - "config": { - "label": "Custom Format String", - "helper": "Use the Mask Pattern format
Date ##/##/####
SSN ###-##-####
Phone (###) ###-####", - "validation": null - } - }, - { - "type": "FormInput", - "field": "customCssSelector", - "config": { - "label": "CSS Selector Name", - "helper": "Use this in your custom css rules", - "validation": "regex: [-?[_a-zA-Z]+[_-a-zA-Z0-9]*]" - } - }, - { - "type": "FormInput", - "field": "ariaLabel", - "config": { - "label": "Aria Label", - "helper": "Attribute designed to help assistive technology (e.g. screen readers) attach a label" - } - }, - { - "type": "FormInput", - "field": "tabindex", - "config": { - "label": "Tab Order", - "helper": "Order in which a user will move focus from one control to another by pressing the Tab key", - "validation": "regex: [0-9]*" - } - }, - { - "type": "EncryptedConfig", - "field": "encryptedConfig", - "config": { - "label": "Encrypted", - "helper": null - } - } - ], - "editor-control": "LinkButton", - "editor-component": "LinkButton" - }, - { - "uuid": "1c520abd-b1ef-4d42-9b69-887c70bb94b6", - "label": "Rich Text", - "config": { - "icon": "fas fa-pencil-ruler", - "label": null, - "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
TaskCaseDue DateAssigned By
{{element_name}}{{case_title}}{{due_date}}{{assigned_by}}
", - "interactive": true, - "renderVarHtml": false - }, - "component": "FormHtmlViewer", - "inspector": [ - { - "type": "FormTextArea", - "field": "content", - "config": { - "rows": 5, - "label": "Content", - "value": null, - "helper": "The HTML text to display" - } - }, - { - "type": "FormCheckbox", - "field": "renderVarHtml", - "config": { - "label": "Render HTML from a Variable", - "value": null, - "helper": null - } - }, - { - "type": "FormInput", - "field": "conditionalHide", - "config": { - "label": "Visibility Rule", - "helper": "This control is hidden until this expression is true" - } - }, - { - "type": "DeviceVisibility", - "field": "deviceVisibility", - "config": { - "label": "Device Visibility", - "helper": "This control is hidden until this expression is true" - } - }, - { - "type": "FormInput", - "field": "customFormatter", - "config": { - "label": "Custom Format String", - "helper": "Use the Mask Pattern format
Date ##/##/####
SSN ###-##-####
Phone (###) ###-####", - "validation": null - } - }, - { - "type": "FormInput", - "field": "customCssSelector", - "config": { - "label": "CSS Selector Name", - "helper": "Use this in your custom css rules", - "validation": "regex: [-?[_a-zA-Z]+[_-a-zA-Z0-9]*]" - } - }, - { - "type": "FormInput", - "field": "ariaLabel", - "config": { - "label": "Aria Label", - "helper": "Attribute designed to help assistive technology (e.g. screen readers) attach a label" - } - }, - { - "type": "FormInput", - "field": "tabindex", - "config": { - "label": "Tab Order", - "helper": "Order in which a user will move focus from one control to another by pressing the Tab key", - "validation": "regex: [0-9]*" - } - }, - { - "type": "EncryptedConfig", - "field": "encryptedConfig", - "config": { - "label": "Encrypted", - "helper": null - } - } - ], - "editor-control": "FormHtmlEditor", - "editor-component": "FormHtmlEditor" - } + "name": "Default Email Task Notification", + "items": [ + { + "uuid": "c331f828-3b0f-47a3-bf6e-9037717a7690", + "label": "Rich Text", + "config": { + "icon": "fas fa-pencil-ruler", + "label": null, + "content": "

", + "interactive": true, + "renderVarHtml": false + }, + "component": "FormHtmlViewer", + "inspector": [ + { + "type": "FormTextArea", + "field": "content", + "config": { + "rows": 5, + "label": "Content", + "value": null, + "helper": "The HTML text to display" + } + }, + { + "type": "FormCheckbox", + "field": "renderVarHtml", + "config": { + "label": "Render HTML from a Variable", + "value": null, + "helper": null + } + }, + { + "type": "FormInput", + "field": "conditionalHide", + "config": { + "label": "Visibility Rule", + "helper": "This control is hidden until this expression is true" + } + }, + { + "type": "DeviceVisibility", + "field": "deviceVisibility", + "config": { + "label": "Device Visibility", + "helper": "This control is hidden until this expression is true" + } + }, + { + "type": "FormInput", + "field": "customFormatter", + "config": { + "label": "Custom Format String", + "helper": "Use the Mask Pattern format
Date ##/##/####
SSN ###-##-####
Phone (###) ###-####", + "validation": null + } + }, + { + "type": "FormInput", + "field": "customCssSelector", + "config": { + "label": "CSS Selector Name", + "helper": "Use this in your custom css rules", + "validation": "regex: [-?[_a-zA-Z]+[_-a-zA-Z0-9]*]" + } + }, + { + "type": "FormInput", + "field": "ariaLabel", + "config": { + "label": "Aria Label", + "helper": "Attribute designed to help assistive technology (e.g. screen readers) attach a label" + } + }, + { + "type": "FormInput", + "field": "tabindex", + "config": { + "label": "Tab Order", + "helper": "Order in which a user will move focus from one control to another by pressing the Tab key", + "validation": "regex: [0-9]*" + } + }, + { + "type": "EncryptedConfig", + "field": "encryptedConfig", + "config": { + "label": "Encrypted", + "helper": null + } + } + ], + "editor-control": "FormHtmlEditor", + "editor-component": "FormHtmlEditor" + }, + { + "uuid": "64801c0f-3b96-4261-a1ab-76f521a537ba", + "label": "Rich Text", + "config": { + "icon": "fas fa-pencil-ruler", + "label": null, + "content": "

You have been assigned to a new task: {{_task_name}}
Please log in to your account to review and complete the task.

", + "interactive": true, + "renderVarHtml": false + }, + "component": "FormHtmlViewer", + "inspector": [ + { + "type": "FormTextArea", + "field": "content", + "config": { + "rows": 5, + "label": "Content", + "value": null, + "helper": "The HTML text to display" + } + }, + { + "type": "FormCheckbox", + "field": "renderVarHtml", + "config": { + "label": "Render HTML from a Variable", + "value": null, + "helper": null + } + }, + { + "type": "FormInput", + "field": "conditionalHide", + "config": { + "label": "Visibility Rule", + "helper": "This control is hidden until this expression is true" + } + }, + { + "type": "DeviceVisibility", + "field": "deviceVisibility", + "config": { + "label": "Device Visibility", + "helper": "This control is hidden until this expression is true" + } + }, + { + "type": "FormInput", + "field": "customFormatter", + "config": { + "label": "Custom Format String", + "helper": "Use the Mask Pattern format
Date ##/##/####
SSN ###-##-####
Phone (###) ###-####", + "validation": null + } + }, + { + "type": "FormInput", + "field": "customCssSelector", + "config": { + "label": "CSS Selector Name", + "helper": "Use this in your custom css rules", + "validation": "regex: [-?[_a-zA-Z]+[_-a-zA-Z0-9]*]" + } + }, + { + "type": "FormInput", + "field": "ariaLabel", + "config": { + "label": "Aria Label", + "helper": "Attribute designed to help assistive technology (e.g. screen readers) attach a label" + } + }, + { + "type": "FormInput", + "field": "tabindex", + "config": { + "label": "Tab Order", + "helper": "Order in which a user will move focus from one control to another by pressing the Tab key", + "validation": "regex: [0-9]*" + } + }, + { + "type": "EncryptedConfig", + "field": "encryptedConfig", + "config": { + "label": "Encrypted", + "helper": null + } + } + ], + "editor-control": "FormHtmlEditor", + "editor-component": "FormHtmlEditor" + }, + { + "uuid": "1329abf7-3427-49cf-98f8-31f64fcbdd1b", + "label": "Link URL", + "config": { + "icon": "fas fa-link", + "event": "link", + "label": "View in Processmaker", + "linkUrl": "{{link_review_task}}", + "variant": "primary", + "variantStyle": "button" + }, + "component": "LinkButton", + "inspector": [ + { + "type": "FormInput", + "field": "label", + "config": { + "label": "Label", + "helper": "The label describes the button's text" + } + }, + { + "type": "FormInput", + "field": "linkUrl", + "config": { + "label": "Link URL", + "helper": "Type here the URL link. Mustache syntax is supported." + } + }, + { + "type": "FormMultiselect", + "field": "variant", + "config": { + "label": "Button Variant Style", + "helper": "The variant determines the appearance of the button", + "options": [ + { + "value": "primary", + "content": "Primary" + }, + { + "value": "secondary", + "content": "Secondary" + }, + { + "value": "success", + "content": "Success" + }, + { + "value": "danger", + "content": "Danger" + }, + { + "value": "warning", + "content": "Warning" + }, + { + "value": "info", + "content": "Info" + }, + { + "value": "light", + "content": "Light" + }, + { + "value": "dark", + "content": "Dark" + }, + { + "value": "link", + "content": "Link" + } ] + } + }, + { + "type": "FormInput", + "field": "conditionalHide", + "config": { + "label": "Visibility Rule", + "helper": "This control is hidden until this expression is true" + } + }, + { + "type": "DeviceVisibility", + "field": "deviceVisibility", + "config": { + "label": "Device Visibility", + "helper": "This control is hidden until this expression is true" + } + }, + { + "type": "FormInput", + "field": "customFormatter", + "config": { + "label": "Custom Format String", + "helper": "Use the Mask Pattern format
Date ##/##/####
SSN ###-##-####
Phone (###) ###-####", + "validation": null + } + }, + { + "type": "FormInput", + "field": "customCssSelector", + "config": { + "label": "CSS Selector Name", + "helper": "Use this in your custom css rules", + "validation": "regex: [-?[_a-zA-Z]+[_-a-zA-Z0-9]*]" + } + }, + { + "type": "FormInput", + "field": "ariaLabel", + "config": { + "label": "Aria Label", + "helper": "Attribute designed to help assistive technology (e.g. screen readers) attach a label" + } + }, + { + "type": "FormInput", + "field": "tabindex", + "config": { + "label": "Tab Order", + "helper": "Order in which a user will move focus from one control to another by pressing the Tab key", + "validation": "regex: [0-9]*" + } + }, + { + "type": "EncryptedConfig", + "field": "encryptedConfig", + "config": { + "label": "Encrypted", + "helper": null + } + } + ], + "editor-control": "LinkButton", + "editor-component": "LinkButton" + }, + { + "uuid": "68ec3d88-f1be-4e30-a5d1-4ff914d58dd0", + "label": "Rich Text", + "config": { + "icon": "fas fa-pencil-ruler", + "label": null, + "content": "

Review Task Details

", + "interactive": true, + "renderVarHtml": false + }, + "component": "FormHtmlViewer", + "inspector": [ + { + "type": "FormTextArea", + "field": "content", + "config": { + "rows": 5, + "label": "Content", + "value": null, + "helper": "The HTML text to display" + } + }, + { + "type": "FormCheckbox", + "field": "renderVarHtml", + "config": { + "label": "Render HTML from a Variable", + "value": null, + "helper": null + } + }, + { + "type": "FormInput", + "field": "conditionalHide", + "config": { + "label": "Visibility Rule", + "helper": "This control is hidden until this expression is true" + } + }, + { + "type": "DeviceVisibility", + "field": "deviceVisibility", + "config": { + "label": "Device Visibility", + "helper": "This control is hidden until this expression is true" + } + }, + { + "type": "FormInput", + "field": "customFormatter", + "config": { + "label": "Custom Format String", + "helper": "Use the Mask Pattern format
Date ##/##/####
SSN ###-##-####
Phone (###) ###-####", + "validation": null + } + }, + { + "type": "FormInput", + "field": "customCssSelector", + "config": { + "label": "CSS Selector Name", + "helper": "Use this in your custom css rules", + "validation": "regex: [-?[_a-zA-Z]+[_-a-zA-Z0-9]*]" + } + }, + { + "type": "FormInput", + "field": "ariaLabel", + "config": { + "label": "Aria Label", + "helper": "Attribute designed to help assistive technology (e.g. screen readers) attach a label" + } + }, + { + "type": "FormInput", + "field": "tabindex", + "config": { + "label": "Tab Order", + "helper": "Order in which a user will move focus from one control to another by pressing the Tab key", + "validation": "regex: [0-9]*" + } + }, + { + "type": "EncryptedConfig", + "field": "encryptedConfig", + "config": { + "label": "Encrypted", + "helper": null + } + } + ], + "editor-control": "FormHtmlEditor", + "editor-component": "FormHtmlEditor" + }, + { + "uuid": "1c520abd-b1ef-4d42-9b69-887c70bb94b6", + "label": "Rich Text", + "config": { + "icon": "fas fa-pencil-ruler", + "label": null, + "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
TaskCaseDue DateAssigned By
{{_task_name}}{{_case_title}}{{_due_date}}{{_assigned_by}}
", + "interactive": true, + "renderVarHtml": false + }, + "component": "FormHtmlViewer", + "inspector": [ + { + "type": "FormTextArea", + "field": "content", + "config": { + "rows": 5, + "label": "Content", + "value": null, + "helper": "The HTML text to display" + } + }, + { + "type": "FormCheckbox", + "field": "renderVarHtml", + "config": { + "label": "Render HTML from a Variable", + "value": null, + "helper": null + } + }, + { + "type": "FormInput", + "field": "conditionalHide", + "config": { + "label": "Visibility Rule", + "helper": "This control is hidden until this expression is true" + } + }, + { + "type": "DeviceVisibility", + "field": "deviceVisibility", + "config": { + "label": "Device Visibility", + "helper": "This control is hidden until this expression is true" + } + }, + { + "type": "FormInput", + "field": "customFormatter", + "config": { + "label": "Custom Format String", + "helper": "Use the Mask Pattern format
Date ##/##/####
SSN ###-##-####
Phone (###) ###-####", + "validation": null + } + }, + { + "type": "FormInput", + "field": "customCssSelector", + "config": { + "label": "CSS Selector Name", + "helper": "Use this in your custom css rules", + "validation": "regex: [-?[_a-zA-Z]+[_-a-zA-Z0-9]*]" + } + }, + { + "type": "FormInput", + "field": "ariaLabel", + "config": { + "label": "Aria Label", + "helper": "Attribute designed to help assistive technology (e.g. screen readers) attach a label" + } + }, + { + "type": "FormInput", + "field": "tabindex", + "config": { + "label": "Tab Order", + "helper": "Order in which a user will move focus from one control to another by pressing the Tab key", + "validation": "regex: [0-9]*" + } + }, + { + "type": "EncryptedConfig", + "field": "encryptedConfig", + "config": { + "label": "Encrypted", + "helper": null + } } - ], - "computed": [], - "custom_css": "", - "status": "ACTIVE", - "key": "default-email-task-notification", - "watchers": [], - "translations": null, - "is_template": 0, - "categories": null + ], + "editor-control": "FormHtmlEditor", + "editor-component": "FormHtmlEditor" + } + ], + "order": 1 } - ], - "screen_categories": [], - "scripts": [] - } \ No newline at end of file + ], + "computed": [], + "custom_css": ".link-button {\n max-width: max-content;\n margin-left: auto;\n margin-right: auto;\n}\n.email-wrapper {\n background-color: #F3F5F7;\n}", + "status": "ACTIVE", + "key": "default-email-task-notification", + "watchers": [], + "translations": null, + "is_template": 0, + "categories": null + } + ], + "screen_categories": [], + "scripts": [] +} \ No newline at end of file diff --git a/database/seeders/ScreenEmailSeeder.php b/database/seeders/ScreenEmailSeeder.php index 3e5260dfdc..e72a4dbf17 100644 --- a/database/seeders/ScreenEmailSeeder.php +++ b/database/seeders/ScreenEmailSeeder.php @@ -14,6 +14,6 @@ class ScreenEmailSeeder extends Seeder */ public function run() { - return Screen::getScreenByKey('default-email-task-notification'); + return Screen::getScreenByKeyPerDefault('default-email-task-notification'); } } diff --git a/resources/img/arrow-top-right.png b/resources/img/arrow-top-right.png new file mode 100644 index 0000000000..4d1cdfa154 Binary files /dev/null and b/resources/img/arrow-top-right.png differ diff --git a/resources/img/arrow_top_right.svg b/resources/img/arrow_top_right.svg new file mode 100644 index 0000000000..58dacc1c1b --- /dev/null +++ b/resources/img/arrow_top_right.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/js/processes/modeler/modelerInit.js b/resources/js/processes/modeler/modelerInit.js index 9722d04267..d6e6ba92cc 100644 --- a/resources/js/processes/modeler/modelerInit.js +++ b/resources/js/processes/modeler/modelerInit.js @@ -1,24 +1,41 @@ -import { nextTick } from "vue"; - export default {}; // Highlight the node when it is added // TODO: This is a workaround to highlight the node when it is added // because the highlightNode method is not working when the node is added export const configureTaskNotifications = ({ modeler }) => { - modeler.$on("node-added", (node) => { - if (node.type.includes("task") && node.notifications) { - node.notifications.assignee = { - assigned: true, - completed: false, - due: true, - default: false, + modeler.$on("before-node-added", (node) => { + if (node.type.includes("task") && !node.notifications) { + node.notifications = { + assignee: { + assigned: true, + completed: false, + due: true, + default: false, + }, + requester : { + assigned: false, + completed: false, + due: false, + }, + participants : { + assigned: false, + completed: false, + due: false, + }, + manager : { + assigned: false, + completed: false, + due: false, + }, + }; + } + + if (node.type.includes("task") && !node.config) { + node.config = { + email_notifications: {}, }; } - modeler.clearSelection(); - nextTick(() => { - modeler.highlightNode(node); - }); }); }; diff --git a/resources/views/processes/modeler/index.blade.php b/resources/views/processes/modeler/index.blade.php index 4cd8fe38ae..621fa3550c 100644 --- a/resources/views/processes/modeler/index.blade.php +++ b/resources/views/processes/modeler/index.blade.php @@ -61,6 +61,8 @@ }, ] + window.ProcessMaker.defaultEmailNotification = @json($defaultEmailNotification); + window.ProcessMaker.multiplayer = { broadcaster: "{{config('multiplayer.default')}}", host: "{{config('multiplayer.url')}}", diff --git a/resources/views/shared/users/sidebar.blade.php b/resources/views/shared/users/sidebar.blade.php index 61ea8d3183..68a60d4853 100644 --- a/resources/views/shared/users/sidebar.blade.php +++ b/resources/views/shared/users/sidebar.blade.php @@ -95,18 +95,6 @@ @endif -
-
- {{__('Task Notifications Email')}} -
-
-
- {{ html()->checkbox('email_task_notification', 1, false)->id('email_task_notification')->class('custom-control-input')->attribute('v-model', 'formData.email_task_notification') }} - -
-
-
- @isset($addons) @foreach ($addons as $addon) {!! $addon['content'] ?? '' !!} diff --git a/tests/Feature/TaskControllerTest.php b/tests/Feature/TaskControllerTest.php index 8924442338..262d17ae3c 100644 --- a/tests/Feature/TaskControllerTest.php +++ b/tests/Feature/TaskControllerTest.php @@ -146,33 +146,4 @@ public function testReturnMessageTokenNoFound() $response->assertSee('Token not found'); $response->assertStatus(404); } - - /** - * Test email task notification - */ - public function testEmailTaskNotificationInFormTask() - { - $user = User::factory()->create([ - 'email_task_notification' => 1, - ]); - Auth::login($user); - $process = Process::factory()->create([ - 'bpmn' => file_get_contents(__DIR__ . '/../Fixtures/email_task_notification_process.bpmn'), - ]); - // Start a request - $route = route('api.process_events.trigger', [$process->id, 'event' => 'node_1']); - $data = []; - $response = $this->apiCall('POST', $route, $data); - $response->assertStatus(201); - // Find the request - $instance = ProcessRequest::first(); - $task = ProcessRequestToken::where('element_type', 'task')->where('process_id', $process->id)->where('status', 'ACTIVE')->first(); - $this->assertEquals(0, $task->is_emailsent); - $user = User::where('id', $task->user_id)->first(); - $user->email_task_notification = 1; - $user->save(); - WorkflowManager::completeTask($process, $instance, $task, []); - $task = ProcessRequestToken::where('element_type', 'task')->where('process_id', $process->id)->where('status', 'ACTIVE')->first(); - $this->assertEquals(0, $task->is_emailsent); - } } diff --git a/tests/unit/ScreenEmailSeederTest.php b/tests/unit/ScreenEmailSeederTest.php new file mode 100644 index 0000000000..638d3ddd8c --- /dev/null +++ b/tests/unit/ScreenEmailSeederTest.php @@ -0,0 +1,48 @@ +run(); + + $screen = Screen::where('key', 'default-email-task-notification')->first(); + + $this->assertNotNull($screen); + $this->assertEquals('default-email-task-notification', $screen->key); + + $systemCategory = ScreenCategory::where('name', 'System')->first(); + if ($systemCategory) { + $this->assertFalse($screen->categories()->where('category_id', $systemCategory->id)->exists()); + } + + $this->assertNull($screen->screen_category_id); + } + + public function test_get_screen_by_key_non_system_method() + { + $screen = Screen::getScreenByKeyPerDefault('default-email-task-notification'); + + $this->assertNotNull($screen); + $this->assertEquals('default-email-task-notification', $screen->key); + + $systemCategory = ScreenCategory::where('name', 'System')->first(); + if ($systemCategory) { + $this->assertFalse($screen->categories()->where('category_id', $systemCategory->id)->exists()); + } + + $this->assertNull($screen->screen_category_id); + $this->assertEquals($screen->is_default, 1); + } +} diff --git a/upgrades/2025_07_08_151252_update_default_email_task_notification_screen_category.php b/upgrades/2025_07_08_151252_update_default_email_task_notification_screen_category.php new file mode 100644 index 0000000000..668a8bc2fd --- /dev/null +++ b/upgrades/2025_07_08_151252_update_default_email_task_notification_screen_category.php @@ -0,0 +1,54 @@ +first(); + + if ($screen) { + // Remove the screen from the System category + $systemCategory = ScreenCategory::where('is_system', 1)->first(); + + if ($systemCategory) { + $screen->categories()->detach($systemCategory->id); + } + + // Set screen_category_id to null to remove any category association + $screen->update(['screen_category_id' => null, 'title' => 'Default Email Task Notification', 'is_default' => 1]); + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // Find the default email task notification screen + $screen = Screen::where('key', 'default-email-task-notification')->first(); + + if ($screen) { + // Re-add the screen to the System category + $systemCategory = ScreenCategory::where('is_system', 1)->first(); + + if ($systemCategory) { + $screen->categories()->attach($systemCategory->id); + $screen->update(['screen_category_id' => $systemCategory->id, 'title' => 'DEFAULT_EMAIL_TASK_NOTIFICATION']); + } + } + } +}