Skip to content
Snippets Groups Projects
ContactController.php 67.2 KiB
Newer Older
  • Learn to ignore specific revisions
  • <?php
    
    /**
     * Copyright Maarch since 2008 under licence GPLv3.
     * See LICENCE.txt file at the root folder for more details.
     * This file is part of Maarch software.
     */
    
    /**
     * @brief Contact Controller
     * @author dev@maarch.org
     */
    
    
    namespace Contact\controllers;
    
    use AcknowledgementReceipt\models\AcknowledgementReceiptModel;
    use Attachment\models\AttachmentModel;
    
    use Contact\models\ContactCustomFieldListModel;
    
    use Contact\models\ContactFillingModel;
    
    use Contact\models\ContactModel;
    
    use Contact\models\ContactParameterModel;
    
    use Entity\models\EntityModel;
    
    use Group\controllers\PrivilegeController;
    
    use History\controllers\HistoryController;
    
    use MessageExchange\controllers\AnnuaryController;
    
    use Parameter\models\ParameterModel;
    
    use Resource\controllers\ResController;
    
    use Resource\models\ResourceContactModel;
    
    use Respect\Validation\Validator;
    
    use Slim\Http\Request;
    use Slim\Http\Response;
    
    use SrcCore\controllers\AutoCompleteController;
    
    use SrcCore\models\CoreConfigModel;
    
    use SrcCore\models\TextFormatModel;
    
    use SrcCore\models\ValidatorModel;
    
    
    class ContactController
    {
    
        const MAPPING_FIELDS = [
            'civility'              => 'civility',
            'firstname'             => 'firstname',
            'lastname'              => 'lastname',
            'company'               => 'company',
            'department'            => 'department',
            'function'              => 'function',
            'addressNumber'         => 'address_number',
            'addressStreet'         => 'address_street',
            'addressAdditional1'    => 'address_additional1',
            'addressAdditional2'    => 'address_additional2',
            'addressPostcode'       => 'address_postcode',
            'addressTown'           => 'address_town',
            'addressCountry'        => 'address_country',
            'email'                 => 'email',
            'phone'                 => 'phone',
            'notes'                 => 'notes'
        ];
    
    
        public function get(Request $request, Response $response)
    
            if (!PrivilegeController::hasPrivilege(['privilegeId' => 'admin_contacts', 'userId' => $GLOBALS['id']])) {
                return $response->withStatus(403)->withJson(['errors' => 'Service forbidden']);
            }
    
            $queryParams = $request->getQueryParams();
    
            $queryParams['offset'] = (empty($queryParams['offset']) || !is_numeric($queryParams['offset']) ? 0 : (int)$queryParams['offset']);
            $queryParams['limit'] = (empty($queryParams['limit']) || !is_numeric($queryParams['limit']) ? 25 : (int)$queryParams['limit']);
            $order = !in_array($queryParams['order'], ['asc', 'desc']) ? '' : $queryParams['order'];
    
            $orderBy = !in_array($queryParams['orderBy'], ['firstname', 'lastname', 'company']) ? ['id'] : ["{$queryParams['orderBy']} {$order}", 'id'];
    
            if (!empty($queryParams['search'])) {
                $fields = ['firstname', 'lastname', 'company', 'address_number', 'address_street', 'address_additional1', 'address_additional2', 'address_postcode', 'address_town', 'address_country'];
                $fieldsNumber = count($fields);
                $fields = AutoCompleteController::getUnsensitiveFieldsForRequest(['fields' => $fields]);
    
                $requestData = AutoCompleteController::getDataForRequest([
                    'search'        => $queryParams['search'],
                    'fields'        => $fields,
                    'where'         => [],
                    'data'          => [],
    
                    'fieldsNumber'  => $fieldsNumber
    
                ]);
            }
    
            $contacts = ContactModel::get([
                'select'    => [
                    'id', 'firstname', 'lastname', 'company', 'address_number as "addressNumber"', 'address_street as "addressStreet"',
                    'address_additional1 as "addressAdditional1"', 'address_additional2 as "addressAdditional2"', 'address_postcode as "addressPostcode"',
                    'address_town as "addressTown"', 'address_country as "addressCountry"', 'enabled', 'count(1) OVER()'
                ],
                'where'     => $requestData['where'] ?? null,
                'data'      => $requestData['data'] ?? null,
    
                'orderBy'   => $orderBy,
    
                'offset'    => $queryParams['offset'],
                'limit'     => $queryParams['limit']
            ]);
    
            $count = $contacts[0]['count'] ?? 0;
    
            if (empty($contacts)) {
                return $response->withJson(['contacts' => $contacts, 'count' => $count]);
            }
    
            $contactIds = array_column($contacts, 'id');
            $contactsUsed = ContactController::isContactUsed(['ids' => $contactIds]);
    
    
            foreach ($contacts as $key => $contact) {
                unset($contacts[$key]['count']);
    
                $filling = ContactController::getFillingRate(['contactId' => $contact['id']]);
    
                $contacts[$key]['isUsed'] = $contactsUsed[$contact['id']];
    
                $contacts[$key]['filling'] = $filling;
    
            if ($queryParams['orderBy'] == 'filling') {
    
                usort($contacts, function ($a, $b) {
    
                    return $a['filling']['rate'] <=> $b['filling']['rate'];
                });
                if ($queryParams['order'] == 'desc') {
                    $contacts = array_reverse($contacts);
                }
            }
    
    
            return $response->withJson(['contacts' => $contacts, 'count' => $count]);
    
        public function create(Request $request, Response $response)
        {
    
            if (!PrivilegeController::hasPrivilege(['privilegeId' => 'create_contacts', 'userId' => $GLOBALS['id']])
                && !PrivilegeController::hasPrivilege(['privilegeId' => 'admin_contacts', 'userId' => $GLOBALS['id']])) {
                return $response->withStatus(403)->withJson(['errors' => 'Service forbidden']);
            }
    
            $body = $request->getParsedBody();
    
    
            $control = ContactController::controlContact(['body' => $body]);
            if (!empty($control['errors'])) {
                return $response->withStatus(400)->withJson(['errors' => $control['errors']]);
    
            if (!empty($body['email'])) {
                $contact = ContactModel::get(['select' => ['id'], 'where' => ['email = ?'], 'data' => [$body['email']]]);
                if (!empty($contact[0]['id'])) {
                    return $response->withJson(['id' => $contact[0]['id']]);
                }
    
            if (!empty($body['communicationMeans'])) {
                if (filter_var($body['communicationMeans'], FILTER_VALIDATE_EMAIL)) {
                    $body['communicationMeans'] = ['email' => $body['communicationMeans']];
                } elseif (filter_var($body['communicationMeans'], FILTER_VALIDATE_URL)) {
                    $body['communicationMeans'] = ['url' => $body['communicationMeans']];
    
                } else {
                    return $response->withStatus(400)->withJson(['errors' => _COMMUNICATION_MEANS_VALIDATOR]);
    
            $annuaryReturn = ContactController::addContactToM2MAnnuary(['body' => $body]);
            $body = $annuaryReturn['body'];
    
            if (!empty($body['externalId']) && is_array($body['externalId'])) {
                $externalId = json_encode($body['externalId']);
            } else {
                $externalId = '{}';
            }
    
    
            if (!empty($body['customFields'])) {
                foreach ($body['customFields'] as $key => $value) {
                    $customField = ContactCustomFieldListModel::getById(['id' => $key, 'select' => ['type']]);
                    if ($customField['type'] == 'date') {
                        $date = new \DateTime($value);
                        $value = $date->format('Y-m-d');
                        $body['customFields'][$key] = $value;
                    }
                }
            }
    
    
            $id = ContactModel::create([
                'civility'              => $body['civility'] ?? null,
                'firstname'             => $body['firstname'] ?? null,
                'lastname'              => $body['lastname'] ?? null,
                'company'               => $body['company'] ?? null,
                'department'            => $body['department'] ?? null,
                'function'              => $body['function'] ?? null,
                'address_number'        => $body['addressNumber'] ?? null,
                'address_street'        => $body['addressStreet'] ?? null,
    
                'address_additional1'   => $body['addressAdditional1'] ?? null,
                'address_additional2'   => $body['addressAdditional2'] ?? null,
    
                'address_postcode'      => $body['addressPostcode'] ?? null,
                'address_town'          => $body['addressTown'] ?? null,
                'address_country'       => $body['addressCountry'] ?? null,
                'email'                 => $body['email'] ?? null,
                'phone'                 => $body['phone'] ?? null,
                'communication_means'   => !empty($body['communicationMeans']) ? json_encode($body['communicationMeans']) : null,
                'notes'                 => $body['notes'] ?? null,
                'creator'               => $GLOBALS['id'],
                'enabled'               => 'true',
    
                'custom_fields'         => !empty($body['customFields']) ? json_encode($body['customFields']) : null,
    
                'external_id'           => $externalId
            ]);
    
            $historyInfoContact = '';
            if (!empty($body['firstname']) || !empty($body['lastname'])) {
                $historyInfoContact .= $body['firstname'] . ' ' . $body['lastname'];
            }
            if (!empty($historyInfoContact) && !empty($body['company'])) {
                $historyInfoContact .= ' (' . $body['company'] . ')';
            } else {
                $historyInfoContact .= $body['company'];
            }
    
            HistoryController::add([
                'tableName' => 'contacts',
                'recordId'  => $id,
                'eventType' => 'ADD',
                'info'      => _CONTACT_CREATION . " : " . trim($historyInfoContact),
                'moduleId'  => 'contact',
                'eventId'   => 'contactCreation',
            ]);
    
    
            return $response->withJson(['id' => $id, 'warning' => $annuaryReturn['warning']]);
    
        public function getById(Request $request, Response $response, array $args)
        {
    
            if (!Validator::intVal()->validate($args['id'])) {
                return $response->withStatus(400)->withJson(['errors' => 'Route id is not an integer']);
            }
    
            $rawContact = ContactModel::getById(['id' => $args['id'], 'select' => ['*']]);
            if (empty($rawContact)) {
                return $response->withStatus(400)->withJson(['errors' => 'Contact does not exist']);
    
            $contact = [
                'id'                    => $rawContact['id'],
    
                'firstname'             => $rawContact['firstname'],
                'lastname'              => $rawContact['lastname'],
                'company'               => $rawContact['company'],
                'department'            => $rawContact['department'],
                'function'              => $rawContact['function'],
                'addressNumber'         => $rawContact['address_number'],
                'addressStreet'         => $rawContact['address_street'],
    
                'addressAdditional1'    => $rawContact['address_additional1'],
                'addressAdditional2'    => $rawContact['address_additional2'],
    
                'addressPostcode'       => $rawContact['address_postcode'],
                'addressTown'           => $rawContact['address_town'],
                'addressCountry'        => $rawContact['address_country'],
                'email'                 => $rawContact['email'],
                'phone'                 => $rawContact['phone'],
    
                'communicationMeans'    => null,
    
                'notes'                 => $rawContact['notes'],
                'creator'               => $rawContact['creator'],
                'creatorLabel'          => UserModel::getLabelledUserById(['id' => $rawContact['creator']]),
                'enabled'               => $rawContact['enabled'],
                'creationDate'          => $rawContact['creation_date'],
                'modificationDate'      => $rawContact['modification_date'],
    
                'customFields'          => !empty($rawContact['custom_fields']) ? json_decode($rawContact['custom_fields'], true) : null,
    
                'externalId'            => json_decode($rawContact['external_id'], true)
            ];
    
    
            if (!empty($rawContact['civility'])) {
                $civilities = ContactModel::getCivilities();
                $contact['civility'] = [
                    'id'           => $rawContact['civility'],
                    'label'        => $civilities[$rawContact['civility']]['label'],
                    'abbreviation' => $civilities[$rawContact['civility']]['abbreviation']
                ];
            }
    
            if (!empty($rawContact['communication_means'])) {
                $communicationMeans = json_decode($rawContact['communication_means'], true);
                $contact['communicationMeans'] = $communicationMeans['url'] ?? $communicationMeans['email'];
            }
    
            $filling = ContactController::getFillingRate(['contactId' => $rawContact['id']]);
    
            $contact['fillingRate'] = empty($filling) ? null : $filling;
    
            return $response->withJson($contact);
    
        public function update(Request $request, Response $response, array $args)
    
            if (!PrivilegeController::hasPrivilege(['privilegeId' => 'update_contacts', 'userId' => $GLOBALS['id']])
                && !PrivilegeController::hasPrivilege(['privilegeId' => 'admin_contacts', 'userId' => $GLOBALS['id']])) {
                return $response->withStatus(403)->withJson(['errors' => 'Service forbidden']);
            }
    
            if (!Validator::intVal()->validate($args['id'])) {
                return $response->withStatus(400)->withJson(['errors' => 'Route id is not an integer']);
            }
    
            $body = $request->getParsedBody();
    
            $control = ContactController::controlContact(['body' => $body]);
            if (!empty($control['errors'])) {
                return $response->withStatus(400)->withJson(['errors' => $control['errors']]);
            }
    
    
            $contact = ContactModel::getById(['id' => $args['id'], 'select' => [1]]);
    
            if (empty($contact)) {
                return $response->withStatus(400)->withJson(['errors' => 'Contact does not exist']);
            }
    
    
            if (!empty($body['communicationMeans'])) {
                if (filter_var($body['communicationMeans'], FILTER_VALIDATE_EMAIL)) {
                    $body['communicationMeans'] = ['email' => $body['communicationMeans']];
                } elseif (filter_var($body['communicationMeans'], FILTER_VALIDATE_URL)) {
                    $body['communicationMeans'] = ['url' => $body['communicationMeans']];
                }
            }
    
    
            $annuaryReturn = ContactController::addContactToM2MAnnuary(['body' => $body]);
            $body = $annuaryReturn['body'];
    
    
            if (!empty($body['externalId']) && is_array($body['externalId'])) {
                $externalId = json_encode($body['externalId']);
            } else {
                $externalId = '{}';
            }
    
            ContactModel::update([
                'set'   => [
                        'civility'              => $body['civility'] ?? null,
                        'firstname'             => $body['firstname'] ?? null,
                        'lastname'              => $body['lastname'] ?? null,
                        'company'               => $body['company'] ?? null,
                        'department'            => $body['department'] ?? null,
                        'function'              => $body['function'] ?? null,
    
                        'address_number'        => $body['addressNumber'] ?? null,
                        'address_street'        => $body['addressStreet'] ?? null,
    
                        'address_additional1'   => $body['addressAdditional1'] ?? null,
                        'address_additional2'   => $body['addressAdditional2'] ?? null,
    
                        'address_postcode'      => $body['addressPostcode'] ?? null,
                        'address_town'          => $body['addressTown'] ?? null,
                        'address_country'       => $body['addressCountry'] ?? null,
    
                        'email'                 => $body['email'] ?? null,
                        'phone'                 => $body['phone'] ?? null,
                        'communication_means'   => !empty($body['communicationMeans']) ? json_encode($body['communicationMeans']) : null,
                        'notes'                 => $body['notes'] ?? null,
                        'modification_date'     => 'CURRENT_TIMESTAMP',
    
                        'custom_fields'         => !empty($body['customFields']) ? json_encode($body['customFields']) : null,
    
                        'external_id'           => $externalId
                    ],
                'where' => ['id = ?'],
                'data'  => [$args['id']]
            ]);
    
            $historyInfoContact = '';
            if (!empty($body['firstname']) || !empty($body['lastname'])) {
                $historyInfoContact .= $body['firstname'] . ' ' . $body['lastname'];
            }
            if (!empty($historyInfoContact) && !empty($body['company'])) {
                $historyInfoContact .= ' (' . $body['company'] . ')';
            } else {
                $historyInfoContact .= $body['company'];
            }
    
            HistoryController::add([
                'tableName' => 'contacts',
                'recordId'  => $args['id'],
                'eventType' => 'UP',
                'info'      => _CONTACT_MODIFICATION . " : " . trim($historyInfoContact),
                'moduleId'  => 'contact',
                'eventId'   => 'contactModification',
            ]);
    
    
            if (!empty($annuaryReturn['warning'])) {
                return $response->withJson(['warning' => $annuaryReturn['warning']]);
            }
    
    
            return $response->withStatus(204);
    
        public function addContactToM2MAnnuary($args = [])
        {
            $warning = '';
            $body = $args['body'];
            if (!empty($body['externalId']['m2m']) && !empty($body['company']) && empty($body['externalId']['m2m_annuary_id'])) {
                if (empty($body['company']) || (empty($body['communicationMeans']['email']) && empty($body['communicationMeans']['url'])) || empty($body['department'])) {
    
                    $control = AnnuaryController::getAnnuaries();
                    if (!empty($control['annuaries'])) {
                        $warning = _CANNOT_SYNCHRONIZE_M2M_ANNUARY;
                    }
    
                } else {
                    $annuaryInfo = AnnuaryController::addContact([
                        'ouName'             => $body['company'],
                        'communicationValue' => $body['communicationMeans']['email'] ?? $body['communicationMeans']['url'],
                        'serviceName'        => $body['department'],
                        'm2mId'              => $body['externalId']['m2m']
                    ]);
                    if (!empty($annuaryInfo['errors'])) {
                        $warning = $annuaryInfo['errors'];
                    } else {
                        $body['externalId']['m2m_annuary_id'] = $annuaryInfo['entryUUID'];
                    }
                }
            }
    
            return ['body' => $body, 'warning' => $warning];
        }
    
    
        public function updateActivation(Request $request, Response $response, array $args)
        {
    
            if (!PrivilegeController::hasPrivilege(['privilegeId' => 'admin_contacts', 'userId' => $GLOBALS['id']])) {
                return $response->withStatus(403)->withJson(['errors' => 'Service forbidden']);
            }
    
            if (!Validator::intVal()->validate($args['id'])) {
                return $response->withStatus(400)->withJson(['errors' => 'Route id is not an integer']);
            }
    
    
            $contact = ContactModel::getById(['id' => $args['id'], 'select' => [1]]);
            if (empty($contact)) {
                return $response->withStatus(400)->withJson(['errors' => 'Contact does not exist']);
            }
    
            $body = $request->getParsedBody();
    
            ContactModel::update([
                'set'   => ['enabled' => empty($body['enabled']) ? 'false' : 'true'],
                'where' => ['id = ?'],
                'data'  => [$args['id']]
            ]);
    
            return $response->withStatus(204);
        }
    
    
        public function delete(Request $request, Response $response, array $args)
    
            if (!PrivilegeController::hasPrivilege(['privilegeId' => 'admin_contacts', 'userId' => $GLOBALS['id']])) {
                return $response->withStatus(403)->withJson(['errors' => 'Service forbidden']);
            }
    
            if (!Validator::intVal()->validate($args['id'])) {
                return $response->withStatus(400)->withJson(['errors' => 'Route id is not an integer']);
            }
    
            $contact = ContactModel::getById(['id' => $args['id'], 'select' => ['lastname', 'firstname', 'company']]);
    
            if (empty($contact)) {
                return $response->withStatus(400)->withJson(['errors' => 'Contact does not exist']);
            }
    
    
            $queryParams = $request->getQueryParams();
    
            if (!empty($queryParams['redirect'])) {
                if (!Validator::intVal()->validate($queryParams['redirect'])) {
                    return $response->withStatus(400)->withJson(['errors' => 'Query param redirect is not an integer']);
    
                } elseif ($queryParams['redirect'] == $args['id']) {
    
                    return $response->withStatus(400)->withJson(['errors' => 'Cannot redirect to contact you are deleting']);
                }
    
                $contactRedirect = ContactModel::getById(['id' => $queryParams['redirect'], 'select' => [1]]);
                if (empty($contactRedirect)) {
                    return $response->withStatus(400)->withJson(['errors' => 'Contact does not exist']);
                }
    
                $resourcesContacts = ResourceContactModel::get([
                    'select' => ['res_id', 'mode'],
                    'where'  => ['item_id = ?', "type = 'contact'"],
                    'data'   => [$args['id']]
                ]);
    
    
                ResourceContactModel::update([
                    'set'   => ['item_id' => $queryParams['redirect']],
    
                    'where' => ['item_id = ?', 'type = ?'],
                    'data'  => [$args['id'], 'contact']
    
                foreach ($resourcesContacts as $resourcesContact) {
                    $resContact = ResourceContactModel::get([
                        'select'  => ['id'],
                        'where'   => ['res_id = ?', 'item_id = ?', 'mode = ?', "type = 'contact'"],
                        'data'    => [$resourcesContact['res_id'], $queryParams['redirect'], $resourcesContact['mode']],
                        'orderBy' => ['id desc']
                    ]);
    
                    if (count($resContact) > 1) {
                        $toDelete[] = $resContact[0]['id'];
                    }
    
                }
                if (!empty($toDelete)) {
                    ResourceContactModel::delete([
                        'where' => ['id in (?)'],
                        'data' => [$toDelete]
                    ]);
    
                }
    
                AcknowledgementReceiptModel::update([
                    'set'   => ['contact_id' => $queryParams['redirect']],
                    'where' => ['contact_id = ?'],
                    'data'  => [$args['id']]
                ]);
    
                AttachmentModel::update([
                    'set'   => ['recipient_id' => $queryParams['redirect']],
                    'where' => ['recipient_id = ?', "recipient_type = 'contact'"],
                    'data'  => [$args['id']]
                ]);
            }
    
            AttachmentModel::update([
                'set'   => ['recipient_id' => null, 'recipient_type' => null],
                'where' => ['recipient_id = ?', "recipient_type = 'contact'"],
                'data'  => [$args['id']]
            ]);
    
            ResourceContactModel::delete([
                'where' => ['item_id = ?', "type = 'contact'"],
    
                'data'  => [$args['id']]
    
            ContactModel::delete([
                'where' => ['id = ?'],
                'data'  => [$args['id']]
            ]);
    
            ContactGroupModel::deleteByContactId(['contactId' => $args['id']]);
    
    
            if (!empty($contact['firstname']) || !empty($contact['lastname'])) {
                $historyInfoContact .= $contact['firstname'] . ' ' . $contact['lastname'];
    
            if (!empty($historyInfoContact) && !empty($contact['company'])) {
                $historyInfoContact .= ' (' . $contact['company'] . ')';
    
                $historyInfoContact .= $contact['company'];
    
            }
    
            HistoryController::add([
                'tableName' => 'contacts',
                'recordId'  => $args['id'],
                'eventType' => 'DEL',
                'info'      => _CONTACT_SUPPRESSION . " : " . trim($historyInfoContact),
                'moduleId'  => 'contact',
                'eventId'   => 'contactSuppression',
            ]);
    
    
            return $response->withStatus(204);
    
        public function getContactsParameters(Request $request, Response $response)
    
        {
            $contactsFilling = ContactFillingModel::get();
    
            $contactParameters = ContactParameterModel::get([
                'select' => ['*'],
                'orderBy' => ['identifier=\'civility\' desc, identifier=\'firstname\' desc, identifier=\'lastname\' desc, identifier=\'company\' desc, identifier=\'department\' desc, 
                identifier=\'function\' desc, identifier=\'address_number\' desc, identifier=\'address_street\' desc, identifier=\'address_additional1\' desc, identifier=\'address_additional2\' desc, 
                identifier=\'address_postcode\' desc, identifier=\'address_town\' desc, identifier=\'address_country\' desc, identifier=\'email\' desc, identifier=\'phone\' desc']
            ]);
    
            foreach ($contactParameters as $key => $parameter) {
                if (strpos($parameter['identifier'], 'contactCustomField_') !== false) {
                    $contactCustomId = str_replace("contactCustomField_", "", $parameter['identifier']);
                    $customField = ContactCustomFieldListModel::getById(['select' => ['label'], 'id' => $contactCustomId]);
                    $contactParameters[$key]['label'] = $customField['label'];
                } else {
                    $contactParameters[$key]['label'] = null;
                }
            }
    
            $loadedXml = CoreConfigModel::getXmlLoaded(['path' => 'apps/maarch_entreprise/xml/m2m_config.xml']);
    
            $annuaryEnabled = true;
            if (!$loadedXml) {
                $annuaryEnabled = false;
            }
            if (empty($loadedXml->annuaries) || $loadedXml->annuaries->enabled == 'false') {
                $annuaryEnabled = false;
            }
    
            return $response->withJson(['contactsFilling' => $contactsFilling, 'contactsParameters' => $contactParameters, 'annuaryEnabled' => $annuaryEnabled]);
    
        public function updateContactsParameters(Request $request, Response $response)
    
            if (!PrivilegeController::hasPrivilege(['privilegeId' => 'admin_contacts', 'userId' => $GLOBALS['id']])) {
    
                return $response->withStatus(403)->withJson(['errors' => 'Service forbidden']);
            }
    
            $data = $request->getParams();
    
            $check = Validator::arrayType()->validate($data['contactsParameters']);
            $check = $check && Validator::arrayType()->validate($data['contactsFilling']);
            $check = $check && Validator::boolType()->validate($data['contactsFilling']['enable']);
            $check = $check && Validator::intVal()->notEmpty()->validate($data['contactsFilling']['first_threshold']) && $data['contactsFilling']['first_threshold'] > 0 && $data['contactsFilling']['first_threshold'] < 99;
            $check = $check && Validator::intVal()->notEmpty()->validate($data['contactsFilling']['second_threshold']) && $data['contactsFilling']['second_threshold'] > 1 && $data['contactsFilling']['second_threshold'] < 100;
            $check = $check && $data['contactsFilling']['first_threshold'] < $data['contactsFilling']['second_threshold'];
    
            if (!$check) {
                return $response->withStatus(400)->withJson(['errors' => 'Bad Request']);
            }
    
    
            foreach ($data['contactsParameters'] as $contactParameter) {
                unset($contactParameter['label']);
                ContactParameterModel::update([
                    'set'   => [
                        'mandatory'   => empty($contactParameter['mandatory']) ? 'false' : 'true',
                        'filling'     => empty($contactParameter['filling']) ? 'false' : 'true',
                        'searchable'  => empty($contactParameter['searchable']) ? 'false' : 'true',
                        'displayable' => empty($contactParameter['displayable']) ? 'false' : 'true',
                    ],
                    'where' => ['id = ?'],
                    'data'  => [$contactParameter['id']]
                ]);
            }
            
            ContactFillingModel::update($data['contactsFilling']);
    
    
            return $response->withJson(['success' => 'success']);
        }
    
    
        public function getByResId(Request $request, Response $response, array $args)
    
            if (!Validator::intVal()->validate($args['resId']) || !ResController::hasRightByResId(['resId' => [$args['resId']], 'userId' => $GLOBALS['id']])) {
                return $response->withStatus(403)->withJson(['errors' => 'Document out of perimeter']);
            }
    
    
            $resource = ResModel::getById(['select' => ['res_id'], 'resId' => $args['resId']]);
    
    
            if (empty($resource)) {
                return $response->withStatus(404)->withJson(['errors' => 'Document does not exist']);
            }
    
            $queryParams = $request->getQueryParams();
    
            $contacts = [];
            if ($queryParams['type'] == 'senders') {
    
                $contacts = ContactController::getParsedContacts(['resId' => $resource['res_id'], 'mode' => 'sender']);
    
            } elseif ($queryParams['type'] == 'recipients') {
    
                $contacts = ContactController::getParsedContacts(['resId' => $resource['res_id'], 'mode' => 'recipient']);
    
            }
    
            return $response->withJson(['contacts' => $contacts]);
        }
    
    
        public function getLightFormattedContact(Request $request, Response $response, array $args)
    
        {
            if (!Validator::intVal()->notEmpty()->validate($args['id'])) {
                return $response->withStatus(400)->withJson(['errors' => 'Query params id is not an integer']);
            }
    
            if ($args['type'] == 'contact') {
                $contact = ContactModel::getById([
                    'select'    => [
                        'firstname', 'lastname', 'company', 'address_number as "addressNumber"', 'address_street as "addressStreet"',
                        'address_postcode as "addressPostcode"', 'address_town as "addressTown"', 'address_country as "addressCountry"'],
                    'id'        => $args['id']
                ]);
            } elseif ($args['type'] == 'user') {
                $contact = UserModel::getById(['id' => $args['id'], 'select' => ['firstname', 'lastname']]);
            } elseif ($args['type'] == 'entity') {
                $contact = EntityModel::getById(['id' => $args['id'], 'select' => ['entity_label as label']]);
            }
    
            if (empty($contact)) {
                return $response->withStatus(400)->withJson(['errors' => 'Contact does not exist']);
            }
    
            return $response->withJson(['contact' => $contact]);
        }
    
    
        public function getCivilities(Request $request, Response $response)
    
        {
            $civilities = ContactModel::getCivilities();
    
            return $response->withJson(['civilities' => $civilities]);
        }
    
    
        public static function getFillingRate(array $aArgs)
        {
    
            ValidatorModel::notEmpty($aArgs, ['contactId']);
            ValidatorModel::intVal($aArgs, ['contactId']);
    
    
            $contactsFilling = ContactFillingModel::get();
    
            $contactsParameters = ContactParameterModel::get(['select' => ['identifier'], 'where' => ['filling = ?'], 'data' => ['true']]);
    
            if ($contactsFilling['enable'] && !empty($contactsParameters)) {
                $contactRaw = ContactModel::getById([
    
                    'select'    => [
                        'civility', 'firstname', 'lastname', 'company', 'department', 'function', 'address_number as "addressNumber"', 'address_street as "addressStreet"',
                        'address_additional1 as "addressAdditional1"', 'address_additional2 as "addressAdditional2"', 'address_postcode as "addressPostcode"',
                        'address_town as "addressTown"', 'address_country as "addressCountry"', 'email', 'phone', 'custom_fields'
                    ],
    
                $customFields = json_decode($contactRaw['custom_fields'], true);
    
                $percent = 0;
    
                foreach ($contactsParameters as $ratingColumn) {
    
                    if (strpos($ratingColumn['identifier'], 'contactCustomField_') !== false && !empty($customFields[str_replace("contactCustomField_", "", $ratingColumn['identifier'])])) {
    
                    } elseif (!empty($contactRaw[$ratingColumn['identifier']])) {
    
                $percent = $percent * 100 / count($contactsParameters);
    
                if ($percent <= $contactsFilling['first_threshold']) {
    
                    $thresholdLevel = 'first';
    
                } elseif ($percent <= $contactsFilling['second_threshold']) {
    
                    $thresholdLevel = 'second';
    
                } else {
    
                    $thresholdLevel = 'third';
    
                return ['rate' => round($percent, 2), 'thresholdLevel' => $thresholdLevel];
    
        public static function getContactAfnor(array $args)
    
        {
            $afnorAddress = ['Afnor',
                '',
                '',
                '',
                '',
                '',
                ''
            ];
    
    
            if (!empty($args['company'])) {
    
                // Ligne 1
    
                $afnorAddress[1] = trim(substr($args['company'], 0, 38));
    
            // Ligne 2
            if (!empty($args['civility']) || !empty($args['firstname']) || !empty($args['lastname'])) {
                $afnorAddress[2] = ContactController::controlLengthNameAfnor([
                    'civility'      => $args['civility'],
                    'fullName'      => $args['firstname'].' '.$args['lastname'],
                    'strMaxLength'  => 38
                ]);
    
                $afnorAddress[2] = trim($afnorAddress[2]);
    
            // Ligne 3
    
            if (!empty($args['address_additional1'])) {
    
                $afnorAddress[3] = trim(substr($args['address_additional1'], 0, 38));
    
            if (!empty($args['address_number'])) {
                $args['address_number'] = TextFormatModel::normalize(['string' => $args['address_number']]);
                $args['address_number'] = preg_replace('/[^\w]/s', ' ', $args['address_number']);
                $args['address_number'] = strtoupper($args['address_number']);
    
            if (!empty($args['address_street'])) {
                $args['address_street'] = TextFormatModel::normalize(['string' => $args['address_street']]);
                $args['address_street'] = preg_replace('/[^\w]/s', ' ', $args['address_street']);
                $args['address_street'] = strtoupper($args['address_street']);
    
            $afnorAddress[4] = trim(substr($args['address_number'].' '.$args['address_street'], 0, 38));
    
            if (!empty($args['address_additional2'])) {
    
                $afnorAddress[5] = trim(substr($args['address_additional2'], 0, 38));
    
            $args['address_postcode'] = strtoupper($args['address_postcode']);
            $args['address_town'] = strtoupper($args['address_town']);
    
            $afnorAddress[6] = trim(substr($args['address_postcode'].' '.$args['address_town'], 0, 38));
    
    
            return $afnorAddress;
        }
    
    
        public static function controlLengthNameAfnor(array $args)
    
            $aCivility = ContactModel::getCivilities();
    
            if (strlen($args['civility'].' '.$args['fullName']) > $args['strMaxLength']) {
                $args['civility'] = $aCivility[$args['civility']]['abbreviation'];
    
            } else {
    
                $args['civility'] = $aCivility[$args['civility']]['label'];
    
            return substr($args['civility'].' '.$args['fullName'], 0, $args['strMaxLength']);
    
        public function getAvailableDepartments(Request $request, Response $response)
    
            $customId = CoreConfigModel::getCustomId();
    
    
            $referentialDirectory = 'referential/ban/indexes';
    
            if (is_dir("custom/{$customId}/".$referentialDirectory)) {
                $customFilesDepartments = scandir("custom/{$customId}/".$referentialDirectory);
    
            if (is_dir($referentialDirectory)) {
                $filesDepartments = scandir($referentialDirectory);
    
            $departments = [];
            if (!empty($customFilesDepartments)) {
                foreach ($customFilesDepartments as $value) {
    
                    if ($value != '.' && $value != '..' && is_writable("custom/{$customId}/".$referentialDirectory.'/'.$value)) {
    
                        $departments[] = $value;
                    }
                }
    
            if (!empty($filesDepartments)) {
                foreach ($filesDepartments as $value) {
    
                    if ($value != '.' && $value != '..' && !in_array($value, $departments) && is_writable($referentialDirectory.'/'.$value)) {
    
                        $departments[] = $value;
                    }
    
            if (empty($departments)) {
                return $response->withJson(['departments' => []]);
    
    
            sort($departments, SORT_NUMERIC);
    
    
            $defaultDepartment = ParameterModel::getById(['id' => 'defaultDepartment', 'select' => ['param_value_int']]);
    
            return $response->withJson(['departments' => $departments, 'default' => empty($defaultDepartment['param_value_int']) ? null : $defaultDepartment['param_value_int']]);
    
        public function getDuplicatedContacts(Request $request, Response $response)
        {
            if (!PrivilegeController::hasPrivilege(['privilegeId' => 'admin_contacts', 'userId' => $GLOBALS['id']])) {
                return $response->withStatus(403)->withJson(['errors' => 'Service forbidden']);
            }
    
            $queryParams = $request->getQueryParams();
    
    
            // [fieldNameInFront] => field_name_in_db
            $allowedFields = [
                'firstname'          => 'firstname',
                'lastname'           => 'lastname',
                'company'            => 'company',
                'addressNumber'      => 'address_number',
                'addressStreet'      => 'address_street',
                'addressAdditional1' => 'address_additional1',
                'addressAdditional2' => 'address_additional2',
                'addressPostcode'    => 'address_postcode',
                'addressTown'        => 'address_town',
                'addressCountry'     => 'address_country',
                'department'         => 'department',
                'function'           => 'function',
                'email'              => 'email',
                'phone'              => 'phone'
            ];
    
    
            if (!Validator::arrayType()->notEmpty()->validate($queryParams['criteria'])) {
                return $response->withStatus(400)->withJson(['errors' => 'criteria is empty or not an array']);
            }
    
    
            $contactCustoms = ContactCustomFieldListModel::get(['select' => ['id']]);
            $contactCustoms = array_column($contactCustoms, 'id');
    
    
            $allowedFieldsKeys = array_keys($allowedFields);
    
            foreach ($queryParams['criteria'] as $criterion) {
    
                if (strpos($criterion, 'contactCustomField_') !== false) {
                    $customId = explode('_', $criterion)[1];
                    if (!in_array($customId, $contactCustoms)) {
                        return $response->withStatus(400)->withJson(['errors' => 'Custom criteria does not exist']);
                    }
                } else {
                    if (!in_array($criterion, $allowedFieldsKeys)) {
                        return $response->withStatus(400)->withJson(['errors' => 'criteria does not exist']);
                    }
    
            // Construct the query to get all duplicates on criteria
            $criteria = [];
            $order = [];
            foreach ($queryParams['criteria'] as $criterion) {
    
                if (strpos($criterion, 'contactCustomField_') !== false) {
                    if (!in_array('custom_fields', $order)) {
                        $order[] = 'custom_fields';
                    }
                    $customId = explode('_', $criterion)[1];
                    $criteria[] = "replace(lower(translate(custom_fields->>'" . $customId . "', 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûýýþÿŔŕ',
                               'aaaaaaaceeeeiiiidnoooooouuuuybsaaaaaaaceeeeiiiidnoooooouuuyybyRr') ), ' ', '')";
                } else {
                    $order[] = $allowedFields[$criterion];
                    $criteria[] = "replace(lower(translate(" . $allowedFields[$criterion] . ", 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûýýþÿŔŕ',
    
                               'aaaaaaaceeeeiiiidnoooooouuuuybsaaaaaaaceeeeiiiidnoooooouuuyybyRr') ), ' ', '')";
    
            $fields = ['distinct(id)', 'enabled', 'dense_rank() over (order by ' . implode(',', $criteria) . ') duplicate_id', 'custom_fields'];
    
            foreach ($allowedFields as $field) {
                $fields[] = $field;
            }
    
            $where = [];
    
    
            foreach ($criteria as $criterion) {
                $subQuery = "SELECT " . $criterion . ' as field FROM contacts c GROUP BY field HAVING count(*) > 1';
    
                $where[] = $criterion . " in (" . $subQuery . ") ";
            }
    
            $duplicatesQuery = "SELECT " . implode(', ', $fields) . ' FROM contacts WHERE ' . implode(' AND ', $where);
    
            // Create a query that will have the number of duplicates for each duplicate group
            // this is needed to avoid getting result that only appears once in the result list (and the function dense_rank cannot be used in group by)
            $duplicatesCountQuery = 'SELECT duplicate_id, count(*) as duplicate_count FROM (' . $duplicatesQuery . ') as duplicates_id group by duplicate_id';
    
    
            $fields = ['distinct(id)', 'count(*) over () as total', 'duplicates_info.duplicate_id', 'enabled', 'custom_fields'];
    
            foreach ($allowedFields as $field) {
                $fields[] = $field;
    
            // Get all the duplicates
            $duplicates = DatabaseModel::select([
                'select'   => $fields,
                'table'    => ['( ' . $duplicatesQuery . ') as duplicates_info, (' . $duplicatesCountQuery . ') as duplicates_ids'],
                'where'    => ['duplicates_ids.duplicate_id = duplicates_info.duplicate_id', 'duplicate_count > 1'],
                'order_by' => $order,
                'limit'    => 500
    
                return $response->withJson(['returnedCount' => 0, 'realCount' => 0, 'contacts' => []]);
    
            $contactIds = array_column($duplicates, 'id');
            $contactsUsed = ContactController::isContactUsed(['ids' => $contactIds]);
    
            $contacts = [];
            foreach ($duplicates as $key => $contact) {
                unset($duplicates[$key]['count']);
                $filling = ContactController::getFillingRate(['contactId' => $contact['id']]);
    
                $contacts[] = [
                    'duplicateId'        => $contact['duplicate_id'],
                    'id'                 => $contact['id'],
                    'firstname'          => $contact['firstname'],
                    'lastname'           => $contact['lastname'],
                    'company'            => $contact['company'],
                    'addressNumber'      => $contact['address_number'],
                    'addressStreet'      => $contact['address_street'],
                    'addressAdditional1' => $contact['address_additional1'],
                    'addressAdditional2' => $contact['address_additional2'],
                    'addressPostcode'    => $contact['address_postcode'],
                    'addressTown'        => $contact['address_town'],
                    'addressCountry'     => $contact['address_country'],
                    'enabled'            => $contact['enabled'],
    
                    'function'           => $contact['function'],
                    'department'         => $contact['department'],
                    'email'              => $contact['email'],
                    'phone'              => $contact['phone'],
    
                    'isUsed'             => $contactsUsed[$contact['id']],
                    'filling'            => $filling,
                    'customFields'       => !empty($contact['custom_fields']) ? json_decode($contact['custom_fields'], true) : null,
                ];
            }
    
            return $response->withJson(['returnedCount' => count($contacts), 'realCount' => $count, 'contacts' => $contacts]);
    
        public function mergeContacts(Request $request, Response $response, array $args)
        {
            if (!PrivilegeController::hasPrivilege(['privilegeId' => 'admin_contacts', 'userId' => $GLOBALS['id']])) {
                return $response->withStatus(403)->withJson(['errors' => 'Service forbidden']);
            }
    
            if (!Validator::intVal()->validate($args['id'])) {
                return $response->withStatus(400)->withJson(['errors' => 'Route id is not an integer']);
            }
    
            $body = $request->getParsedBody();
            if (!Validator::arrayType()->notEmpty()->validate($body['duplicates'])) {
                return $response->withStatus(400)->withJson(['errors' => 'Body duplicates is empty or not an array']);
            }
    
            $fields = ['firstname', 'lastname', 'company', 'address_number', 'address_street', 'address_additional1', 'address_additional2',
                       'address_postcode', 'address_town', 'address_country', 'department', 'function', 'email', 'phone'];
    
            $master = ContactModel::getById([
                'select' => $fields,
                'id'     => $args['id']
            ]);
    
            if (empty($master)) {
                return $response->withStatus(400)->withJson(['errors' => 'master does not exist']);
            }
    
            $duplicates = ContactModel::get([
                'select' => $fields,
                'where'  => ['id in (?)'],
                'data'   => [$body['duplicates']]
            ]);
    
            if (count($duplicates) != count($body['duplicates'])) {
                return $response->withStatus(400)->withJson(['errors' => 'duplicates do not exist']);
            }
    
            $set = [];
            foreach ($fields as $field) {
                if (empty($master[$field])) {
                    foreach ($duplicates as $duplicate) {
                        if (!empty($duplicate[$field])) {
                            $set[$field] = $duplicate[$field];