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| Task | \nCase | \nDue Date | \nAssigned By | \n
\n\n\n\n| {{element_name}} | \n{{case_title}} | \n{{due_date}} | \n{{assigned_by}} | \n
\n\n
",
- "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| Task | \nCase | \nDue Date | \nAssigned By | \n
\n\n\n\n| {{_task_name}} | \n{{_case_title}} | \n{{_due_date}} | \n{{_assigned_by}} | \n
\n\n
",
+ "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')}}
-
-
-
-
@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']);
+ }
+ }
+ }
+}