From 7c3bb65f8b1921ff4609a8bac4536d9b2e2fc3b1 Mon Sep 17 00:00:00 2001 From: osaajani <> Date: Mon, 20 Feb 2023 03:17:53 +0100 Subject: [PATCH] Add phone group support --- controllers/internals/PhoneGroup.php | 139 ++++++++++ controllers/internals/Scheduled.php | 94 +++++-- controllers/internals/Sended.php | 2 +- controllers/publics/Api.php | 58 ++++- controllers/publics/Discussion.php | 2 +- controllers/publics/Phone.php | 9 + controllers/publics/PhoneGroup.php | 245 ++++++++++++++++++ controllers/publics/Scheduled.php | 21 +- .../20230219014653_create_phone_group.php | 52 ++++ ...102411_add_id_phone_group_to_scheduled.php | 39 +++ models/PhoneGroup.php | 123 +++++++++ routes.php | 15 +- templates/group/list.php | 1 - templates/incs/nav.php | 12 +- templates/phone_group/add.php | 78 ++++++ templates/phone_group/edit.php | 93 +++++++ templates/phone_group/list.php | 159 ++++++++++++ templates/scheduled/add.php | 15 +- templates/scheduled/edit.php | 13 +- 19 files changed, 1123 insertions(+), 47 deletions(-) create mode 100644 controllers/internals/PhoneGroup.php create mode 100644 controllers/publics/PhoneGroup.php create mode 100644 db/migrations/20230219014653_create_phone_group.php create mode 100644 db/migrations/20230219102411_add_id_phone_group_to_scheduled.php create mode 100644 models/PhoneGroup.php create mode 100644 templates/phone_group/add.php create mode 100644 templates/phone_group/edit.php create mode 100644 templates/phone_group/list.php diff --git a/controllers/internals/PhoneGroup.php b/controllers/internals/PhoneGroup.php new file mode 100644 index 0000000..5ce213e --- /dev/null +++ b/controllers/internals/PhoneGroup.php @@ -0,0 +1,139 @@ + + * + * This source file is subject to the GPL-3.0 license that is bundled + * with this source code in the file LICENSE. + */ + +namespace controllers\internals; + + /** + * Classe des groups. + */ + class PhoneGroup extends StandardController + { + protected $model; + + /** + * Create a new phone group for a user. + * + * @param int $id_user : user id + * @param stirng $name : Group name + * @param array $phones_ids : Ids of the phones of the group + * + * @return mixed bool|int : false on error, new group id + */ + public function create(int $id_user, string $name, array $phones_ids) + { + $group = [ + 'id_user' => $id_user, + 'name' => $name, + ]; + + $id_group = $this->get_model()->insert($group); + if (!$id_group) + { + return false; + } + + $internal_phone = new Phone($this->bdd); + foreach ($phones_ids as $phone_id) + { + $phone = $internal_phone->get_for_user($id_user, $phone_id); + if (!$phone) + { + continue; + } + + $this->get_model()->insert_phone_group_phone_relation($id_group, $phone_id); + } + + $internal_event = new Event($this->bdd); + $internal_event->create($id_user, 'PHONE_GROUP_ADD', 'Ajout phone group : ' . $name); + + return $id_group; + } + + /** + * Update a phone group for a user. + * + * @param int $id_user : User id + * @param int $id_group : Group id + * @param stirng $name : Group name + * @param array $phones_ids : Ids of the phones of the group + * + * @return bool : False on error, true on success + */ + public function update_for_user(int $id_user, int $id_group, string $name, array $phones_ids) + { + $group = [ + 'name' => $name, + ]; + + $result = $this->get_model()->update_for_user($id_user, $id_group, $group); + + $this->get_model()->delete_phone_group_phone_relations($id_group); + + $internal_phone = new Phone($this->bdd); + $nb_phone_insert = 0; + foreach ($phones_ids as $phone_id) + { + $phone = $internal_phone->get_for_user($id_user, $phone_id); + if (!$phone) + { + continue; + } + + if ($this->get_model()->insert_phone_group_phone_relation($id_group, $phone_id)) + { + ++$nb_phone_insert; + } + } + + if (!$result && $nb_phone_insert !== \count($phones_ids)) + { + return false; + } + + return true; + } + + /** + * Return a group by his name for a user. + * + * @param int $id_user : User id + * @param string $name : Group name + * + * @return array + */ + public function get_by_name_for_user(int $id_user, string $name) + { + return $this->get_model()->get_by_name_for_user($id_user, $name); + } + + /** + * Get groups phones. + * + * @param int $id_group : Group id + * + * @return array : phones of the group + */ + public function get_phones($id_group) + { + return $this->get_model()->get_phones($id_group); + } + + /** + * Get the model for the Controller. + */ + protected function get_model(): \models\PhoneGroup + { + $this->model = $this->model ?? new \models\PhoneGroup($this->bdd); + + return $this->model; + } + } diff --git a/controllers/internals/Scheduled.php b/controllers/internals/Scheduled.php index 3297160..e5122e3 100644 --- a/controllers/internals/Scheduled.php +++ b/controllers/internals/Scheduled.php @@ -25,6 +25,7 @@ use Monolog\Logger; * @param $at : Scheduled date to send * @param string $text : Text of the message * @param ?int $id_phone : Id of the phone to send message with, null by default + * @param ?int $id_phone_group : Id of the phone group to send message with, null by default * @param bool $flash : Is the sms a flash sms, by default false * @param bool $mms : Is the sms a mms, by default false * @param array $numbers : Array of numbers to send message to, a number is an array ['number' => '+33XXX', 'data' => '{"key":"value", ...}'] @@ -35,13 +36,14 @@ use Monolog\Logger; * * @return bool : false on error, new id on success */ - public function create(int $id_user, $at, string $text, ?int $id_phone = null, bool $flash = false, bool $mms = false, array $numbers = [], array $contacts_ids = [], array $groups_ids = [], array $conditional_group_ids = [], array $media_ids = []) + public function create(int $id_user, $at, string $text, ?int $id_phone = null, ?int $id_phone_group = null, bool $flash = false, bool $mms = false, array $numbers = [], array $contacts_ids = [], array $groups_ids = [], array $conditional_group_ids = [], array $media_ids = []) { $scheduled = [ 'id_user' => $id_user, 'at' => $at, 'text' => $text, 'id_phone' => $id_phone, + 'id_phone_group' => $id_phone_group, 'flash' => $flash, 'mms' => $mms, ]; @@ -62,6 +64,17 @@ use Monolog\Logger; } } + if (null !== $id_phone_group) + { + $internal_phone_group = new PhoneGroup($this->bdd); + $find_phone_group = $internal_phone_group->get_for_user($id_user, $id_phone_group); + + if (!$find_phone_group) + { + return false; + } + } + //Use transaction to garanty atomicity $this->bdd->beginTransaction(); @@ -147,6 +160,7 @@ use Monolog\Logger; * @param $at : Scheduled date to send * @param string $text : Text of the message * @param ?int $id_phone : Id of the phone to send message with, null by default + * @param ?int $id_phone_group : Id of the phone group to send message with, null by default * @param bool $flash : Is the sms a flash sms, by default false * @param bool $mms : Is the sms a mms, by default false * @param array $numbers : Array of numbers to send message to, a number is an array ['number' => '+33XXX', 'data' => '{"key":"value", ...}'] @@ -157,13 +171,14 @@ use Monolog\Logger; * * @return bool : false on error, true on success */ - public function update_for_user(int $id_user, int $id_scheduled, $at, string $text, ?string $id_phone = null, bool $flash = false, bool $mms = false, array $numbers = [], array $contacts_ids = [], array $groups_ids = [], array $conditional_group_ids = [], array $media_ids = []) + public function update_for_user(int $id_user, int $id_scheduled, $at, string $text, ?int $id_phone = null, ?int $id_phone_group = null, bool $flash = false, bool $mms = false, array $numbers = [], array $contacts_ids = [], array $groups_ids = [], array $conditional_group_ids = [], array $media_ids = []) { $scheduled = [ 'id_user' => $id_user, 'at' => $at, 'text' => $text, 'id_phone' => $id_phone, + 'id_phone_group' => $id_phone_group, 'mms' => $mms, 'flash' => $flash, ]; @@ -179,6 +194,17 @@ use Monolog\Logger; } } + if (null !== $id_phone_group) + { + $internal_phone_group = new PhoneGroup($this->bdd); + $find_phone_group = $internal_phone_group->get_for_user($id_user, $id_phone_group); + + if (!$find_phone_group) + { + return false; + } + } + //Ensure atomicity $this->bdd->beginTransaction(); @@ -413,13 +439,14 @@ use Monolog\Logger; $internal_group = new \controllers\internals\Group($this->bdd); $internal_conditional_group = new \controllers\internals\ConditionalGroup($this->bdd); $internal_phone = new \controllers\internals\Phone($this->bdd); + $internal_phone_group = new \controllers\internals\PhoneGroup($this->bdd); $internal_smsstop = new \controllers\internals\SmsStop($this->bdd); $internal_sended = new \controllers\internals\Sended($this->bdd); $users_smsstops = []; $users_settings = []; $users_phones = []; - $users_mms_phones = []; + $users_phone_groups = []; $now = new \DateTime(); $now = $now->format('Y-m-d H:i:s'); @@ -457,7 +484,6 @@ use Monolog\Logger; if (!isset($users_phones[$id_user])) { $users_phones[$id_user] = []; - $users_mms_phones[$id_user] = []; $phones = $internal_phone->gets_for_user($id_user); foreach ($phones as &$phone) @@ -475,25 +501,25 @@ use Monolog\Logger; $phone['remaining_volume'] = $remaining_volume; $users_phones[$id_user][$phone['id']] = $phone; } - - $mms_phones = $internal_phone->gets_phone_supporting_mms_for_user($id_user, $internal_phone::MMS_SENDING); - foreach ($mms_phones as &$mms_phone) - { - $limits = $internal_phone->get_limits($mms_phone['id']); - - $remaining_volume = PHP_INT_MAX; - foreach ($limits as $limit) - { - $startpoint = new \DateTime($limit['startpoint']); - $consumed = $internal_sended->count_since_for_phone_and_user($id_user, $mms_phone['id'], $startpoint); - $remaining_volume = min(($limit['volume'] - $consumed), $remaining_volume); - } - - $mms_phone['remaining_volume'] = $remaining_volume; - $mms_phones[$id_user][$mms_phone['id']] = $mms_phone; - } } + if (!isset($users_phone_groups[$id_user])) + { + $users_phone_groups[$id_user] = []; + + $phone_groups = $internal_phone_group->gets_for_user($id_user); + foreach ($phone_groups as $phone_group) + { + $phones = $internal_phone_group->get_phones($phone_group['id']); + $phone_group['phones'] = []; + foreach ($phones as $phone) + { + $phone_group['phones'][] = $phone['id']; + } + + $users_phone_groups[$id_user][$phone_group['id']] = $phone_group; + } + } //Add medias to mms $scheduled['medias'] = []; @@ -509,6 +535,12 @@ use Monolog\Logger; $phone_to_use = $users_phones[$id_user][$scheduled['id_phone']] ?? null; } + $phone_group_to_use = null; + if ($scheduled['id_phone_group']) + { + $phone_group_to_use = $users_phone_groups[$id_user][$scheduled['id_phone_group']] ?? null; + } + // We turn all contacts, groups and conditional groups into just contacts $contacts = $this->get_contacts($id_scheduled); @@ -620,9 +652,21 @@ use Monolog\Logger; if (null === $phone_to_use) { $phones_subset = $users_phones[$id_user]; + + if ($phone_group_to_use) + { + $phones_subset = array_filter($phones_subset, function ($phone) use ($phone_group_to_use) { + return in_array($phone['id'], $phone_group_to_use['phones']); + }); + } + if ($scheduled['mms']) { - $phones_subset = $users_mms_phones[$id_user] ?: $phones_subset; + $mms_only = array_filter($phones_subset, function ($phone) { + return $phone['adapter']::meta_support_mms_sending(); + }); + + $phones_subset = $mms_only ?: $phones_subset; } // Keep only phones with remaining volume and available status @@ -675,12 +719,8 @@ use Monolog\Logger; 'text' => $text, ]; - // Consume one sms from remaining volume of phone, dont forget to do the same for the entry in mms phones + // Consume one sms from remaining volume of phone $users_phones[$id_user][$id_phone]['remaining_volume'] --; - if ($users_mms_phones[$id_user][$id_phone] ?? false) - { - $users_mms_phones[$id_user][$id_phone] --; - } } } diff --git a/controllers/internals/Sended.php b/controllers/internals/Sended.php index e67affc..fc723dd 100644 --- a/controllers/internals/Sended.php +++ b/controllers/internals/Sended.php @@ -329,13 +329,13 @@ use Exception; $return['error'] = true; $return['error_message'] = $e->getMessage(); - $uid = uniqid(); $status = \models\Sended::STATUS_FAILED; return $return; } finally { + $uid = $uid ?? uniqid(); $sended_id = $this->create($id_user, $id_phone, $at, $text, $destination, $uid, $adapter->meta_classname(), $flash, $mms, $medias, $originating_scheduled, $status); $webhook_body = [ diff --git a/controllers/publics/Api.php b/controllers/publics/Api.php index c5547d6..54b31d8 100644 --- a/controllers/publics/Api.php +++ b/controllers/publics/Api.php @@ -49,6 +49,7 @@ namespace controllers\publics; private $internal_user; private $internal_phone; + private $internal_phone_group; private $internal_received; private $internal_sended; private $internal_scheduled; @@ -72,6 +73,7 @@ namespace controllers\publics; $bdd = \descartes\Model::_connect(DATABASE_HOST, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD); $this->internal_user = new \controllers\internals\User($bdd); $this->internal_phone = new \controllers\internals\Phone($bdd); + $this->internal_phone_group = new \controllers\internals\PhoneGroup($bdd); $this->internal_received = new \controllers\internals\Received($bdd); $this->internal_sended = new \controllers\internals\Sended($bdd); $this->internal_scheduled = new \controllers\internals\Scheduled($bdd); @@ -118,14 +120,14 @@ namespace controllers\publics; /** * List all entries of a certain type for the current user, sorted by id. * - * @param string $entry_type : Type of entries we want to list ['sended', 'received', 'scheduled', 'contact', 'group', 'conditional_group', 'phone', 'media'] + * @param string $entry_type : Type of entries we want to list ['sended', 'received', 'scheduled', 'contact', 'group', 'conditional_group', 'phone', 'phone_group', 'media'] * @param int $page : Pagination number, Default = 0. Group of 25 results. * * @return : List of entries */ public function get_entries(string $entry_type, int $page = 0) { - $entry_types = ['sended', 'received', 'scheduled', 'contact', 'group', 'conditional_group', 'phone', 'media']; + $entry_types = ['sended', 'received', 'scheduled', 'contact', 'group', 'conditional_group', 'phone', 'phone_group', 'media']; if (!\in_array($entry_type, $entry_types, true)) { @@ -191,6 +193,26 @@ namespace controllers\publics; unset($entries[$key]['adapter_data']); } } + // Special case for phone group we must add phones because its a join + elseif ('phone_group' === $entry_type) + { + foreach ($entries as $key => $entry) + { + $phones = $this->internal_phone_group->get_phones($entry['id']); + // Hide meta data of phones if needed + foreach ($phones as &$phone) + { + if (!$phone['adapter']::meta_hide_data()) + { + continue; + } + + unset($phone['adapter_data']); + } + + $entries[$key]['phones'] = $phones; + } + } $return = self::DEFAULT_RETURN; $return['response'] = $entries; @@ -215,7 +237,8 @@ namespace controllers\publics; * * @param string $_POST['at'] : Date to send message at format Y-m-d H:i:s * @param string $_POST['text'] : Text of the message to send - * @param string $_POST['id_phone'] : Default null. Id of phone to send the message from. If null use a random phone + * @param string $_POST['id_phone'] : Default null. Id of phone to send the message from. If null and id_phone_group null, use a random phone + * @param string $_POST['id_phone_group'] : Default null. Id of phone group to send the message from. If null abd id_phone null, use a random phone * @param string $_POST['flash'] : Default false. Is the sms a flash sms. * @param string $_POST['mms'] : Default false. Is the sms a mms. * @param string $_POST['numbers'] : Array of numbers to send message to @@ -231,6 +254,7 @@ namespace controllers\publics; $at = $_POST['at'] ?? false; $text = $_POST['text'] ?? false; $id_phone = empty($_POST['id_phone']) ? null : $_POST['id_phone']; + $id_phone_group = empty($_POST['id_phone_group']) ? null : $_POST['id_phone_group']; $flash = (bool) ($_POST['flash'] ?? false); $mms = (bool) ($_POST['mms'] ?? false); $numbers = $_POST['numbers'] ?? []; @@ -417,6 +441,16 @@ namespace controllers\publics; return $this->json($return); } + if ($id_phone && $id_phone_group) + { + $return = self::DEFAULT_RETURN; + $return['error'] = self::ERROR_CODES['INVALID_PARAMETER']; + $return['message'] = self::ERROR_MESSAGES['INVALID_PARAMETER'] . 'id_phone, id_phone_group : You must specify at most one of id_phone or id_phone_group, not both.'; + $this->auto_http_code(false); + + return $this->json($return); + } + $phone = null; if ($id_phone) { @@ -433,6 +467,22 @@ namespace controllers\publics; return $this->json($return); } + $phone_group = null; + if ($id_phone_group) + { + $phone_group = $this->internal_phone_group->get_for_user($this->user['id'], $id_phone_group); + } + + if ($id_phone_group && !$phone_group) + { + $return = self::DEFAULT_RETURN; + $return['error'] = self::ERROR_CODES['INVALID_PARAMETER']; + $return['message'] = self::ERROR_MESSAGES['INVALID_PARAMETER'] . 'id_phone_group : You must specify an id_phone_group number among thoses of user phone groups.'; + $this->auto_http_code(false); + + return $this->json($return); + } + if ($mms) { foreach ($files_arrays as $file) @@ -455,7 +505,7 @@ namespace controllers\publics; } } - $scheduled_id = $this->internal_scheduled->create($this->user['id'], $at, $text, $id_phone, $flash, $mms, $numbers, $contacts, $groups, $conditional_groups, $media_ids); + $scheduled_id = $this->internal_scheduled->create($this->user['id'], $at, $text, $id_phone, $id_phone_group, $flash, $mms, $numbers, $contacts, $groups, $conditional_groups, $media_ids); if (!$scheduled_id) { $return = self::DEFAULT_RETURN; diff --git a/controllers/publics/Discussion.php b/controllers/publics/Discussion.php index 7729908..ae58195 100644 --- a/controllers/publics/Discussion.php +++ b/controllers/publics/Discussion.php @@ -315,7 +315,7 @@ namespace controllers\publics; //Destinations must be an array of number $destinations = [['number' => $destination, 'data' => '[]']]; - if (!$this->internal_scheduled->create($id_user, $at, $text, $id_phone, false, $mms, $destinations, [], [], [], $media_ids)) + if (!$this->internal_scheduled->create($id_user, $at, $text, $id_phone, null, false, $mms, $destinations, [], [], [], $media_ids)) { $return['success'] = false; $return['message'] = 'Impossible de créer le Sms'; diff --git a/controllers/publics/Phone.php b/controllers/publics/Phone.php index 80c5912..3ce81e0 100644 --- a/controllers/publics/Phone.php +++ b/controllers/publics/Phone.php @@ -551,4 +551,13 @@ class Phone extends \descartes\Controller return $this->redirect(\descartes\Router::url('Phone', 'list')); } + + /** + * Return a list of phones as a JSON array + */ + public function json_list() + { + header('Content-Type: application/json'); + echo json_encode($this->internal_phone->list_for_user($_SESSION['user']['id'])); + } } diff --git a/controllers/publics/PhoneGroup.php b/controllers/publics/PhoneGroup.php new file mode 100644 index 0000000..57d6fb7 --- /dev/null +++ b/controllers/publics/PhoneGroup.php @@ -0,0 +1,245 @@ + + * + * This source file is subject to the GPL-3.0 license that is bundled + * with this source code in the file LICENSE. + */ + +namespace controllers\publics; + + /** + * Page of phone groups. + */ + class PhoneGroup extends \descartes\Controller + { + private $internal_phone_group; + private $internal_phone; + private $internal_event; + + /** + * Call before any other func to check user is connected + * + * @return void; + */ + public function __construct() + { + $bdd = \descartes\Model::_connect(DATABASE_HOST, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD); + + $this->internal_phone_group = new \controllers\internals\PhoneGroup($bdd); + $this->internal_phone = new \controllers\internals\Phone($bdd); + $this->internal_event = new \controllers\internals\Event($bdd); + + \controllers\internals\Tool::verifyconnect(); + } + + /** + * Return all groups as an array for administration. + */ + public function list() + { + $this->render('phone_group/list'); + } + + /** + * Return groups as json. + */ + public function list_json() + { + $entities = $this->internal_phone_group->list_for_user($_SESSION['user']['id']); + header('Content-Type: application/json'); + echo json_encode(['data' => $entities]); + } + + /** + * Delete a list of phone groups + * + * @param array int $_GET['ids'] : Ids of phone groups to delete + * @param mixed $csrf + * + * @return boolean; + */ + public function delete($csrf) + { + if (!$this->verify_csrf($csrf)) + { + \FlashMessage\FlashMessage::push('danger', 'Jeton CSRF invalid !'); + + return $this->redirect(\descartes\Router::url('PhoneGroup', 'list')); + } + + $ids = $_GET['ids'] ?? []; + foreach ($ids as $id) + { + $this->internal_phone_group->delete_for_user($_SESSION['user']['id'], $id); + } + + return $this->redirect(\descartes\Router::url('PhoneGroup', 'list')); + } + + /** + * Return the creation page of a group + */ + public function add() + { + $this->render('phone_group/add'); + } + + /** + * Return the edition page for phone groups + * + * @param array $_GET['ids'] : Ids of phone groups to edit + */ + public function edit() + { + $ids = $_GET['ids'] ?? []; + + $groups = $this->internal_phone_group->gets_in_for_user($_SESSION['user']['id'], $ids); + + foreach ($groups as $key => $group) + { + $groups[$key]['phones'] = $this->internal_phone_group->get_phones($group['id']); + } + + $this->render('phone_group/edit', [ + 'phone_groups' => $groups, + ]); + } + + /** + * Create a new phone group + * + * @param $csrf : CSRF token + * @param string $_POST['name'] : Name of phone group + * @param array $_POST['phones'] : Ids of phones to put in the group + */ + public function create($csrf) + { + if (!$this->verify_csrf($csrf)) + { + \FlashMessage\FlashMessage::push('danger', 'Jeton CSRF invalid !'); + + return $this->redirect(\descartes\Router::url('PhoneGroup', 'add')); + } + + $name = $_POST['name'] ?? false; + $phones_ids = $_POST['phones'] ?? false; + + if (!$name || !$phones_ids) + { + \FlashMessage\FlashMessage::push('danger', 'Des champs sont manquants !'); + + return $this->redirect(\descartes\Router::url('PhoneGroup', 'add')); + } + + $id_group = $this->internal_phone_group->create($_SESSION['user']['id'], $name, $phones_ids); + if (!$id_group) + { + \FlashMessage\FlashMessage::push('danger', 'Impossible de créer ce groupe.'); + + return $this->redirect(\descartes\Router::url('PhoneGroup', 'add')); + } + + \FlashMessage\FlashMessage::push('success', 'Le groupe a bien été créé.'); + + return $this->redirect(\descartes\Router::url('PhoneGroup', 'list')); + } + + /** + * Update a list of phone groups + * + * @param $csrf : CSRF token + * @param array $_POST['phone_groups'] : An array of phone groups with group id as keys + * + * @return boolean; + */ + public function update($csrf) + { + if (!$this->verify_csrf($csrf)) + { + \FlashMessage\FlashMessage::push('danger', 'Jeton CSRF invalid !'); + + return $this->redirect(\descartes\Router::url('PhoneGroup', 'list')); + } + + $groups = $_POST['phone_groups'] ?? []; + + $nb_groups_update = 0; + foreach ($groups as $id => $group) + { + foreach ($group['phones_ids'] as $key => $value) + { + $group['phones_ids'][$key] = (int) $value; + } + + $nb_groups_update += (int) $this->internal_phone_group->update_for_user($_SESSION['user']['id'], $id, $group['name'], $group['phones_ids']); + } + + if ($nb_groups_update !== \count($groups)) + { + \FlashMessage\FlashMessage::push('danger', 'Certains groupes n\'ont pas pu êtres mis à jour.'); + + return $this->redirect(\descartes\Router::url('PhoneGroup', 'list')); + } + + \FlashMessage\FlashMessage::push('success', 'Tous les groupes ont été modifiés avec succès.'); + + return $this->redirect(\descartes\Router::url('PhoneGroup', 'list')); + } + + /** + * Return phones of a group as json array + * @param int $id_group = PhoneGroup id + * + * @return json + */ + public function preview (int $id_group) + { + $return = [ + 'success' => false, + 'result' => 'Une erreur inconnue est survenue.', + ]; + + $group = $this->internal_phone_group->get_for_user($_SESSION['user']['id'], $id_group); + + if (!$group) + { + $return['result'] = 'Ce groupe n\'existe pas.'; + echo json_encode($return); + + return false; + } + + $phones = $this->internal_phone_group->get_phones($id_group); + if (!$phones) + { + $return['result'] = 'Aucun téléphone dans le groupe.'; + echo json_encode($return); + + return false; + } + + foreach ($phones as &$phone) + { + $phone['adapter_name'] = call_user_func([$phone['adapter'], 'meta_name']); + } + + $return['success'] = true; + $return['result'] = $phones; + echo json_encode($return); + + return true; + } + + /** + * Cette fonction retourne la liste des groups sous forme JSON. + */ + public function json_list() + { + header('Content-Type: application/json'); + echo json_encode($this->internal_phone_group->list_for_user($_SESSION['user']['id'])); + } + } diff --git a/controllers/publics/Scheduled.php b/controllers/publics/Scheduled.php index 2c6e0e3..d64df34 100644 --- a/controllers/publics/Scheduled.php +++ b/controllers/publics/Scheduled.php @@ -18,6 +18,7 @@ namespace controllers\publics; { private $internal_scheduled; private $internal_phone; + private $internal_phone_group; private $internal_contact; private $internal_group; private $internal_conditional_group; @@ -34,6 +35,7 @@ namespace controllers\publics; $bdd = \descartes\Model::_connect(DATABASE_HOST, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD); $this->internal_scheduled = new \controllers\internals\Scheduled($bdd); $this->internal_phone = new \controllers\internals\Phone($bdd); + $this->internal_phone_group = new \controllers\internals\PhoneGroup($bdd); $this->internal_contact = new \controllers\internals\Contact($bdd); $this->internal_group = new \controllers\internals\Group($bdd); $this->internal_conditional_group = new \controllers\internals\ConditionalGroup($bdd); @@ -118,6 +120,7 @@ namespace controllers\publics; $contacts = $this->internal_contact->gets_for_user($id_user); $phones = $this->internal_phone->gets_for_user($id_user); + $phone_groups = $this->internal_phone_group->gets_for_user($id_user); $contact_ids = (isset($_GET['contact_ids']) && \is_array($_GET['contact_ids'])) ? $_GET['contact_ids'] : []; $group_ids = (isset($_GET['group_ids']) && \is_array($_GET['group_ids'])) ? $_GET['group_ids'] : []; @@ -153,6 +156,7 @@ namespace controllers\publics; 'now' => $now->format('Y-m-d H:i'), 'contacts' => $contacts, 'phones' => $phones, + 'phone_groups' => $phone_groups, 'prefilled_contacts' => $prefilled_contacts, 'prefilled_groups' => $prefilled_groups, 'prefilled_conditional_groups' => $prefilled_conditional_groups, @@ -179,6 +183,7 @@ namespace controllers\publics; $all_contacts = $this->internal_contact->gets_for_user($_SESSION['user']['id']); $phones = $this->internal_phone->gets_for_user($_SESSION['user']['id']); + $phone_groups = $this->internal_phone_group->gets_for_user($id_user); $scheduleds = $this->internal_scheduled->gets_in_for_user($_SESSION['user']['id'], $ids); //Pour chaque message on ajoute les numéros, les contacts & les groups @@ -226,6 +231,7 @@ namespace controllers\publics; $this->render('scheduled/edit', [ 'scheduleds' => $scheduleds, 'phones' => $phones, + 'phone_groups' => $phone_groups, 'contacts' => $all_contacts, ]); } @@ -238,7 +244,7 @@ namespace controllers\publics; * @param string $_POST['at'] : Date to send message for * @param string $_POST['text'] : Text of the message * @param ?bool $_POST['flash'] : Is the message a flash message (by default false) - * @param ?int $_POST['id_phone'] : Id of the phone to send message from, if null use random phone + * @param ?int $_POST['id_phone'] : Id of the phone or phone group to send message from. id will be preceed by phone_ of phonegroup_ depending on type of ressource to use, if null use random phone * @param ?array $_POST['numbers'] : Numbers to send the message to * @param ?array $_POST['contacts'] : Numbers to send the message to * @param ?array $_POST['groups'] : Numbers to send the message to @@ -433,7 +439,12 @@ namespace controllers\publics; $mms = (bool) count($media_ids); - $scheduled_id = $this->internal_scheduled->create($id_user, $at, $text, $id_phone, $flash, $mms, $numbers, $contacts, $groups, $conditional_groups, $media_ids); + // Check if we must send message to a phone or a phone_group based on if id_phone start with 'phone_' or 'phonegroup_' + $original_id_phone = $id_phone; + $id_phone = str_starts_with($original_id_phone, 'phone_') ? mb_substr($original_id_phone, mb_strlen('phone_')) : null; + $id_phone_group = str_starts_with($original_id_phone, 'phonegroup_') ? mb_substr($original_id_phone, mb_strlen('phonegroup_')) : null; + + $scheduled_id = $this->internal_scheduled->create($id_user, $at, $text, $id_phone, $id_phone_group, $flash, $mms, $numbers, $contacts, $groups, $conditional_groups, $media_ids); if (!$scheduled_id) { \FlashMessage\FlashMessage::push('danger', 'Impossible de créer le Sms.'); @@ -650,7 +661,11 @@ namespace controllers\publics; $mms = (bool) count($media_ids); - $this->internal_scheduled->update_for_user($id_user, $id_scheduled, $at, $text, $id_phone, $flash, $mms, $numbers, $contacts, $groups, $conditional_groups, $media_ids); + $original_id_phone = $id_phone; + $id_phone = str_starts_with($original_id_phone, 'phone_') ? mb_substr($original_id_phone, mb_strlen('phone_')) : null; + $id_phone_group = str_starts_with($original_id_phone, 'phonegroup_') ? mb_substr($original_id_phone, mb_strlen('phonegroup_')) : null; + + $this->internal_scheduled->update_for_user($id_user, $id_scheduled, $at, $text, $id_phone, $id_phone_group, $flash, $mms, $numbers, $contacts, $groups, $conditional_groups, $media_ids); ++$nb_update; } diff --git a/db/migrations/20230219014653_create_phone_group.php b/db/migrations/20230219014653_create_phone_group.php new file mode 100644 index 0000000..d8e3b4b --- /dev/null +++ b/db/migrations/20230219014653_create_phone_group.php @@ -0,0 +1,52 @@ +table('phone_group') + ->addColumn('id_user', 'integer', ['null' => false]) + ->addColumn('name', 'string', ['null' => false, 'limit' => 100]) + ->addColumn('created_at', 'timestamp', ['null' => false, 'default' => 'CURRENT_TIMESTAMP']) + ->addColumn('updated_at', 'timestamp', ['null' => true, 'update' => 'CURRENT_TIMESTAMP']) + ->addForeignKey('id_user', 'user', 'id', ['delete' => 'CASCADE', 'update' => 'CASCADE']) + ->create(); + + $this->table('phone_group_phone') + ->addColumn('id_phone_group', 'integer', ['null' => false]) + ->addColumn('id_phone', 'integer', ['null' => false]) + ->addColumn('created_at', 'timestamp', ['null' => false, 'default' => 'CURRENT_TIMESTAMP']) + ->addColumn('updated_at', 'timestamp', ['null' => true, 'update' => 'CURRENT_TIMESTAMP']) + ->addForeignKey('id_phone_group', 'phone_group', 'id', ['delete' => 'CASCADE', 'update' => 'CASCADE']) + ->addForeignKey('id_phone', 'phone', 'id', ['delete' => 'CASCADE', 'update' => 'CASCADE']) + ->create(); + } +} diff --git a/db/migrations/20230219102411_add_id_phone_group_to_scheduled.php b/db/migrations/20230219102411_add_id_phone_group_to_scheduled.php new file mode 100644 index 0000000..ee12008 --- /dev/null +++ b/db/migrations/20230219102411_add_id_phone_group_to_scheduled.php @@ -0,0 +1,39 @@ +table('scheduled') + ->addColumn('id_phone_group', 'integer', ['null' => true, 'default' => null, 'after' => 'id_phone']) + ->addForeignKey('id_phone_group', 'phone_group', 'id', ['delete' => 'CASCADE', 'update' => 'CASCADE']) + ->update(); + } +} diff --git a/models/PhoneGroup.php b/models/PhoneGroup.php new file mode 100644 index 0000000..0a71a24 --- /dev/null +++ b/models/PhoneGroup.php @@ -0,0 +1,123 @@ + + * + * This source file is subject to the GPL-3.0 license that is bundled + * with this source code in the file LICENSE. + */ + +namespace models; + + class PhoneGroup extends StandardModel + { + /** + * Return a list of phone groups for a user. + * Add a column nb_phone. + * + * @param int $id_user : user id + * @param ?int $limit : Number of entry to return or null + * @param ?int $offset : Number of entry to ignore or null + * + * @return array + */ + public function list_for_user(int $id_user, $limit, $offset) + { + $query = ' + SELECT pg.*, COUNT(pgp.id) as nb_phone + FROM `phone_group` as pg + LEFT JOIN phone_group_phone as pgp + ON pg.id = pgp.id_phone_group + WHERE pg.id_user = :id_user + GROUP BY pg.id + '; + + if (null !== $limit) + { + $limit = (int) $limit; + + $query .= ' LIMIT ' . $limit; + if (null !== $offset) + { + $offset = (int) $offset; + $query .= ' OFFSET ' . $offset; + } + } + + $params = [ + 'id_user' => $id_user, + ]; + + return $this->_run_query($query, $params); + } + + /** + * Return a phone group by his name for a user. + * + * @param int $id_user : User id + * @param string $name : Group name + * + * @return array + */ + public function get_by_name_for_user(int $id_user, string $name) + { + return $this->_select_one($this->get_table_name(), ['id_user' => $id_user, 'name' => $name]); + } + + /** + * Delete relations between phone group and phone for a group. + * + * @param int $id_phone_group : Group id + * + * @return int : Number of deleted rows + */ + public function delete_phone_group_phone_relations(int $id_phone_group) + { + return $this->_delete('phone_group_phone', ['id_phone_group' => $id_phone_group]); + } + + /** + * Insert a relation between a phone group and a phone. + * + * @param int $id_phone_group : Phone Group id + * @param int $id_phone : Phone id + * + * @return mixed (bool|int) : False on error, new row id else + */ + public function insert_phone_group_phone_relation(int $id_phone_group, int $id_phone) + { + $success = (bool) $this->_insert('phone_group_phone', ['id_phone_group' => $id_phone_group, 'id_phone' => $id_phone]); + + return $success ? $this->_last_id() : false; + } + + /** + * Get phone groups phones. + * + * @param int $id_phone_group : Phone Group id + * + * @return array : Phones of the group + */ + public function get_phones(int $id_phone_group) + { + $query = ' + SELECT * + FROM `phone` + WHERE id IN (SELECT id_phone FROM `phone_group_phone` WHERE id_phone_group = :id_phone_group) + '; + + $params = ['id_phone_group' => $id_phone_group]; + + return $this->_run_query($query, $params); + } + + /** + * Return table name. + */ + protected function get_table_name(): string + { + return 'phone_group'; + } + } diff --git a/routes.php b/routes.php index 1de71f8..26170ca 100644 --- a/routes.php +++ b/routes.php @@ -164,7 +164,20 @@ 'delete' => '/phone/delete/{csrf}/', 'edit' => '/phone/edit/', 'update' => '/phone/update/{csrf}/', - 'update_status' => '/phone/update_status/{csrf}/' + 'update_status' => '/phone/update_status/{csrf}/', + 'json_list' => '/phones.json/', + ], + + 'PhoneGroup' => [ + 'list' => '/phone_group/', + 'list_json' => '/phone_group/json/', + 'add' => '/phone_group/add/', + 'create' => '/phone_group/create/{csrf}/', + 'delete' => '/phone_group/delete/{csrf}/', + 'edit' => '/phone_group/edit/', + 'update' => '/phone_group/update/{csrf}/', + 'preview' => '/phone_group/preview/{id_group}/', + 'json_list' => '/phone_group.json/', ], 'Call' => [ diff --git a/templates/group/list.php b/templates/group/list.php index 4af8d06..2b7728c 100644 --- a/templates/group/list.php +++ b/templates/group/list.php @@ -145,7 +145,6 @@ jQuery(document).ready(function () html += '
' + jQuery.fn.dataTable.render.text().display(contact.number) + '
' html += ' ' + jQuery.fn.dataTable.render.text().display(contact.data) + '' html += ''; - console.log(contact); } jQuery('#preview-text-modal').find('.modal-body').html(html); diff --git a/templates/incs/nav.php b/templates/incs/nav.php index 91e2340..b232d1b 100644 --- a/templates/incs/nav.php +++ b/templates/incs/nav.php @@ -110,8 +110,16 @@ -
  • > - Téléphones +
  • + Téléphones +
  • diff --git a/templates/phone_group/add.php b/templates/phone_group/add.php new file mode 100644 index 0000000..c062db7 --- /dev/null +++ b/templates/phone_group/add.php @@ -0,0 +1,78 @@ +render('incs/head', ['title' => 'Groupes de Téléphones - Add']) +?> +
    +render('incs/nav', ['page' => 'phone_groups']) +?> +
    +
    + +
    +
    +

    + Nouveau groupe de téléphones +

    + +
    +
    + + +
    +
    +
    +
    +

    Ajout d'un groupe de téléphones

    +
    +
    +
    +
    + +
    + + +
    +
    +
    + + +
    + Annuler + +
    +
    +
    +
    +
    +
    +
    +
    + +render('incs/footer'); diff --git a/templates/phone_group/edit.php b/templates/phone_group/edit.php new file mode 100644 index 0000000..5432b71 --- /dev/null +++ b/templates/phone_group/edit.php @@ -0,0 +1,93 @@ +render('incs/head', ['title' => 'Groupes de Téléphones - Edit']) +?> +
    +render('incs/nav', ['page' => 'phone_groups']) +?> +
    +
    + +
    +
    +

    + Modification groupes de téléphones +

    + +
    +
    + + +
    +
    +
    +
    +

    Modification des groupes de téléphones

    +
    +
    +
    + + +
    + +
    + + +
    +
    +
    + + +
    +
    + + Annuler + +
    +
    +
    +
    +
    +
    +
    +
    + +render('incs/footer'); diff --git a/templates/phone_group/list.php b/templates/phone_group/list.php new file mode 100644 index 0000000..28d653e --- /dev/null +++ b/templates/phone_group/list.php @@ -0,0 +1,159 @@ +render('incs/head', ['title' => 'Groupes de Téléphones - Show All']) +?> +
    +render('incs/nav', ['page' => 'phone_groups']) +?> +
    +
    + +
    +
    +

    + Dashboard Groupes de téléphones +

    + +
    +
    + + +
    +
    +
    +
    +

    Liste des groupes de téléphones

    +
    +
    +
    +
    + + + + + + + + + + + + + +
    NomNombre de téléphonesDate de créationDernière modificationPreview
    +
    +
    + +
    + Action pour la séléction : + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +render('incs/footer'); diff --git a/templates/scheduled/add.php b/templates/scheduled/add.php index bd744de..d59e77d 100644 --- a/templates/scheduled/add.php +++ b/templates/scheduled/add.php @@ -131,14 +131,21 @@ - +
    diff --git a/templates/scheduled/edit.php b/templates/scheduled/edit.php index e982389..155ddc9 100644 --- a/templates/scheduled/edit.php +++ b/templates/scheduled/edit.php @@ -156,9 +156,16 @@