mirror of
https://github.com/Dolibarr/dolibarr.git
synced 2025-12-05 09:08:09 +01:00
NEW : Major overhaul of DataPolicyCron and add Recruitment policy (#34704)
* refactor(datapolicy): redesign setup and logic for GDPR compliance - Introduced a data-driven configuration structure for setup.php. - Added support for a new "recruitment" GDPR datapolicy configuration. - Streamlined code for managing anonymization and deletion delays. - Enhanced dropdown generation logic to improve maintainability. - Simplified data anonymization and deletion methods in cron execution. This change provides more flexibility and modularity while ensuring compliance with GDPR standards. * refactor(datapolicy): enhance data cleaning logic and add modular handlers - Refactored `cleanDataForDataPolicy` method for better readability and efficiency. - Implemented modular handlers for deletion and anonymization (_handleDelete and _handleAnonymize). - Added dynamic argument building for method calls based on policy configurations. - Improved SQL query preparation with placeholders for better database compatibility. - Enhanced error handling and outcome recording in data processing. This update simplifies extensibility and ensures robust handling of datapolicies. * fix precommit * fix * fix(datapolicy): resolve uninitialized langs object and improve type hints - Added a check to ensure `$langs` object is properly initialized, preventing runtime errors. - Improved type hints in `datapolicycron.class.php` for stronger type checking and clarity. - Replaced `empty` checks with `isset` for better null safety in setup.php dropdown logic. - Adjusted SQL placeholders to cast values as strings for compatibility. These changes enhance code robustness, type safety, and error handling. * fix(datapolicy): improve type hints and streamline dropdown logic - Added precise type hints (`string`, `array`) to enhance code clarity and type safety. - Removed redundant condition in dropdown generation for improved maintainability. - Added type specification for `$paramName` in `array_map` to strengthen type checking. - Inserted missing class docblock for `CommonObject` in `datapolicycron.class.php`. These updates lead to cleaner, safer, and more robust code. * fix(datapolicy): improve type hints and streamline form submission logic - Added return type hints (`: int`, `: void`, `: array`) in `datapolicycron.class.php` for better type safety and clarity. - Simplified loop structures in `setup.php` by removing unnecessary variables. - Revised `buttonsSaveCancel` method usage to eliminate redundant cancel parameter logic. - Enhanced maintainability through refactoring `_processPolicyAction`, `_handleDelete`, and `_handleAnonymize` methods. These refinements improve code readability, maintainability, and robustness. * refactor(datapolicy): replace custom dropdown generation with `Form::selectarray` - Removed the custom `$generateSelectHtml` function, simplifying code by using the existing `Form::selectarray` method. - Updated dropdown generation for 'anonymize' and 'delete' actions to align with Dolibarr's standard practices. - Enhances maintainability by leveraging built-in utilities and reducing custom logic. * Update datapolicycron.class.php * Try to fix CI * Update datapolicycron.class.php --------- Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
This commit is contained in:
committed by
GitHub
parent
dff2bd9d7c
commit
5cf613a269
@@ -119,6 +119,7 @@ class modDataPolicy extends DolibarrModules
|
||||
array('DATAPOLICY_CONTACT_NIPROSPECT_NICLIENT', 'chaine', '', $langs->trans('NUMBER_MONTH_BEFORE_DELETION'), 0),
|
||||
array('DATAPOLICY_CONTACT_FOURNISSEUR', 'chaine', '', $langs->trans('NUMBER_MONTH_BEFORE_DELETION'), 0),
|
||||
array('DATAPOLICY_ADHERENT', 'chaine', '', $langs->trans('NUMBER_MONTH_BEFORE_DELETION'), 0),
|
||||
array('DATAPOLICY_RECRUITMENT_CANDIDATURE', 'chaine', '', $langs->trans('NUMBER_MONTH_BEFORE_DELETION'), 0),
|
||||
);
|
||||
|
||||
//$country = explode(":", getDolGlobalString('MAIN_INFO_SOCIETE_COUNTRY'));
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
|
||||
* Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
|
||||
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
|
||||
* Copyright (C) 2025 Quentin VIAL--GOUTEYRON <quentin.vial-gouteyron@atm-consulting.fr>
|
||||
*
|
||||
* 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
|
||||
@@ -38,8 +39,12 @@ require_once DOL_DOCUMENT_ROOT.'/datapolicy/lib/datapolicy.lib.php';
|
||||
* @var User $user
|
||||
*/
|
||||
|
||||
if (!$langs instanceof Translate) {
|
||||
trigger_error("Langs object was not initialized correctly.", E_USER_ERROR);
|
||||
}
|
||||
|
||||
// Translations
|
||||
$langs->loadLangs(array('admin', 'companies', 'cron', 'members', 'datapolicy'));
|
||||
$langs->loadLangs(array('admin', 'companies', 'members', 'cron', 'datapolicy', 'recruitment'));
|
||||
|
||||
// Parameters
|
||||
$action = GETPOST('action', 'aZ09');
|
||||
@@ -49,32 +54,135 @@ if (empty($action)) {
|
||||
$action = 'edit';
|
||||
}
|
||||
|
||||
// ==============================================================================
|
||||
// == DATA-DRIVEN CONFIGURATION STRUCTURE
|
||||
// ==============================================================================
|
||||
// This array drives the entire page logic (saving and rendering).
|
||||
// It is indexed by a logical key (e.g., 'tiers_client') for each data entity.
|
||||
// Each entry defines:
|
||||
// - 'label_key': The translation key for the row label.
|
||||
// - 'picto': The icon to display.
|
||||
// - 'config_keys': An associative array mapping an action ('anonymize', 'delete')
|
||||
// to the specific constant name stored in the database.
|
||||
// This structure allows defining anonymization, deletion, or both for any entity.
|
||||
$arrayofparameters = array();
|
||||
|
||||
// ThirdParty
|
||||
$arrayofparameters['ThirdParty'] = array(
|
||||
'DATAPOLICY_TIERS_CLIENT'=>array('css'=>'minwidth200', 'picto'=>img_picto('', 'company', 'class="pictofixedwidth"')),
|
||||
'DATAPOLICY_TIERS_PROSPECT'=>array('css'=>'minwidth200', 'picto'=>img_picto('', 'company', 'class="pictofixedwidth"')),
|
||||
'DATAPOLICY_TIERS_PROSPECT_CLIENT'=>array('css'=>'minwidth200', 'picto'=>img_picto('', 'company', 'class="pictofixedwidth"')),
|
||||
'DATAPOLICY_TIERS_NIPROSPECT_NICLIENT'=>array('css'=>'minwidth200', 'picto'=>img_picto('', 'company', 'class="pictofixedwidth"')),
|
||||
'DATAPOLICY_TIERS_FOURNISSEUR'=>array('css'=>'minwidth200', 'picto'=>img_picto('', 'supplier', 'class="pictofixedwidth"')),
|
||||
);
|
||||
'tiers_client' => array(
|
||||
'label_key' => 'DATAPOLICY_TIERS_CLIENT',
|
||||
'picto' => img_picto('', 'company', 'class="pictofixedwidth"'),
|
||||
'config_keys' => array(
|
||||
'anonymize' => 'DATAPOLICY_TIERS_CLIENT_ANONYMIZE_DELAY',
|
||||
'delete' => 'DATAPOLICY_TIERS_CLIENT_DELETE_DELAY'
|
||||
)
|
||||
),
|
||||
'tiers_prospect' => array(
|
||||
'label_key' => 'DATAPOLICY_TIERS_PROSPECT',
|
||||
'picto' => img_picto('', 'company', 'class="pictofixedwidth"'),
|
||||
'config_keys' => array(
|
||||
'anonymize' => 'DATAPOLICY_TIERS_PROSPECT_ANONYMIZE_DELAY',
|
||||
'delete' => 'DATAPOLICY_TIERS_PROSPECT_DELETE_DELAY'
|
||||
)
|
||||
),
|
||||
'tiers_prospect_client' => array(
|
||||
'label_key' => 'DATAPOLICY_TIERS_PROSPECT_CLIENT',
|
||||
'picto' => img_picto('', 'company', 'class="pictofixedwidth"'),
|
||||
'config_keys' => array(
|
||||
'anonymize' => 'DATAPOLICY_TIERS_PROSPECT_CLIENT_ANONYMIZE_DELAY',
|
||||
'delete' => 'DATAPOLICY_TIERS_PROSPECT_CLIENT_DELETE_DELAY'
|
||||
)
|
||||
),
|
||||
'tiers_niprosp_niclient' => array(
|
||||
'label_key' => 'DATAPOLICY_TIERS_NIPROSPECT_NICLIENT',
|
||||
'picto' => img_picto('', 'company', 'class="pictofixedwidth"'),
|
||||
'config_keys' => array(
|
||||
'anonymize' => 'DATAPOLICY_TIERS_NIPROSPECT_NICLIENT_ANONYMIZE_DELAY',
|
||||
'delete' => 'DATAPOLICY_TIERS_NIPROSPECT_NICLIENT_DELETE_DELAY'
|
||||
)
|
||||
),
|
||||
'tiers_fournisseur' => array(
|
||||
'label_key' => 'DATAPOLICY_TIERS_FOURNISSEUR',
|
||||
'picto' => img_picto('', 'supplier', 'class="pictofixedwidth"'),
|
||||
'config_keys' => array(
|
||||
'anonymize' => 'DATAPOLICY_TIERS_FOURNISSEUR_ANONYMIZE_DELAY',
|
||||
'delete' => 'DATAPOLICY_TIERS_FOURNISSEUR_DELETE_DELAY'
|
||||
)
|
||||
)
|
||||
);
|
||||
// Contact
|
||||
if (getDolGlobalString('DATAPOLICY_USE_SPECIFIC_DELAY_FOR_CONTACT')) {
|
||||
$arrayofparameters['Contact'] = array(
|
||||
'DATAPOLICY_CONTACT_CLIENT' => array('css' => 'minwidth200', 'picto' => img_picto('', 'contact', 'class="pictofixedwidth"')),
|
||||
'DATAPOLICY_CONTACT_PROSPECT' => array('css' => 'minwidth200', 'picto' => img_picto('', 'contact', 'class="pictofixedwidth"')),
|
||||
'DATAPOLICY_CONTACT_PROSPECT_CLIENT' => array('css' => 'minwidth200', 'picto' => img_picto('', 'contact', 'class="pictofixedwidth"')),
|
||||
'DATAPOLICY_CONTACT_NIPROSPECT_NICLIENT' => array('css' => 'minwidth200', 'picto' => img_picto('', 'contact', 'class="pictofixedwidth"')),
|
||||
'DATAPOLICY_CONTACT_FOURNISSEUR' => array('css' => 'minwidth200', 'picto' => img_picto('', 'contact', 'class="pictofixedwidth"')),
|
||||
'contact_client' => array(
|
||||
'label_key' => 'DATAPOLICY_CONTACT_CLIENT',
|
||||
'picto' => img_picto('', 'contact', 'class="pictofixedwidth"'),
|
||||
'config_keys' => array(
|
||||
'anonymize' => 'DATAPOLICY_CONTACT_CLIENT_ANONYMIZE_DELAY',
|
||||
'delete' => 'DATAPOLICY_CONTACT_CLIENT_DELETE_DELAY'
|
||||
)
|
||||
),
|
||||
'contact_prospect' => array(
|
||||
'label_key' => 'DATAPOLICY_CONTACT_PROSPECT',
|
||||
'picto' => img_picto('', 'contact', 'class="pictofixedwidth"'),
|
||||
'config_keys' => array(
|
||||
'anonymize' => 'DATAPOLICY_CONTACT_PROSPECT_ANONYMIZE_DELAY',
|
||||
'delete' => 'DATAPOLICY_CONTACT_PROSPECT_DELETE_DELAY'
|
||||
)
|
||||
),
|
||||
'contact_prospect_client' => array(
|
||||
'label_key' => 'DATAPOLICY_CONTACT_PROSPECT_CLIENT',
|
||||
'picto' => img_picto('', 'contact', 'class="pictofixedwidth"'),
|
||||
'config_keys' => array(
|
||||
'anonymize' => 'DATAPOLICY_CONTACT_PROSPECT_CLIENT_ANONYMIZE_DELAY',
|
||||
'delete' => 'DATAPOLICY_CONTACT_PROSPECT_CLIENT_DELETE_DELAY'
|
||||
)
|
||||
),
|
||||
'contact_niprosp_niclient' => array(
|
||||
'label_key' => 'DATAPOLICY_CONTACT_NIPROSPECT_NICLIENT',
|
||||
'picto' => img_picto('', 'contact', 'class="pictofixedwidth"'),
|
||||
'config_keys' => array(
|
||||
'anonymize' => 'DATAPOLICY_CONTACT_NIPROSPECT_NICLIENT_ANONYMIZE_DELAY',
|
||||
'delete' => 'DATAPOLICY_CONTACT_NIPROSPECT_NICLIENT_DELETE_DELAY'
|
||||
)
|
||||
),
|
||||
'contact_fournisseur' => array(
|
||||
'label_key' => 'DATAPOLICY_CONTACT_FOURNISSEUR',
|
||||
'picto' => img_picto('', 'contact', 'class="pictofixedwidth"'),
|
||||
'config_keys' => array(
|
||||
'anonymize' => 'DATAPOLICY_CONTACT_FOURNISSEUR_ANONYMIZE_DELAY',
|
||||
'delete' => 'DATAPOLICY_CONTACT_FOURNISSEUR_DELETE_DELAY'
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
// Member
|
||||
if (isModEnabled('member')) {
|
||||
$arrayofparameters['Member'] = array(
|
||||
'DATAPOLICY_ADHERENT' => array('css' => 'minwidth200', 'picto' => img_picto('', 'member', 'class="pictofixedwidth"')),
|
||||
'adherent' => array(
|
||||
'label_key' => 'DATAPOLICY_ADHERENT',
|
||||
'picto' => img_picto('', 'member', 'class="pictofixedwidth"'),
|
||||
'config_keys' => array(
|
||||
'anonymize' => 'DATAPOLICY_ADHERENT_ANONYMIZE_DELAY',
|
||||
'delete' => 'DATAPOLICY_ADHERENT_DELETE_DELAY'
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
// Recruitment: This entry demonstrates flexibility. Only a 'delete' action is defined.
|
||||
// The rendering logic will automatically leave the 'anonymize' column empty for this row.
|
||||
if (isModEnabled('recruitment')) {
|
||||
$arrayofparameters['Recruitment'] = array(
|
||||
'recruitment_candidature' => array(
|
||||
'label_key' => 'DATAPOLICY_RECRUITMENT_CANDIDATURE',
|
||||
'picto' => img_picto('', 'recruitmentcandidature', 'class="pictofixedwidth"'),
|
||||
'config_keys' => array(
|
||||
'delete' => 'DATAPOLICY_RECRUITMENT_CANDIDATURE_DELETE_DELAY'
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Dropdown options for delay selection
|
||||
$valTab = array(
|
||||
'' => $langs->trans('Never'),
|
||||
'6' => $langs->trans('NB_MONTHS', 6),
|
||||
@@ -96,45 +204,38 @@ if (!$user->admin) {
|
||||
accessforbidden();
|
||||
}
|
||||
|
||||
|
||||
'@phan-var-force array<string,array<string,array{type?:string,css?:string,picto?:string}>> $arrayofparameters';
|
||||
|
||||
/*
|
||||
* Actions
|
||||
*/
|
||||
|
||||
$nbdone = 0;
|
||||
$error = 0;
|
||||
|
||||
foreach ($arrayofparameters as $title => $tab) {
|
||||
foreach ($tab as $key => $val) {
|
||||
// Modify constant only if key was posted (avoid resetting key to the null value)
|
||||
if (GETPOSTISSET($key)) {
|
||||
if (preg_match('/category:/', (string) $val['type'])) {
|
||||
if (GETPOSTINT($key) == '-1') {
|
||||
$val_const = '';
|
||||
} else {
|
||||
$val_const = GETPOSTINT($key);
|
||||
}
|
||||
} else {
|
||||
$val_const = GETPOST($key, 'alpha');
|
||||
}
|
||||
|
||||
$result = dolibarr_set_const($db, $key, $val_const, 'chaine', 0, '', $conf->entity);
|
||||
if ($result < 0) {
|
||||
$error++;
|
||||
break;
|
||||
} else {
|
||||
$nbdone++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($nbdone) {
|
||||
setEventMessages($langs->trans("SetupSaved"), null, 'mesgs');
|
||||
}
|
||||
// Handle form submission to update constants
|
||||
if ($action == 'update') {
|
||||
$nbdone = 0;
|
||||
$error = 0;
|
||||
|
||||
// Loop through the data structure to find all possible constants to save.
|
||||
foreach ($arrayofparameters as $tab) {
|
||||
foreach ($tab as $logicalKey => $val) {
|
||||
// Iterate through defined actions ('anonymize', 'delete') for the entity.
|
||||
foreach ($val['config_keys'] as $actionType => $constKey) {
|
||||
// Save the constant only if its value was submitted in the form.
|
||||
if (GETPOSTISSET($constKey)) {
|
||||
$val_const = GETPOST($constKey, 'alpha');
|
||||
if (dolibarr_set_const($db, $constKey, $val_const, 'chaine', 0, '', $conf->entity) >= 0) {
|
||||
$nbdone++;
|
||||
} else {
|
||||
$error++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($nbdone > 0) {
|
||||
setEventMessages($langs->trans("SetupSaved"), null, 'mesgs');
|
||||
}
|
||||
if ($error > 0) {
|
||||
setEventMessages($langs->trans("Error"), null, 'errors');
|
||||
}
|
||||
$action = 'edit';
|
||||
}
|
||||
|
||||
@@ -146,32 +247,25 @@ if ($action == 'update') {
|
||||
$page_name = "datapolicySetup";
|
||||
llxHeader('', $langs->trans($page_name));
|
||||
|
||||
// Subheader
|
||||
$linkback = '<a href="'.($backtopage ? $backtopage : DOL_URL_ROOT.'/admin/modules.php?restore_lastsearch_values=1').'">'.$langs->trans("BackToModuleList").'</a>';
|
||||
|
||||
print load_fiche_titre($langs->trans($page_name), $linkback, 'generic');
|
||||
|
||||
// Configuration header
|
||||
$head = datapolicyAdminPrepareHead();
|
||||
print dol_get_fiche_head($head, 'settings', '', -1, '');
|
||||
|
||||
// Setup page goes here
|
||||
print '<span class="opacitymedium">'.$langs->trans("datapolicySetupPage").'</span>';
|
||||
print $form->textwithpicto('', $langs->trans('DATAPOLICY_Tooltip_SETUP', $langs->trans("DATAPOLICYJob"), $langs->transnoentities("CronList")));
|
||||
print '<br>';
|
||||
print '<br>';
|
||||
print '<br>';
|
||||
|
||||
// TODO Show the last date of execution of the job DATAPOLICYJob
|
||||
print '<br><br><br>';
|
||||
|
||||
if ($action == 'edit') {
|
||||
print '<form method="POST" action="'.$_SERVER["PHP_SELF"].'">';
|
||||
print '<input type="hidden" name="token" value="'.newToken().'">';
|
||||
print '<input type="hidden" name="action" value="update">';
|
||||
|
||||
print '<div class="div-table-responsive">'; // You can use div-table-responsive-no-min if you don't need reserved height for your table
|
||||
print '<div class="div-table-responsive">';
|
||||
print '<table class="tagtable nobottomiftotal liste">';
|
||||
|
||||
// Table Headers
|
||||
print '<tr class="liste_titre"><td class="titlefield"></td>';
|
||||
print '<td>'.$langs->trans("DelayForAnonymization").'</td>';
|
||||
if (getDolGlobalInt('MAIN_FEATURES_LEVEL') >= 2) {
|
||||
@@ -179,6 +273,12 @@ if ($action == 'edit') {
|
||||
}
|
||||
print '</tr>';
|
||||
|
||||
// ==============================================================================
|
||||
// == DYNAMIC VIEW RENDERING
|
||||
// ==============================================================================
|
||||
|
||||
|
||||
// Loop through each configuration group (e.g., ThirdParty, Member).
|
||||
foreach ($arrayofparameters as $title => $tab) {
|
||||
print '<tr class="trforbreak liste_titre"><td class="titlefield trforbreak">'.$langs->trans($title).'</td>';
|
||||
print '<td></td>';
|
||||
@@ -187,26 +287,28 @@ if ($action == 'edit') {
|
||||
}
|
||||
print '</tr>';
|
||||
|
||||
foreach ($tab as $key => $val) {
|
||||
// Loop through each entity within the group to create a table row.
|
||||
foreach ($tab as $logicalKey => $val) {
|
||||
print '<tr class="oddeven"><td>';
|
||||
print $val['picto'];
|
||||
print $langs->trans($key);
|
||||
print '</td><td>';
|
||||
print '<select name="'.$key.'" id="'.$key.'" class="flat '.(empty($val['css']) ? 'minwidth200' : $val['css']).'">';
|
||||
foreach ($valTab as $key1 => $val1) {
|
||||
print '<option value="'.$key1.'" '.(getDolGlobalString($key) == $key1 ? 'selected="selected"' : '').'>';
|
||||
print $val1;
|
||||
print '</option>';
|
||||
}
|
||||
print '</select>';
|
||||
print ajax_combobox($key);
|
||||
print $langs->trans($val['label_key']);
|
||||
print '</td>';
|
||||
|
||||
// Column 1: Anonymization
|
||||
print '<td>';
|
||||
// Display the dropdown only if a constant key is defined for the 'anonymize' action.
|
||||
if (isset($val['config_keys']['anonymize'])) {
|
||||
print Form::selectarray($val['config_keys']['anonymize'], $valTab, getDolGlobalString($val['config_keys']['anonymize']));
|
||||
}
|
||||
print '</td>';
|
||||
|
||||
// Column 2: Deletion
|
||||
if (getDolGlobalInt('MAIN_FEATURES_LEVEL') >= 2) {
|
||||
print '<td>';
|
||||
print $langs->trans("FeatureNotYetAvailable");
|
||||
// Display the dropdown only if a constant key is defined for the 'delete' action.
|
||||
print Form::selectarray($val['config_keys']['delete'], $valTab, getDolGlobalString($val['config_keys']['delete']));
|
||||
print '</td>';
|
||||
}
|
||||
|
||||
print '</tr>';
|
||||
}
|
||||
}
|
||||
@@ -218,29 +320,8 @@ if ($action == 'edit') {
|
||||
|
||||
print '</form>';
|
||||
print '<br>';
|
||||
} else {
|
||||
print '<table class="noborder centpercent">';
|
||||
|
||||
foreach ($arrayofparameters as $title => $tab) {
|
||||
print '<tr class="trforbreak"><td class="titlefield trforbreak" colspan="2">'.$langs->trans($title).'</td></tr>';
|
||||
foreach ($tab as $key => $val) {
|
||||
print '<tr class="oddeven"><td>';
|
||||
print $val['picto'];
|
||||
print $langs->trans($key);
|
||||
print '</td><td>'.(getDolGlobalString($key) == '' ? '<span class="opacitymedium">'.$valTab[''].'</span>' : $valTab[getDolGlobalString($key)]).'</td></tr>';
|
||||
}
|
||||
}
|
||||
|
||||
print '</table>';
|
||||
|
||||
print '<div class="tabsAction">';
|
||||
print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?action=edit&token='.newToken().'">'.$langs->trans("Modify").'</a>';
|
||||
print '</div>';
|
||||
}
|
||||
|
||||
|
||||
// Page end
|
||||
print dol_get_fiche_end();
|
||||
|
||||
llxFooter();
|
||||
$db->close();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* Copyright (C) 2018-2024 Frédéric France <frederic.france@free.fr>
|
||||
* Copyright (C) 2024 William Mead <william.mead@manchenumerique.fr>
|
||||
* Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
|
||||
* Copyright (C) 2025 Quentin VIAL--GOUTEYRON <quentin.vial-gouteyron@atm-consulting.fr>
|
||||
*
|
||||
* 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
|
||||
@@ -29,25 +30,23 @@
|
||||
*/
|
||||
class DataPolicyCron
|
||||
{
|
||||
/**
|
||||
* @var DoliDB Database handler.
|
||||
*/
|
||||
/** @var DoliDB Database handler. */
|
||||
public $db;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
/** @var string Final error message if any. */
|
||||
public $error;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
/** @var string Final output message on success. */
|
||||
public $output;
|
||||
|
||||
/** @var int Counter for updated records. */
|
||||
private $nbupdated = 0;
|
||||
/** @var int Counter for deleted records. */
|
||||
private $nbdeleted = 0;
|
||||
/** @var int Counter for errors. */
|
||||
private $errorCount = 0;
|
||||
/** @var string[] Array to store detailed error messages. */
|
||||
private $errorMessages = array();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param DoliDB $db Database handler
|
||||
*/
|
||||
public function __construct(DoliDB $db)
|
||||
@@ -55,490 +54,325 @@ class DataPolicyCron
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function exec
|
||||
* CAN BE A CRON TASK
|
||||
*
|
||||
* @return int if OK: 0 (this function is used also by cron so only 0 is OK)
|
||||
* Main cron task execution method.
|
||||
* Orchestrates the data cleaning process by iterating through all defined policies.
|
||||
* @return int Returns 0 for success, 1 for failure, as required for cron jobs.
|
||||
*/
|
||||
public function cleanDataForDataPolicy()
|
||||
public function cleanDataForDataPolicy() : int
|
||||
{
|
||||
global $conf, $langs, $user;
|
||||
global $conf, $user;
|
||||
|
||||
$langs->load('datapolicy@datapolicy');
|
||||
// Reset state properties for this specific execution run.
|
||||
$this->nbupdated = 0;
|
||||
$this->nbdeleted = 0;
|
||||
$this->errorCount = 0;
|
||||
$this->errorMessages = array();
|
||||
|
||||
$error = 0;
|
||||
$errormsg = '';
|
||||
$nbupdated = $nbdeleted = 0;
|
||||
// Tracks record IDs that have been processed in this run to prevent duplicate actions (e.g., anonymizing a just-deleted record).
|
||||
$processedIds = array();
|
||||
// Caches object instances to avoid redundant 'new Class()' calls, improving performance.
|
||||
$objectInstances = array();
|
||||
|
||||
// FIXME Exclude data from the selection if there is at least 1 invoice.
|
||||
$arrayofparameters = array(
|
||||
'DATAPOLICY_TIERS_CLIENT' => array(
|
||||
'sql' => "
|
||||
SELECT s.rowid FROM ".MAIN_DB_PREFIX."societe as s
|
||||
WHERE s.entity = %d
|
||||
AND s.client = 1
|
||||
AND s.fournisseur = 0
|
||||
AND s.tms < DATE_SUB('".$this->db->idate(dol_now())."', INTERVAL %d MONTH)
|
||||
AND NOT EXISTS (
|
||||
SELECT id FROM ".MAIN_DB_PREFIX."actioncomm as a WHERE a.fk_soc = s.rowid AND a.tms > DATE_SUB('".$this->db->idate(dol_now())."', INTERVAL %d MONTH)
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT rowid FROM ".MAIN_DB_PREFIX."facture as f WHERE f.fk_soc = s.rowid
|
||||
)
|
||||
",
|
||||
"class" => "Societe",
|
||||
"file" => DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php',
|
||||
'fields_anonym' => array(
|
||||
'name' => 'MAKEANONYMOUS',
|
||||
'name_bis' => '',
|
||||
'name_alias' => '',
|
||||
'address' => '',
|
||||
'town' => '',
|
||||
'zip' => '',
|
||||
'phone' => '',
|
||||
'email' => '',
|
||||
'url' => '',
|
||||
'fax' => '',
|
||||
'state' => '',
|
||||
'country' => '',
|
||||
'state_id' => 1,
|
||||
'socialnetworks' => [],
|
||||
'country_id' => 0,
|
||||
)
|
||||
),
|
||||
'DATAPOLICY_TIERS_PROSPECT' => array(
|
||||
'sql' => "
|
||||
SELECT s.rowid FROM ".MAIN_DB_PREFIX."societe as s
|
||||
WHERE s.entity = %d
|
||||
AND s.client = 2
|
||||
AND s.fournisseur = 0
|
||||
AND s.tms < DATE_SUB('".$this->db->idate(dol_now())."', INTERVAL %d MONTH)
|
||||
AND NOT EXISTS (
|
||||
SELECT id FROM ".MAIN_DB_PREFIX."actioncomm as a WHERE a.fk_soc = s.rowid AND a.tms > DATE_SUB('".$this->db->idate(dol_now())."', INTERVAL %d MONTH)
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT rowid FROM ".MAIN_DB_PREFIX."facture as f WHERE f.fk_soc = s.rowid
|
||||
)
|
||||
",
|
||||
"class" => "Societe",
|
||||
"file" => DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php',
|
||||
'fields_anonym' => array(
|
||||
'name' => 'MAKEANONYMOUS',
|
||||
'name_bis' => '',
|
||||
'name_alias' => '',
|
||||
'address' => '',
|
||||
'town' => '',
|
||||
'zip' => '',
|
||||
'phone' => '',
|
||||
'email' => '',
|
||||
'url' => '',
|
||||
'fax' => '',
|
||||
'state' => '',
|
||||
'country' => '',
|
||||
'state_id' => 1,
|
||||
'socialnetworks' => [],
|
||||
'country_id' => 0,
|
||||
)
|
||||
),
|
||||
'DATAPOLICY_TIERS_PROSPECT_CLIENT' => array(
|
||||
'sql' => "
|
||||
SELECT s.rowid FROM ".MAIN_DB_PREFIX."societe as s
|
||||
WHERE s.entity = %d
|
||||
AND s.client = 3
|
||||
AND s.fournisseur = 0
|
||||
AND s.tms < DATE_SUB('".$this->db->idate(dol_now())."', INTERVAL %d MONTH)
|
||||
AND NOT EXISTS (
|
||||
SELECT id FROM ".MAIN_DB_PREFIX."actioncomm as a WHERE a.fk_soc = s.rowid AND a.tms > DATE_SUB('".$this->db->idate(dol_now())."', INTERVAL %d MONTH)
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT rowid FROM ".MAIN_DB_PREFIX."facture as f WHERE f.fk_soc = s.rowid
|
||||
)
|
||||
",
|
||||
"class" => "Societe",
|
||||
"file" => DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php',
|
||||
'fields_anonym' => array(
|
||||
'name' => 'MAKEANONYMOUS',
|
||||
'name_bis' => '',
|
||||
'name_alias' => '',
|
||||
'address' => '',
|
||||
'town' => '',
|
||||
'zip' => '',
|
||||
'phone' => '',
|
||||
'email' => '',
|
||||
'url' => '',
|
||||
'fax' => '',
|
||||
'state' => '',
|
||||
'country' => '',
|
||||
'state_id' => 1,
|
||||
'socialnetworks' => [],
|
||||
'country_id' => 0,
|
||||
)
|
||||
),
|
||||
'DATAPOLICY_TIERS_NIPROSPECT_NICLIENT' => array(
|
||||
'sql' => "
|
||||
SELECT s.rowid FROM ".MAIN_DB_PREFIX."societe as s
|
||||
WHERE s.entity = %d
|
||||
AND s.client = 0
|
||||
AND s.fournisseur = 0
|
||||
AND s.tms < DATE_SUB('".$this->db->idate(dol_now())."', INTERVAL %d MONTH)
|
||||
AND NOT EXISTS (
|
||||
SELECT id FROM ".MAIN_DB_PREFIX."actioncomm as a WHERE a.fk_soc = s.rowid AND a.tms > DATE_SUB('".$this->db->idate(dol_now())."', INTERVAL %d MONTH)
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT rowid FROM ".MAIN_DB_PREFIX."facture as f WHERE f.fk_soc = s.rowid
|
||||
)
|
||||
",
|
||||
"class" => "Societe",
|
||||
"file" => DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php',
|
||||
'fields_anonym' => array(
|
||||
'name' => 'MAKEANONYMOUS',
|
||||
'name_bis' => '',
|
||||
'name_alias' => '',
|
||||
'address' => '',
|
||||
'town' => '',
|
||||
'zip' => '',
|
||||
'phone' => '',
|
||||
'email' => '',
|
||||
'url' => '',
|
||||
'fax' => '',
|
||||
'state' => '',
|
||||
'country' => '',
|
||||
'state_id' => 1,
|
||||
'socialnetworks' => [],
|
||||
'country_id' => 0,
|
||||
)
|
||||
),
|
||||
'DATAPOLICY_TIERS_FOURNISSEUR' => array(
|
||||
'sql' => "
|
||||
SELECT s.rowid FROM ".MAIN_DB_PREFIX."societe as s
|
||||
WHERE s.entity = %d
|
||||
AND s.fournisseur = 1
|
||||
AND s.tms < DATE_SUB('".$this->db->idate(dol_now())."', INTERVAL %d MONTH)
|
||||
AND NOT EXISTS (
|
||||
SELECT id FROM ".MAIN_DB_PREFIX."actioncomm as a WHERE a.fk_soc = s.rowid AND a.tms > DATE_SUB('".$this->db->idate(dol_now())."', INTERVAL %d MONTH)
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT rowid FROM ".MAIN_DB_PREFIX."facture as f WHERE f.fk_soc = s.rowid
|
||||
)
|
||||
",
|
||||
"class" => "Societe",
|
||||
"file" => DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php',
|
||||
'fields_anonym' => array(
|
||||
'name' => 'MAKEANONYMOUS',
|
||||
'name_bis' => '',
|
||||
'name_alias' => '',
|
||||
'address' => '',
|
||||
'town' => '',
|
||||
'zip' => '',
|
||||
'phone' => '',
|
||||
'email' => '',
|
||||
'url' => '',
|
||||
'fax' => '',
|
||||
'state' => '',
|
||||
'country' => '',
|
||||
'state_id' => 1,
|
||||
'socialnetworks' => [],
|
||||
'country_id' => 0,
|
||||
)
|
||||
),
|
||||
'DATAPOLICY_CONTACT_CLIENT' => array(
|
||||
'sql' => "
|
||||
SELECT c.rowid FROM ".MAIN_DB_PREFIX."socpeople as c
|
||||
INNER JOIN ".MAIN_DB_PREFIX."societe as s ON s.rowid = c.fk_soc
|
||||
WHERE c.entity = %d
|
||||
AND c.tms < DATE_SUB('".$this->db->idate(dol_now())."', INTERVAL %d MONTH)
|
||||
AND s.client = 1
|
||||
AND s.fournisseur = 0
|
||||
AND NOT EXISTS (
|
||||
SELECT id FROM ".MAIN_DB_PREFIX."actioncomm as a WHERE a.fk_contact = c.rowid AND a.tms > DATE_SUB('".$this->db->idate(dol_now())."', INTERVAL %d MONTH)
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT rowid FROM ".MAIN_DB_PREFIX."facture as f WHERE f.fk_soc = s.rowid
|
||||
)
|
||||
",
|
||||
"class" => "Contact",
|
||||
"file" => DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php',
|
||||
'fields_anonym' => array(
|
||||
'lastname' => 'MAKEANONYMOUS',
|
||||
'firstname' => '',
|
||||
'civility_id' => '',
|
||||
'poste' => '',
|
||||
'address' => '',
|
||||
'town' => '',
|
||||
'zip' => '',
|
||||
'phone_pro' => '',
|
||||
'phone_perso' => '',
|
||||
'phone_mobile' => '',
|
||||
'email' => '',
|
||||
'url' => '',
|
||||
'fax' => '',
|
||||
'state' => '',
|
||||
'country' => '',
|
||||
'state_id' => 1,
|
||||
'socialnetworks' => [],
|
||||
'country_id' => 0,
|
||||
)
|
||||
),
|
||||
'DATAPOLICY_CONTACT_PROSPECT' => array(
|
||||
'sql' => "
|
||||
SELECT c.rowid FROM ".MAIN_DB_PREFIX."socpeople as c
|
||||
INNER JOIN ".MAIN_DB_PREFIX."societe as s ON s.rowid = c.fk_soc
|
||||
WHERE c.entity = %d
|
||||
AND c.tms < DATE_SUB('".$this->db->idate(dol_now())."', INTERVAL %d MONTH)
|
||||
AND s.client = 2
|
||||
AND s.fournisseur = 0
|
||||
AND NOT EXISTS (
|
||||
SELECT id FROM ".MAIN_DB_PREFIX."actioncomm as a WHERE a.fk_contact = c.rowid AND a.tms > DATE_SUB('".$this->db->idate(dol_now())."', INTERVAL %d MONTH)
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT rowid FROM ".MAIN_DB_PREFIX."facture as f WHERE f.fk_soc = s.rowid
|
||||
)
|
||||
",
|
||||
"class" => "Contact",
|
||||
"file" => DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php',
|
||||
'fields_anonym' => array(
|
||||
'lastname' => 'MAKEANONYMOUS',
|
||||
'firstname' => '',
|
||||
'civility_id' => '',
|
||||
'poste' => '',
|
||||
'address' => '',
|
||||
'town' => '',
|
||||
'zip' => '',
|
||||
'phone_pro' => '',
|
||||
'phone_perso' => '',
|
||||
'phone_mobile' => '',
|
||||
'email' => '',
|
||||
'url' => '',
|
||||
'fax' => '',
|
||||
'state' => '',
|
||||
'country' => '',
|
||||
'state_id' => 1,
|
||||
'socialnetworks' => [],
|
||||
'country_id' => 0,
|
||||
)
|
||||
),
|
||||
'DATAPOLICY_CONTACT_PROSPECT_CLIENT' => array(
|
||||
'sql' => "
|
||||
SELECT c.rowid FROM ".MAIN_DB_PREFIX."socpeople as c
|
||||
INNER JOIN ".MAIN_DB_PREFIX."societe as s ON s.rowid = c.fk_soc
|
||||
WHERE c.entity = %d
|
||||
AND c.tms < DATE_SUB('".$this->db->idate(dol_now())."', INTERVAL %d MONTH)
|
||||
AND s.client = 3
|
||||
AND s.fournisseur = 0
|
||||
AND NOT EXISTS (
|
||||
SELECT id FROM ".MAIN_DB_PREFIX."actioncomm as a WHERE a.fk_contact = c.rowid AND a.tms > DATE_SUB('".$this->db->idate(dol_now())."', INTERVAL %d MONTH)
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT rowid FROM ".MAIN_DB_PREFIX."facture as f WHERE f.fk_soc = s.rowid
|
||||
)
|
||||
",
|
||||
"class" => "Contact",
|
||||
"file" => DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php',
|
||||
'fields_anonym' => array(
|
||||
'lastname' => 'MAKEANONYMOUS',
|
||||
'firstname' => '',
|
||||
'civility_id' => '',
|
||||
'poste' => '',
|
||||
'address' => '',
|
||||
'town' => '',
|
||||
'zip' => '',
|
||||
'phone_pro' => '',
|
||||
'phone_perso' => '',
|
||||
'phone_mobile' => '',
|
||||
'email' => '',
|
||||
'url' => '',
|
||||
'fax' => '',
|
||||
'state' => '',
|
||||
'country' => '',
|
||||
'state_id' => 1,
|
||||
'socialnetworks' => [],
|
||||
'country_id' => 0,
|
||||
)
|
||||
),
|
||||
'DATAPOLICY_CONTACT_NIPROSPECT_NICLIENT' => array(
|
||||
'sql' => "
|
||||
SELECT c.rowid FROM ".MAIN_DB_PREFIX."socpeople as c
|
||||
INNER JOIN ".MAIN_DB_PREFIX."societe as s ON s.rowid = c.fk_soc
|
||||
WHERE c.entity = %d
|
||||
AND c.tms < DATE_SUB('".$this->db->idate(dol_now())."', INTERVAL %d MONTH)
|
||||
AND s.client = 0
|
||||
AND s.fournisseur = 0
|
||||
AND NOT EXISTS (
|
||||
SELECT id FROM ".MAIN_DB_PREFIX."actioncomm as a WHERE a.fk_contact = c.rowid AND a.tms > DATE_SUB('".$this->db->idate(dol_now())."', INTERVAL %d MONTH)
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT rowid FROM ".MAIN_DB_PREFIX."facture as f WHERE f.fk_soc = s.rowid
|
||||
)
|
||||
",
|
||||
"class" => "Contact",
|
||||
"file" => DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php',
|
||||
'fields_anonym' => array(
|
||||
'lastname' => 'MAKEANONYMOUS',
|
||||
'firstname' => '',
|
||||
'civility_id' => '',
|
||||
'poste' => '',
|
||||
'address' => '',
|
||||
'town' => '',
|
||||
'zip' => '',
|
||||
'phone_pro' => '',
|
||||
'phone_perso' => '',
|
||||
'phone_mobile' => '',
|
||||
'email' => '',
|
||||
'url' => '',
|
||||
'fax' => '',
|
||||
'state' => '',
|
||||
'country' => '',
|
||||
'state_id' => 1,
|
||||
'socialnetworks' => [],
|
||||
'country_id' => 0,
|
||||
)
|
||||
),
|
||||
'DATAPOLICY_CONTACT_FOURNISSEUR' => array(
|
||||
'sql' => "
|
||||
SELECT c.rowid FROM ".MAIN_DB_PREFIX."socpeople as c
|
||||
INNER JOIN ".MAIN_DB_PREFIX."societe as s ON s.rowid = c.fk_soc
|
||||
WHERE c.entity = %d
|
||||
AND c.tms < DATE_SUB('".$this->db->idate(dol_now())."', INTERVAL %d MONTH)
|
||||
AND s.fournisseur = 1
|
||||
AND NOT EXISTS (
|
||||
SELECT id FROM ".MAIN_DB_PREFIX."actioncomm as a WHERE a.fk_contact = c.rowid AND a.tms > DATE_SUB('".$this->db->idate(dol_now())."', INTERVAL %d MONTH)
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT rowid FROM ".MAIN_DB_PREFIX."facture as f WHERE f.fk_soc = s.rowid
|
||||
)
|
||||
",
|
||||
"class" => "Contact",
|
||||
"file" => DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php',
|
||||
'fields_anonym' => array(
|
||||
'lastname' => 'MAKEANONYMOUS',
|
||||
'firstname' => '',
|
||||
'civility_id' => '',
|
||||
'poste' => '',
|
||||
'address' => '',
|
||||
'town' => '',
|
||||
'zip' => '',
|
||||
'phone_pro' => '',
|
||||
'phone_perso' => '',
|
||||
'phone_mobile' => '',
|
||||
'email' => '',
|
||||
'url' => '',
|
||||
'fax' => '',
|
||||
'state' => '',
|
||||
'country' => '',
|
||||
'state_id' => 1,
|
||||
'socialnetworks' => [],
|
||||
'country_id' => 0,
|
||||
)
|
||||
),
|
||||
'DATAPOLICY_ADHERENT' => array(
|
||||
'sql' => "
|
||||
SELECT a.rowid FROM ".MAIN_DB_PREFIX."adherent as a
|
||||
WHERE a.entity = %d
|
||||
AND a.tms < DATE_SUB('".$this->db->idate(dol_now())."', INTERVAL %d MONTH)
|
||||
AND NOT EXISTS (
|
||||
SELECT id FROM ".MAIN_DB_PREFIX."actioncomm as a WHERE a.fk_element = a.rowid AND a.tms > DATE_SUB('".$this->db->idate(dol_now())."', INTERVAL %d MONTH) AND a.elementtype LIKE 'member'
|
||||
)
|
||||
",
|
||||
"class" => "Adherent",
|
||||
"file" => DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php',
|
||||
'fields_anonym' => array(
|
||||
'lastname' => 'MAKEANONYMOUS',
|
||||
'firstname' => 'MAKEANONYMOUS',
|
||||
'civility_id' => '',
|
||||
'societe' => '',
|
||||
'address' => '',
|
||||
'town' => '',
|
||||
'zip' => '',
|
||||
'phone' => '',
|
||||
'phone_perso' => '',
|
||||
'phone_mobile' => '',
|
||||
'email' => '',
|
||||
'url' => '',
|
||||
'fax' => '',
|
||||
'state' => '',
|
||||
'country' => '',
|
||||
'state_id' => 1,
|
||||
'socialnetworks' => [],
|
||||
'country_id' => 0,
|
||||
)
|
||||
),
|
||||
);
|
||||
// Retrieve the master list of all data policies. This separates configuration from execution.
|
||||
$dataPolicies = $this->_getDataPolicies();
|
||||
|
||||
$this->db->begin();
|
||||
|
||||
// Loop on each type of data
|
||||
foreach ($arrayofparameters as $key => $params) {
|
||||
if (getDolGlobalInt($key) > 0) {
|
||||
// @phan-suppress-next-line PhanPluginPrintfVariableFormatString
|
||||
$sql = sprintf($params['sql'], (int) $conf->entity, (int) getDolGlobalInt($key), (int) getDolGlobalInt($key));
|
||||
// Iterate through each defined policy to apply its rules.
|
||||
foreach ($dataPolicies as $policy) {
|
||||
// Instantiate object only once per class type for efficiency.
|
||||
if (! isset($objectInstances[$policy['class']])) {
|
||||
require_once $policy['file'];
|
||||
$objectInstances[$policy['class']] = new $policy['class']($this->db);
|
||||
}
|
||||
$object = $objectInstances[$policy['class']];
|
||||
|
||||
// The order of operations is critical: deletion is always processed before anonymization.
|
||||
// This ensures that if a record meets criteria for both, it is deleted as the final action.
|
||||
$this->_processPolicyAction($policy, 'delete', $object, $processedIds, $conf, $user);
|
||||
$this->_processPolicyAction($policy, 'anonymize', $object, $processedIds, $conf, $user);
|
||||
}
|
||||
|
||||
// Finalize the transaction based on the outcome of all operations.
|
||||
if (! $this->errorCount) {
|
||||
$this->db->commit();
|
||||
$this->output = $this->nbupdated . ' record(s) anonymized, ' . $this->nbdeleted . ' record(s) deleted.';
|
||||
} else {
|
||||
$this->db->rollback();
|
||||
$this->error = implode("\n", $this->errorMessages);
|
||||
}
|
||||
|
||||
return $this->errorCount ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a specific action (delete or anonymize) for a given policy.
|
||||
* This method orchestrates the process by delegating to specialized handlers.
|
||||
*
|
||||
* @param array<string, mixed> $policy The policy definition array.
|
||||
* @param string $action The action to perform: 'delete' or 'anonymize'.
|
||||
* @param CommonObject $object The instantiated Dolibarr object.
|
||||
* @param int[] $processedIds Reference to the array of processed IDs.
|
||||
* @param object $conf The global conf object.
|
||||
* @param User $user The user object for history tracking.
|
||||
* @return void
|
||||
*/
|
||||
private function _processPolicyAction($policy, $action, $object, &$processedIds, $conf, $user)
|
||||
{
|
||||
$constName = $policy['const_' . $action] ?? null;
|
||||
$delay = $constName ? getDolGlobalInt($constName) : 0;
|
||||
|
||||
if ($delay <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare SQL query
|
||||
$sqlPlaceholders = array(
|
||||
'__ENTITY__' => (string) $conf->entity,
|
||||
'__DELAY__' => (string) $delay,
|
||||
'__NOW__' => "'" . $this->db->idate(dol_now()) . "'"
|
||||
);
|
||||
$sql = str_replace(array_keys($sqlPlaceholders), array_values($sqlPlaceholders), $policy['sql_template']);
|
||||
|
||||
$resql = $this->db->query($sql);
|
||||
|
||||
if ($resql && $this->db->num_rows($resql) > 0) {
|
||||
$num = $this->db->num_rows($resql);
|
||||
$i = 0;
|
||||
if (! $resql) {
|
||||
$this->errorCount++;
|
||||
$this->errorMessages[] = 'Error executing ' . $action . ' query for policy ' . $constName . ': ' . $this->db->lasterror();
|
||||
|
||||
require_once $params['file'];
|
||||
$object = new $params['class']($this->db);
|
||||
return;
|
||||
}
|
||||
|
||||
while ($i < $num && !$error) {
|
||||
$obj = $this->db->fetch_object($resql);
|
||||
// Define the handler method for the action
|
||||
$handlerMethod = '_handle' . ucfirst($action);
|
||||
|
||||
// Process the records found by the query
|
||||
while ($obj = $this->db->fetch_object($resql)) {
|
||||
if (in_array($obj->rowid, $processedIds) || ! method_exists($this, $handlerMethod)) {
|
||||
continue;
|
||||
}
|
||||
/** @var CommonObject $object */
|
||||
$object = clone $object;
|
||||
$object->fetch($obj->rowid);
|
||||
$object->id = $obj->rowid;
|
||||
|
||||
$action = 'anonymize'; // TODO Offer also action "delete" in the setup of the module
|
||||
if (!empty($object->childtables) && method_exists($object, 'isObjectUsed') && $object->isObjectUsed() != 0) {
|
||||
continue; // Not an error, just skipping.
|
||||
}
|
||||
|
||||
// Manage action 'anonymize'
|
||||
if ($action == 'anonymize') {
|
||||
if ($object->isObjectUsed($obj->rowid) == 0) { // If object to clean is not used
|
||||
// Loop on each field to anonymize
|
||||
foreach ($params['fields_anonym'] as $field => $val) {
|
||||
if ($val == 'MAKEANONYMOUS') {
|
||||
$object->$field = $field.'-anonymous-'.$obj->rowid; // @phpstan-ignore-line
|
||||
// Dynamically call the appropriate handler (_handleDelete or _handleAnonymize)
|
||||
$result = $this->$handlerMethod($object, $user, $policy);
|
||||
|
||||
// Record the outcome and add to processed list on success
|
||||
$this->_recordActionResult($result, $object, $action);
|
||||
$processedIds[] = $obj->rowid;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the specific logic for deleting an object.
|
||||
*
|
||||
* @param CommonObject $object The object to delete.
|
||||
* @param User $user The user performing the action.
|
||||
* @param array<string, mixed> $policy The policy configuration.
|
||||
* @return int The result of the delete operation.
|
||||
*/
|
||||
private function _handleDelete($object, $user, $policy): int
|
||||
{
|
||||
$callArgs = $this->_buildCallArguments($object, $user, $policy, 'delete');
|
||||
|
||||
return $object->delete(...$callArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the specific logic for anonymizing an object.
|
||||
*
|
||||
* @param CommonObject $object The object to anonymize.
|
||||
* @param User $user The user performing the action.
|
||||
* @param array<string, mixed> $policy The policy configuration.
|
||||
* @return int The result of the update operation, or 0 if skipped.
|
||||
*/
|
||||
private function _handleAnonymize($object, $user, $policy) : int
|
||||
{
|
||||
foreach ($policy['anonymize_fields'] as $field => $val) {
|
||||
$object->$field = ($val == 'MAKEANONYMOUS') ? $field . '-anonymous-' . $object->id : $val;
|
||||
}
|
||||
|
||||
$callArgs = $this->_buildCallArguments($object, $user, $policy, 'update');
|
||||
|
||||
return $object->update(...$callArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the dynamic argument list for method calls based on policy configuration.
|
||||
*
|
||||
* @param CommonObject $object The target object.
|
||||
* @param User $user The user object.
|
||||
* @param array<string, mixed> $policy The policy configuration.
|
||||
* @param string $method The method key ('delete' or 'update').
|
||||
* @return mixed[] The list of arguments for the call.
|
||||
*/
|
||||
private function _buildCallArguments($object, $user, $policy, $method)
|
||||
{
|
||||
$availableArgs = array(
|
||||
'id' => $object->id,
|
||||
'user' => $user
|
||||
);
|
||||
|
||||
$paramConfig = $policy['call_params'][$method] ?? [];
|
||||
|
||||
return array_map(function (string $paramName) use ($availableArgs) {
|
||||
return $availableArgs[$paramName];
|
||||
}, $paramConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Records the result of an action, updating counters and error messages.
|
||||
*
|
||||
* @param int $result The result code from the action (<0 for error).
|
||||
* @param CommonObject $object The processed object.
|
||||
* @param string $action The action that was performed ('delete' or 'anonymize').
|
||||
* @return void
|
||||
*/
|
||||
private function _recordActionResult($result, $object, $action)
|
||||
{
|
||||
if ($result <= 0) {
|
||||
$this->errorCount++;
|
||||
$this->errorMessages[] = 'Failed to ' . $action . ' record ID ' . $object->id . ' from class ' . get_class($object) . '. Error: ' . $object->errorsToString();
|
||||
} else {
|
||||
$object->$field = $val;
|
||||
}
|
||||
}
|
||||
|
||||
// Update record
|
||||
$result = $object->update($obj->rowid, $user);
|
||||
|
||||
if ($result > 0) {
|
||||
$errormsg = $object->error;
|
||||
$error++;
|
||||
}
|
||||
$nbupdated++;
|
||||
}
|
||||
}
|
||||
|
||||
// Manage action 'deletion'
|
||||
if ($action == 'delete') { // If object to clean is not used
|
||||
$result = $object->delete($user);
|
||||
if ($result < 0) {
|
||||
$errormsg = $object->error;
|
||||
$error++;
|
||||
}
|
||||
|
||||
$nbdeleted++;
|
||||
}
|
||||
|
||||
$i++;
|
||||
}
|
||||
if ($action === 'delete') {
|
||||
$this->nbdeleted++;
|
||||
} elseif ($action === 'anonymize') {
|
||||
// Only count as updated if the update method returns a positive result
|
||||
$this->nbupdated++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->db->commit();
|
||||
/**
|
||||
* Defines and returns the centralized data policy configuration.
|
||||
* Separating this makes the main method cleaner.
|
||||
* @return array<string, array<string, mixed>> The array of all data policies.
|
||||
*/
|
||||
private function _getDataPolicies() : array
|
||||
{
|
||||
$prefix = $this->db->prefix();
|
||||
|
||||
if (!$error) {
|
||||
$this->output = $nbupdated.' record updated, '.$nbdeleted.' record deleted';
|
||||
} else {
|
||||
$this->error = $errormsg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return array(
|
||||
// --- Third Parties ---
|
||||
'tiers_client' => array(
|
||||
'const_delete' => 'DATAPOLICY_TIERS_CLIENT_DELETE_DELAY', 'const_anonymize' => 'DATAPOLICY_TIERS_CLIENT_ANONYMIZE_DELAY',
|
||||
'sql_template' => "SELECT s.rowid FROM {$prefix}societe as s WHERE s.entity = __ENTITY__ AND s.client = 1 AND s.fournisseur = 0 AND s.tms < DATE_SUB(__NOW__, INTERVAL __DELAY__ MONTH) AND NOT EXISTS (SELECT a.id FROM {$prefix}actioncomm as a WHERE a.fk_soc = s.rowid AND a.tms > DATE_SUB(__NOW__, INTERVAL __DELAY__ MONTH)) AND NOT EXISTS (SELECT f.rowid FROM {$prefix}facture as f WHERE f.fk_soc = s.rowid)",
|
||||
'class' => 'Societe', 'file' => DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php', 'anonymize_fields' => array('name' => 'MAKEANONYMOUS', 'name_bis' => '', 'name_alias' => '', 'address' => '', 'town' => '', 'zip' => '', 'phone' => '', 'email' => '', 'url' => '', 'fax' => '', 'state' => '', 'country' => '', 'state_id' => 1, 'socialnetworks' => [], 'country_id' => 0),
|
||||
'call_params' => array(
|
||||
'delete' => array('id', 'user'), // $object->delete($id, $user)
|
||||
'update' => array('id', 'user') // $object->update($id, $user)
|
||||
)
|
||||
),
|
||||
'tiers_prospect' => array(
|
||||
'const_delete' => 'DATAPOLICY_TIERS_PROSPECT_DELETE_DELAY', 'const_anonymize' => 'DATAPOLICY_TIERS_PROSPECT_ANONYMIZE_DELAY',
|
||||
'sql_template' => "SELECT s.rowid FROM {$prefix}societe as s WHERE s.entity = __ENTITY__ AND s.client = 2 AND s.fournisseur = 0 AND s.tms < DATE_SUB(__NOW__, INTERVAL __DELAY__ MONTH) AND NOT EXISTS (SELECT a.id FROM {$prefix}actioncomm as a WHERE a.fk_soc = s.rowid AND a.tms > DATE_SUB(__NOW__, INTERVAL __DELAY__ MONTH)) AND NOT EXISTS (SELECT f.rowid FROM {$prefix}facture as f WHERE f.fk_soc = s.rowid)",
|
||||
'class' => 'Societe', 'file' => DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php', 'anonymize_fields' => array('name' => 'MAKEANONYMOUS', 'name_bis' => '', 'name_alias' => '', 'address' => '', 'town' => '', 'zip' => '', 'phone' => '', 'email' => '', 'url' => '', 'fax' => '', 'state' => '', 'country' => '', 'state_id' => 1, 'socialnetworks' => [], 'country_id' => 0),
|
||||
'call_params' => array(
|
||||
'delete' => array('id', 'user'), // $object->delete($id, $user)
|
||||
'update' => array('id', 'user') // $object->update($id, $user)
|
||||
)
|
||||
),
|
||||
'tiers_prospect_client' => array(
|
||||
'const_delete' => 'DATAPOLICY_TIERS_PROSPECT_CLIENT_DELETE_DELAY', 'const_anonymize' => 'DATAPOLICY_TIERS_PROSPECT_CLIENT_ANONYMIZE_DELAY',
|
||||
'sql_template' => "SELECT s.rowid FROM {$prefix}societe as s WHERE s.entity = __ENTITY__ AND s.client = 3 AND s.fournisseur = 0 AND s.tms < DATE_SUB(__NOW__, INTERVAL __DELAY__ MONTH) AND NOT EXISTS (SELECT a.id FROM {$prefix}actioncomm as a WHERE a.fk_soc = s.rowid AND a.tms > DATE_SUB(__NOW__, INTERVAL __DELAY__ MONTH)) AND NOT EXISTS (SELECT f.rowid FROM {$prefix}facture as f WHERE f.fk_soc = s.rowid)",
|
||||
'class' => 'Societe', 'file' => DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php', 'anonymize_fields' => array('name' => 'MAKEANONYMOUS', 'name_bis' => '', 'name_alias' => '', 'address' => '', 'town' => '', 'zip' => '', 'phone' => '', 'email' => '', 'url' => '', 'fax' => '', 'state' => '', 'country' => '', 'state_id' => 1, 'socialnetworks' => [], 'country_id' => 0),
|
||||
'call_params' => array(
|
||||
'delete' => array('id', 'user'), // $object->delete($id, $user)
|
||||
'update' => array('id', 'user') // $object->update($id, $user)
|
||||
)
|
||||
),
|
||||
'tiers_niprosp_niclient' => array(
|
||||
'const_delete' => 'DATAPOLICY_TIERS_NIPROSPECT_NICLIENT_DELETE_DELAY', 'const_anonymize' => 'DATAPOLICY_TIERS_NIPROSPECT_NICLIENT_ANONYMIZE_DELAY',
|
||||
'sql_template' => "SELECT s.rowid FROM {$prefix}societe as s WHERE s.entity = __ENTITY__ AND s.client = 0 AND s.fournisseur = 0 AND s.tms < DATE_SUB(__NOW__, INTERVAL __DELAY__ MONTH) AND NOT EXISTS (SELECT a.id FROM {$prefix}actioncomm as a WHERE a.fk_soc = s.rowid AND a.tms > DATE_SUB(__NOW__, INTERVAL __DELAY__ MONTH)) AND NOT EXISTS (SELECT f.rowid FROM {$prefix}facture as f WHERE f.fk_soc = s.rowid)",
|
||||
'class' => 'Societe', 'file' => DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php', 'anonymize_fields' => array('name' => 'MAKEANONYMOUS', 'name_bis' => '', 'name_alias' => '', 'address' => '', 'town' => '', 'zip' => '', 'phone' => '', 'email' => '', 'url' => '', 'fax' => '', 'state' => '', 'country' => '', 'state_id' => 1, 'socialnetworks' => [], 'country_id' => 0),
|
||||
'call_params' => array(
|
||||
'delete' => array('id', 'user'), // $object->delete($id, $user)
|
||||
'update' => array('id', 'user') // $object->update($id, $user)
|
||||
)
|
||||
),
|
||||
'tiers_fournisseur' => array(
|
||||
'const_delete' => 'DATAPOLICY_TIERS_FOURNISSEUR_DELETE_DELAY', 'const_anonymize' => 'DATAPOLICY_TIERS_FOURNISSEUR_ANONYMIZE_DELAY',
|
||||
'sql_template' => "SELECT s.rowid FROM {$prefix}societe as s WHERE s.entity = __ENTITY__ AND s.fournisseur = 1 AND s.tms < DATE_SUB(__NOW__, INTERVAL __DELAY__ MONTH) AND NOT EXISTS (SELECT a.id FROM {$prefix}actioncomm as a WHERE a.fk_soc = s.rowid AND a.tms > DATE_SUB(__NOW__, INTERVAL __DELAY__ MONTH)) AND NOT EXISTS (SELECT f.rowid FROM {$prefix}facture as f WHERE f.fk_soc = s.rowid)",
|
||||
'class' => 'Societe', 'file' => DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php', 'anonymize_fields' => array('name' => 'MAKEANONYMOUS', 'name_bis' => '', 'name_alias' => '', 'address' => '', 'town' => '', 'zip' => '', 'phone' => '', 'email' => '', 'url' => '', 'fax' => '', 'state' => '', 'country' => '', 'state_id' => 1, 'socialnetworks' => [], 'country_id' => 0),
|
||||
'call_params' => array(
|
||||
'delete' => array('id', 'user'), // $object->delete($id, $user)
|
||||
'update' => array('id', 'user') // $object->update($id, $user)
|
||||
)
|
||||
),
|
||||
// --- Contacts ---
|
||||
'contact_client' => array(
|
||||
'const_delete' => 'DATAPOLICY_CONTACT_CLIENT_DELETE_DELAY', 'const_anonymize' => 'DATAPOLICY_CONTACT_CLIENT_ANONYMIZE_DELAY',
|
||||
'sql_template' => "SELECT c.rowid FROM {$prefix}socpeople as c INNER JOIN {$prefix}societe as s ON s.rowid = c.fk_soc WHERE c.entity = __ENTITY__ AND c.tms < DATE_SUB(__NOW__, INTERVAL __DELAY__ MONTH) AND s.client = 1 AND s.fournisseur = 0 AND NOT EXISTS (SELECT a.id FROM {$prefix}actioncomm as a WHERE a.fk_contact = c.rowid AND a.tms > DATE_SUB(__NOW__, INTERVAL __DELAY__ MONTH)) AND NOT EXISTS (SELECT f.rowid FROM {$prefix}facture as f WHERE f.fk_soc = s.rowid)",
|
||||
'class' => 'Contact', 'file' => DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php', 'anonymize_fields' => array('lastname' => 'MAKEANONYMOUS', 'firstname' => '', 'civility_id' => '', 'poste' => '', 'address' => '', 'town' => '', 'zip' => '', 'phone_pro' => '', 'phone_perso' => '', 'phone_mobile' => '', 'email' => '', 'url' => '', 'fax' => '', 'state' => '', 'country' => '', 'state_id' => 1, 'socialnetworks' => [], 'country_id' => 0),
|
||||
'call_params' => array(
|
||||
'delete' => array('user'), // $object->delete($user)
|
||||
'update' => array('id', 'user') // $object->update($id, $user)
|
||||
)
|
||||
),
|
||||
'contact_prospect' => array(
|
||||
'const_delete' => 'DATAPOLICY_CONTACT_PROSPECT_DELETE_DELAY', 'const_anonymize' => 'DATAPOLICY_CONTACT_PROSPECT_ANONYMIZE_DELAY',
|
||||
'sql_template' => "SELECT c.rowid FROM {$prefix}socpeople as c INNER JOIN {$prefix}societe as s ON s.rowid = c.fk_soc WHERE c.entity = __ENTITY__ AND c.tms < DATE_SUB(__NOW__, INTERVAL __DELAY__ MONTH) AND s.client = 2 AND s.fournisseur = 0 AND NOT EXISTS (SELECT a.id FROM {$prefix}actioncomm as a WHERE a.fk_contact = c.rowid AND a.tms > DATE_SUB(__NOW__, INTERVAL __DELAY__ MONTH)) AND NOT EXISTS (SELECT f.rowid FROM {$prefix}facture as f WHERE f.fk_soc = s.rowid)",
|
||||
'class' => 'Contact', 'file' => DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php', 'anonymize_fields' => array('lastname' => 'MAKEANONYMOUS', 'firstname' => '', 'civility_id' => '', 'poste' => '', 'address' => '', 'town' => '', 'zip' => '', 'phone_pro' => '', 'phone_perso' => '', 'phone_mobile' => '', 'email' => '', 'url' => '', 'fax' => '', 'state' => '', 'country' => '', 'state_id' => 1, 'socialnetworks' => [], 'country_id' => 0),
|
||||
'call_params' => array(
|
||||
'delete' => array('user'), // $object->delete($user)
|
||||
'update' => array('id', 'user') // $object->update($id, $user)
|
||||
)
|
||||
),
|
||||
'contact_prospect_client' => array(
|
||||
'const_delete' => 'DATAPOLICY_CONTACT_PROSPECT_CLIENT_DELETE_DELAY', 'const_anonymize' => 'DATAPOLICY_CONTACT_PROSPECT_CLIENT_ANONYMIZE_DELAY',
|
||||
'sql_template' => "SELECT c.rowid FROM {$prefix}socpeople as c INNER JOIN {$prefix}societe as s ON s.rowid = c.fk_soc WHERE c.entity = __ENTITY__ AND c.tms < DATE_SUB(__NOW__, INTERVAL __DELAY__ MONTH) AND s.client = 3 AND s.fournisseur = 0 AND NOT EXISTS (SELECT a.id FROM {$prefix}actioncomm as a WHERE a.fk_contact = c.rowid AND a.tms > DATE_SUB(__NOW__, INTERVAL __DELAY__ MONTH)) AND NOT EXISTS (SELECT f.rowid FROM {$prefix}facture as f WHERE f.fk_soc = s.rowid)",
|
||||
'class' => 'Contact', 'file' => DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php', 'anonymize_fields' => array('lastname' => 'MAKEANONYMOUS', 'firstname' => '', 'civility_id' => '', 'poste' => '', 'address' => '', 'town' => '', 'zip' => '', 'phone_pro' => '', 'phone_perso' => '', 'phone_mobile' => '', 'email' => '', 'url' => '', 'fax' => '', 'state' => '', 'country' => '', 'state_id' => 1, 'socialnetworks' => [], 'country_id' => 0),
|
||||
'call_params' => array(
|
||||
'delete' => array('user'), // $object->delete($user)
|
||||
'update' => array('id', 'user') // $object->update($id, $user)
|
||||
)
|
||||
),
|
||||
'contact_niprosp_niclient' => array(
|
||||
'const_delete' => 'DATAPOLICY_CONTACT_NIPROSPECT_NICLIENT_DELETE_DELAY', 'const_anonymize' => 'DATAPOLICY_CONTACT_NIPROSPECT_NICLIENT_ANONYMIZE_DELAY',
|
||||
'sql_template' => "SELECT c.rowid FROM {$prefix}socpeople as c INNER JOIN {$prefix}societe as s ON s.rowid = c.fk_soc WHERE c.entity = __ENTITY__ AND c.tms < DATE_SUB(__NOW__, INTERVAL __DELAY__ MONTH) AND s.client = 0 AND s.fournisseur = 0 AND NOT EXISTS (SELECT a.id FROM {$prefix}actioncomm as a WHERE a.fk_contact = c.rowid AND a.tms > DATE_SUB(__NOW__, INTERVAL __DELAY__ MONTH)) AND NOT EXISTS (SELECT f.rowid FROM {$prefix}facture as f WHERE f.fk_soc = s.rowid)",
|
||||
'class' => 'Contact', 'file' => DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php', 'anonymize_fields' => array('lastname' => 'MAKEANONYMOUS', 'firstname' => '', 'civility_id' => '', 'poste' => '', 'address' => '', 'town' => '', 'zip' => '', 'phone_pro' => '', 'phone_perso' => '', 'phone_mobile' => '', 'email' => '', 'url' => '', 'fax' => '', 'state' => '', 'country' => '', 'state_id' => 1, 'socialnetworks' => [], 'country_id' => 0),
|
||||
'call_params' => array(
|
||||
'delete' => array('user'), // $object->delete($user)
|
||||
'update' => array('id', 'user') // $object->update($id, $user)
|
||||
)
|
||||
),
|
||||
'contact_fournisseur' => array(
|
||||
'const_delete' => 'DATAPOLICY_CONTACT_FOURNISSEUR_DELETE_DELAY', 'const_anonymize' => 'DATAPOLICY_CONTACT_FOURNISSEUR_ANONYMIZE_DELAY',
|
||||
'sql_template' => "SELECT c.rowid FROM {$prefix}socpeople as c INNER JOIN {$prefix}societe as s ON s.rowid = c.fk_soc WHERE c.entity = __ENTITY__ AND c.tms < DATE_SUB(__NOW__, INTERVAL __DELAY__ MONTH) AND s.fournisseur = 1 AND NOT EXISTS (SELECT a.id FROM {$prefix}actioncomm as a WHERE a.fk_contact = c.rowid AND a.tms > DATE_SUB(__NOW__, INTERVAL __DELAY__ MONTH)) AND NOT EXISTS (SELECT f.rowid FROM {$prefix}facture as f WHERE f.fk_soc = s.rowid)",
|
||||
'class' => 'Contact', 'file' => DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php', 'anonymize_fields' => array('lastname' => 'MAKEANONYMOUS', 'firstname' => '', 'civility_id' => '', 'poste' => '', 'address' => '', 'town' => '', 'zip' => '', 'phone_pro' => '', 'phone_perso' => '', 'phone_mobile' => '', 'email' => '', 'url' => '', 'fax' => '', 'state' => '', 'country' => '', 'state_id' => 1, 'socialnetworks' => [], 'country_id' => 0),
|
||||
'call_params' => array(
|
||||
'delete' => array('user'), // $object->delete($user)
|
||||
'update' => array('id', 'user') // $object->update($id, $user)
|
||||
)
|
||||
),
|
||||
// --- Members ---
|
||||
'adherent' => array(
|
||||
'const_delete' => 'DATAPOLICY_ADHERENT_DELETE_DELAY', 'const_anonymize' => 'DATAPOLICY_ADHERENT_ANONYMIZE_DELAY',
|
||||
'sql_template' => "SELECT a.rowid FROM {$prefix}adherent as a WHERE a.entity = __ENTITY__ AND a.tms < DATE_SUB(__NOW__, INTERVAL __DELAY__ MONTH) AND NOT EXISTS (SELECT ac.id FROM {$prefix}actioncomm as ac WHERE ac.fk_element = a.rowid AND ac.elementtype = 'member' AND ac.tms > DATE_SUB(__NOW__, INTERVAL __DELAY__ MONTH))",
|
||||
'class' => 'Adherent', 'file' => DOL_DOCUMENT_ROOT . '/adherents/class/adherent.class.php', 'anonymize_fields' => array('lastname' => 'MAKEANONYMOUS', 'firstname' => 'MAKEANONYMOUS', 'civility_id' => '', 'societe' => '', 'address' => '', 'town' => '', 'zip' => '', 'phone' => '', 'phone_perso' => '', 'phone_mobile' => '', 'email' => '', 'url' => '', 'fax' => '', 'state' => '', 'country' => '', 'state_id' => 1, 'socialnetworks' => [], 'country_id' => 0),
|
||||
'call_params' => array(
|
||||
'delete' => array('user'), // $object->delete($user)
|
||||
'update' => array('user') // $object->update($user)
|
||||
)
|
||||
),
|
||||
// --- Recruitment ---
|
||||
'recruitment_candidature' => array(
|
||||
'const_delete' => 'DATAPOLICY_RECRUITMENT_CANDIDATURE_DELETE_DELAY', 'const_anonymize' => '', // Anonymization not applicable
|
||||
'sql_template' => "SELECT c.rowid FROM {$prefix}recruitment_recruitmentcandidature as c WHERE c.entity = __ENTITY__ AND c.tms < DATE_SUB(__NOW__, INTERVAL __DELAY__ MONTH) AND NOT EXISTS (SELECT ac.id FROM {$prefix}actioncomm as ac WHERE ac.elementtype = 'recruitmentcandidature@recruitment' AND ac.fk_element = c.rowid AND ac.tms > DATE_SUB(__NOW__, INTERVAL __DELAY__ MONTH))",
|
||||
'class' => 'RecruitmentCandidature',
|
||||
'file' => DOL_DOCUMENT_ROOT . '/recruitment/class/recruitmentcandidature.class.php',
|
||||
'anonymize_fields' => array(),
|
||||
'call_params' => array(
|
||||
'delete' => array('user'), // $object->delete($user)
|
||||
'update' => array('user') // $object->update($user)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ DATAPOLICY_CONTACT_PROSPECT_CLIENT = Prospect/Customer
|
||||
DATAPOLICY_CONTACT_NIPROSPECT_NICLIENT = Nor prospect/Nor customer
|
||||
DATAPOLICY_CONTACT_FOURNISSEUR = Supplier
|
||||
DATAPOLICY_ADHERENT = Member
|
||||
DATAPOLICY_RECRUITMENT_CANDIDATURE = Application
|
||||
DATAPOLICY_Tooltip_SETUP=The anonymization is done by the scheduled job "%s" ran by the module "%s", so this module must be enabled and working correctly.
|
||||
SendAgreementText = You can send a GDPR email to all your relevant contacts (who have not yet received an email and for which you have not registered anything about their GDPR agreement). To do this, use the following button.
|
||||
SendAgreement = Send emails
|
||||
|
||||
Reference in New Issue
Block a user