From ff6b3e79dfcdf3d0db141873cdee2e5e2428f965 Mon Sep 17 00:00:00 2001 From: osaajani <> Date: Fri, 19 Mar 2021 02:45:12 +0100 Subject: [PATCH] start adding mms to a lot of places, no tests, not done --- adapters/AdapterInterface.php | 253 +++++------ adapters/BenchmarkAdapter.php | 33 +- adapters/GammuAdapter.php | 61 +-- adapters/OctopushShortcodeAdapter.php | 61 +-- adapters/OctopushVirtualNumberAdapter.php | 61 +-- adapters/OvhSmsShortcodeAdapter.php | 61 +-- adapters/OvhSmsVirtualNumberAdapter.php | 61 +-- adapters/TestAdapter.php | 63 +-- adapters/TwilioVirtualNumberAdapter.php | 61 +-- composer.json | 3 +- controllers/internals/Adapter.php | 31 +- controllers/internals/Media.php | 175 +++++--- controllers/internals/Phone.php | 61 +++ controllers/internals/Received.php | 45 +- controllers/internals/Scheduled.php | 89 +++- controllers/internals/Sended.php | 48 ++- controllers/internals/Tool.php | 103 ++--- controllers/publics/Api.php | 105 ++++- controllers/publics/Callback.php | 54 ++- controllers/publics/Scheduled.php | 5 +- daemons/Phone.php | 2 +- .../20210317214910_add_media_links_to_sms.php | 87 ++++ db/migrations/20210318142759_add_is_mms.php | 46 ++ models/Media.php | 393 +++++++++--------- 24 files changed, 1174 insertions(+), 788 deletions(-) create mode 100644 db/migrations/20210317214910_add_media_links_to_sms.php create mode 100644 db/migrations/20210318142759_add_is_mms.php diff --git a/adapters/AdapterInterface.php b/adapters/AdapterInterface.php index 8760620..2940452 100644 --- a/adapters/AdapterInterface.php +++ b/adapters/AdapterInterface.php @@ -11,140 +11,155 @@ namespace adapters; +/** + * Interface for phones adapters + * Phone's adapters allow RaspiSMS to use a platform to communicate with a phone number. + * Its an adapter between internal and external code, as an API, command line software, physical modem, etc. + * + * All Phone Adapters must implement this interface + */ +interface AdapterInterface +{ /** - * Interface for phones adapters - * Phone's adapters allow RaspiSMS to use a platform to communicate with a phone number. - * Its an adapter between internal and external code, as an API, command line software, physical modem, etc. + * Adapter constructor, called when instanciated by RaspiSMS. * - * All Phone Adapters must implement this interface + * @param json string $data : JSON string of the data to configure interaction with the implemented service */ - interface AdapterInterface - { - /** - * Adapter constructor, called when instanciated by RaspiSMS. - * - * @param json string $data : JSON string of the data to configure interaction with the implemented service - */ - public function __construct(string $data); + public function __construct(string $data); - /** - * Classname of the adapter. - */ - public static function meta_classname(): string; + /** + * Classname of the adapter. + */ + public static function meta_classname(): string; - /** - * Uniq name of the adapter - * It should be the classname of the adapter un snakecase. - */ - public static function meta_uid(): string; + /** + * Uniq name of the adapter + * It should be the classname of the adapter un snakecase. + */ + public static function meta_uid(): string; - /** - * Should this adapter be hidden in user interface for phone creation and - * available to creation through API only. - */ - public static function meta_hidden(): bool; + /** + * Should this adapter be hidden in user interface for phone creation and + * available to creation through API only. + */ + public static function meta_hidden(): bool; - /** - * Name of the adapter. - * It should probably be the name of the service it adapt (e.g : Gammu SMSD, OVH SMS, SIM800L, etc.). - */ - public static function meta_name(): string; + /** + * Name of the adapter. + * It should probably be the name of the service it adapt (e.g : Gammu SMSD, OVH SMS, SIM800L, etc.). + */ + public static function meta_name(): string; - /** - * Description of the adapter. - * A short description of the service the adapter implements. - */ - public static function meta_description(): string; + /** + * Description of the adapter. + * A short description of the service the adapter implements. + */ + public static function meta_description(): string; - /** - * List of entries we want in data for the adapter. - * - * @return array : Eachline line is a field as an array with keys : name, title, description, required - */ - public static function meta_data_fields(): array; + /** + * List of entries we want in data for the adapter. + * + * @return array : Eachline line is a field as an array with keys : name, title, description, required + */ + public static function meta_data_fields(): array; - /** - * Does the implemented service support flash smss. - */ - public static function meta_support_flash(): bool; + /** + * Does the implemented service support flash smss. + */ + public static function meta_support_flash(): bool; - /** - * Does the implemented service support reading smss. - */ - public static function meta_support_read(): bool; + /** + * Does the implemented service support reading smss. + */ + public static function meta_support_read(): bool; - /** - * Does the implemented service support reception callback. - */ - public static function meta_support_reception(): bool; + /** + * Does the implemented service support reception callback. + */ + public static function meta_support_reception(): bool; - /** - * Does the implemented service support status change callback. - */ - public static function meta_support_status_change(): bool; + /** + * Does the implemented service support status change callback. + */ + public static function meta_support_status_change(): bool; - /** - * Method called to send a SMS to a number. - * - * @param string $destination : Phone number to send the sms to - * @param string $text : Text of the SMS to send - * @param bool $flash : Is the SMS a Flash SMS - * - * @return array : [ - * bool 'error' => false if no error, true else - * ?string 'error_message' => null if no error, else error message - * ?string 'uid' => Uid of the sms created on success - * ] - */ - public function send(string $destination, string $text, bool $flash = false); + /** + * Does the implemented service support mms reception + */ + public static function meta_support_mms_reception(): bool; - /** - * Method called to read SMSs of the number. - * - * @return array : [ - * bool 'error' => false if no error, true else - * ?string 'error_message' => null if no error, else error message - * array 'smss' => Array of the sms reads - * [ - * [ - * string 'at' => sms reception date, - * string 'text' => sms text, - * string 'origin' => phone number who sent the sms - * ], - * ... - * ] - * ] - */ - public function read(): array; + /** + * Does the implemented service support mms sending + */ + public static function meta_support_mms_sending(): bool; - /** - * Method called to verify if the adapter is working correctly - * should be use for exemple to verify that credentials and number are both valid. - * - * @return bool : False on error, true else - */ - public function test(): bool; + /** + * Method called to send a SMS to a number. + * + * @param string $destination : Phone number to send the sms to + * @param string $text : Text of the SMS to send + * @param bool $flash : Is the SMS a Flash SMS + * @param bool $mms : Is the SMS a MMS + * @param array $medias : Array of medias to link to the MMS, [['http_url' => HTTP public url of the media et 'local_uri' => local uri to media file]] + * + * @return array : [ + * bool 'error' => false if no error, true else + * ?string 'error_message' => null if no error, else error message + * array 'uid' => Uid of the sms created on success + * ] + */ + public function send(string $destination, string $text, bool $flash = false, bool $mms = false, array $medias = []) : array; - /** - * Method called on reception of a status update notification for a SMS. - * - * @return mixed : False on error, else array ['uid' => uid of the sms, 'status' => New status of the sms (\models\Sended::STATUS_UNKNOWN, \models\Sended::STATUS_DELIVERED, \models\Sended::STATUS_FAILED)] - */ - public static function status_change_callback(); + /** + * Method called to read SMSs of the number. + * + * @return array : [ + * bool 'error' => false if no error, true else + * ?string 'error_message' => null if no error, else error message + * array 'smss' => Array of the sms reads [[ + * (optional) bool 'mms' => default to false, true if mms + * (optional) array 'medias' => default to [], list of array representing medias to link to sms, with [ + * 'filepath' => local file copy of the media, + * 'extension' (optional) => extension of the media, + * 'mimetype' (optional) => mimetype of the media + * ] + * ], ...] + * ] + */ + public function read(): array; - /** - * Method called on reception of a sms notification. - * - * @return array : [ - * bool 'error' => false on success, true on error - * ?string 'error_message' => null on success, error message else - * array 'sms' => array [ - * string 'at' : Recepetion date format Y-m-d H:i:s, - * string 'text' : SMS body, - * string 'origin' : SMS sender, - * ] - * - * ] - */ - public static function reception_callback(): array; - } + /** + * Method called to verify if the adapter is working correctly + * should be use for exemple to verify that credentials and number are both valid. + * + * @return bool : False on error, true else + */ + public function test(): bool; + + /** + * Method called on reception of a status update notification for a SMS. + * + * @return mixed : False on error, else array ['uid' => uid of the sms, 'status' => New status of the sms (\models\Sended::STATUS_UNKNOWN, \models\Sended::STATUS_DELIVERED, \models\Sended::STATUS_FAILED)] + */ + public static function status_change_callback(); + + /** + * Method called on reception of a sms notification. + * + * @return array : [ + * bool 'error' => false on success, true on error + * ?string 'error_message' => null on success, error message else + * array 'sms' => array [ + * string 'at' : Recepetion date format Y-m-d H:i:s, + * string 'text' : SMS body, + * string 'origin' : SMS sender, + * (optional) array 'medias' => default to [], list of array representing medias to link to sms, with [ + * 'filepath' => local file copy of the media, + * 'extension' (optional) => extension of the media, + * 'mimetype' (optional) => mimetype of the media + * ] + * ] + * ] + */ + public static function reception_callback(): array; +} diff --git a/adapters/BenchmarkAdapter.php b/adapters/BenchmarkAdapter.php index e874c8f..45b7ef6 100644 --- a/adapters/BenchmarkAdapter.php +++ b/adapters/BenchmarkAdapter.php @@ -125,21 +125,24 @@ namespace adapters; { return false; } + + /** + * Does the implemented service support mms reception + */ + public static function meta_support_mms_reception(): bool + { + return false; + } /** - * Method called to send a SMS to a number. - * - * @param string $destination : Phone number to send the sms to - * @param string $text : Text of the SMS to send - * @param bool $flash : Is the SMS a Flash SMS - * - * @return array : [ - * bool 'error' => false if no error, true else - * ?string 'error_message' => null if no error, else error message - * int 'uid' => Uid of the sms created on success - * ] + * Does the implemented service support mms sending */ - public function send(string $destination, string $text, bool $flash = false) + public static function meta_support_mms_sending(): bool + { + return false; + } + + public function send(string $destination, string $text, bool $flash = false, bool $mms = false, array $medias = []) : array { $response = [ 'error' => false, @@ -213,12 +216,6 @@ namespace adapters; return []; } - /** - * Method called to verify if the adapter is working correctly - * should be use for exemple to verify that credentials and number are both valid. - * - * @return bool : False on error, true else - */ public function test(): bool { return true; diff --git a/adapters/GammuAdapter.php b/adapters/GammuAdapter.php index cf1bb18..4829ab1 100644 --- a/adapters/GammuAdapter.php +++ b/adapters/GammuAdapter.php @@ -135,21 +135,24 @@ namespace adapters; { return false; } + + /** + * Does the implemented service support mms reception + */ + public static function meta_support_mms_reception(): bool + { + return false; + } /** - * Method called to send a SMS to a number. - * - * @param string $destination : Phone number to send the sms to - * @param string $text : Text of the SMS to send - * @param bool $flash : Is the SMS a Flash SMS - * - * @return array : [ - * bool 'error' => false if no error, true else - * ?string 'error_message' => null if no error, else error message - * ?string 'uid' => Uid of the sms created on success, null on error - * ] + * Does the implemented service support mms sending */ - public function send(string $destination, string $text, bool $flash = false) + public static function meta_support_mms_sending(): bool + { + return false; + } + + public function send(string $destination, string $text, bool $flash = false, bool $mms = false, array $medias = []) : array { $response = [ 'error' => false, @@ -230,15 +233,6 @@ namespace adapters; return $response; } - /** - * Method called to read SMSs of the number. - * - * @return array : [ - * bool 'error' => false if no error, true else - * ?string 'error_message' => null if no error, else error message - * array 'sms' => Array of the sms reads - * ] - */ public function read(): array { $response = [ @@ -287,42 +281,17 @@ namespace adapters; return $response; } - /** - * Method called to verify if the adapter is working correctly - * should be use for exemple to verify that credentials and number are both valid. - * - * @return bool : False on error, true else - */ public function test(): bool { //Always return true as we cannot test because we would be needing a root account return true; } - /** - * Method called on reception of a status update notification for a SMS. - * - * @return mixed : False on error, else array ['uid' => uid of the sms, 'status' => New status of the sms (\models\Sended::STATUS_UNKNOWN, \models\Sended::STATUS_DELIVERED, \models\Sended::STATUS_FAILED)] - */ public static function status_change_callback() { return false; } - /** - * Method called on reception of a sms notification. - * - * @return array : [ - * bool 'error' => false on success, true on error - * ?string 'error_message' => null on success, error message else - * array 'sms' => array [ - * string 'at' : Recepetion date format Y-m-d H:i:s, - * string 'text' : SMS body, - * string 'origin' : SMS sender, - * ] - * - * ] - */ public static function reception_callback(): array { return []; diff --git a/adapters/OctopushShortcodeAdapter.php b/adapters/OctopushShortcodeAdapter.php index 667a872..04e54bc 100644 --- a/adapters/OctopushShortcodeAdapter.php +++ b/adapters/OctopushShortcodeAdapter.php @@ -172,21 +172,24 @@ class OctopushShortcodeAdapter implements AdapterInterface { return true; } + + /** + * Does the implemented service support mms reception + */ + public static function meta_support_mms_reception(): bool + { + return false; + } /** - * Method called to send a SMS to a number. - * - * @param string $destination : Phone number to send the sms to - * @param string $text : Text of the SMS to send - * @param bool $flash : Is the SMS a Flash SMS - * - * @return array : [ - * bool 'error' => false if no error, true else - * ?string 'error_message' => null if no error, else error message - * array 'uid' => Uid of the sms created on success - * ] + * Does the implemented service support mms sending */ - public function send(string $destination, string $text, bool $flash = false) + public static function meta_support_mms_sending(): bool + { + return false; + } + + public function send(string $destination, string $text, bool $flash = false, bool $mms = false, array $medias = []) : array { $response = [ 'error' => false, @@ -268,26 +271,11 @@ class OctopushShortcodeAdapter implements AdapterInterface } } - /** - * Method called to read SMSs of the number. - * - * @return array : [ - * bool 'error' => false if no error, true else - * ?string 'error_message' => null if no error, else error message - * array 'sms' => Array of the sms reads - * ] - */ public function read(): array { return []; } - /** - * Method called to verify if the adapter is working correctly - * should be use for exemple to verify that credentials and number are both valid. - * - * @return bool : False on error, true else - */ public function test(): bool { try @@ -339,11 +327,6 @@ class OctopushShortcodeAdapter implements AdapterInterface } } - /** - * Method called on reception of a status update notification for a SMS. - * - * @return mixed : False on error, else array ['uid' => uid of the sms, 'status' => New status of the sms (\models\Sended::STATUS_UNKNOWN, \models\Sended::STATUS_DELIVERED, \models\Sended::STATUS_FAILED)] - */ public static function status_change_callback() { header('Connection: close'); @@ -383,20 +366,6 @@ class OctopushShortcodeAdapter implements AdapterInterface return ['uid' => $uid, 'status' => $status]; } - /** - * Method called on reception of a sms notification. - * - * @return array : [ - * bool 'error' => false on success, true on error - * ?string 'error_message' => null on success, error message else - * array 'sms' => array [ - * string 'at' : Recepetion date format Y-m-d H:i:s, - * string 'text' : SMS body, - * string 'origin' : SMS sender, - * ] - * - * ] - */ public static function reception_callback(): array { $response = [ diff --git a/adapters/OctopushVirtualNumberAdapter.php b/adapters/OctopushVirtualNumberAdapter.php index 1fc5305..d0d7cdc 100644 --- a/adapters/OctopushVirtualNumberAdapter.php +++ b/adapters/OctopushVirtualNumberAdapter.php @@ -177,21 +177,24 @@ class OctopushVirtualNumberAdapter implements AdapterInterface { return true; } + + /** + * Does the implemented service support mms reception + */ + public static function meta_support_mms_reception(): bool + { + return false; + } /** - * Method called to send a SMS to a number. - * - * @param string $destination : Phone number to send the sms to - * @param string $text : Text of the SMS to send - * @param bool $flash : Is the SMS a Flash SMS - * - * @return array : [ - * bool 'error' => false if no error, true else - * ?string 'error_message' => null if no error, else error message - * array 'uid' => Uid of the sms created on success - * ] + * Does the implemented service support mms sending */ - public function send(string $destination, string $text, bool $flash = false) + public static function meta_support_mms_sending(): bool + { + return false; + } + + public function send(string $destination, string $text, bool $flash = false, bool $mms = false, array $medias = []) : array { $response = [ 'error' => false, @@ -268,26 +271,11 @@ class OctopushVirtualNumberAdapter implements AdapterInterface } } - /** - * Method called to read SMSs of the number. - * - * @return array : [ - * bool 'error' => false if no error, true else - * ?string 'error_message' => null if no error, else error message - * array 'sms' => Array of the sms reads - * ] - */ public function read(): array { return []; } - /** - * Method called to verify if the adapter is working correctly - * should be use for exemple to verify that credentials and number are both valid. - * - * @return bool : False on error, true else - */ public function test(): bool { try @@ -339,11 +327,6 @@ class OctopushVirtualNumberAdapter implements AdapterInterface } } - /** - * Method called on reception of a status update notification for a SMS. - * - * @return mixed : False on error, else array ['uid' => uid of the sms, 'status' => New status of the sms (\models\Sended::STATUS_UNKNOWN, \models\Sended::STATUS_DELIVERED, \models\Sended::STATUS_FAILED)] - */ public static function status_change_callback() { header('Connection: close'); @@ -383,20 +366,6 @@ class OctopushVirtualNumberAdapter implements AdapterInterface return ['uid' => $uid, 'status' => $status]; } - /** - * Method called on reception of a sms notification. - * - * @return array : [ - * bool 'error' => false on success, true on error - * ?string 'error_message' => null on success, error message else - * array 'sms' => array [ - * string 'at' : Recepetion date format Y-m-d H:i:s, - * string 'text' : SMS body, - * string 'origin' : SMS sender, - * ] - * - * ] - */ public static function reception_callback(): array { $response = [ diff --git a/adapters/OvhSmsShortcodeAdapter.php b/adapters/OvhSmsShortcodeAdapter.php index dd68aff..aa98bbd 100644 --- a/adapters/OvhSmsShortcodeAdapter.php +++ b/adapters/OvhSmsShortcodeAdapter.php @@ -169,21 +169,24 @@ namespace adapters; { return false; } + + /** + * Does the implemented service support mms reception + */ + public static function meta_support_mms_reception(): bool + { + return false; + } /** - * Method called to send a SMS to a number. - * - * @param string $destination : Phone number to send the sms to - * @param string $text : Text of the SMS to send - * @param bool $flash : Is the SMS a Flash SMS - * - * @return array : [ - * bool 'error' => false if no error, true else - * ?string 'error_message' => null if no error, else error message - * ?string 'uid' => Uid of the sms created on success - * ] + * Does the implemented service support mms sending */ - public function send(string $destination, string $text, bool $flash = false) + public static function meta_support_mms_sending(): bool + { + return false; + } + + public function send(string $destination, string $text, bool $flash = false, bool $mms = false, array $medias = []) : array { $response = [ 'error' => false, @@ -241,15 +244,6 @@ namespace adapters; } } - /** - * Method called to read SMSs of the number. - * - * @return array : [ - * bool 'error' => false if no error, true else - * ?string 'error_message' => null if no error, else error message - * array 'sms' => Array of the sms reads - * ] - */ public function read(): array { $response = [ @@ -306,12 +300,6 @@ namespace adapters; } } - /** - * Method called to verify if the adapter is working correctly - * should be use for exemple to verify that credentials and number are both valid. - * - * @return bool : False on error, true else - */ public function test(): bool { try @@ -335,11 +323,6 @@ namespace adapters; } } - /** - * Method called on reception of a status update notification for a SMS. - * - * @return mixed : False on error, else array ['uid' => uid of the sms, 'status' => New status of the sms (\models\Sended::STATUS_UNKNOWN, \models\Sended::STATUS_DELIVERED, \models\Sended::STATUS_FAILED)] - */ public static function status_change_callback() { $uid = $_GET['id'] ?? false; @@ -372,20 +355,6 @@ namespace adapters; return ['uid' => $uid, 'status' => $status]; } - /** - * Method called on reception of a sms notification. - * - * @return array : [ - * bool 'error' => false on success, true on error - * ?string 'error_message' => null on success, error message else - * array 'sms' => array [ - * string 'at' : Recepetion date format Y-m-d H:i:s, - * string 'text' : SMS body, - * string 'origin' : SMS sender, - * ] - * - * ] - */ public static function reception_callback(): array { return []; diff --git a/adapters/OvhSmsVirtualNumberAdapter.php b/adapters/OvhSmsVirtualNumberAdapter.php index 1217295..1f8995f 100644 --- a/adapters/OvhSmsVirtualNumberAdapter.php +++ b/adapters/OvhSmsVirtualNumberAdapter.php @@ -180,21 +180,24 @@ namespace adapters; { return false; } + + /** + * Does the implemented service support mms reception + */ + public static function meta_support_mms_reception(): bool + { + return false; + } /** - * Method called to send a SMS to a number. - * - * @param string $destination : Phone number to send the sms to - * @param string $text : Text of the SMS to send - * @param bool $flash : Is the SMS a Flash SMS - * - * @return array : [ - * bool 'error' => false if no error, true else - * ?string 'error_message' => null if no error, else error message - * array 'uid' => Uid of the sms created on success - * ] + * Does the implemented service support mms sending */ - public function send(string $destination, string $text, bool $flash = false) + public static function meta_support_mms_sending(): bool + { + return false; + } + + public function send(string $destination, string $text, bool $flash = false, bool $mms = false, array $medias = []) : array { $response = [ 'error' => false, @@ -245,15 +248,6 @@ namespace adapters; } } - /** - * Method called to read SMSs of the number. - * - * @return array : [ - * bool 'error' => false if no error, true else - * ?string 'error_message' => null if no error, else error message - * array 'sms' => Array of the sms reads - * ] - */ public function read(): array { $response = [ @@ -304,12 +298,6 @@ namespace adapters; } } - /** - * Method called to verify if the adapter is working correctly - * should be use for exemple to verify that credentials and number are both valid. - * - * @return bool : False on error, true else - */ public function test(): bool { try @@ -334,11 +322,6 @@ namespace adapters; } } - /** - * Method called on reception of a status update notification for a SMS. - * - * @return mixed : False on error, else array ['uid' => uid of the sms, 'status' => New status of the sms (\models\Sended::STATUS_UNKNOWN, \models\Sended::STATUS_DELIVERED, \models\Sended::STATUS_FAILED)] - */ public static function status_change_callback() { $uid = $_GET['id'] ?? false; @@ -371,20 +354,6 @@ namespace adapters; return ['uid' => $uid, 'status' => $status]; } - /** - * Method called on reception of a sms notification. - * - * @return array : [ - * bool 'error' => false on success, true on error - * ?string 'error_message' => null on success, error message else - * array 'sms' => array [ - * string 'at' : Recepetion date format Y-m-d H:i:s, - * string 'text' : SMS body, - * string 'origin' : SMS sender, - * ] - * - * ] - */ public static function reception_callback(): array { return []; diff --git a/adapters/TestAdapter.php b/adapters/TestAdapter.php index 2851fb6..6a20baf 100644 --- a/adapters/TestAdapter.php +++ b/adapters/TestAdapter.php @@ -130,21 +130,24 @@ namespace adapters; { return false; } + + /** + * Does the implemented service support mms reception + */ + public static function meta_support_mms_reception(): bool + { + return true; + } /** - * Method called to send a SMS to a number. - * - * @param string $destination : Phone number to send the sms to - * @param string $text : Text of the SMS to send - * @param bool $flash : Is the SMS a Flash SMS - * - * @return array : [ - * bool 'error' => false if no error, true else - * ?string 'error_message' => null if no error, else error message - * array 'uid' => Uid of the sms created on success - * ] + * Does the implemented service support mms sending */ - public function send(string $destination, string $text, bool $flash = false) + public static function meta_support_mms_sending(): bool + { + return true; + } + + public function send(string $destination, string $text, bool $flash = false, bool $mms = false, array $medias = []) : array { $response = [ 'error' => false, @@ -154,8 +157,10 @@ namespace adapters; $uid = uniqid(); + $medias = []; + $at = (new \DateTime())->format('Y-m-d H:i:s'); - $success = file_put_contents($this->test_file_write, json_encode(['uid' => $uid, 'at' => $at, 'destination' => $destination, 'text' => $text, 'flash' => $flash]) . "\n", FILE_APPEND); + $success = file_put_contents($this->test_file_write, json_encode(['uid' => $uid, 'at' => $at, 'destination' => $destination, 'text' => $text, 'flash' => $flash, 'mms' => $mms, 'medias' => $medias]) . "\n", FILE_APPEND); if (false === $success) { $response['error'] = true; @@ -169,15 +174,6 @@ namespace adapters; return $response; } - /** - * Method called to read SMSs of the number. - * - * @return array : [ - * bool 'error' => false if no error, true else - * ?string 'error_message' => null if no error, else error message - * array 'sms' => Array of the sms reads - * ] - */ public function read(): array { $response = [ @@ -231,20 +227,11 @@ namespace adapters; } } - /** - * Method called to verify if the adapter is working correctly - * should be use for exemple to verify that credentials and number are both valid. - * - * @return bool : False on error, true else - */ public function test(): bool { return true; } - /** - * Method called on reception of a status update notification for a SMS. - */ public static function status_change_callback() { $uid = $_GET['uid'] ?? false; @@ -281,20 +268,6 @@ namespace adapters; return $return; } - /** - * Method called on reception of a sms notification. - * - * @return array : [ - * bool 'error' => false on success, true on error - * ?string 'error_message' => null on success, error message else - * array 'sms' => array [ - * string 'at' : Recepetion date format Y-m-d H:i:s, - * string 'text' : SMS body, - * string 'origin' : SMS sender, - * ] - * - * ] - */ public static function reception_callback(): array { return []; diff --git a/adapters/TwilioVirtualNumberAdapter.php b/adapters/TwilioVirtualNumberAdapter.php index 2835d84..b5065e7 100644 --- a/adapters/TwilioVirtualNumberAdapter.php +++ b/adapters/TwilioVirtualNumberAdapter.php @@ -176,19 +176,22 @@ class TwilioVirtualNumberAdapter implements AdapterInterface } /** - * Method called to send a SMS to a number. - * - * @param string $destination : Phone number to send the sms to - * @param string $text : Text of the SMS to send - * @param bool $flash : Is the SMS a Flash SMS - * - * @return array : [ - * bool 'error' => false if no error, true else - * ?string 'error_message' => null if no error, else error message - * array 'uid' => Uid of the sms created on success - * ] + * Does the implemented service support mms reception */ - public function send(string $destination, string $text, bool $flash = false) + public static function meta_support_mms_reception(): bool + { + return false; + } + + /** + * Does the implemented service support mms sending + */ + public static function meta_support_mms_sending(): bool + { + return false; + } + + public function send(string $destination, string $text, bool $flash = false, bool $mms = false, array $medias = []) : array { $response = [ 'error' => false, @@ -228,15 +231,6 @@ class TwilioVirtualNumberAdapter implements AdapterInterface } } - /** - * Method called to read SMSs of the number. - * - * @return array : [ - * bool 'error' => false if no error, true else - * ?string 'error_message' => null if no error, else error message - * array 'sms' => Array of the sms reads - * ] - */ public function read(): array { $response = [ @@ -282,12 +276,6 @@ class TwilioVirtualNumberAdapter implements AdapterInterface } } - /** - * Method called to verify if the adapter is working correctly - * should be use for exemple to verify that credentials and number are both valid. - * - * @return bool : False on error, true else - */ public function test(): bool { try @@ -313,11 +301,6 @@ class TwilioVirtualNumberAdapter implements AdapterInterface } } - /** - * Method called on reception of a status update notification for a SMS. - * - * @return mixed : False on error, else array ['uid' => uid of the sms, 'status' => New status of the sms (\models\Sended::STATUS_UNKNOWN, \models\Sended::STATUS_DELIVERED, \models\Sended::STATUS_FAILED)] - */ public static function status_change_callback() { $sid = $_REQUEST['MessageSid'] ?? false; @@ -349,20 +332,6 @@ class TwilioVirtualNumberAdapter implements AdapterInterface return ['uid' => $sid, 'status' => $status]; } - /** - * Method called on reception of a sms notification. - * - * @return array : [ - * bool 'error' => false on success, true on error - * ?string 'error_message' => null on success, error message else - * array 'sms' => array [ - * string 'at' : Recepetion date format Y-m-d H:i:s, - * string 'text' : SMS body, - * string 'origin' : SMS sender, - * ] - * - * ] - */ public static function reception_callback(): array { return []; diff --git a/composer.json b/composer.json index 608752a..4173614 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,8 @@ "ovh/ovh": "^2.0", "twilio/sdk": "^6.1", "symfony/yaml": "^5.0", - "phpmailer/phpmailer": "^6.1" + "phpmailer/phpmailer": "^6.1", + "ralouphie/mimey": "^2.1" }, "require-dev": { } diff --git a/controllers/internals/Adapter.php b/controllers/internals/Adapter.php index 50ef643..1fae465 100644 --- a/controllers/internals/Adapter.php +++ b/controllers/internals/Adapter.php @@ -20,9 +20,9 @@ namespace controllers\internals; private const ADAPTERS_META_START = 'meta_'; /** - * List adapters using internal metas. + * List adapters with filepath and internal metas. * - * @return array + * @return array : ['adapter_filepath' => ['meta...' => value, ...], ...] */ public function list_adapters() { @@ -42,7 +42,7 @@ namespace controllers\internals; continue; } - $adapters[] = $metas; + $adapters[$file] = $metas; } return $adapters; @@ -116,4 +116,29 @@ namespace controllers\internals; return $metas; } + + /** + * List all adapters for a meta value + * + * @param $search_name : Name of the meta + * @param $search_value : Value of the meta + * + * @return array : Array with ['adapter filepath' => ['search_name' => value, ...], ...] + */ + public function list_adapters_with_meta_equal($search_name, $search_value) + { + $adapters = $this->list_adapters(); + return array_filter($adapters, function($metas) use ($search_name, $search_value) { + $match = false; + foreach ($metas as $name => $value) + { + if ($name === $search_name && $value === $search_value) + { + $match = true; + } + } + + return $match; + }); + } } diff --git a/controllers/internals/Media.php b/controllers/internals/Media.php index b02ff5f..4dd6580 100644 --- a/controllers/internals/Media.php +++ b/controllers/internals/Media.php @@ -19,32 +19,105 @@ namespace controllers\internals; * Create a media. * * @param int $id_user : Id of the user - * @param int $id_scheduled : Id of the scheduled - * @param array $media : $_FILES media array + * @param string $path : path of the media in data dir * - * @return bool : false on error, new media id else + * @return mixed bool|int : false on error, new media id else */ - public function create(int $id_user, int $id_scheduled, array $media): bool + public function create(int $id_user, string $path): bool { - $internal_scheduled = new Scheduled($this->bdd); - $scheduled = $internal_scheduled->get_for_user($id_user, $id_scheduled); - if (!$scheduled) - { - return false; - } - - $result_upload_media = \controllers\internals\Tool::upload_file($media); - if (false === $result_upload_media['success']) - { - return false; - } - $data = [ - 'id_scheduled' => $id_scheduled, - 'path' => $result_upload_media['content'], + 'path' => $path, + 'id_user' => $id_user, ]; - return (bool) $this->get_model()->insert($data); + return $this->get_model()->insert($data); + } + + /** + * Link a media to a scheduled, a received or a sended message + * @param int $id_media : Id of the media + * @param string $resource_type : Type of resource to link the media to ('scheduled', 'received' or 'sended') + * @param int $resource_id : Id of the resource to link the media to + * + * @return mixed bool|int : false on error, the new link id else + */ + public function link_to(int $id_media, int $resource_type, int $resource_id) + { + switch ($resource_type) + { + case 'scheduled': + return $this->get_model()->insert_media_scheduled($id_media, $resource_id); + break; + + case 'received': + return $this->get_model()->insert_media_received($id_media, $resource_id); + break; + + case 'sended': + return $this->get_model()->insert_media_sended($id_media, $resource_id); + break; + + default: + return false; + } + } + + + /** + * Unlink a media of a scheduled, a received or a sended message + * @param int $id_media : Id of the media + * @param string $resource_type : Type of resource to unlink the media of ('scheduled', 'received' or 'sended') + * @param int $resource_id : Id of the resource to unlink the media of + * + * @return mixed bool : false on error, true on success + */ + public function unlink_of(int $id_media, int $resource_type, int $resource_id) + { + switch ($resource_type) + { + case 'scheduled': + return $this->get_model()->delete_media_scheduled($id_media, $resource_id); + break; + + case 'received': + return $this->get_model()->delete_media_received($id_media, $resource_id); + break; + + case 'sended': + return $this->get_model()->delete_media_sended($id_media, $resource_id); + break; + + default: + return false; + } + } + + /** + * Unlink all medias of a scheduled, a received or a sended message + * @param string $resource_type : Type of resource to unlink the media of ('scheduled', 'received' or 'sended') + * @param int $resource_id : Id of the resource to unlink the media of + * + * @return mixed bool : false on error, true on success + */ + public function unlink_all_of(int $resource_type, int $resource_id) + { + switch ($resource_type) + { + case 'scheduled': + return $this->get_model()->delete_all_for_scheduled($resource_id); + break; + + case 'received': + return $this->get_model()->delete_all_for_received($resource_id); + break; + + case 'sended': + return $this->get_model()->delete_all_for_sended($resource_id); + break; + + default: + return false; + } } /** @@ -52,25 +125,16 @@ namespace controllers\internals; * * @param int $id_user : user id * @param int $id_media : Media id - * @param int $id_scheduled : Id of the scheduled * @param string $path : Path of the file * * @return bool : false on error, true on success */ - public function update_for_user(int $id_user, int $id_media, int $id_scheduled, string $path): bool + public function update_for_user(int $id_user, int $id_media, string $path): bool { $media = [ - 'id_scheduled' => $id_scheduled, 'path' => $path, ]; - $internal_scheduled = new Scheduled($this->bdd); - $scheduled = $this->get_for_user($id_user, $id_scheduled); - if (!$scheduled) - { - return false; - } - return (bool) $this->get_model()->update_for_user($id_user, $id_media, $media); } @@ -95,36 +159,43 @@ namespace controllers\internals; return $this->get_model()->delete_for_user($id_user, $id_media); } - /** - * Delete a media for a scheduled and a user. - * - * @param int $id_user : User id - * @param int $id_scheduled : Scheduled id to delete medias for - * - * @return int : Number of removed rows - */ - public function delete_for_scheduled_and_user(int $id_user, int $id_scheduled): bool - { - $media = $this->get_model()->get_for_scheduled_and_user($id_user, $id_scheduled); - if ($media) - { - unlink($media['path']); - } - - return $this->get_model()->delete_for_scheduled_and_user($id_user, $id_scheduled); - } - /** * Find medias for a scheduled and a user. * * @param int $id_user : User id - * @param int $id_scheduled : Scheduled id to delete medias for + * @param int $id_scheduled : Scheduled id to fin medias for * * @return mixed : Medias || false */ - public function get_for_scheduled_and_user(int $id_user, int $id_scheduled) + public function gets_for_scheduled_and_user(int $id_user, int $id_scheduled) { - return $this->get_model()->get_for_scheduled_and_user($id_user, $id_scheduled); + return $this->get_model()->gets_for_scheduled_and_user($id_user, $id_scheduled); + } + + /** + * Find medias for a sended and a user. + * + * @param int $id_user : User id + * @param int $id_sended : Scheduled id to fin medias for + * + * @return mixed : Medias || false + */ + public function gets_for_sended_and_user(int $id_user, int $id_sended) + { + return $this->get_model()->gets_for_sended_and_user($id_user, $id_sended); + } + + /** + * Find medias for a received and a user. + * + * @param int $id_user : User id + * @param int $id_received : Scheduled id to fin medias for + * + * @return mixed : Medias || false + */ + public function gets_for_received_and_user(int $id_user, int $id_received) + { + return $this->get_model()->gets_for_received_and_user($id_user, $id_received); } /** diff --git a/controllers/internals/Phone.php b/controllers/internals/Phone.php index 63a0b96..3b54c86 100644 --- a/controllers/internals/Phone.php +++ b/controllers/internals/Phone.php @@ -13,6 +13,10 @@ namespace controllers\internals; class Phone extends StandardController { + const MMS_SENDING = 'sending'; + const MMS_RECEPTION = 'reception'; + const MMS_BOTH = 'both'; + protected $model; /** @@ -39,6 +43,63 @@ namespace controllers\internals; return $this->get_model()->get_by_name($name); } + /** + * Check if a phone support mms + * + * @param int $id : id of the phone to check + * @param $type : type of sms support, a const from Phone, MMS_SENDING, MMS_RECEPTION or MMS_BOTH + * @return bool : true if support, false else + */ + public function support_mms (int $id, string $type) + { + $phone = $this->get_model()->get($id); + if (!$phone) + { + return false; + } + + switch ($type) + { + case self::MMS_SENDING : + return $phone['adapter']::meta_support_mms_sending(); + break; + + case self::MMS_RECEPTION : + return $phone['adapter']::meta_support_mms_reception(); + break; + + case self::MMS_BOTH : + return $phone['adapter']::meta_support_mms_sending() && $phone['adapter']::meta_support_mms_reception(); + break; + + default: + return false; + } + } + + /** + * Get all phones supporting mms for a user + * + * @param int $id_user : id of the user + * @param $type : type of sms support, a const from Phone, MMS_SENDING, MMS_RECEPTION or MMS_BOTH + * @return array : array of phones supporting mms + */ + public function gets_phone_supporting_mms_for_user (int $id_user, string $type) + { + $phones = $this->get_model()->gets_for_user($id_user); + + $valid_phones = []; + foreach ($phones as $phone) + { + if ($this->support_mms($phone['id'], $type)) + { + $valid_phones[] = $phone; + } + } + + return $valid_phones; + } + /** * Return a phone for a user by a name. * diff --git a/controllers/internals/Received.php b/controllers/internals/Received.php index 2293693..0f0388a 100644 --- a/controllers/internals/Received.php +++ b/controllers/internals/Received.php @@ -39,10 +39,12 @@ namespace controllers\internals; * @param string $origin : Number of the sender * @param string $status : Status of the received message * @param bool $command : Is the sms a command + * @param bool $mms : Is the sms a mms + * @param array $media_ids : Ids of the medias to link to received * * @return mixed : false on error, new received id else */ - public function create(int $id_user, int $id_phone, $at, string $text, string $origin, string $status = 'unread', bool $command = false) + public function create(int $id_user, int $id_phone, $at, string $text, string $origin, string $status = 'unread', bool $command = false, bool $mms = false, array $media_ids = []) { $received = [ 'id_user' => $id_user, @@ -52,9 +54,39 @@ namespace controllers\internals; 'origin' => $origin, 'status' => $status, 'command' => $command, + 'mms' => $mms, ]; - return $this->get_model()->insert($received); + //use a transaction to ensure received and media links are created at the same time + $this->bdd->beginTransaction(); + + $id_received = $this->get_model()->insert($received); + if (!$id_received) + { + $this->bdd->rollBack(); + return false; + } + + //Link medias + $internal_media = new Media($this->bdd); + foreach ($media_ids as $media_id) + { + $id_media_received = $internal_media->link_to($media_id, 'received', $id_received); + if (!$id_media_received) + { + $this->bdd->rollBack(); + return false; + } + } + + //All ok, commit + $success = $this->bdd->commit(); + if (!$success) + { + return false; + } + + return $id_received; } /** @@ -211,13 +243,15 @@ namespace controllers\internals; * @param string $origin : Number of the sender * @param ?string $at : Message reception date, if null use current date * @param string $status : Status of a the sms. By default \models\Received::STATUS_UNREAD + * @param bool $mms : Is the sms a mms + * @param array $media_ids : Ids of the medias to link to received * * @return array : [ * bool 'error' => false if success, true else * ?string 'error_message' => null if success, error message else * ] */ - public function receive(int $id_user, int $id_phone, string $text, string $origin, ?string $at = null, string $status = \models\Received::STATUS_UNREAD): array + public function receive(int $id_user, int $id_phone, string $text, string $origin, ?string $at = null, string $status = \models\Received::STATUS_UNREAD, bool $mms = false, array $media_ids = []): array { $return = [ 'error' => false, @@ -236,7 +270,7 @@ namespace controllers\internals; $text = $response; } - $received_id = $this->create($id_user, $id_phone, $at, $text, $origin, $status, $is_command); + $received_id = $this->create($id_user, $id_phone, $at, $text, $origin, $status, $is_command, $mms, $media_ids); if (!$received_id) { $return['error'] = true; @@ -251,6 +285,9 @@ namespace controllers\internals; 'text' => $text, 'destination' => $id_phone, 'origin' => $origin, + 'command' => $is_command, + 'mms' => $mms, + 'medias' => $media_ids, ]; $internal_webhook = new Webhook($this->bdd); diff --git a/controllers/internals/Scheduled.php b/controllers/internals/Scheduled.php index ae99361..25d1a35 100644 --- a/controllers/internals/Scheduled.php +++ b/controllers/internals/Scheduled.php @@ -23,14 +23,16 @@ namespace controllers\internals; * @param string $text : Text of the message * @param ?int $id_phone : Id of the phone 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 : Numbers to send message to * @param array $contacts_ids : Contact ids to send message to * @param array $groups_ids : Group ids to send message to * @param array $conditional_group_ids : Conditional Groups ids to send message to + * @param array $media_ids : Ids of the medias to link to scheduled message * * @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, array $numbers = [], array $contacts_ids = [], array $groups_ids = [], array $conditional_group_ids = []) + 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 = []) { $scheduled = [ 'id_user' => $id_user, @@ -38,6 +40,7 @@ namespace controllers\internals; 'text' => $text, 'id_phone' => $id_phone, 'flash' => $flash, + 'mms' => $mms, ]; if (null !== $id_phone) @@ -51,12 +54,28 @@ namespace controllers\internals; } } + //Use transaction to garanty atomicity + $this->bdd->beginTransaction(); + $id_scheduled = $this->get_model()->insert($scheduled); if (!$id_scheduled) { + $this->bdd->rollBack(); return false; } + $internal_media = new Media($this->bdd); + foreach ($media_ids as $media_id) + { + $id_media_scheduled = $internal_media->link_to($media_id, 'scheduled', $id_scheduled); + if (!$id_media_scheduled) + { + $this->bdd->rollBack(); + return false; + } + } + + foreach ($numbers as $number) { $this->get_model()->insert_scheduled_number($id_scheduled, $number); @@ -98,6 +117,12 @@ namespace controllers\internals; $this->get_model()->insert_scheduled_conditional_group_relation($id_scheduled, $conditional_group_id); } + $success = $this->bdd->commit(); + if (!$success) + { + return false; + } + $date = date('Y-m-d H:i:s'); $internal_event = new Event($this->bdd); $internal_event->create($id_user, 'SCHEDULED_ADD', 'Ajout d\'un Sms pour le ' . $date . '.'); @@ -114,20 +139,23 @@ namespace controllers\internals; * @param string $text : Text of the message * @param ?int $id_phone : Id of the phone 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 : Numbers to send message to * @param array $contacts_ids : Contact ids to send message to * @param array $groups_ids : Group ids to send message to * @param array $conditional_group_ids : Conditional Groups ids to send message to + * @param array $media_ids : Ids of the medias to link to scheduled message * - * @return bool : false on error, new id on success + * @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, array $numbers = [], array $contacts_ids = [], array $groups_ids = [], array $conditional_group_ids = []) + 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 = []) { $scheduled = [ 'id_user' => $id_user, 'at' => $at, 'text' => $text, 'id_phone' => $id_phone, + 'mms' => $mms, 'flash' => $flash, ]; @@ -142,12 +170,27 @@ namespace controllers\internals; } } + //Ensure atomicity + $this->bdd->beginTransaction(); + $success = (bool) $this->get_model()->update_for_user($id_user, $id_scheduled, $scheduled); $this->get_model()->delete_scheduled_numbers($id_scheduled); $this->get_model()->delete_scheduled_contact_relations($id_scheduled); $this->get_model()->delete_scheduled_group_relations($id_scheduled); $this->get_model()->delete_scheduled_conditional_group_relations($id_scheduled); + $internal_media = new Media($this->bdd); + $internal_media->unlink_all_of('scheduled', $id_scheduled); + + foreach ($media_ids as $media_id) + { + $id_media_scheduled = $internal_media->link_to($media_id, 'scheduled', $id_scheduled); + if (!$id_media_scheduled) + { + $this->bdd->rollBack(); + return false; + } + } foreach ($numbers as $number) { @@ -190,7 +233,7 @@ namespace controllers\internals; $this->get_model()->insert_scheduled_conditional_group_relation($id_scheduled, $conditional_group_id); } - return true; + return $this->bdd->commit(); } /** @@ -224,6 +267,7 @@ namespace controllers\internals; $users_settings = []; $users_phones = []; + $users_mms_phones = []; $now = new \DateTime(); $now = $now->format('Y-m-d H:i:s'); @@ -244,7 +288,16 @@ namespace controllers\internals; if (!isset($users_phones[$scheduled['id_user']])) { $phones = $internal_phone->gets_for_user($scheduled['id_user']); + $mms_phones = $internal_phone->gets_phone_supporting_mms_for_user($scheduled['id_user'], $internal_phone::MMS_SENDING); $users_phones[$scheduled['id_user']] = $phones ?: []; + $users_mms_phones[$scheduled['id_user']] = $mms_phones ?: []; + } + + //Add medias to mms + if ($scheduled['mms']) + { + $internal_media = new Media($this->bdd); + $scheduled['medias'] = $internal_media->gets_for_scheduled_and_user($scheduled['id_user'], $scheduled['id']); } $phone_to_use = null; @@ -266,8 +319,16 @@ namespace controllers\internals; { if (null === $phone_to_use) { - $rnd_key = array_rand($users_phones[$scheduled['id_user']]); - $random_phone = $users_phones[$scheduled['id_user']][$rnd_key]; + if ($scheduled['mms']) + { + $rnd_key = array_rand($users_mms_phones[$scheduled['id_user']]); + $random_phone = $users_mms_phones[$scheduled['id_user']][$rnd_key]; + } + else + { + $rnd_key = array_rand($users_phones[$scheduled['id_user']]); + $random_phone = $users_phones[$scheduled['id_user']][$rnd_key]; + } } $message = [ @@ -276,6 +337,8 @@ namespace controllers\internals; 'id_phone' => $phone_to_use['id'] ?? $random_phone['id'], 'destination' => $number['number'], 'flash' => $scheduled['flash'], + 'mms' => $scheduled['mms'], + 'medias' => $scheduled['medias'], ]; if ((int) ($users_settings[$scheduled['id_user']]['templating'] ?? false)) @@ -326,8 +389,16 @@ namespace controllers\internals; if (null === $phone_to_use) { - $rnd_key = array_rand($users_phones[$scheduled['id_user']]); - $random_phone = $users_phones[$scheduled['id_user']][$rnd_key]; + if ($scheduled['mms']) + { + $rnd_key = array_rand($users_mms_phones[$scheduled['id_user']]); + $random_phone = $users_mms_phones[$scheduled['id_user']][$rnd_key]; + } + else + { + $rnd_key = array_rand($users_phones[$scheduled['id_user']]); + $random_phone = $users_phones[$scheduled['id_user']][$rnd_key]; + } } $message = [ @@ -336,6 +407,8 @@ namespace controllers\internals; 'id_phone' => $phone_to_use['id'] ?? $random_phone['id'], 'destination' => $contact['number'], 'flash' => $scheduled['flash'], + 'mms' => $scheduled['mms'], + 'medias' => $scheduled['medias'], ]; if ((int) ($users_settings[$scheduled['id_user']]['templating'] ?? false)) diff --git a/controllers/internals/Sended.php b/controllers/internals/Sended.php index 82bd14e..132f082 100644 --- a/controllers/internals/Sended.php +++ b/controllers/internals/Sended.php @@ -26,11 +26,13 @@ namespace controllers\internals; * @param string $uid : Uid of the sms on the adapter service used * @param string $adapter : Name of the adapter service used to send the message * @param bool $flash : Is the sms a flash + * @param bool $mms : Is the sms a MMS. By default false. + * @param array $medias : Array of medias to link to the MMS. * @param string $status : Status of a the sms. By default \models\Sended::STATUS_UNKNOWN * * @return mixed : false on error, new sended id else */ - public function create(int $id_user, int $id_phone, $at, string $text, string $destination, string $uid, string $adapter, bool $flash = false, ?string $status = \models\Sended::STATUS_UNKNOWN) + public function create(int $id_user, int $id_phone, $at, string $text, string $destination, string $uid, string $adapter, bool $flash = false, bool $mms = false, array $medias = [], ?string $status = \models\Sended::STATUS_UNKNOWN) { $sended = [ 'id_user' => $id_user, @@ -41,10 +43,33 @@ namespace controllers\internals; 'uid' => $uid, 'adapter' => $adapter, 'flash' => $flash, + 'mms' => $mms, 'status' => $status, ]; - return $this->get_model()->insert($sended); + //Ensure atomicity + $this->bdd->beginTransaction(); + + $id_sended = $this->get_model()->insert($sended); + if (!$id_sended) + { + $this->bdd->rollback(); + return false; + } + + //Link medias + $internal_media = new Media($this->bdd); + foreach ($medias as $media) + { + $internal_media->link_to($media['id'], 'sended', $id_sended); //No rollback on error, keeping track of mms is more important than integrity + } + + if (!$this->bdd->commit()) + { + return false; + } + + return $id_sended; } /** @@ -165,6 +190,8 @@ namespace controllers\internals; * @param $text : Text of the message * @param string $destination : Number of the receiver * @param bool $flash : Is the sms a flash. By default false. + * @param bool $mms : Is the sms a MMS. By default false. + * @param array $medias : Array of medias to link to the MMS. * @param string $status : Status of a the sms. By default \models\Sended::STATUS_UNKNOWN * * @return array : [ @@ -172,7 +199,7 @@ namespace controllers\internals; * ?string 'error_message' => null if success, error message else * ] */ - public function send(\adapters\AdapterInterface $adapter, int $id_user, int $id_phone, string $text, string $destination, bool $flash = false, string $status = \models\Sended::STATUS_UNKNOWN): array + public function send(\adapters\AdapterInterface $adapter, int $id_user, int $id_phone, string $text, string $destination, bool $flash = false, bool $mms = false, array $medias = [], string $status = \models\Sended::STATUS_UNKNOWN): array { $return = [ 'error' => false, @@ -180,19 +207,28 @@ namespace controllers\internals; ]; $at = (new \DateTime())->format('Y-m-d H:i:s'); - $response = $adapter->send($destination, $text, $flash); + $media_uris = []; + foreach ($medias as $media) + { + $media_uris[] = [ + 'http_url' => HTTP_PWD . '/data/' . $media['path'], + 'local_uri' => PWD_DATA . '/data/' . $media['path'], + ]; + } + + $response = $adapter->send($destination, $text, $flash, $mms, $media_uris); if ($response['error']) { $return['error'] = true; $return['error_message'] = $response['error_message']; $status = \models\Sended::STATUS_FAILED; - $this->create($id_user, $id_phone, $at, $text, $destination, $response['uid'] ?? uniqid(), $adapter->meta_classname(), $flash, $status); + $this->create($id_user, $id_phone, $at, $text, $destination, $response['uid'] ?? uniqid(), $adapter->meta_classname(), $flash, $mms, $medias, $status); return $return; } - $sended_id = $this->create($id_user, $id_phone, $at, $text, $destination, $response['uid'] ?? uniqid(), $adapter->meta_classname(), $flash, $status); + $sended_id = $this->create($id_user, $id_phone, $at, $text, $destination, $response['uid'] ?? uniqid(), $adapter->meta_classname(), $flash, $mms, $medias, $status); $sended = [ 'id' => $sended_id, diff --git a/controllers/internals/Tool.php b/controllers/internals/Tool.php index 843d066..0f42360 100644 --- a/controllers/internals/Tool.php +++ b/controllers/internals/Tool.php @@ -266,7 +266,8 @@ namespace controllers\internals; return $result; } - $result['extension'] = pathinfo($file['name'])['extension']; + $result['tmp_name'] = $tmp_filename; + $result['extension'] = pathinfo($file['name'], PATHINFO_EXTENSION); $result['mime_type'] = mime_content_type($tmp_filename); $file_handler = fopen($tmp_filename, 'r'); @@ -277,13 +278,18 @@ namespace controllers\internals; } /** - * Allow to upload file. + * Allow to save an uploaded file from the $_FILE['file'] array * * @param array $file : The array extracted from $_FILES['file'] + * @param string $dirpath : The directory to save the file in + * @param bool $override : If true, override the file if another file with this name exists + * @param ?string $filename : The name to use for the file, if null use a highly random name + * @param ?string $extension : The extension to use for the file, if null try to determine it using original file extension, then mime_type + * @param bool $use_mimetype : If true, ignore original file extension to determine final file extension and use file real mimetype instead * - * @return array : ['success' => bool, 'content' => file path | error message, 'error_code' => $file['error']] + * @return array : ['success' => bool, 'content' => new file name | error message, 'error_code' => $file['error']] */ - public static function upload_file(array $file) + public static function save_uploaded_file(array $file, string $dirpath, bool $override = false, ?string $filename = null, ?string $extension = null, bool $use_mimetype = false) { $result = [ 'success' => false, @@ -291,82 +297,61 @@ namespace controllers\internals; 'error_code' => $file['error'] ?? 99, ]; - if (UPLOAD_ERR_OK !== $file['error']) + $upload_info = self::read_uploaded_file($file); + if (!$upload_info['success']) { - switch ($file['error']) + $result['content'] = $upload_info['content']; + return $result; + } + + if ($extension === null) + { + $extension = $upload_info['extension']; + if ($extension === '' || $use_mimetype) { - case UPLOAD_ERR_INI_SIZE: - $result['content'] = 'Impossible de télécharger le fichier car il dépasse les ' . ini_get('upload_max_filesize') / (1000 * 1000) . ' Mégaoctets.'; - - break; - - case UPLOAD_ERR_FORM_SIZE: - $result['content'] = 'Le fichier dépasse la limite de taille.'; - - break; - - case UPLOAD_ERR_PARTIAL: - $result['content'] = 'L\'envoi du fichier a été interrompu.'; - - break; - - case UPLOAD_ERR_NO_FILE: - $result['content'] = 'Aucun fichier n\'a été envoyé.'; - - break; - - case UPLOAD_ERR_NO_TMP_DIR: - $result['content'] = 'Le serveur ne dispose pas de fichier temporaire permettant l\'envoi de fichiers.'; - - break; - - case UPLOAD_ERR_CANT_WRITE: - $result['content'] = 'Impossible d\'envoyer le fichier car il n\'y a plus de place sur le serveur.'; - - break; - - case UPLOAD_ERR_EXTENSION: - $result['content'] = 'Le serveur a interrompu l\'envoi du fichier.'; - - break; + $mimey = new \Mimey\MimeTypes; + $extension = $mimey->getExtension($upload_info['mime_type']); } - - return $result; } - $tmp_filename = $file['tmp_name'] ?? false; - if (!$tmp_filename || !is_readable($tmp_filename)) + if ($filename === null) { - return $result; + $filename = self::random_uuid(); } - $md5_filename = md5_file($tmp_filename); - if (!$md5_filename) + $filename = $filename . '.' . $extension; + $filepath = $dirpath . '/' . $filename; + + if (file_exists($filepath) && !$override) { - return $result; - } - - $new_file_path = PWD_DATA . '/' . $md5_filename; - - if (file_exists($new_file_path)) - { - $result['success'] = true; - $result['content'] = $new_file_path; + $result['content'] = 'Le fichier ' . $filepath . ' existe déjà.'; return $result; } - $success = move_uploaded_file($tmp_filename, $new_file_path); + $success = move_uploaded_file($upload_info['tmp_name'], $filepath); if (!$success) { - $result['content'] = 'Impossible d\'écrire le fichier sur le serveur.'; + $result['content'] = 'Impossible de délplacer le fichier vers ' . $filepath; return $result; } $result['success'] = true; - $result['content'] = $new_file_path; + $result['content'] = $filename; return $result; } + + + /** + * Generate a highly random uuid based on timestamp and strong cryptographic random + * + * @return string + */ + public static function random_uuid() + { + $bytes = random_bytes(16); + return time() . '-' . bin2hex($bytes); + } } diff --git a/controllers/publics/Api.php b/controllers/publics/Api.php index 02b09d7..6e0ab20 100644 --- a/controllers/publics/Api.php +++ b/controllers/publics/Api.php @@ -32,6 +32,7 @@ namespace controllers\publics; 'CANNOT_CREATE' => 8, 'SUSPENDED_USER' => 16, 'CANNOT_DELETE' => 32, + 'CANNOT_UPLOAD_FILE' => 64, ]; const ERROR_MESSAGES = [ @@ -41,6 +42,7 @@ namespace controllers\publics; 'CANNOT_CREATE' => 'Cannot create a new entry.', 'SUSPENDED_USER' => 'This user account is currently suspended.', 'CANNOT_DELETE' => 'Cannot delete this entry.', + 'CANNOT_UPLOAD_FILE' => 'Failed to upload or save an uploaded file : ', ]; private $internal_user; @@ -52,6 +54,8 @@ namespace controllers\publics; private $internal_group; private $internal_conditional_group; private $internal_adapter; + private $internal_media; + private $internal_setting; private $user; /** @@ -73,6 +77,8 @@ namespace controllers\publics; $this->internal_group = new \controllers\internals\Group($bdd); $this->internal_conditional_group = new \controllers\internals\ConditionalGroup($bdd); $this->internal_adapter = new \controllers\internals\Adapter(); + $this->internal_media = new \controllers\internals\Media($bdd); + $this->internal_setting = new \controllers\internals\Setting($bdd); //If no user, quit with error $this->user = false; @@ -93,6 +99,8 @@ namespace controllers\publics; exit(self::ERROR_CODES['INVALID_CREDENTIALS']); } + $this->user['settings'] = $this->internal_setting->gets_for_user($this->user['id']); + if (\models\User::STATUS_ACTIVE !== $this->user['status']) { $return = self::DEFAULT_RETURN; @@ -108,14 +116,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'] + * @param string $entry_type : Type of entries we want to list ['sended', 'received', 'scheduled', 'contact', 'group', 'conditional_group', 'phone', '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']; + $entry_types = ['sended', 'received', 'scheduled', 'contact', 'group', 'conditional_group', 'phone', 'media']; if (!\in_array($entry_type, $entry_types, true)) { @@ -179,6 +187,7 @@ namespace controllers\publics; * @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['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 * @param string $_POST['contacts'] : Array of ids of contacts to send message to * @param string $_POST['groups'] : Array of ids of groups to send message to @@ -192,16 +201,20 @@ namespace controllers\publics; $text = $_POST['text'] ?? false; $id_phone = empty($_POST['id_phone']) ? null : $_POST['id_phone']; $flash = (bool) ($_POST['flash'] ?? false); + $mms = (bool) ($_POST['mms'] ?? false); $numbers = $_POST['numbers'] ?? []; $contacts = $_POST['contacts'] ?? []; $groups = $_POST['groups'] ?? []; $conditional_groups = $_POST['conditional_groups'] ?? []; + $files = $_FILES ?? []; $numbers = \is_array($numbers) ? $numbers : [$numbers]; $contacts = \is_array($contacts) ? $contacts : [$contacts]; $groups = \is_array($groups) ? $groups : [$groups]; $conditional_groups = \is_array($conditional_groups) ? $conditional_groups : [$conditional_groups]; + $media_ids = []; + if (!$at) { $at = (new \DateTime())->format('Y-m-d H:i:s'); @@ -227,6 +240,17 @@ namespace controllers\publics; return $this->json($return); } + //TODO : Check if phone adapter support mms and if mms are enabled + if (($this->user['settings']['mms'] ?? false) && $mms) + { + $return = self::DEFAULT_RETURN; + $return['error'] = self::ERROR_CODES['INVALID_PARAMETER']; + $return['message'] = self::ERROR_MESSAGES['INVALID_PARAMETER'] . 'mms is set to true, but mms are disabled in settings.'; + $this->auto_http_code(false); + + return $this->json($return); + } + foreach ($numbers as $key => $number) { $number = \controllers\internals\Tool::parse_phone($number); @@ -251,7 +275,13 @@ namespace controllers\publics; return $this->json($return); } - if ($id_phone && !$this->internal_phone->get_for_user($this->user['id'], $id_phone)) + $phone = null; + if ($id_phone) + { + $phone = $this->internal_phone->get_for_user($this->user['id'], $id_phone); + } + + if ($id_phone && !$phone) { $return = self::DEFAULT_RETURN; $return['error'] = self::ERROR_CODES['INVALID_PARAMETER']; @@ -261,7 +291,74 @@ namespace controllers\publics; return $this->json($return); } - $scheduled_id = $this->internal_scheduled->create($this->user['id'], $at, $text, $id_phone, $flash, $numbers, $contacts, $groups, $conditional_groups); + if ($id_phone && $mms && !$this->internal_phone->support_mms($id_phone, $this->internal_phone::MMS_SENDING)) + { + $return = self::DEFAULT_RETURN; + $return['error'] = self::ERROR_CODES['INVALID_PARAMETER']; + $return['message'] = self::ERROR_MESSAGES['INVALID_PARAMETER'] . 'mms : You try to send a mms with a phone that does not support mms.'; + $this->auto_http_code(false); + + return $this->json($return); + } + + //if try to send mms and no available phone support mms, return error + if (!$id_phone && $mms) + { + $phones_supporting_mms = $this->internal_phone->gets_phone_supporting_mms_for_user($this->user['id'], $this->internal_phone::MMS_SENDING); + if (!count($phones_supporting_mms)) + { + $return = self::DEFAULT_RETURN; + $return['error'] = self::ERROR_CODES['INVALID_PARAMETER']; + $return['message'] = self::ERROR_MESSAGES['INVALID_PARAMETER'] . 'mms : You try to send a mms but you dont have any phone supporting mms. Please add at least one phone supporting mms before trying to send one.'; + $this->auto_http_code(false); + + return $this->json($return); + } + } + + foreach ($files as $file) + { + $user_media_path = PWD_DATA . '/medias/' . $this->user['id']; + + //Create user medias dir if not exists + if (!file_exists($user_media_path)) + { + if (!mkdir($user_media_path, fileperms(PWD_DATA), true)) + { + $return = self::DEFAULT_RETURN; + $return['error'] = self::ERROR_CODES['CANNOT_UPLOAD_FILE']; + $return['message'] = self::ERROR_MESSAGES['CANNOT_UPLOAD_FILE'] . ' : Because cannot create medias dir on server for the user.'; + $this->auto_http_code(false); + + return $this->json($return); + } + } + + $result = \controllers\internals\Tool::save_uploaded_file($file, $user_media_path); + if ($result['success'] !== true) + { + $return = self::DEFAULT_RETURN; + $return['error'] = self::ERROR_CODES['CANNOT_UPLOAD_FILE']; + $return['message'] = self::ERROR_MESSAGES['CANNOT_UPLOAD_FILE'] . $file['name'] . ' with error : ' . $result['content']; + $this->auto_http_code(false); + + return $this->json($return); + } + + $new_filepath = 'medias/' . $this->user['id'] . '/' . $result['content']; + $new_media_id = $this->internal_media->create($this->user['id'], $new_filepath); + if (!$new_media_id) + { + $return = self::DEFAULT_RETURN; + $return['error'] = self::ERROR_CODES['CANNOT_CREATE']; + $return['message'] = self::ERROR_MESSAGES['CANNOT_CREATE']; + $this->auto_http_code(false); + + return $this->json($return); + } + } + + $scheduled_id = $this->internal_scheduled->create($this->user['id'], $at, $text, $id_phone, $flash, $mms, $numbers, $contacts, $groups, $conditional_groups, $media_ids); if (!$scheduled_id) { $return = self::DEFAULT_RETURN; diff --git a/controllers/publics/Callback.php b/controllers/publics/Callback.php index c7d905d..1a4c951 100644 --- a/controllers/publics/Callback.php +++ b/controllers/publics/Callback.php @@ -25,6 +25,7 @@ use Monolog\Logger; private $internal_sended; private $internal_received; private $internal_adapter; + private $internal_media; public function __construct() { @@ -33,6 +34,7 @@ use Monolog\Logger; $this->internal_user = new \controllers\internals\User($bdd); $this->internal_sended = new \controllers\internals\Sended($bdd); $this->internal_received = new \controllers\internals\Received($bdd); + $this->internal_media = new \controllers\internals\Media($bdd); $this->internal_adapter = new \controllers\internals\Adapter(); //Logger @@ -177,8 +179,58 @@ use Monolog\Logger; } $sms = $response['sms']; + $mms = !empty($sms['mms']); + $medias = empty($sms['medias']) ? [] : $sms['medias']; + $media_ids = []; - $response = $this->internal_received->receive($this->user['id'], $id_phone, $sms['text'], $sms['origin'], $sms['at']); + //We create medias to link to the sms + if ($mms) + { + foreach ($medias as $media) + { + try + { + $media['mimetype'] = empty($media['mimetype']) ? mime_content_type($media['filepath']) : $media['mimetype']; + + $mimey = new \Mimey\MimeTypes; + $extension = empty($media['extension']) ? $mimey->getExtension($media['mimetype']) : $media['extension']; + + $new_filename = \controllers\internals\Tool::random_uuid() . '.' . $extension; + $new_filedir = PWD_DATA . '/medias/' . $this->user['id']; + $new_filerelpath = 'medias/' . $this->user['id'] . '/' . $new_filename; + $new_filepath = $new_filedir . '/' . $new_filename; + + //Create user dir if not exists + if (!file_exists($new_filedir)) + { + if (!mkdir($new_filedir, fileperms(PWD_DATA), true)) + { + throw new \Exception('Cannot create dir ' . $new_filedir . ' to copy media : ' . json_encode($media)); + } + } + + if (!rename($media['filepath'], $new_filepath)) + { + throw new \Exception('Cannot copy media : ' . json_encode($media) . ' to ' . $new_filepath); + } + + $new_media_id = $this->internal_media->create($this->user['id'], $new_filerelpath); + if (!$new_media_id) + { + throw new \Exception('Cannot save into db media : ' . json_encode($media)); + } + + $media_ids[] = $new_media_id; + } + catch (\Throwable $t) + { + $this->logger->error($t->getMessage()); + continue; + } + } + } + + $response = $this->internal_received->receive($this->user['id'], $id_phone, $sms['text'], $sms['origin'], $sms['at'], \models\Received::STATUS_UNREAD, $mms, $media_ids); if ($response['error']) { $this->logger->error('Failed receive message : ' . json_encode($sms) . ' with error : ' . $response['error_message']); diff --git a/controllers/publics/Scheduled.php b/controllers/publics/Scheduled.php index 8f270df..81ccd3b 100644 --- a/controllers/publics/Scheduled.php +++ b/controllers/publics/Scheduled.php @@ -250,6 +250,7 @@ namespace controllers\publics; $at = $_POST['at'] ?? false; $text = $_POST['text'] ?? false; $flash = (bool) ($_POST['flash'] ?? false); + $mms = $_FILES['media'] ?? false; $id_phone = empty($_POST['id_phone']) ? null : $_POST['id_phone']; $numbers = $_POST['numbers'] ?? []; $contacts = $_POST['contacts'] ?? []; @@ -292,7 +293,7 @@ namespace controllers\publics; return $this->redirect(\descartes\Router::url('Scheduled', 'add')); } - $scheduled_id = $this->internal_scheduled->create($id_user, $at, $text, $id_phone, $flash, $numbers, $contacts, $groups, $conditional_groups); + $scheduled_id = $this->internal_scheduled->create($id_user, $at, $text, $id_phone, $flash, $mms, $numbers, $contacts, $groups, $conditional_groups); if (!$scheduled_id) { \FlashMessage\FlashMessage::push('danger', 'Impossible de créer le Sms.'); @@ -300,7 +301,7 @@ namespace controllers\publics; return $this->redirect(\descartes\Router::url('Scheduled', 'add')); } - //If mms is enabled, try to process a media to link to the scheduled + //If mms is disabled or no media uploaded, do not process if (!($_SESSION['user']['settings']['mms'] ?? false) || !$media) { \FlashMessage\FlashMessage::push('success', 'Le Sms a bien été créé pour le ' . $at . '.'); diff --git a/daemons/Phone.php b/daemons/Phone.php index 83a4b01..69ef0f2 100644 --- a/daemons/Phone.php +++ b/daemons/Phone.php @@ -139,7 +139,7 @@ class Phone extends AbstractDaemon //Do message sending $this->logger->info('Try send message : ' . json_encode($message)); - $response = $internal_sended->send($this->adapter, $this->phone['id_user'], $this->phone['id'], $message['text'], $message['destination'], $message['flash']); + $response = $internal_sended->send($this->adapter, $this->phone['id_user'], $this->phone['id'], $message['text'], $message['destination'], $message['flash'], $message['mms'], $message['medias']); if ($response['error']) { $this->logger->error('Failed send message : ' . json_encode($message) . ' with error : ' . $response['error_message']); diff --git a/db/migrations/20210317214910_add_media_links_to_sms.php b/db/migrations/20210317214910_add_media_links_to_sms.php new file mode 100644 index 0000000..93e63a2 --- /dev/null +++ b/db/migrations/20210317214910_add_media_links_to_sms.php @@ -0,0 +1,87 @@ +table('media'); + + if ($table->hasColumn('id_scheduled')) + { + if ($table->hasForeignKey('id_scheduled')) + { + $table->dropForeignKey('id_scheduled'); + } + + $table->removeColumn('id_scheduled'); + $table->update(); + } + + if ($table->hasColumn('id_user')) + { + if ($table->hasForeignKey('id_user')) + { + $table->dropForeignKey('id_user'); + } + + $table->removeColumn('id_user'); + $table->update(); + } + + $table->addColumn('id_user', 'integer') + ->addForeignKey('id_user', 'user', 'id', ['delete' => 'CASCADE', 'update' => 'CASCADE']) + ->update(); + + //Add table to join scheduled and media + $table = $this->table('media_scheduled'); + $table->addColumn('id_media', 'integer') + ->addColumn('id_scheduled', 'integer') + ->addForeignKey('id_media', 'media', 'id', ['delete' => 'CASCADE', 'update' => 'CASCADE']) + ->addForeignKey('id_scheduled', 'scheduled', 'id', ['delete' => 'CASCADE', 'update' => 'CASCADE']) + ->create(); + + //Add table to join sended and media + $table = $this->table('media_sended'); + $table->addColumn('id_media', 'integer') + ->addColumn('id_sended', 'integer') + ->addForeignKey('id_media', 'media', 'id', ['delete' => 'CASCADE', 'update' => 'CASCADE']) + ->addForeignKey('id_sended', 'sended', 'id', ['delete' => 'CASCADE', 'update' => 'CASCADE']) + ->create(); + + //Add table to join received and media + $table = $this->table('media_received'); + $table->addColumn('id_media', 'integer') + ->addColumn('id_received', 'integer') + ->addForeignKey('id_media', 'media', 'id', ['delete' => 'CASCADE', 'update' => 'CASCADE']) + ->addForeignKey('id_received', 'received', 'id', ['delete' => 'CASCADE', 'update' => 'CASCADE']) + ->create(); + } +} diff --git a/db/migrations/20210318142759_add_is_mms.php b/db/migrations/20210318142759_add_is_mms.php new file mode 100644 index 0000000..1e7ec14 --- /dev/null +++ b/db/migrations/20210318142759_add_is_mms.php @@ -0,0 +1,46 @@ +table('scheduled'); + $table->addColumn('mms', 'boolean', ['default' => 0, 'null' => false]) + ->update(); + + $table = $this->table('sended'); + $table->addColumn('mms', 'boolean', ['default' => 0, 'null' => false]) + ->update(); + + $table = $this->table('received'); + $table->addColumn('mms', 'boolean', ['default' => 0, 'null' => false]) + ->update(); + } +} diff --git a/models/Media.php b/models/Media.php index 7b7cd83..12da786 100644 --- a/models/Media.php +++ b/models/Media.php @@ -16,53 +16,6 @@ namespace models; */ class Media extends StandardModel { - /** - * Return an entry by his id for a user. - * - * @param int $id_user : user id - * @param int $id : entry id - * - * @return array - */ - public function get_for_user(int $id_user, int $id) - { - $query = ' - SELECT * FROM `' . $this->get_table_name() . '` - WHERE id_scheduled IN (SELECT id FROM scheduled WHERE id_user = :id_user) - AND id = :id - '; - - $params = [ - 'id' => $id, - 'id_user' => $id_user, - ]; - - $receiveds = $this->_run_query($query, $params); - - return $receiveds[0] ?? []; - } - - /** - * Return all entries for a user. - * - * @param int $id_user : user id - * - * @return array - */ - public function gets_for_user(int $id_user) - { - $query = ' - SELECT * FROM `' . $this->get_table_name() . '` - WHERE id_scheduled IN (SELECT id FROM scheduled WHERE id_user = :id_user) - '; - - $params = [ - 'id_user' => $id_user, - ]; - - $receiveds = $this->_run_query($query, $params); - } - /** * Return a media for a user and a scheduled. * @@ -71,12 +24,15 @@ namespace models; * * @return array */ - public function get_for_scheduled_and_user(int $id_user, int $id_scheduled) + public function gets_for_scheduled_and_user(int $id_user, int $id_scheduled) { $query = ' - SELECT * FROM `' . $this->get_table_name() . '` - WHERE id_scheduled IN (SELECT id FROM scheduled WHERE id_user = :id_user) - AND id_scheduled = :id_scheduled + SELECT m.id as id, m.user_id as user_id, m.path as path + FROM `' . $this->get_table_name() . '` as m + INNER JOIN media_scheduled as ms + ON m.id = ms.id_media + WHERE m.id_user = :id_user + AND ms.id_scheduled = :id_scheduled '; $params = [ @@ -84,159 +40,218 @@ namespace models; 'id_scheduled' => $id_scheduled, ]; - $receiveds = $this->_run_query($query, $params); - if (!$receiveds) - { - return false; - } - - return $receiveds[0]; - } - - /** - * Return a list of media for a user. - * - * @param int $id_user : User id - * @param int $limit : Max results to return - * @param int $offset : Number of results to ignore - */ - public function list_for_user($id_user, $limit, $offset) - { - $limit = (int) $limit; - $offset = (int) $offset; - - $query = ' - SELECT * FROM media - WHERE id_scheduled IN (SELECT id FROM scheduled WHERE id_user = :id_user) - LIMIT ' . $limit . ' OFFSET ' . $offset; - - $params = [ - 'id_user' => $id_user, - ]; - return $this->_run_query($query, $params); } - + /** - * Return a list of medias in a group of ids and for a user. + * Return a media for a user and a sended. * - * @param int $id_user : user id - * @param array $ids : ids of medias to find + * @param int $id_user : user id + * @param int $id_sended : sended id * * @return array */ - public function gets_in_for_user(int $id_user, $ids) + public function gets_for_sended_and_user(int $id_user, int $id_sended) { $query = ' - SELECT * FROM media - WHERE id_scheduled IN (SELECT id FROM scheduled WHERE id_user = :id_user) - AND id '; - - //On génère la clause IN et les paramètres adaptés depuis le tableau des id - $generated_in = $this->_generate_in_from_array($ids); - $query .= $generated_in['QUERY']; - $params = $generated_in['PARAMS']; - $params['id_user'] = $id_user; - - return $this->_run_query($query, $params); - } - - /** - * Delete a entry by his id for a user. - * - * @param int $id_user : User id - * @param int $id : Entry id - * - * @return int : Number of removed rows - */ - public function delete_for_user(int $id_user, int $id) - { - $query = ' - DELETE FROM media - WHERE id = :id - AND id_scheduled IN (SELECT id FROM scheduled WHERE id_user = :id_user) - '; - - $params = ['id_user' => $id_user, 'id' => $id]; - - return $this->_run_query($query, $params, self::ROWCOUNT); - } - - /** - * Delete a entry by his id for a user. - * - * @param int $id_user : User id - * @param int $id_scheduled : Scheduled id - * - * @return int : Number of removed rows - */ - public function delete_for_scheduled_and_user(int $id_user, int $id_scheduled) - { - $query = ' - DELETE FROM media - WHERE id_scheduled = :id_scheduled - AND id_scheduled IN (SELECT id FROM scheduled WHERE id_user = :id_user) - '; - - $params = ['id_user' => $id_user, 'id_scheduled' => $id_scheduled]; - - return $this->_run_query($query, $params, self::ROWCOUNT); - } - - /** - * Update a media sms for a user. - * - * @param int $id_user : User id - * @param int $id : Entry id - * @param array $data : data to update - * - * @return int : number of modified rows - */ - public function update_for_user(int $id_user, int $id, array $data) - { - $params = []; - $sets = []; - - foreach ($data as $label => $value) - { - $label = preg_replace('#[^a-zA-Z0-9_]#', '', $label); - $params['set_' . $label] = $value; - $sets[] = '`' . $label . '` = :set_' . $label . ' '; - } - - $query = ' - UPDATE `media` - SET ' . implode(', ', $sets) . ' - WHERE id = :id - AND id_scheduled IN (SELECT id FROM scheduled WHERE id_user = :id_user) - '; - - $params['id'] = $id; - $params['id_user'] = $id_user; - - return $this->_run_query($query, $params, self::ROWCOUNT); - } - - /** - * Count number of media sms for user. - * - * @param int $id_user : user id - * - * @return int : Number of media SMS for user - */ - public function count_for_user($id_user) - { - $query = ' - SELECT COUNT(id) as nb - FROM media - WHERE id_scheduled IN (SELECT id FROM scheduled WHERE id_user = :id_user) + SELECT m.id as id, m.user_id as user_id, m.path as path + FROM `' . $this->get_table_name() . '` as m + INNER JOIN media_sended as ms + ON m.id = ms.id_media + WHERE m.id_user = :id_user + AND ms.id_sended = :id_sended '; $params = [ 'id_user' => $id_user, + 'id_sended' => $id_sended, ]; - return $this->_run_query($query, $params)[0]['nb'] ?? 0; + return $this->_run_query($query, $params); + } + + /** + * Return a media for a user and a received. + * + * @param int $id_user : user id + * @param int $id_received : received id + * + * @return array + */ + public function gets_for_received_and_user(int $id_user, int $id_received) + { + $query = ' + SELECT m.id as id, m.user_id as user_id, m.path as path + FROM `' . $this->get_table_name() . '` as m + INNER JOIN media_received as mr + ON m.id = mr.id_media + WHERE m.id_user = :id_user + AND mr.id_received = :id_received + '; + + $params = [ + 'id_user' => $id_user, + 'id_received' => $id_received, + ]; + + return $this->_run_query($query, $params); + } + + /** + * Link a media to a scheduled + * + * @param int $id_media : Media id + * @param int $id_scheduled : Scheduled id + * + * @return bool | int + */ + public function insert_media_scheduled (int $id_media, int $id_scheduled) + { + $entry = [ + 'id_media' => $id_media, + 'id_scheduled' => $id_scheduled, + ]; + + return $this->_insert('media_scheduled', $entry) ? $this->_last_id() : false; + } + + /** + * Link a media to a received + * + * @param int $id_media : Media id + * @param int $id_received : Scheduled id + * + * @return bool | int + */ + public function insert_media_received (int $id_media, int $id_received) + { + $entry = [ + 'id_media' => $id_media, + 'id_received' => $id_received, + ]; + + return $this->_insert('media_received', $entry) ? $this->_last_id() : false; + } + + /** + * Link a media to a sended + * + * @param int $id_media : Media id + * @param int $id_sended : Scheduled id + * + * @return bool | int + */ + public function insert_media_sended (int $id_media, int $id_sended) + { + $entry = [ + 'id_media' => $id_media, + 'id_sended' => $id_sended, + ]; + + return $this->_insert('media_sended', $entry) ? $this->_last_id() : false; + } + + /** + * Unlink a media of a scheduled + * + * @param int $id_media : Media id + * @param int $id_scheduled : Scheduled id + * + * @return bool | int + */ + public function delete_media_scheduled (int $id_media, int $id_scheduled) + { + $where = [ + 'id_media' => $id_media, + 'id_scheduled' => $id_scheduled, + ]; + + return $this->_delete('media_scheduled', $where); + } + + /** + * Unlink a media of a received + * + * @param int $id_media : Media id + * @param int $id_received : Scheduled id + * + * @return bool | int + */ + public function delete_media_received (int $id_media, int $id_received) + { + $where = [ + 'id_media' => $id_media, + 'id_received' => $id_received, + ]; + + return $this->_delete('media_received', $where); + } + + /** + * Unlink a media of a sended + * + * @param int $id_media : Media id + * @param int $id_sended : Scheduled id + * + * @return bool | int + */ + public function delete_media_sended (int $id_media, int $id_sended) + { + $where = [ + 'id_media' => $id_media, + 'id_sended' => $id_sended, + ]; + + return $this->_delete('media_sended', $where); + } + + + /** + * Unlink all medias of a scheduled + * + * @param int $id_scheduled : Scheduled id + * + * @return bool | int + */ + public function delete_all_for_scheduled (int $id_scheduled) + { + $where = [ + 'id_scheduled' => $id_scheduled, + ]; + + return $this->_delete('media_scheduled', $where); + } + + /** + * Unlink all medias of a received + * + * @param int $id_received : Scheduled id + * + * @return bool | int + */ + public function delete_all_for_received (int $id_received) + { + $where = [ + 'id_received' => $id_received, + ]; + + return $this->_delete('media_received', $where); + } + + /** + * Unlink all medias of a sended + * + * @param int $id_sended : Scheduled id + * + * @return bool | int + */ + public function delete_all_for_sended (int $id_sended) + { + $where = [ + 'id_sended' => $id_sended, + ]; + + return $this->_delete('media_sended', $where); } /**