diff --git a/contrib/openldap/core-fd.schema b/contrib/openldap/core-fd.schema index c8429d3d40c021d55667914baa8bfa4663cc8356..070a1ba9f090a47f4662ae17ae86fc0e49f5469b 100644 --- a/contrib/openldap/core-fd.schema +++ b/contrib/openldap/core-fd.schema @@ -1,7 +1,10 @@ ## -## core-fd.schema - Needed by FusionDirectory for its basic fonctionnalities +## core-fd.schema - Needed by FusionDirectory for its basic functionalities ## +# Last OID used for attributes : 1.3.6.1.4.1.38414.62.1.67 05/02/24 # +# Last OID used for objectClass : 1.3.6.1.4.1.38414.62.2.11 29/01/24 # + ##### Attributes from gosa ###### attributetype ( 1.3.6.1.4.1.10098.1.1.12.30 NAME 'gosaGroupObjects' @@ -139,6 +142,64 @@ attributetype ( 1.3.6.1.4.1.38414.62.1.10 NAME 'fdMailTemplateSubject' SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40) +##### Tasks Life Cycle Attributes ##### + +attributetype ( 1.3.6.1.4.1.38414.62.1.65 NAME 'fdTasksLifeCycleMembers' + DESC 'Fusion Directory - List of members or groups in task life cycle' + EQUALITY caseExactMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) + +attributetype ( 1.3.6.1.4.1.38414.62.1.66 NAME 'fdTasksLifeCycleListOfDN' + DESC 'Fusion Directory - Emails derived from DN' + EQUALITY caseExactMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) + +## Previous / existing states ## + +attributetype ( 1.3.6.1.4.1.38414.62.1.58 NAME 'fdTasksLifeCyclePreResource' + DESC 'Fusion Directory - Tasks for life cycle - previous supann resource' + EQUALITY caseExactMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 + SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.38414.62.1.59 NAME 'fdTasksLifeCyclePreState' + DESC 'Fusion Directory - Tasks for life cycle - previous supann state' + EQUALITY caseExactMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 + SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.38414.62.1.60 NAME 'fdTasksLifeCyclePreSubState' + DESC 'Fusion Directory - Tasks for life cycle - previous supann sub state' + EQUALITY caseExactMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 + SINGLE-VALUE ) + +## Posteriors / new desired states ## + +attributetype ( 1.3.6.1.4.1.38414.62.1.61 NAME 'fdTasksLifeCyclePostResource' + DESC 'Fusion Directory - Tasks for life cycle - new resource' + EQUALITY caseExactMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 + SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.38414.62.1.62 NAME 'fdTasksLifeCyclePostState' + DESC 'Fusion Directory - Tasks for life cycle - new state' + EQUALITY caseExactMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 + SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.38414.62.1.63 NAME 'fdTasksLifeCyclePostSubState' + DESC 'Fusion Directory - Tasks for life cycle - new sub state' + EQUALITY caseExactMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 + SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.38414.62.1.64 NAME 'fdTasksLifeCyclePostEndDate' + DESC 'Fusion Directory - Tasks for life cycle - days to be added to end date' + EQUALITY caseExactMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 + SINGLE-VALUE ) + ##### Tasks Attributes ##### attributetype ( 1.3.6.1.4.1.38414.62.1.11 NAME 'fdTasksMailObject' @@ -148,7 +209,7 @@ attributetype ( 1.3.6.1.4.1.38414.62.1.11 NAME 'fdTasksMailObject' SINGLE-VALUE ) attributetype ( 1.3.6.1.4.1.38414.62.1.12 NAME 'fdTasksScheduleDate' - DESC 'Scheduling of the Task - required processed date' + DESC 'Scheduling of the Task - required processed date' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) @@ -167,6 +228,11 @@ attributetype ( 1.3.6.1.4.1.38414.62.1.15 NAME 'fdTasksEndDate' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) +attributetype ( 1.3.6.1.4.1.38414.62.1.57 NAME 'fdTasksLastExec' + DESC 'Fusion Directory - Time when tasks was last executed' + EQUALITY caseExactMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) + attributetype ( 1.3.6.1.4.1.38414.62.1.16 NAME 'fdTasksCreationDate' DESC 'Fusion Directory - Task Start Date' EQUALITY caseExactMatch @@ -201,7 +267,7 @@ attributetype ( 1.3.6.1.4.1.38414.62.1.20 NAME 'fdTasksGranularStatus' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) attributetype ( 1.3.6.1.4.1.38414.62.1.21 NAME 'fdTasksGranularSchedule' - DESC 'Scheduling of the Task - required processed date' + DESC 'Scheduling of the Task - required processed date' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) @@ -238,6 +304,13 @@ attributetype ( 1.3.6.1.4.1.38414.62.1.26 NAME 'fdTasksGranularRef' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE ) +## Any tasks requiring to store DN (Such as lifeCycle). ## + +attributetype ( 1.3.6.1.4.1.38414.62.1.67 NAME 'fdTasksGranularDN' + DESC 'Fusion Directory - DN of the targeted user' + EQUALITY caseExactMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) + ##### Tasks Conf ##### attributetype ( 1.3.6.1.4.1.38414.62.1.27 NAME 'fdTasksRDN' @@ -341,7 +414,7 @@ attributetype ( 1.3.6.1.4.1.38414.62.1.43 NAME 'fdPluginManagerSupportDownloadUr SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) attributetype ( 1.3.6.1.4.1.38414.62.1.44 NAME 'fdPluginManagerInfoTags' - DESC 'FusionDirectory - Plugin Tag for identiy plugins goals' + DESC 'FusionDirectory - Plugin Tag for identity plugins goals' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) @@ -476,7 +549,8 @@ objectclass ( 1.3.6.1.4.1.38414.62.2.5 NAME 'fdMailTemplateConf' objectclass (1.3.6.1.4.1.38414.62.2.6 NAME 'fdTasks' DESC 'FusionDirectory - Tasks objects' MUST ( cn $ fdTasksStatus $ fdTasksCreationDate ) - MAY ( fdTasksScheduleDate $ fdTasksEndDate $ fdTasksRepeatableSchedule $ fdTasksUpdatable $ fdTasksRepeatable ) ) + MAY ( fdTasksScheduleDate $ fdTasksEndDate $ fdTasksRepeatableSchedule $ fdTasksUpdatable $ fdTasksRepeatable + $ fdTasksLastExec)) objectclass (1.3.6.1.4.1.38414.62.2.7 NAME 'fdTasksMail' DESC 'FusionDirectory - Tasks objects Mail' @@ -484,11 +558,17 @@ objectclass (1.3.6.1.4.1.38414.62.2.7 NAME 'fdTasksMail' MUST ( fdTasksMailObject $ fdTasksEmailSender ) MAY ( fdTasksMailUsers $ fdTasksEmailsFromDN $ fdTasksMailType $ fdTasksEmailBCC ) ) +objectclass (1.3.6.1.4.1.38414.62.2.11 NAME 'fdTasksLifeCycle' + DESC 'FusionDirectory - Tasks objects Life Cycle' + SUP top AUXILIARY + MUST ( fdTasksLifeCyclePreResource $ fdTasksLifeCyclePreState $ fdTasksLifeCyclePostResource $ fdTasksLifeCyclePostState) + MAY ( fdTasksLifeCyclePreSubState $ fdTasksLifeCyclePostEndDate $ fdTasksLifeCyclePostSubState $ fdTasksLifeCycleMembers + $ fdTasksLifeCycleListOfDN ) ) + objectclass (1.3.6.1.4.1.38414.62.2.8 NAME 'fdTasksGranular' DESC 'FusionDirectory - Tasks granular objects' - MUST ( fdTasksGranularMaster $ cn $ fdTasksGranularMail $ fdTasksGranularMailFrom $ - fdTasksGranularRef $ fdTasksGranularType $ fdTasksGranularSchedule $ fdTasksGranularStatus ) - MAY (fdTasksGranularMailBCC)) + MUST ( fdTasksGranularMaster $ cn $ fdTasksGranularType $ fdTasksGranularSchedule $ fdTasksGranularStatus ) + MAY (fdTasksGranularMailBCC $ fdTasksGranularDN $ fdTasksGranularRef $ fdTasksGranularMail $ fdTasksGranularMailFrom)) objectclass (1.3.6.1.4.1.38414.62.2.9 NAME 'fdTasksConf' DESC 'FusionDirectory - Tasks objects Configuration' diff --git a/html/themes/breezy/less/plugin.less b/html/themes/breezy/less/plugin.less index 69ff126a0db18f5ae01df3b890f3fafffdd305e9..17ac4da1eb1a5377944cc69c00d250be00637744 100644 --- a/html/themes/breezy/less/plugin.less +++ b/html/themes/breezy/less/plugin.less @@ -244,17 +244,27 @@ html.rtl .plugin-section { fieldset.plugin-section.critical legend span, .plugin-section.critical > span.legend { font-weight: bold; - background-color: @critical-section-legend-background-color; + background-color: #bbccff; +} + +fieldset.plugin-section.information legend span, +.plugin-section.information > span.legend { + font-weight: bold; + background-color: #bbccff; } fieldset.plugin-section.critical legend span:after, .plugin-section.critical > span.legend:after { content: " (editing this can break your LDAP)"; - color: @critical-section-warning-color; + color: #dd0000; } .plugin-section.critical > div { - background-color: @critical-section-background-color; + background-color: #dfefff; +} + +.plugin-section.information > div { + background-color: #dfefff; } /* On small screens */ diff --git a/html/themes/breezy/plugin.css b/html/themes/breezy/plugin.css index 12fd99b8e6fc1cff96997a6e0756f6d9729e91fd..11ef02861e325c740d0683497a1b7076669c45f4 100644 --- a/html/themes/breezy/plugin.css +++ b/html/themes/breezy/plugin.css @@ -231,6 +231,13 @@ fieldset.plugin-section.critical legend span, font-weight: bold; background-color: #bbccff; } + +fieldset.plugin-section.information legend span, +.plugin-section.information > span.legend { + font-weight: bold; + background-color: #bbccff; +} + fieldset.plugin-section.critical legend span:after, .plugin-section.critical > span.legend:after { content: " (editing this can break your LDAP)"; @@ -239,6 +246,10 @@ fieldset.plugin-section.critical legend span:after, .plugin-section.critical > div { background-color: #dfefff; } + +.plugin-section.information > div { + background-color: #dfefff; +} /* On small screens */ @media (max-width: 640px) { .plugin-window { diff --git a/html/themes/legacy/plugin.css b/html/themes/legacy/plugin.css index 2e357ce3c5bc19782251430d5f843cd83e28214a..c8ec19c9e3eb5f68fd78fa9a46b61ea36d11844b 100644 --- a/html/themes/legacy/plugin.css +++ b/html/themes/legacy/plugin.css @@ -201,11 +201,20 @@ font-weight: bold; background-color: #bcf; } +fieldset.plugin-section.critical legend span, .plugin-section.information > span.legend { + font-weight: bold; + background-color: #bcf; +} + fieldset.plugin-section.critical legend span:after, .plugin-section.critical > span.legend:after { content: " (editing this can break your LDAP)"; color: #d00; } +.plugin-section.information > div { + background-color: #dfefff; +} + .plugin-section.critical > div { background-color: #dfefff; } diff --git a/plugins/configuration/tasks/class_tasks.inc b/plugins/configuration/tasks/class_tasks.inc index ccf98b0f053cb074803c36b5b0faa01f675f335b..02a4bdff06fbbda62c4216786bf79eae3018d16e 100644 --- a/plugins/configuration/tasks/class_tasks.inc +++ b/plugins/configuration/tasks/class_tasks.inc @@ -26,15 +26,15 @@ class tasks extends simplePlugin static function plInfo (): array { return [ - 'plShortName' => _('Tasks'), - 'plDescription' => _('Tasks'), - 'plObjectClass' => ['fdTasks'], - 'plFilter' => '(objectClass=fdTasks)', - 'plPriority' => 41, - 'plObjectType' => ['tasks' => [ - 'name' => _('Tasks'), - 'ou' => get_ou('tasksRDN'), - 'icon' => 'geticon.php?context=applications&icon=tasks&size=16', + 'plShortName' => _('Tasks'), + 'plDescription' => _('Tasks'), + 'plObjectClass' => ['fdTasks'], + 'plFilter' => '(objectClass=fdTasks)', + 'plPriority' => 41, + 'plObjectType' => ['tasks' => [ + 'name' => _('Tasks'), + 'ou' => get_ou('tasksRDN'), + 'icon' => 'geticon.php?context=applications&icon=tasks&size=16', ]], 'plProvidedAcls' => parent::generatePlProvidedAcls(static::getAttributesInfo()) ]; @@ -44,24 +44,25 @@ class tasks extends simplePlugin { return [ // Attributes are grouped by section - 'tasks' => [ + 'tasks' => [ 'name' => _('Tasks Generic'), 'attrs' => [ - new StringAttribute( - _('Task Name'), _('Name for this task'), - 'cn', TRUE - ), - new DateTimeAttribute( - _('Schedule'), '', - 'fdTasksScheduleDate', FALSE - ), - - new HiddenAttribute('fdTasksStatus', TRUE, '1', '', 'Status', 'Status of the task'), - new HiddenAttribute('fdTasksCreationDate', TRUE, date("Y-m-d h:i:sa"), '', 'StartDate', 'Start Date And Time Of A Task'), + new StringAttribute( + _('Task Name'), _('Name for this task'), + 'cn', TRUE + ), + new DateTimeAttribute( + _('Schedule'), '', + 'fdTasksScheduleDate', FALSE + ), + + new HiddenAttribute('fdTasksStatus', TRUE, '1', '', 'Status', 'Status of the task'), + new HiddenAttribute('fdTasksLastExec', FALSE, '', '', 'LastExec', 'Last exec date'), + new HiddenAttribute('fdTasksCreationDate', TRUE, date("Y-m-d h:i:sa"), '', 'StartDate', 'Start Date And Time Of A Task'), ] ], - 'subTasks' => [ - 'name' => _('Creation of Sub Tasks - Starting this task'), + 'subTasks' => [ + 'name' => _('Creation of Sub Tasks - Starting this task'), 'attrs' => [ new BooleanAttribute( _('Activate SubTasks'), _('Trigger the creation of this task and related subtasks'), @@ -70,7 +71,7 @@ class tasks extends simplePlugin ] ], 'taskSetting' => [ - 'name' => _('Advanced settings'), + 'name' => _('Advanced settings'), 'attrs' => [ new BooleanAttribute( _('Only with new members'), _('Allows creation of sub-tasks for "NEW MEMBERS" only. (Case of Dynamic Group)'), @@ -92,7 +93,6 @@ class tasks extends simplePlugin function __construct ($dn = NULL, $object = NULL, $parent = NULL, $mainTab = FALSE) { - global $config; parent::__construct($dn, $object, $parent, $mainTab); $this->attributesAccess['fdTasksRepeatable']->setManagedAttributes( @@ -108,4 +108,166 @@ class tasks extends simplePlugin $this->attributesAccess['fdSubTasksActivation']->setInLdap(FALSE); } + function save (): array + { + // Verification if the bool of activation is ticked and activate the last exec accordingly. + if ($this->fdSubTasksActivation === TRUE) { + $currentDateTime = date("Y-m-d h:i:sa", time()); + $this->fdTasksLastExec = $currentDateTime; + } + return parent::save(); + } + + public function createSlaveTasks (array $listOfDN, string $attributeType, array $attrs = NULL): void + { + global $config; + $ldap = $config->get_ldap_link(); + + // Take the attribute from the other tabs - attribute cannot be null or unset by default + $schedule = $this->fdTasksScheduleDate ?? NULL; + // Verify if members can have multiple sub-tasks for that main task. + $newMemberOnly = $this->fdTasksUpdatable; + + // remove 'dn' keeping only 'cn' + $rmDn = preg_replace('/(?=,).*/', '', $this->dn); + // only take the cn without dc + preg_match('/cn=(.*)/', $rmDn, $matches); + + if (!empty($listOfDN)) { + // Condition allowing the creation of subtasks for existing members + if ($newMemberOnly === TRUE) { + $ldap->cd($config->current['BASE']); + $filter = '(&(objectClass=fdTasksGranular)(fdTasksGranularMaster=' . $this->dn . '))'; + $ldap->search($filter, [$attributeType]); + + // The while loop is important to get all info from ldap into the array. + while ($info = $ldap->fetch()) { + $subTasks[] = $info; + } + + if (!empty($subTasks)) { + // Recuperate members DN from the ldap search. + foreach ($subTasks as $subTask) { + $membersDN[] = $subTask['fdTasksGranularDN'][0]; + } + // Verify the DN differences and only keep those. + if (!empty($membersDN)) { + $listOfDN = array_diff($listOfDN, $membersDN); + } + // Simple re-index the array. + $listOfDN = array_values($listOfDN); + } + } + + foreach ($listOfDN as $dn) { + // Here we create the object taskGranular + $tabObject = objects::create('TasksGranular'); + + // Create a unique ID based on timestamp (Allowing duplicate subtasks for same members in case of repeat). + $timestamp = microtime(TRUE); // Get the current timestamp with microseconds + $timestamp = (string)$timestamp; // Convert the float to a string, str_replace expect array or string. + $uniqueID = str_replace(".", "_", $timestamp); // Remove . with _ for correct CN + + // Array matches come from preg_match function above with rmDn + $subTaskName = $matches[1] . '-SubTask-' . $uniqueID; + + // Define the type of the granular task based on the attribute type passed to this method. + $prepData = NULL; + switch ($attributeType) { + case 'fdTasksGranularMail': + $prepData['tasksGranular'] = [ + "fdTasksGranularMail" => $dn, + "fdTasksGranularType" => 'Mail Object', + "fdTasksGranularRef" => $attrs['ref'], + "fdTasksGranularMailFrom" => $attrs['from'], + "fdTasksGranularMailBCC" => $attrs['bcc'] + ]; + break; + case 'fdTasksLifeCycle' : + $prepData['tasksGranular'] = [ + "fdTasksGranularDN" => $dn, + "fdTasksGranularType" => 'Life Cycle', + ]; + break; + } + + // Common attributes to be filled for object tasksGranular. + $defaultData['tasksGranular'] = [ + "cn" => $subTaskName, + "fdTasksGranularMaster" => $this->dn, + "fdTasksGranularSchedule" => $schedule, + ]; + // Simply merged the common values and the custom ones depending on the attribute type passed. + $values['tasksGranular'] = array_merge($prepData['tasksGranular'], $defaultData['tasksGranular']); + + foreach ($values as $tab => $tabvalues) { + if (!isset($tabObject->by_object[$tab])) { + echo "Error tab does not contains attributes values" . PHP_EOL; + } + $error = $tabObject->by_object[$tab]->deserializeValues($tabvalues); + if ($error !== TRUE) { + echo 'Error during deserializing' . $error . PHP_EOL; + } + + $tabObject->current = $tab; + $tabObject->update(); + $tabObject->loadTabs(); + } + $errors = $tabObject->save(); + + if (!empty($errors)) { + msg_dialog::displayChecks($errors); + } + } + } + } + + /** + * @param array $groups + * @return array + */ + public static function extractMembersFromGroups (array $groups): array + { + global $config; + + $ldap = $config->get_ldap_link(); + $listMemberDN = []; + + if (!empty($groups)) { + // Verify if the values received is a member or a group and collect the members DN + foreach ($groups as $group) { + if (strpos($group, "ou=groups") !== FALSE) { + + // Position ldap to the dn required (limit search). + $ldap->cd($group); + $filter = '(|(objectClass=groupOfUrls)(objectClass=groupOfNames))'; + $attrs = ['member']; + + $ldap->search($filter, $attrs); + $info = $ldap->fetch(); + + // Verify if the group is not empty of members + if (!empty($info['member'])) { + //unset the count from the array + unset($info['member']['count']); + foreach ($info['member'] as $memberDN) { + $listMemberDN[] = $memberDN; + } + + // Add the member DN to the list of DN + $listMemberDN = array_unique($listMemberDN); + } + } + } + + // Iterate on the DN list to remove any members representing a group (members of that potential groups were extracted). + foreach ($listMemberDN as $key => $value) { + if (strpos($value, 'ou=groups') !== FALSE) { + unset($listMemberDN[$key]); + } + } + } + return $listMemberDN; + } + } diff --git a/plugins/configuration/tasks/class_tasksGranular.inc b/plugins/configuration/tasks/class_tasksGranular.inc index 27de2a9e3a5735690ab3637ec48dd691a52c8505..445e3ab04b9be5a21c7f57f333bb02bf57dc3f33 100644 --- a/plugins/configuration/tasks/class_tasksGranular.inc +++ b/plugins/configuration/tasks/class_tasksGranular.inc @@ -26,7 +26,7 @@ class tasksGranular extends simplePlugin { return [ 'plShortName' => _('Tasks Granular'), - 'plDescription' => _('Granular tasks mangement allowing details reports'), + 'plDescription' => _('Granular tasks management allowing details reports'), 'plObjectClass' => ['fdTasksGranular'], 'plFilter' => '(objectClass=fdTasksGranular)', 'plPriority' => 41, @@ -59,20 +59,26 @@ class tasksGranular extends simplePlugin new HiddenAttribute('fdTasksGranularStatus', TRUE, '1', '', 'Status', 'Status of the task'), new HiddenAttribute('fdTasksGranularMaster', TRUE, '', '', 'Master CN', 'Name of the Master task'), new HiddenAttribute('fdTasksGranularType', TRUE, '', '', 'Type', 'Type of the task'), - new HiddenAttribute('fdTasksGranularRef', TRUE, '', '', 'Type', 'Reference towards a required CN (mail template E.g'), + new HiddenAttribute('fdTasksGranularRef', FALSE, '', '', 'Type', 'Reference towards a required CN (mail template E.g'), + new DateTimeAttribute( + _('Schedule'), '', + 'fdTasksGranularSchedule', FALSE + ), + // Below attribute are for tasks of type lifeCycle + new StringAttribute( + _('lifeCycleDN'), _('DN list filled by tasks lifeCycle'), + 'fdTasksGranularDN', FALSE + ), + // Below attributes are for tasks of type Mail new MailAttribute( _('Email'), - _('Email address to which messages will be sent'), 'fdTasksGranularMail', TRUE), + _('Email address to which messages will be sent'), 'fdTasksGranularMail', FALSE), new MailAttribute( _('Email'), - _('Email address from which emails will be sent'), 'fdTasksGranularMailFrom', TRUE), + _('Email address from which emails will be sent'), 'fdTasksGranularMailFrom', FALSE), new MailAttribute( _('Email'), _('BCC Email address'), 'fdTasksGranularMailBCC', FALSE), - new DateTimeAttribute( - _('Schedule'), '', - 'fdTasksGranularSchedule', FALSE - ) ] ] ]; @@ -80,7 +86,6 @@ class tasksGranular extends simplePlugin function __construct ($dn = NULL, $object = NULL, $parent = NULL, $mainTab = FALSE) { - global $config; parent::__construct($dn, $object, $parent, $mainTab); } diff --git a/plugins/configuration/tasks/class_tasksLifeCycle.inc b/plugins/configuration/tasks/class_tasksLifeCycle.inc new file mode 100644 index 0000000000000000000000000000000000000000..3c01e5ad12007e970c6e0b55d40daddbc9aa0d11 --- /dev/null +++ b/plugins/configuration/tasks/class_tasksLifeCycle.inc @@ -0,0 +1,217 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org) + + Copyright (C) 2024 FusionDirectory + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +class tasksLifeCycle extends simplePlugin +{ + protected $displayHeader = TRUE; + /** + * @var array|array[] + */ + private $subStates; + + static function plInfo (): array + { + return [ + 'plShortName' => _('Tasks Life Cycle'), + 'plDescription' => _('Tasks Life Cycle Object'), + 'plIcon' => 'geticon.php?context=applications&icon=tasks&size=16', + 'plPriority' => 42, + 'plObjectClass' => ['fdTasksLifeCycle'], + 'plFilter' => '(objectClass=fdTasksLifeCycle)', + 'plObjectType' => ['tasks'], + 'plConflicts' => ['tasksMail'], + 'plProvidedAcls' => parent::generatePlProvidedAcls(static::getAttributesInfo()), + 'plForeignKeys' => [], + ]; + } + + static function getAttributesInfo (): array + { + return [ + 'section1' => [ + 'class' => ['fullwidth', 'information'], + 'name' => _('We recommend the task redundancy to be set to daily, and the case “new members only†to be unpicked'), + 'attrs' => [ + // This hiddenAttribute is only present to show the information label on the section. + new HiddenAttribute( + 'fdShowInformation', FALSE, + ), + // Following attributes will contain all the DNs required to be verified by Orchestrator. + new HiddenArrayAttribute('fdTasksLifeCycleListOfDN', FALSE, ''), + ] + ], + 'section3' => [ + 'name' => _('Post resources details'), + 'attrs' => [ + new SelectAttribute( + _('Resource'), _('Supann resources'), + 'fdTasksLifeCyclePostResource', TRUE + ), + new SelectAttribute( + _('State'), _('Resource state'), + 'fdTasksLifeCyclePostState', TRUE + ), + new SelectAttribute( + _('Sub state'), _('Resource sub state'), + 'fdTasksLifeCyclePostSubState', FALSE + ), + new IntAttribute( + _('Extra days to add'), _('Extra days to be added after today'), + 'fdTasksLifeCyclePostEndDate', FALSE, '0', FALSE, '0' + ), + ] + ], + // Attributes are grouped by section + 'section2' => [ + 'name' => _('Pre / existing resources details'), + 'attrs' => [ + new SelectAttribute( + _('Resource'), _('Supann resources'), + 'fdTasksLifeCyclePreResource', TRUE + ), + new SelectAttribute( + _('State'), _('Resource state'), + 'fdTasksLifeCyclePreState', TRUE + ), + new SelectAttribute( + _('Sub state'), _('Resource sub state'), + 'fdTasksLifeCyclePreSubState', FALSE + ), + ] + ], + 'UserGroupSelection' => [ + 'name' => _('Recipients Users and/or Groups'), + 'class' => ['fullwidth'], + 'attrs' => [ + new UsersGroupsRolesAttribute( + _('Members'), _('Users or groups to assign to this task.'), + 'fdTasksLifeCycleMembers', TRUE + ), + ], + ], + ]; + } + + /** + * @throws NonExistingLdapNodeException + */ + function __construct ($dn = NULL, $object = NULL, $parent = NULL, $mainTab = FALSE) + { + parent::__construct($dn, $object, $parent, $mainTab); + $this->attributesAccess['fdShowInformation']->setInLdap(FALSE); + + if (class_available('supannAccountStatus')) { + $this->setSupannStates(); + } else { + $warning = new FusionDirectoryWarning(htmlescape(_('The plugin Supann does not seem to be installed !'))); + $warning->display(); + } + + } + + /** + * Set attributes values with defined supann states from configuration + */ + protected function setSupannStates (): void + { + global $config; + + // Define the mandatory ones and get the remaining from configuration. + $resources = ['COMPTE' => _('Account'), 'MAIL' => _('Mail')]; + + foreach ($config->get_cfg_value('SupannRessourceLabels', []) as $line) { + list($resource, $label) = explode(':', $line, 2); + $resources[$resource] = $label; + } + + $this->attributesAccess['fdTasksLifeCyclePreResource']->setChoices(array_keys($resources), array_values($resources)); + $this->attributesAccess['fdTasksLifeCyclePostResource']->setChoices(array_keys($resources), array_values($resources)); + // @phpstan-ignore-next-line - Class verification is performed prior but class is only available in plugin not CORE. + $this->subStates = supannAccountStatus::getConfiguredSubstates(); //Keys are states and Values are subStates + $this->attributesAccess['fdTasksLifeCyclePreState']->setChoices(array_keys($this->subStates)); + $this->attributesAccess['fdTasksLifeCyclePostState']->setChoices(array_keys($this->subStates)); + + // Allows the sub states to be listed when state is modified. + $this->attributesAccess['fdTasksLifeCyclePreState']->setSubmitForm('updateFieldsChoices'); + $this->attributesAccess['fdTasksLifeCyclePostState']->setSubmitForm('updateFieldsChoices'); + // This initial fill the list choices based on registered states. + $this->updateFieldsChoices(); + + } + + /* Update list of subStates which depends on the state selected */ + function updateFieldsChoices () + { + // Update pre sub states + $preSubStatesList = $this->subStates[$this->attributesAccess['fdTasksLifeCyclePreState']->getValue()] ?? []; + $this->attributesAccess['fdTasksLifeCyclePreSubState']->setChoices(array_keys($preSubStatesList), array_values($preSubStatesList)); + // Update post sub states + $postSubStatesList = $this->subStates[$this->attributesAccess['fdTasksLifeCyclePostState']->getValue()] ?? []; + $this->attributesAccess['fdTasksLifeCyclePostSubState']->setChoices(array_keys($postSubStatesList), array_values($postSubStatesList)); + } + + /** + * Retrieve all the DNs from groups or members + */ + public function getListOfDN (): void + { + // Get the members or groups selected + $membersAndGroups = $this->attributesAccess['fdTasksLifeCycleMembers']->getValue(); + // Call static function from tasks object, always return an array. + $this->attributesAccess['fdTasksLifeCycleListOfDN']->setValue(array_values(tasks::extractMembersFromGroups($membersAndGroups))); + } + + + /** + * Generate slave tasks, careful that main task cannot be changed cause subtasks are not updated. + * It would be dangerous to edit subs tasks if some are under processed already. + */ + public function generateSlaveTasks () + { + $listOfDN = $this->attributesAccess['fdTasksLifeCycleListOfDN']->getValue(); + $attributeType = 'fdTasksLifeCycle'; + + // Call the method from parent tasks object (first tab) to create sub-tasks. + $this->parent->getBaseObject()->createSlaveTasks($listOfDN, $attributeType); + } + + public function update (): bool + { + parent::update(); + + // Take the list of DN based on the groups or members list selected and filled the related attributes + $this->getListOfDN(); + + return TRUE; + } + + function save (): array + { + // Verify if this tasks has to be executed upon saving. + $execTasks = $this->parent->getBaseObject()->fdSubTasksActivation ?? NULL; + + if ($execTasks) { + $this->generateSlaveTasks(); + } + + return parent::save(); + } +} diff --git a/plugins/configuration/tasks/class_tasksMail.inc b/plugins/configuration/tasks/class_tasksMail.inc index ee4a548f41a5fbb47b7c9f08bf9f3af827e58085..b8e71de320be8f754becd20498051841ed5b55a7 100644 --- a/plugins/configuration/tasks/class_tasksMail.inc +++ b/plugins/configuration/tasks/class_tasksMail.inc @@ -26,16 +26,16 @@ class tasksMail extends simplePlugin static function plInfo (): array { return [ - 'plShortName' => _('Tasks Mail'), - 'plDescription' => _('Tasks Mail Object'), - 'plIcon' => 'geticon.php?context=applications&icon=tasks&size=16', - 'plPriority' => 42, - 'plObjectClass' => ['fdTasksMail'], - 'plFilter' => '(objectClass=fdTasksMail)', - 'plObjectType' => ['tasks'], - 'plConflicts' => [''], + 'plShortName' => _('Tasks Mail'), + 'plDescription' => _('Tasks Mail Object'), + 'plIcon' => 'geticon.php?context=applications&icon=tasks&size=16', + 'plPriority' => 42, + 'plObjectClass' => ['fdTasksMail'], + 'plFilter' => '(objectClass=fdTasksMail)', + 'plObjectType' => ['tasks'], + 'plConflicts' => ['tasksLifeCycle'], 'plProvidedAcls' => parent::generatePlProvidedAcls(static::getAttributesInfo()), - 'plForeignKeys' => [ + 'plForeignKeys' => [ 'fdTasksMailUsers' => [ ['user', 'dn', 'fdTasksMailUsers=%oldvalue%', '*'] ] @@ -47,8 +47,8 @@ class tasksMail extends simplePlugin { return [ // Attributes are grouped by section - 'taskMail' => [ - 'name' => _('Task Mail Object'), + 'taskMail' => [ + 'name' => _('Task Mail Object'), 'attrs' => [ new SelectAttribute( _('Mail Template'), _('Mail Template Object Selection'), @@ -57,8 +57,8 @@ class tasksMail extends simplePlugin new HiddenArrayAttribute('fdTasksEmailsFromDN', FALSE, ''), ] ], - 'From Component' => [ - 'name' => _('Sender and BCC email address'), + 'From Component' => [ + 'name' => _('Sender and BCC email address'), 'attrs' => [ new MailAttribute( _('Sender email address'), @@ -69,7 +69,7 @@ class tasksMail extends simplePlugin ] ], 'UserGroupSelection' => [ - 'name' => _('Recipients Users and/or Groups'), + 'name' => _('Recipients Users and/or Groups'), 'attrs' => [ new UsersGroupsRolesAttribute( _('Members'), _('Users or groups to assign to this task.'), @@ -77,8 +77,8 @@ class tasksMail extends simplePlugin ), ], ], - 'tasksMailType' => [ - 'name' => _('Type of e-mail address desired'), + 'tasksMailType' => [ + 'name' => _('Type of e-mail address desired'), 'attrs' => [ new SelectAttribute( _('Mail Type - If not found, priority will apply'), _('Mail Type Object Selection'), @@ -106,12 +106,12 @@ class tasksMail extends simplePlugin $this->attributesAccess['fdTasksMailObject']->setChoices(array_keys($tmpSearch), array_values($tmpSearch)); $mailAttrTypes = [ - 'mail' => 'mail - [gosaMailAccount primary]', - 'gosaMailAlternateAddress' => 'gosaMailAlternateAddress', + 'mail' => 'mail - [gosaMailAccount primary]', + 'gosaMailAlternateAddress' => 'gosaMailAlternateAddress', 'gosaMailForwardingAddress' => 'gosaMailForwardingAddress', - 'supannAutreMail' => 'supannAutreMail', - 'supannMailPerso' => 'supannMailPerso', - 'supannMailPrive' => 'supannMailPrive' + 'supannAutreMail' => 'supannAutreMail', + 'supannMailPerso' => 'supannMailPerso', + 'supannMailPrive' => 'supannMailPrive' ]; $this->attributesAccess['fdTasksMailType']->setChoices(array_keys($mailAttrTypes), array_values($mailAttrTypes)); @@ -168,63 +168,35 @@ class tasksMail extends simplePlugin public function setEmailsFromSelectedDN ($mailObject, $mailAttr): void { global $config; - - $ldap = $config->get_ldap_link(); + $ldap = $config->get_ldap_link(); + $mailList = []; // Get the members or groups selected $attributeValue = $this->attributesAccess['fdTasksMailUsers']->getValue(); + // Extract members from potential groups and dyn-groups. + $listOfDN = tasks::extractMembersFromGroups($attributeValue); - if (!empty($attributeValue)) { - - // listOfDN will contain only DN of members after below methods condition - $listOfDN = $attributeValue; - - // Verify if the values received is a member or a group and collect the members DN - foreach ($attributeValue as $group) { - if (strpos($group, "ou=groups") !== FALSE) { - - // Position ldap to the dn required (limit search). - $ldap->cd($group); - - $filter = '(|(objectClass=groupOfUrls)(objectClass=groupOfNames))'; - $attrs = ['member']; - $ldap->search($filter, $attrs); - $info = $ldap->fetch(); - - // Remove the DN of the group from the list of DN - unset($listOfDN[$group]); - - // Add the member DN to the list of DN - foreach ($info['member'] as $memberDN) { - $listOfDN[] = $memberDN; - } - } - } - - $mailList = []; - foreach ($listOfDN as $dn) { + foreach ($listOfDN as $dn) { + // Position ldap to the dn required (limit search). + $ldap->cd($dn); - // Position ldap to the dn required (limit search). - $ldap->cd($dn); + // filter and attributes should be equals to the arguments passed to this method + $filter = "(objectClass=$mailObject)"; + $attrs = [$mailAttr]; - // filter and attributes should be equals to the arguments passed to this method - $filter = "(objectClass=$mailObject)"; - $attrs = [$mailAttr]; + $ldap->search($filter, $attrs); + $info = $ldap->fetch(); - $ldap->search($filter, $attrs); - $info = $ldap->fetch(); + if (!empty($info[$mailAttr][0])) { + // In case of private supann mail, remove the prefix + $mailList[] = preg_replace('/.+?(?=supann)/', '', $info[$mailAttr][0]); - if (!empty($info[$mailAttr][0])) { - // In case of private supann mail, remove the prefix - $mailList[] = preg_replace('/.+?(?=supann)/', '', $info[$mailAttr][0]); - - // Render the mailing list unique, somewhat mandatory when updating the members lists with dynGroups and members. - $mailList = array_unique($mailList); - // A possible enhancement is to recall itself with another mailObject / attr - } + // Render the mailing list unique, somewhat mandatory when updating the members lists with dynGroups and members. + $mailList = array_unique($mailList); + // A possible enhancement is to recall itself with another mailObject / attr } - $this->attributesAccess['fdTasksEmailsFromDN']->setValue(array_values($mailList)); } + $this->attributesAccess['fdTasksEmailsFromDN']->setValue(array_values($mailList)); } function save (): array @@ -244,99 +216,15 @@ class tasksMail extends simplePlugin */ public function generateSlaveTasks () { - global $config; - $ldap = $config->get_ldap_link(); + $listOfDN = $this->attributesAccess['fdTasksEmailsFromDN']->getValue(); + $attributeType = 'fdTasksGranularMail'; - $emails = $this->attributesAccess['fdTasksEmailsFromDN']->getValue(); // Ref is supposed to be the mail object CN in this class - $ref = $this->attributesAccess['fdTasksMailObject']->getValue(); - $from = $this->attributesAccess['fdTasksEmailSender']->getValue(); - $bcc = $this->attributesAccess['fdTasksEmailBCC']->getValue(); - - // Take the attribute from the other tabs - attribute cannot be null or unset by default - $schedule = $this->parent->getBaseObject()->fdTasksScheduleDate ?? NULL; - - // Verify if members can have multiple sub-tasks for that main task. - $newMemberOnly = $this->parent->getBaseObject()->fdTasksUpdatable; - - // remove 'dn' keeping only 'cn' - $rmDn = preg_replace('/(?=,).*/', '', $this->dn); - // only take the cn without dc - preg_match('/cn=(.*)/', $rmDn, $matches); - - if (!empty($emails)) { - // Condition allowing the creation of subtasks for existing members - if ($newMemberOnly === TRUE) { - $ldap->cd($config->current['BASE']); - $filter = '(&(objectClass=fdTasksGranular)(fdTasksGranularMaster='.$this->dn.'))'; - - $attrs = ['fdTasksGranularMail']; - $ldap->search($filter, $attrs); - - // The while loop is important to get all info from ldap into the array. - while ($info = $ldap->fetch()) { - $subTasks[] = $info; - } - - if (!empty($subTasks)) { - // Recuperate members email from the ldap search. - foreach ($subTasks as $subTask) { - $membersEmailsList[] = $subTask['fdTasksGranularMail'][0]; - } - // Verify the emails differences and only keep those. - if (!empty($membersEmailsList)) { - $emails = array_diff($emails, $membersEmailsList); - } - // Simple re-index the array. - $emails = array_values($emails); - } - } + $attrs['ref'] = $this->attributesAccess['fdTasksMailObject']->getValue(); + $attrs['from'] = $this->attributesAccess['fdTasksEmailSender']->getValue(); + $attrs['bcc'] = $this->attributesAccess['fdTasksEmailBCC']->getValue(); - foreach ($emails as $email) { - // Here we create the object taskGranular - $tabObject = objects::create('TasksGranular'); - - // Create a unique ID based on timestamp (Allowing duplicate subtasks for same members in case of repeat). - $timestamp = microtime(TRUE); // Get the current timestamp with microseconds - $timestamp = (string)$timestamp; // Convert the float to a string, str_replace expect array or string. - $uniqueID = str_replace(".", "_", $timestamp); // Remove . with _ for correct CN - - // Array matches come from preg_match function above with rmDn - $subTaskName = $matches[1] . '-SubTask-' . $uniqueID; - - $values['tasksGranular'] = [ - "cn" => $subTaskName, - "fdTasksGranularType" => 'Mail Object', - "fdTasksGranularMaster" => $this->dn, - "fdTasksGranularMail" => $email, - "fdTasksGranularSchedule" => $schedule, - "fdTasksGranularRef" => $ref, - "fdTasksGranularMailFrom" => $from, - "fdTasksGranularMailBCC" => $bcc - ]; - - foreach ($values as $tab => $tabvalues) { - if (!isset($tabObject->by_object[$tab])) { - echo "Error tab does not contains attributes values" . PHP_EOL; - } - $error = $tabObject->by_object[$tab]->deserializeValues($tabvalues); - if ($error !== TRUE) { - echo 'Error during deserializing' . $error . PHP_EOL; - } - - $tabObject->current = $tab; - $tabObject->update(); - $tabObject->loadTabs(); - } - - $errors = $tabObject->save(); - - // Showing errors should be better, enhancement here required. - if (!empty($errors)) { - $show_error = new SimplePluginError($this, htmlescape(sprintf(_('Error : "%s", already exist ! Editing existing tasks is forbidden.'), $subTaskName))); - $show_error->display(); - } - } - } + // Call the method from parent tasks object (first tab) to create sub-tasks. + $this->parent->getBaseObject()->createSlaveTasks($listOfDN, $attributeType, $attrs); } } diff --git a/plugins/configuration/tasks/class_tasksManagement.inc b/plugins/configuration/tasks/class_tasksManagement.inc index 856c9cb62c286c5d5d01c84cb2186061750326d6..885a0b3b2d2cd9faf1ccb73418ece7dcbab31f52 100644 --- a/plugins/configuration/tasks/class_tasksManagement.inc +++ b/plugins/configuration/tasks/class_tasksManagement.inc @@ -28,7 +28,7 @@ class tasksManagement extends management * TasksColumn is a new class for Tasks based on argonaut column definition */ public static $columns = [ - //below fdTasksMailObject must be change to have a type defined within task creation (new ldap attributes ?hidden) + //below fdTasksMailObject must be changed to have a type defined within task creation (new ldap attributes ?hidden) ['LinkColumn', ['attributes' => 'cn', 'label' => 'Tasks']], ['TasksColumn', ['attributes' => 'fdTasksMailObject', 'label' => 'Types']], ['Column', ['attributes' => 'fdTasksCreationDate', 'label' => 'Creation Date']],