From ff6b3e79dfcdf3d0db141873cdee2e5e2428f965 Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Fri, 19 Mar 2021 02:45:12 +0100
Subject: [PATCH 01/40] 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);
}
/**
From 136b3f76ce2d60e56c3624ca65d5f13d907cdf56 Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Fri, 19 Mar 2021 21:59:49 +0100
Subject: [PATCH 02/40] working api scheduled + working daemons for phone and
sender + working adapters
---
controllers/internals/Scheduled.php | 2 +-
daemons/Sender.php | 2 ++
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/controllers/internals/Scheduled.php b/controllers/internals/Scheduled.php
index 25d1a35..977478f 100644
--- a/controllers/internals/Scheduled.php
+++ b/controllers/internals/Scheduled.php
@@ -253,7 +253,7 @@ namespace controllers\internals;
/**
* Get all messages to send and the number to use to send theme.
*
- * @return array : [['id_scheduled', 'text', 'id_phone', 'destination', 'flash'], ...]
+ * @return array : [['id_scheduled', 'text', 'id_phone', 'destination', 'flash', 'mms', 'medias'], ...]
*/
public function get_smss_to_send()
{
diff --git a/daemons/Sender.php b/daemons/Sender.php
index 202349f..a5504bb 100644
--- a/daemons/Sender.php
+++ b/daemons/Sender.php
@@ -76,6 +76,8 @@ class Sender extends AbstractDaemon
'id_phone' => $sms['id_phone'],
'destination' => $sms['destination'],
'flash' => $sms['flash'],
+ 'mms' => $sms['mms'],
+ 'medias' => $sms['medias'] ?? [],
];
msg_send($this->queues[$queue_id], QUEUE_TYPE_SEND_MSG, $msg);
From 0dac72cf545310725aba4f5e6bbe5baeb1cd08d6 Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Sun, 21 Mar 2021 17:08:05 +0100
Subject: [PATCH 03/40] Update api for scheduled + update get medias for +
update htaccess to make medias accessibles
---
.gitignore | 1 +
.htaccess | 1 +
controllers/internals/Media.php | 21 +++---
controllers/internals/Scheduled.php | 2 +-
controllers/publics/Api.php | 103 ++++++++++++++++++++--------
models/Media.php | 33 ++++-----
6 files changed, 99 insertions(+), 62 deletions(-)
diff --git a/.gitignore b/.gitignore
index 2b007f4..7438c69 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,5 +11,6 @@ phinx.*
data/test_write_sms.json
data/test_read_sms.json
+data/medias/
!*.dist
diff --git a/.htaccess b/.htaccess
index e0be5de..ad6df7b 100644
--- a/.htaccess
+++ b/.htaccess
@@ -1,4 +1,5 @@
RewriteEngine on
RewriteRule ^assets - [L]
RewriteRule ^.well-known - [L]
+RewriteRule ^data/medias - [L]
RewriteRule . index.php
diff --git a/controllers/internals/Media.php b/controllers/internals/Media.php
index 4dd6580..e7f9bae 100644
--- a/controllers/internals/Media.php
+++ b/controllers/internals/Media.php
@@ -23,7 +23,7 @@ namespace controllers\internals;
*
* @return mixed bool|int : false on error, new media id else
*/
- public function create(int $id_user, string $path): bool
+ public function create(int $id_user, string $path)
{
$data = [
'path' => $path,
@@ -41,7 +41,7 @@ namespace controllers\internals;
*
* @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)
+ public function link_to(int $id_media, string $resource_type, int $resource_id)
{
switch ($resource_type)
{
@@ -160,42 +160,39 @@ namespace controllers\internals;
}
/**
- * Find medias for a scheduled and a user.
+ * Find medias for a scheduled.
*
- * @param int $id_user : User id
* @param int $id_scheduled : Scheduled id to fin medias for
*
* @return mixed : Medias || false
*/
- public function gets_for_scheduled_and_user(int $id_user, int $id_scheduled)
+ public function gets_for_scheduled(int $id_scheduled)
{
- return $this->get_model()->gets_for_scheduled_and_user($id_user, $id_scheduled);
+ return $this->get_model()->gets_for_scheduled($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)
+ public function gets_for_sended(int $id_sended)
{
- return $this->get_model()->gets_for_sended_and_user($id_user, $id_sended);
+ return $this->get_model()->gets_for_sended($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)
+ public function gets_for_received(int $id_received)
{
- return $this->get_model()->gets_for_received_and_user($id_user, $id_received);
+ return $this->get_model()->gets_for_received($id_received);
}
/**
diff --git a/controllers/internals/Scheduled.php b/controllers/internals/Scheduled.php
index 977478f..f21cced 100644
--- a/controllers/internals/Scheduled.php
+++ b/controllers/internals/Scheduled.php
@@ -297,7 +297,7 @@ namespace controllers\internals;
if ($scheduled['mms'])
{
$internal_media = new Media($this->bdd);
- $scheduled['medias'] = $internal_media->gets_for_scheduled_and_user($scheduled['id_user'], $scheduled['id']);
+ $scheduled['medias'] = $internal_media->gets_for_scheduled($scheduled['id']);
}
$phone_to_use = null;
diff --git a/controllers/publics/Api.php b/controllers/publics/Api.php
index 6e0ab20..7943bce 100644
--- a/controllers/publics/Api.php
+++ b/controllers/publics/Api.php
@@ -151,6 +151,21 @@ namespace controllers\publics;
$entries[$key]['contacts'] = $this->internal_scheduled->get_contacts($entry['id']);
$entries[$key]['groups'] = $this->internal_scheduled->get_groups($entry['id']);
$entries[$key]['conditional_groups'] = $this->internal_scheduled->get_conditional_groups($entry['id']);
+ $entries[$key]['medias'] = $this->internal_media->gets_for_scheduled($entry['id']);
+ }
+ }
+ elseif ('received' === $entry_type)
+ {
+ foreach ($entries as $key => $entry)
+ {
+ $entries[$key]['medias'] = $this->internal_media->gets_for_received($entry['id']);
+ }
+ }
+ elseif ('sended' === $entry_type)
+ {
+ foreach ($entries as $key => $entry)
+ {
+ $entries[$key]['medias'] = $this->internal_media->gets_for_sended($entry['id']);
}
}
//Special case for group we must add contact because its a join
@@ -206,13 +221,40 @@ namespace controllers\publics;
$contacts = $_POST['contacts'] ?? [];
$groups = $_POST['groups'] ?? [];
$conditional_groups = $_POST['conditional_groups'] ?? [];
- $files = $_FILES ?? [];
+ $files = $_FILES['medias'] ?? false;
$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];
+ //Iterate over files to re-create individual $_FILES array
+ $files_arrays = [];
+
+ if ($files === false)
+ {
+ $files_arrays = [];
+ }
+ elseif (!is_array($files['name'])) //Only one file uploaded
+ {
+ $files_arrays[] = $files;
+ }
+ else //multiple files
+ {
+ foreach ($files as $property_name => $files_values)
+ {
+ foreach ($files_values as $file_key => $property_value)
+ {
+ if (!isset($files_arrays[$file_key]))
+ {
+ $files_arrays[$file_key] = [];
+ }
+
+ $files_arrays[$file_key][$property_name] = $property_value;
+ }
+ }
+ }
+
$media_ids = [];
if (!$at)
@@ -316,45 +358,50 @@ namespace controllers\publics;
}
}
- foreach ($files as $file)
+ if ($mms)
{
- $user_media_path = PWD_DATA . '/medias/' . $this->user['id'];
-
- //Create user medias dir if not exists
- if (!file_exists($user_media_path))
+ foreach ($files_arrays as $file)
{
- if (!mkdir($user_media_path, fileperms(PWD_DATA), true))
+ $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'] . ' : Because cannot create medias dir on server for the user.';
+ $return['message'] = self::ERROR_MESSAGES['CANNOT_UPLOAD_FILE'] . $file['name'] . ' with error : ' . $result['content'];
$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);
+ $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);
- }
+ 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);
+ $media_ids[] = $new_media_id;
}
}
diff --git a/models/Media.php b/models/Media.php
index 12da786..827bcc1 100644
--- a/models/Media.php
+++ b/models/Media.php
@@ -17,26 +17,23 @@ namespace models;
class Media extends StandardModel
{
/**
- * Return a media for a user and a scheduled.
+ * Return all medias for a scheduled.
*
- * @param int $id_user : user id
* @param int $id_scheduled : scheduled id
*
* @return array
*/
- public function gets_for_scheduled_and_user(int $id_user, int $id_scheduled)
+ public function gets_for_scheduled(int $id_scheduled)
{
$query = '
- SELECT m.id as id, m.user_id as user_id, m.path as path
+ SELECT m.id as id, m.id_user as id_user, 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
+ WHERE ms.id_scheduled = :id_scheduled
';
$params = [
- 'id_user' => $id_user,
'id_scheduled' => $id_scheduled,
];
@@ -44,26 +41,23 @@ namespace models;
}
/**
- * Return a media for a user and a sended.
+ * Return all medias for a sended.
*
- * @param int $id_user : user id
* @param int $id_sended : sended id
*
* @return array
*/
- public function gets_for_sended_and_user(int $id_user, int $id_sended)
+ public function gets_for_sended(int $id_sended)
{
$query = '
- SELECT m.id as id, m.user_id as user_id, m.path as path
+ SELECT m.id as id, m.id_user as id_user, 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
+ WHERE ms.id_sended = :id_sended
';
$params = [
- 'id_user' => $id_user,
'id_sended' => $id_sended,
];
@@ -71,26 +65,23 @@ namespace models;
}
/**
- * Return a media for a user and a received.
+ * Return all medias for 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)
+ public function gets_for_received(int $id_received)
{
$query = '
- SELECT m.id as id, m.user_id as user_id, m.path as path
+ SELECT m.id as id, m.id_user as id_user, 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
+ WHERE mr.id_received = :id_received
';
$params = [
- 'id_user' => $id_user,
'id_received' => $id_received,
];
From 70d01be0415bf8145bc2d976a16670f6f54bb6c1 Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Sun, 21 Mar 2021 17:14:52 +0100
Subject: [PATCH 04/40] add HTTP_PWD_DATA
---
controllers/internals/Sended.php | 4 ++--
env.php.dist | 1 +
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/controllers/internals/Sended.php b/controllers/internals/Sended.php
index 132f082..dfac195 100644
--- a/controllers/internals/Sended.php
+++ b/controllers/internals/Sended.php
@@ -211,8 +211,8 @@ namespace controllers\internals;
foreach ($medias as $media)
{
$media_uris[] = [
- 'http_url' => HTTP_PWD . '/data/' . $media['path'],
- 'local_uri' => PWD_DATA . '/data/' . $media['path'],
+ 'http_url' => HTTP_PWD_DATA . '/' . $media['path'],
+ 'local_uri' => PWD_DATA . '/' . $media['path'],
];
}
diff --git a/env.php.dist b/env.php.dist
index 3c18bd8..dd77747 100644
--- a/env.php.dist
+++ b/env.php.dist
@@ -16,6 +16,7 @@
'HTTP_PWD_SOUND' => HTTP_PWD_ASSETS . '/sounds',
'PWD_ADAPTERS' => PWD . '/adapters',
'PWD_DATA' => PWD . '/data',
+ 'HTTP_PWD_DATA' => HTTP_PWD . '/data',
'PWD_LOGS' => '/var/log/raspisms',
'PWD_PID' => '/var/run/raspisms',
'APP_SECRET' => '%APP_SECRET%',
From f330312b55543f29cb8ea2a9a6e5b3affb48984c Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Sun, 21 Mar 2021 18:29:23 +0100
Subject: [PATCH 05/40] Add medias link to mms into received + sended +
scheduled + discussion
---
controllers/publics/Discussion.php | 40 +++++++++++++++++++++++++++++-
controllers/publics/Received.php | 10 ++++++++
controllers/publics/Scheduled.php | 7 ++++++
controllers/publics/Sended.php | 6 +++++
templates/discussion/show.php | 13 ++++++++--
templates/received/list.php | 20 ++++++++++++---
templates/scheduled/list.php | 16 +++++++++++-
templates/sended/list.php | 20 ++++++++++++---
8 files changed, 122 insertions(+), 10 deletions(-)
diff --git a/controllers/publics/Discussion.php b/controllers/publics/Discussion.php
index 7c7b79a..da30f5d 100644
--- a/controllers/publics/Discussion.php
+++ b/controllers/publics/Discussion.php
@@ -21,6 +21,7 @@ namespace controllers\publics;
private $internal_received;
private $internal_contact;
private $internal_phone;
+ private $internal_media;
/**
* Cette fonction est appelée avant toute les autres :
@@ -37,6 +38,7 @@ namespace controllers\publics;
$this->internal_received = new \controllers\internals\Received($bdd);
$this->internal_contact = new \controllers\internals\Contact($bdd);
$this->internal_phone = new \controllers\internals\Phone($bdd);
+ $this->internal_media = new \controllers\internals\Media($bdd);
\controllers\internals\Tool::verifyconnect();
}
@@ -112,12 +114,26 @@ namespace controllers\publics;
foreach ($sendeds as $sended)
{
- $messages[] = [
+ $medias = [];
+ if ($sended['mms'])
+ {
+ $medias = $this->internal_media->gets_for_sended($sended['id']);
+ foreach ($medias as &$media)
+ {
+ $media = HTTP_PWD_DATA . '/' . $media['path'];
+ }
+ }
+
+ $message = [
'date' => htmlspecialchars($sended['at']),
'text' => htmlspecialchars($sended['text']),
'type' => 'sended',
+ 'medias' => $medias,
'status' => $sended['status'],
];
+
+
+ $messages[] = $message;
}
foreach ($receiveds as $received)
@@ -126,21 +142,43 @@ namespace controllers\publics;
{
$this->internal_received->mark_as_read_for_user($id_user, $received['id']);
}
+
+ $medias = [];
+ if ($sended['mms'])
+ {
+ $medias = $this->internal_media->gets_for_received($received['id']);
+ foreach ($medias as &$media)
+ {
+ $media = HTTP_PWD_DATA . '/' . $media['path'];
+ }
+ }
$messages[] = [
'date' => htmlspecialchars($received['at']),
'text' => htmlspecialchars($received['text']),
'type' => 'received',
'md5' => md5($received['at'] . $received['text']),
+ 'medias' => $medias,
];
}
foreach ($scheduleds as $scheduled)
{
+ $medias = [];
+ if ($sended['mms'])
+ {
+ $medias = $this->internal_media->gets_for_scheduled($scheduled['id']);
+ foreach ($medias as &$media)
+ {
+ $media = HTTP_PWD_DATA . '/' . $media['path'];
+ }
+ }
+
$messages[] = [
'date' => htmlspecialchars($scheduled['at']),
'text' => htmlspecialchars($scheduled['text']),
'type' => 'inprogress',
+ 'medias' => $medias,
];
}
diff --git a/controllers/publics/Received.php b/controllers/publics/Received.php
index e5ba67e..d6ab5a4 100644
--- a/controllers/publics/Received.php
+++ b/controllers/publics/Received.php
@@ -19,6 +19,7 @@ namespace controllers\publics;
private $internal_received;
private $internal_contact;
private $internal_phone;
+ private $internal_media;
/**
* Cette fonction est appelée avant toute les autres :
@@ -32,6 +33,7 @@ namespace controllers\publics;
$this->internal_received = new \controllers\internals\Received($bdd);
$this->internal_contact = new \controllers\internals\Contact($bdd);
$this->internal_phone = new \controllers\internals\Phone($bdd);
+ $this->internal_media = new \controllers\internals\Media($bdd);
\controllers\internals\Tool::verifyconnect();
}
@@ -53,6 +55,10 @@ namespace controllers\publics;
foreach ($entities as &$entity)
{
$entity['origin_formatted'] = \controllers\internals\Tool::phone_link($entity['origin']);
+ if ($entity['mms'])
+ {
+ $entity['medias'] = $this->internal_media->gets_for_received($entity['id']);
+ }
}
header('Content-Type: application/json');
@@ -76,6 +82,10 @@ namespace controllers\publics;
foreach ($entities as &$entity)
{
$entity['origin_formatted'] = \controllers\internals\Tool::phone_link($entity['origin']);
+ if ($entity['mms'])
+ {
+ $entity['medias'] = $this->internal_media->gets_for_received($entity['id']);
+ }
}
header('Content-Type: application/json');
diff --git a/controllers/publics/Scheduled.php b/controllers/publics/Scheduled.php
index 81ccd3b..2e4d8a7 100644
--- a/controllers/publics/Scheduled.php
+++ b/controllers/publics/Scheduled.php
@@ -56,6 +56,13 @@ namespace controllers\publics;
public function list_json()
{
$entities = $this->internal_scheduled->list_for_user($_SESSION['user']['id']);
+ foreach ($entities as &$entity)
+ {
+ if ($entity['mms'])
+ {
+ $entity['medias'] = $this->internal_media->gets_for_scheduled($entity['id']);
+ }
+ }
header('Content-Type: application/json');
echo json_encode(['data' => $entities]);
diff --git a/controllers/publics/Sended.php b/controllers/publics/Sended.php
index fa0a67c..8227e38 100644
--- a/controllers/publics/Sended.php
+++ b/controllers/publics/Sended.php
@@ -19,6 +19,7 @@ namespace controllers\publics;
private $internal_sended;
private $internal_phone;
private $internal_contact;
+ private $internal_media;
/**
* Cette fonction est appelée avant toute les autres :
@@ -32,6 +33,7 @@ namespace controllers\publics;
$this->internal_sended = new \controllers\internals\Sended($bdd);
$this->internal_phone = new \controllers\internals\Phone($bdd);
$this->internal_contact = new \controllers\internals\Contact($bdd);
+ $this->internal_media = new \controllers\internals\Media($bdd);
\controllers\internals\Tool::verifyconnect();
}
@@ -55,6 +57,10 @@ namespace controllers\publics;
foreach ($entities as &$entity)
{
$entity['destination_formatted'] = \controllers\internals\Tool::phone_link($entity['destination']);
+ if ($entity['mms'])
+ {
+ $entity['medias'] = $this->internal_media->gets_for_sended($entity['id']);
+ }
}
header('Content-Type: application/json');
diff --git a/templates/discussion/show.php b/templates/discussion/show.php
index bb6a357..835761a 100644
--- a/templates/discussion/show.php
+++ b/templates/discussion/show.php
@@ -76,7 +76,7 @@
//On ajoute la detection de lien dans le texte du message
message.text = Autolinker.link(message.text, {newWindow:true});
-
+
switch (message.type)
{
@@ -85,7 +85,10 @@
'
' +
'
' +
'
' + message.text + '
' +
- '
' + message.date + '
' +
+ '
' +
+ '
' + message.date + '
' +
'
' +
'
';
@@ -101,6 +104,9 @@
'' +
'
' +
'
' + message.text + '
' +
+ '
' +
'
' + message.date + ' ' + (message.status == 'delivered' ? ' ' : (message.status == 'failed' ? ' ' : ' ' )) + '
' +
'
' +
'
';
@@ -111,6 +117,9 @@
'' +
'
' +
'
' + message.text + '
' +
+ '
' +
'
' + message.date + '
' +
'
' +
'';
diff --git a/templates/received/list.php b/templates/received/list.php
index 5611fb0..763a28e 100644
--- a/templates/received/list.php
+++ b/templates/received/list.php
@@ -45,8 +45,8 @@
- De
- À
+ Expéditeur
+ Destinataire
Message
Date
Status
@@ -104,7 +104,21 @@ jQuery(document).ready(function ()
},
},
{data: 'phone_name', render: jQuery.fn.dataTable.render.text()},
- {data: 'text', render: jQuery.fn.dataTable.render.text()},
+ {
+ data: 'text',
+ render: function (data, type, row, meta) {
+ if (row.mms == 1) {
+ var medias = [];
+ for (i = 0; i < row.medias.length; i++) {
+ medias.push('Fichier ' + (i + 1) + ' ');
+ }
+ html = data + ' ' + medias.join(' - ');
+ return html;
+ }
+
+ return data;
+ },
+ },
{data: 'at', render: jQuery.fn.dataTable.render.text()},
{
data: 'status',
diff --git a/templates/scheduled/list.php b/templates/scheduled/list.php
index cfeda14..ecfd708 100644
--- a/templates/scheduled/list.php
+++ b/templates/scheduled/list.php
@@ -87,7 +87,21 @@ jQuery(document).ready(function ()
},
"columns" : [
{data: 'at', render: jQuery.fn.dataTable.render.text()},
- {data: 'text', render: jQuery.fn.dataTable.render.text()},
+ {
+ data: 'text',
+ render: function (data, type, row, meta) {
+ if (row.mms == 1) {
+ var medias = [];
+ for (i = 0; i < row.medias.length; i++) {
+ medias.push('Fichier ' + (i + 1) + ' ');
+ }
+ html = data + ' ' + medias.join(' - ');
+ return html;
+ }
+
+ return data;
+ },
+ },
{
data: 'id',
render: function (data, type, row, meta) {
diff --git a/templates/sended/list.php b/templates/sended/list.php
index 726cb2b..d8c6289 100644
--- a/templates/sended/list.php
+++ b/templates/sended/list.php
@@ -39,8 +39,8 @@
- De
- À
+ Expéditeur
+ Destinataire
Message
Date
Statut
@@ -94,7 +94,21 @@ jQuery(document).ready(function ()
return row.destination_formatted;
},
},
- {data: 'text', render: jQuery.fn.dataTable.render.text()},
+ {
+ data: 'text',
+ render: function (data, type, row, meta) {
+ if (row.mms == 1) {
+ var medias = [];
+ for (i = 0; i < row.medias.length; i++) {
+ medias.push('Fichier ' + (i + 1) + ' ');
+ }
+ html = data + ' ' + medias.join(' - ');
+ return html;
+ }
+
+ return data;
+ },
+ },
{data: 'at', render: jQuery.fn.dataTable.render.text()},
{
data: 'status',
From 3fff7e0abd7ed3aa33d30c2d48a22f296e909e91 Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Mon, 22 Mar 2021 01:29:06 +0100
Subject: [PATCH 06/40] add medias to scheduled creation
---
controllers/internals/Media.php | 32 ++++++-
controllers/publics/Api.php | 32 +------
controllers/publics/Scheduled.php | 145 +++++++++++++++++++++---------
templates/scheduled/add.php | 10 ++-
templates/scheduled/edit.php | 25 +++---
5 files changed, 157 insertions(+), 87 deletions(-)
diff --git a/controllers/internals/Media.php b/controllers/internals/Media.php
index e7f9bae..2f11b76 100644
--- a/controllers/internals/Media.php
+++ b/controllers/internals/Media.php
@@ -33,6 +33,36 @@ namespace controllers\internals;
return $this->get_model()->insert($data);
}
+ /**
+ * Upload and create a media
+ *
+ * @param int $id_user : Id of the user
+ * @param array $file : array representing uploaded file, extracted from $_FILES['yourfile']
+ * @return mixed bool | int : False on error, or new media id on success
+ */
+ public function upload_and_create_for_user(int $id_user, array $file)
+ {
+ $user_media_path = PWD_DATA . '/medias/' . $id_user;
+
+ //Create user medias dir if not exists
+ if (!file_exists($user_media_path))
+ {
+ if (!mkdir($user_media_path, fileperms(PWD_DATA), true))
+ {
+ return false;
+ }
+ }
+
+ $upload_result = \controllers\internals\Tool::save_uploaded_file($file, $user_media_path);
+ if ($upload_result['success'] !== true)
+ {
+ return false;
+ }
+
+ $new_filepath = 'medias/' . $id_user . '/' . $upload_result['content'];
+ return $this->create($id_user, $new_filepath);
+ }
+
/**
* Link a media to a scheduled, a received or a sended message
* @param int $id_media : Id of the media
@@ -99,7 +129,7 @@ namespace controllers\internals;
*
* @return mixed bool : false on error, true on success
*/
- public function unlink_all_of(int $resource_type, int $resource_id)
+ public function unlink_all_of(string $resource_type, int $resource_id)
{
switch ($resource_type)
{
diff --git a/controllers/publics/Api.php b/controllers/publics/Api.php
index 7943bce..a726345 100644
--- a/controllers/publics/Api.php
+++ b/controllers/publics/Api.php
@@ -362,40 +362,12 @@ namespace controllers\publics;
{
foreach ($files_arrays 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);
+ $new_media_id = $this->internal_media->upload_and_create_for_user($this->user['id'], $file);
if (!$new_media_id)
{
$return = self::DEFAULT_RETURN;
$return['error'] = self::ERROR_CODES['CANNOT_CREATE'];
- $return['message'] = self::ERROR_MESSAGES['CANNOT_CREATE'];
+ $return['message'] = self::ERROR_MESSAGES['CANNOT_CREATE'] . ' : Cannot upload and create media file ' . $file['name'];
$this->auto_http_code(false);
return $this->json($return);
diff --git a/controllers/publics/Scheduled.php b/controllers/publics/Scheduled.php
index 2e4d8a7..677da5e 100644
--- a/controllers/publics/Scheduled.php
+++ b/controllers/publics/Scheduled.php
@@ -212,8 +212,8 @@ namespace controllers\publics;
$scheduleds[$key]['groups'][] = (int) $group['id'];
}
- $media = $this->internal_media->get_for_scheduled_and_user($id_user, $scheduled['id']);
- $scheduleds[$key]['media'] = $media;
+ $medias = $this->internal_media->gets_for_scheduled($scheduled['id']);
+ $scheduleds[$key]['medias'] = $medias;
$conditional_groups = $this->internal_scheduled->get_conditional_groups($scheduled['id']);
foreach ($conditional_groups as $conditional_group)
@@ -242,7 +242,7 @@ namespace controllers\publics;
* @param ?array $_POST['contacts'] : Numbers to send the message to
* @param ?array $_POST['groups'] : Numbers to send the message to
* @param ?array $_POST['conditional_groups'] : Numbers to send the message to
- * @param ?array $_FILES['media'] : The media to link to a scheduled
+ * @param ?array $_FILES['medias'] : The media to link to a scheduled
*/
public function create($csrf)
{
@@ -257,13 +257,39 @@ 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'] ?? [];
$groups = $_POST['groups'] ?? [];
$conditional_groups = $_POST['conditional_groups'] ?? [];
- $media = $_FILES['media'] ?? false;
+ $files = $_FILES['medias'] ?? false;
+
+ //Iterate over files to re-create individual $_FILES array
+ $files_arrays = [];
+ if ($files && is_array($files['name']))
+ {
+ foreach ($files as $property_name => $files_values)
+ {
+ foreach ($files_values as $file_key => $property_value)
+ {
+ if (!isset($files_arrays[$file_key]))
+ {
+ $files_arrays[$file_key] = [];
+ }
+
+ $files_arrays[$file_key][$property_name] = $property_value;
+ }
+ }
+ }
+
+ //Remove empty files input
+ foreach ($files_arrays as $key => $file)
+ {
+ if ($file['error'] === UPLOAD_ERR_NO_FILE)
+ {
+ unset($files_arrays[$key]);
+ }
+ }
if (empty($text))
{
@@ -299,8 +325,27 @@ namespace controllers\publics;
return $this->redirect(\descartes\Router::url('Scheduled', 'add'));
}
+
+ //If mms is enable and we have medias uploaded
+ $media_ids = [];
+ if ($_SESSION['user']['settings']['mms'] && $files_arrays)
+ {
+ foreach ($files_arrays as $file)
+ {
+ $new_media_id = $this->internal_media->upload_and_create_for_user($_SESSION['user']['id'], $file);
+ if (!$new_media_id)
+ {
+ \FlashMessage\FlashMessage::push('danger', 'Impossible d\'upload et d\'enregistrer le fichier ' . $file['name']);
+ return $this->redirect(\descartes\Router::url('Scheduled', 'add'));
+ }
- $scheduled_id = $this->internal_scheduled->create($id_user, $at, $text, $id_phone, $flash, $mms, $numbers, $contacts, $groups, $conditional_groups);
+ $media_ids[] = $new_media_id;
+ }
+ }
+
+ $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);
if (!$scheduled_id)
{
\FlashMessage\FlashMessage::push('danger', 'Impossible de créer le Sms.');
@@ -308,22 +353,6 @@ namespace controllers\publics;
return $this->redirect(\descartes\Router::url('Scheduled', 'add'));
}
- //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 . '.');
-
- return $this->redirect(\descartes\Router::url('Scheduled', 'list'));
- }
-
- $success = $this->internal_media->create($id_user, $scheduled_id, $media);
- if (!$success)
- {
- \FlashMessage\FlashMessage::push('success', 'Le SMS a bien été créé mais le média n\'as pas pu être enregistré.');
-
- return $this->redirect(\descartes\Router::url('Scheduled', 'list'));
- }
-
\FlashMessage\FlashMessage::push('success', 'Le Sms a bien été créé pour le ' . $at . '.');
return $this->redirect(\descartes\Router::url('Scheduled', 'list'));
@@ -360,13 +389,43 @@ namespace controllers\publics;
$contacts = $scheduled['contacts'] ?? [];
$groups = $scheduled['groups'] ?? [];
$conditional_groups = $scheduled['conditional_groups'] ?? [];
+ $files = $_FILES['scheduleds_' . $id_scheduled . '_medias'] ?? false;
+ $media_ids = $scheduled['media_ids'] ?? [];
+ //Check scheduled exists and belong to user
$scheduled = $this->internal_scheduled->get($id_scheduled);
if (!$scheduled || $scheduled['id_user'] !== $id_user)
{
continue;
}
+ //Iterate over files to re-create individual $_FILES array
+ $files_arrays = [];
+ if ($files && is_array($files['name']))
+ {
+ foreach ($files as $property_name => $files_values)
+ {
+ foreach ($files_values as $file_key => $property_value)
+ {
+ if (!isset($files_arrays[$file_key]))
+ {
+ $files_arrays[$file_key] = [];
+ }
+
+ $files_arrays[$file_key][$property_name] = $property_value;
+ }
+ }
+ }
+
+ //Remove empty files input
+ foreach ($files_arrays as $key => $file)
+ {
+ if ($file['error'] === UPLOAD_ERR_NO_FILE)
+ {
+ unset($files_arrays[$key]);
+ }
+ }
+
if (empty($text))
{
continue;
@@ -394,32 +453,36 @@ namespace controllers\publics;
{
continue;
}
-
- $success = $this->internal_scheduled->update_for_user($id_user, $id_scheduled, $at, $text, $id_phone, $flash, $numbers, $contacts, $groups, $conditional_groups);
-
- //Check for media
- /*
- $current_media = $scheduled['current_media'] ?? false;
- if (!$current_media)
+
+ //If mms is enable and we have medias uploaded
+ if ($_SESSION['user']['settings']['mms'] && $files_arrays)
{
- $this->internal_media->delete_for_scheduled_and_user($id_user, $id_scheduled);
+ foreach ($files_arrays as $file)
+ {
+ $new_media_id = $this->internal_media->upload_and_create_for_user($_SESSION['user']['id'], $file);
+ if (!$new_media_id)
+ {
+ continue 2;
+ }
+
+ $media_ids[] = $new_media_id;
+ }
}
- $media = $_FILES['media_' . $id_scheduled] ?? false;
- if (!$media)
+ //Ensure media_ids point to medias belongings to the current user
+ foreach ($media_ids as $key => $media_id)
{
- $nb_update += (int) $success;
- continue;
+ $media = $this->internal_media->get($media_id);
+ if (!$media || $media['id_user'] !== $_SESSION['user']['id'])
+ {
+ unset($media_ids[$key]);
+ }
}
- $success = $this->internal_media->create($id_user, $id_scheduled, $media);
- if (!$success)
- {
- continue;
- }
- */
+ $mms = (bool) count($media_ids);
- ++$nb_update;
+ $this->internal_scheduled->update_for_user($id_user, $id_scheduled, $at, $text, $id_phone, $flash, $mms, $numbers, $contacts, $groups, $conditional_groups, $media_ids);
+ $nb_update++;
}
if ($nb_update !== \count($scheduleds))
diff --git a/templates/scheduled/add.php b/templates/scheduled/add.php
index 13ca007..4ce6619 100644
--- a/templates/scheduled/add.php
+++ b/templates/scheduled/add.php
@@ -64,12 +64,14 @@
-
-
Ajouter un média
+
diff --git a/templates/scheduled/edit.php b/templates/scheduled/edit.php
index 794cd0f..bfd788d 100644
--- a/templates/scheduled/edit.php
+++ b/templates/scheduled/edit.php
@@ -64,17 +64,21 @@
Ajouter un média
- Le média sera utilisé uniquement si le téléphone utilisé supporte l'envoi de MMS. Pour plus d'information, consultez la documentation sur l'utilisation des MMS.
+ L'ajout d'un média nécessite un téléphone supportant l'envoi de MMS. Pour plus d'information, consultez la documentation sur l'utilisation des MMS. .
-
-
@@ -246,8 +250,7 @@
jQuery('body').on('click', '.btn-delete-media', function (e)
{
e.preventDefault();
- jQuery(this).parents('.form-group').find('input').removeClass('hidden');
- jQuery(this).parents('.form-group').find('.current-media-container').remove();
+ jQuery(this).parents('.current-media').remove();
});
jQuery('body').on('click', '.preview-button', function (e)
From 0f6bf9ccde8fe09d2bf37769e4bd72a3f8770fc6 Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Mon, 22 Mar 2021 02:08:59 +0100
Subject: [PATCH 07/40] add medias to discussion
---
assets/css/style.css | 6 +++
controllers/internals/Scheduled.php | 7 +++-
controllers/publics/Discussion.php | 63 ++++++++++++++++++++++++++++-
templates/discussion/show.php | 1 +
4 files changed, 75 insertions(+), 2 deletions(-)
diff --git a/assets/css/style.css b/assets/css/style.css
index 8be577c..c852c62 100644
--- a/assets/css/style.css
+++ b/assets/css/style.css
@@ -254,6 +254,12 @@ footer img
margin-top: 8px;
}
+.message-input input[type="file"]
+{
+ margin-top: 8px;
+ display: inline-block;
+}
+
.message-in-progress-hover
{
position: absolute;
diff --git a/controllers/internals/Scheduled.php b/controllers/internals/Scheduled.php
index f21cced..b45c6ce 100644
--- a/controllers/internals/Scheduled.php
+++ b/controllers/internals/Scheduled.php
@@ -43,6 +43,11 @@ namespace controllers\internals;
'mms' => $mms,
];
+ if ($text === '')
+ {
+ return false;
+ }
+
if (null !== $id_phone)
{
$internal_phone = new Phone($this->bdd);
@@ -441,7 +446,7 @@ namespace controllers\internals;
foreach ($messages as $message)
{
//Remove empty messages
- if ('' === trim($message['text']))
+ if ('' === trim($message['text']) && !$message['medias'])
{
continue;
}
diff --git a/controllers/publics/Discussion.php b/controllers/publics/Discussion.php
index da30f5d..42cc010 100644
--- a/controllers/publics/Discussion.php
+++ b/controllers/publics/Discussion.php
@@ -203,6 +203,7 @@ namespace controllers\publics;
* @param string $_POST['text'] : Le contenu du Sms
* @param string $_POST['destination'] : Number to send sms to
* @param string $_POST['id_phone'] : If of phone to send sms with
+ * @param array $_FILES['medias'] : Medias to upload and link to sms
*
* @return string : json string Le statut de l'envoi
*/
@@ -228,6 +229,43 @@ namespace controllers\publics;
$text = $_POST['text'] ?? '';
$destination = $_POST['destination'] ?? false;
$id_phone = $_POST['id_phone'] ?? false;
+ $files = $_FILES['medias'] ?? false;
+
+ //Iterate over files to re-create individual $_FILES array
+ $files_arrays = [];
+ if ($files && is_array($files['name']))
+ {
+ foreach ($files as $property_name => $files_values)
+ {
+ foreach ($files_values as $file_key => $property_value)
+ {
+ if (!isset($files_arrays[$file_key]))
+ {
+ $files_arrays[$file_key] = [];
+ }
+
+ $files_arrays[$file_key][$property_name] = $property_value;
+ }
+ }
+ }
+
+ //Remove empty files input
+ foreach ($files_arrays as $key => $file)
+ {
+ if ($file['error'] === UPLOAD_ERR_NO_FILE)
+ {
+ unset($files_arrays[$key]);
+ }
+ }
+
+ if (!$text)
+ {
+ $return['success'] = false;
+ $return['message'] = 'Vous devez renseigner le texte de votre sms.';
+ echo json_encode($return);
+
+ return false;
+ }
if (!$destination)
{
@@ -243,10 +281,33 @@ namespace controllers\publics;
$id_phone = null;
}
+
+ //If mms is enable and we have medias uploaded
+ $media_ids = [];
+ if ($_SESSION['user']['settings']['mms'] && $files_arrays)
+ {
+ foreach ($files_arrays as $file)
+ {
+ $new_media_id = $this->internal_media->upload_and_create_for_user($_SESSION['user']['id'], $file);
+ if (!$new_media_id)
+ {
+ $return['success'] = false;
+ $return['message'] = 'Impossible d\'upload et d\'enregistrer le fichier ' . $file['name'];
+ echo json_encode($return);
+
+ return false;
+ }
+
+ $media_ids[] = $new_media_id;
+ }
+ }
+
+ $mms = (bool) count($media_ids);
+
//Destinations must be an array of number
$destinations = [$destination];
- if (!$this->internal_scheduled->create($id_user, $at, $text, $id_phone, false, $destinations))
+ if (!$this->internal_scheduled->create($id_user, $at, $text, $id_phone, false, $mms, $destinations, [], [], [], $media_ids))
{
$return['success'] = false;
$return['message'] = 'Impossible de créer le Sms';
diff --git a/templates/discussion/show.php b/templates/discussion/show.php
index 835761a..13402c3 100644
--- a/templates/discussion/show.php
+++ b/templates/discussion/show.php
@@ -42,6 +42,7 @@
+
Envoyer
From a081567a3956c4cd47cb5a8b0a98de089bf3bff0 Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Mon, 22 Mar 2021 17:08:29 +0100
Subject: [PATCH 08/40] add preview of images for mms in discussion
---
assets/css/style.css | 27 +++++++++++++++++++++++++++
templates/discussion/show.php | 33 ++++++++++++++++++++-------------
2 files changed, 47 insertions(+), 13 deletions(-)
diff --git a/assets/css/style.css b/assets/css/style.css
index c852c62..a15c2e1 100644
--- a/assets/css/style.css
+++ b/assets/css/style.css
@@ -205,6 +205,33 @@ footer img
font-size: 0.9em;
}
+.discussion-message-medias img
+{
+ transition-duration: 0.1s;
+ max-width: 150px;
+ max-height: 150px;
+ line-height: 150px;
+ width: auto;
+ height: auto;
+}
+
+.discussion-message-medias img:hover
+{
+ opacity: 0.6;
+}
+
+.discussion-message-media
+{
+ line-height: 160px;
+ width: 160px;
+ height: 160px;
+ padding: 5px;
+ margin-bottom: 15px;
+ background-color: rgba(255, 255, 255, 0.3);
+ vertical-align: middle;
+ text-align: center;
+}
+
.message-container
{
margin-bottom: 10px;
diff --git a/templates/discussion/show.php b/templates/discussion/show.php
index 13402c3..2dbaa47 100644
--- a/templates/discussion/show.php
+++ b/templates/discussion/show.php
@@ -79,16 +79,27 @@
message.text = Autolinker.link(message.text, {newWindow:true});
+ var medias = message.medias.map((mediaUrl, index) => {
+ var extension = mediaUrl.split('.').pop();
+ if (['jpg', 'jpeg', 'png', 'gif'].includes(extension))
+ {
+ return '
';
+ }
+ else
+ {
+ return '
';
+ }
+ });
+ var medias_html = '
' + medias.join('') + '
';
+
switch (message.type)
- {
+ {
case 'received' :
var texte = '' +
'
' +
'
' +
- '
' + message.text + '
' +
- '
' +
+ '
' + message.text + '
' +
+ medias.html +
'
' + message.date + '
' +
'
' +
'
';
@@ -105,10 +116,8 @@
'
' +
'
' +
'
' + message.text + '
' +
- '
' +
- '
' + message.date + ' ' + (message.status == 'delivered' ? ' ' : (message.status == 'failed' ? ' ' : ' ' )) + '
' +
+ medias_html +
+ '
' + message.date + ' ' + (message.status == 'delivered' ? ' ' : (message.status == 'failed' ? ' ' : ' ' )) + '
' +
'
' +
'
';
break;
@@ -117,10 +126,8 @@
'
' +
'
' +
'
' +
- '
' + message.text + '
' +
- '
' +
+ '
' + message.text + '
' +
+ medias_html +
'
' + message.date + '
' +
'
' +
'
';
From e339dc4758f6bbc87c9906382ea1dfce3c13cb49 Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Mon, 22 Mar 2021 17:10:07 +0100
Subject: [PATCH 09/40] inline mms medias
---
assets/css/style.css | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/assets/css/style.css b/assets/css/style.css
index a15c2e1..ba50ec0 100644
--- a/assets/css/style.css
+++ b/assets/css/style.css
@@ -222,14 +222,15 @@ footer img
.discussion-message-media
{
+ display: inline-block;
line-height: 160px;
width: 160px;
height: 160px;
padding: 5px;
- margin-bottom: 15px;
background-color: rgba(255, 255, 255, 0.3);
vertical-align: middle;
text-align: center;
+ margin: 5px;
}
.message-container
From 52a0302dc24b9ee8befc6a03d14b1b1970e4b32f Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Tue, 23 Mar 2021 04:31:13 +0100
Subject: [PATCH 10/40] start adding call support
---
controllers/internals/Received.php | 4 +-
controllers/internals/Sended.php | 4 +-
controllers/internals/Webhook.php | 16 +--
controllers/publics/Call.php | 81 +++++++++++++
controllers/publics/Callback.php | 110 ++++++++++++++++++
.../20210322193953_update_webhook_types.php | 16 +++
db/migrations/20210322223812_create_call.php | 47 ++++++++
models/Call.php | 29 +++++
models/Webhook.php | 5 +-
routes.php | 1 +
templates/webhook/add.php | 1 +
templates/webhook/list.php | 2 +
12 files changed, 298 insertions(+), 18 deletions(-)
create mode 100644 controllers/publics/Call.php
create mode 100644 db/migrations/20210322193953_update_webhook_types.php
create mode 100644 db/migrations/20210322223812_create_call.php
create mode 100644 models/Call.php
diff --git a/controllers/internals/Received.php b/controllers/internals/Received.php
index 0f0388a..c685adb 100644
--- a/controllers/internals/Received.php
+++ b/controllers/internals/Received.php
@@ -287,11 +287,11 @@ namespace controllers\internals;
'origin' => $origin,
'command' => $is_command,
'mms' => $mms,
- 'medias' => $media_ids,
+ 'medias' => $this->get_model()->gets_in_for_user($id_user, $media_ids),
];
$internal_webhook = new Webhook($this->bdd);
- $internal_webhook->trigger($id_user, \models\Webhook::TYPE_RECEIVE, $received);
+ $internal_webhook->trigger($id_user, \models\Webhook::TYPE_RECEIVE_SMS, $received);
$internal_user = new User($this->bdd);
$internal_user->transfer_received($id_user, $received);
diff --git a/controllers/internals/Sended.php b/controllers/internals/Sended.php
index dfac195..ee3ea04 100644
--- a/controllers/internals/Sended.php
+++ b/controllers/internals/Sended.php
@@ -236,10 +236,12 @@ namespace controllers\internals;
'text' => $text,
'destination' => $destination,
'origin' => $id_phone,
+ 'mms' => $mms,
+ 'medias' => $medias,
];
$internal_webhook = new Webhook($this->bdd);
- $internal_webhook->trigger($id_user, \models\Webhook::TYPE_SEND, $sended);
+ $internal_webhook->trigger($id_user, \models\Webhook::TYPE_SEND_SMS, $sended);
return $return;
}
diff --git a/controllers/internals/Webhook.php b/controllers/internals/Webhook.php
index 8d25108..3cbaff0 100644
--- a/controllers/internals/Webhook.php
+++ b/controllers/internals/Webhook.php
@@ -94,17 +94,11 @@ class Webhook extends StandardController
*
* @param int $id_user : User to trigger the webhook for
* @param string $type : Type of webhook to trigger
- * @param array $sms : The sms [
- * int 'id' => SMS id,
- * string 'at' => SMS date,
- * string 'text' => sms body,
- * string 'origin' => sms origin (number or phone id)
- * string 'destination' => sms destination (number or phone id)
- * ]
+ * @param array $body : The body, an array depending on webhook type
*
* @return bool : False if no trigger, true else
*/
- public function trigger(int $id_user, string $type, array $sms)
+ public function trigger(int $id_user, string $type, array $body)
{
$internal_setting = new Setting($this->bdd);
$internal_user = new User($this->bdd);
@@ -137,11 +131,7 @@ class Webhook extends StandardController
'webhook_type' => $webhook['type'],
'webhook_random_id' => $webhook_random_id,
'webhook_signature' => $webhook_signature,
- 'id' => $sms['id'],
- 'at' => $sms['at'],
- 'text' => $sms['text'],
- 'origin' => $sms['origin'],
- 'destination' => $sms['destination'],
+ 'body' => json_encode($body),
],
];
diff --git a/controllers/publics/Call.php b/controllers/publics/Call.php
new file mode 100644
index 0000000..55acbb2
--- /dev/null
+++ b/controllers/publics/Call.php
@@ -0,0 +1,81 @@
+
+ *
+ * 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;
+
+ class Call extends StandardController
+ {
+ protected $model;
+
+ /**
+ * Create a media.
+ *
+ * @param int $id_user : Id of the user
+ * @param int $id_phone : Id of the phone that emitted (outbound) or received (inbound) the call
+ * @param string $uid : Uid of the phone call
+ * @param string $direction : Direction of the call, \models\Call::DIRECTION_INBOUND | \models\Call::DIRECTION_OUTBOUND
+ * @param string $start : Date of the call beginning
+ * @param ?string $end : Date of the call end
+ * @param ?string $origin : Origin of the call or null if outbound
+ * @param ?string $destination : Destination of the call or null if inbound
+ *
+ * @return mixed bool|int : false on error, new call id else
+ */
+ public function create(int $id_user, int $id_phone, string $uid, string $direction, string $start, ?string $end = null, ?string $origin = null, ?string $destination = null)
+ {
+ $call = [
+ 'id_user' => $id_user,
+ 'id_phone' => $id_phone,
+ 'uid' => $uid,
+ 'start' => $start,
+ 'end' => $end,
+ 'direction' => $direction,
+ 'origin' => $origin,
+ 'destination' => $destination,
+ ];
+
+ if (!$origin && !$destination)
+ {
+ return false;
+ }
+
+ switch ($direction)
+ {
+ case \models\Call::DIRECTION_OUTBOUND :
+ null === $destination ?: return false;
+ break;
+
+ case \models\Call::DIRECTION_INBOUND :
+ null === $origin ?: return false;
+ break;
+
+ default :
+ return false;
+ }
+
+ if (!\controllers\internals\Tool::validate_date($start, 'Y-m-d H:i:s'))
+ {
+ return false;
+ }
+
+ if (null !== $end && !\controllers\internals\Tool::validate_date($end, 'Y-m-d H:i:s'))
+ {
+ return false;
+ }
+
+ if (null !== $end && new \DateTime($end) < new \DateTime($start))
+ {
+ return false;
+ }
+
+ return $this->get_model()->insert($call);
+ }
+ }
diff --git a/controllers/publics/Callback.php b/controllers/publics/Callback.php
index 1a4c951..5c6e32e 100644
--- a/controllers/publics/Callback.php
+++ b/controllers/publics/Callback.php
@@ -26,6 +26,7 @@ use Monolog\Logger;
private $internal_received;
private $internal_adapter;
private $internal_media;
+ private $internal_phone;
public function __construct()
{
@@ -36,6 +37,7 @@ use Monolog\Logger;
$this->internal_received = new \controllers\internals\Received($bdd);
$this->internal_media = new \controllers\internals\Media($bdd);
$this->internal_adapter = new \controllers\internals\Adapter();
+ $this->internal_phone = new \controllers\internals\Phone();
//Logger
$this->logger = new Logger('Callback ' . uniqid());
@@ -242,4 +244,112 @@ use Monolog\Logger;
return true;
}
+
+
+ /**
+ * Function call on call reception notification
+ * We return nothing, and we let the adapter do his things.
+ *
+ * @param int $id_phone : Phone id
+ *
+ * @return bool : true on success, false on error
+ */
+ public function inbound_call(int $id_phone)
+ {
+ $this->logger->info('Callback reception call with phone : ' . $id_phone);
+ $phone = $this->internal_phone->get_for_user($this->user['id'], $id_phone);
+
+ if (!$phone)
+ {
+ $this->logger->error('Callback inbound_call use non existing phone : ' . $id_phone);
+
+ return false;
+ }
+
+ if (!class_exists($phone['adapter']))
+ {
+ $this->logger->error('Callback inbound_call use non existing adapter : ' . $phone['adapter']);
+
+ return false;
+ }
+
+ if (!$phone['adapter']::meta_support_inbound_call_callback())
+ {
+ $this->logger->error('Callback inbound_call use adapter ' . $phone['adapter'] . ' which does not support inbound_call callback.');
+
+ return false;
+ }
+
+ $response = $phone['adapter']::inbound_call_callback();
+ if ($response['error'])
+ {
+ $this->logger->error('Callback reception with adapter ' . $adapter_uid . ' failed : ' . $response['error_message']);
+
+ return false;
+ }
+
+ $sms = $response['sms'];
+ $mms = !empty($sms['mms']);
+ $medias = empty($sms['medias']) ? [] : $sms['medias'];
+ $media_ids = [];
+
+ //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']);
+
+ return false;
+ }
+
+ $this->logger->info('Callback reception successfully received message : ' . json_encode($sms));
+
+ return true;
+ }
}
diff --git a/db/migrations/20210322193953_update_webhook_types.php b/db/migrations/20210322193953_update_webhook_types.php
new file mode 100644
index 0000000..80f2392
--- /dev/null
+++ b/db/migrations/20210322193953_update_webhook_types.php
@@ -0,0 +1,16 @@
+execute('ALTER TABLE `webhook` MODIFY `type` ENUM(\'send_sms\', \'receive_sms\', \'inbound_call\')');
+ }
+
+ public function down()
+ {
+ $this->execute('ALTER TABLE `webhook` MODIFY `type` ENUM(\'send_sms\', \'receive_sms\')');
+ }
+}
diff --git a/db/migrations/20210322223812_create_call.php b/db/migrations/20210322223812_create_call.php
new file mode 100644
index 0000000..dda93d0
--- /dev/null
+++ b/db/migrations/20210322223812_create_call.php
@@ -0,0 +1,47 @@
+table('call');
+ $table->addColumn('id_user', 'integer')
+ ->addColumn('id_phone', 'integer', ['null' => true])
+ ->addColumn('uid', 'string', ['limit' => 500])
+ ->addColumn('start', 'datetime')
+ ->addColumn('end', 'datetime', ['null' => true])
+ ->addColumn('direction', 'enum', ['values' => ['inbound', 'outbound']])
+ ->addColumn('origin', 'string', ['limit' => 20, 'null' => true])
+ ->addColumn('destination', 'string', ['limit' => 20, 'null' => true])
+ ->addForeignKey('id_user', 'user', 'id', ['delete' => 'CASCADE', 'update' => 'CASCADE'])
+ ->addForeignKey('id_phone', 'user', 'id', ['delete' => 'SET_NULL', 'update' => 'CASCADE'])
+ ->create();
+ }
+}
diff --git a/models/Call.php b/models/Call.php
new file mode 100644
index 0000000..fee82b3
--- /dev/null
+++ b/models/Call.php
@@ -0,0 +1,29 @@
+
+ *
+ * This source file is subject to the GPL-3.0 license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace models;
+
+ /**
+ * Manage bdd operations for calls
+ */
+ class Call extends StandardModel
+ {
+ const DIRECTION_INBOUND = 'inbound';
+ const DIRECTION_OUTBOUND = 'outbound';
+
+ /**
+ * Return table name.
+ */
+ protected function get_table_name(): string
+ {
+ return 'call';
+ }
+ }
diff --git a/models/Webhook.php b/models/Webhook.php
index 945cd82..48d4998 100644
--- a/models/Webhook.php
+++ b/models/Webhook.php
@@ -13,8 +13,9 @@ namespace models;
class Webhook extends StandardModel
{
- const TYPE_SEND = 'send_sms';
- const TYPE_RECEIVE = 'receive_sms';
+ const TYPE_SEND_SMS = 'send_sms';
+ const TYPE_RECEIVE_SMS = 'receive_sms';
+ const TYPE_INBOUND_CALL = 'inbound_call';
/**
* Find all webhooks for a user and for a type of webhook.
diff --git a/routes.php b/routes.php
index 121d4ba..bf66579 100644
--- a/routes.php
+++ b/routes.php
@@ -168,6 +168,7 @@
'Callback' => [
'update_sended_status' => '/callback/status/{adapter_uid}/',
'reception' => '/callback/reception/{adapter_uid}/{id_phone}/',
+ 'inbound_call' => '/callback/inbound_call/{id_phone}/',
],
'Api' => [
diff --git a/templates/webhook/add.php b/templates/webhook/add.php
index 3fc35b9..6e53c43 100644
--- a/templates/webhook/add.php
+++ b/templates/webhook/add.php
@@ -47,6 +47,7 @@
>Réception d'un SMS
>Envoi d'un SMS
+ >Réception d'un appel téléphonique
Annuler
diff --git a/templates/webhook/list.php b/templates/webhook/list.php
index 0b306dc..20a99b9 100644
--- a/templates/webhook/list.php
+++ b/templates/webhook/list.php
@@ -92,6 +92,8 @@ jQuery(document).ready(function ()
return 'Envoi de SMS';
case 'receive_sms':
return 'Réception de SMS';
+ case 'inbound_call':
+ return 'Réception d\'un appel téléphonique';
default:
return 'Inconnu';
}
From 41c3b3b86e3e6de337c3c2f1e2c93fa4c9f38fdf Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Tue, 23 Mar 2021 17:39:13 +0100
Subject: [PATCH 11/40] Add callback for phone calls, not tested yet
---
adapters/AdapterInterface.php | 41 +++++++
adapters/BenchmarkAdapter.php | 20 ++++
adapters/GammuAdapter.php | 20 ++++
adapters/OctopushShortcodeAdapter.php | 20 ++++
adapters/OctopushVirtualNumberAdapter.php | 20 ++++
adapters/OvhSmsShortcodeAdapter.php | 20 ++++
adapters/OvhSmsVirtualNumberAdapter.php | 20 ++++
adapters/TestAdapter.php | 64 +++++++++++
adapters/TwilioVirtualNumberAdapter.php | 20 ++++
controllers/{publics => internals}/Call.php | 52 ++++++++-
controllers/publics/Callback.php | 114 +++++++++++---------
models/Call.php | 20 ++++
models/StandardModel.php | 4 +-
13 files changed, 378 insertions(+), 57 deletions(-)
rename controllers/{publics => internals}/Call.php (62%)
diff --git a/adapters/AdapterInterface.php b/adapters/AdapterInterface.php
index 2940452..b7fa981 100644
--- a/adapters/AdapterInterface.php
+++ b/adapters/AdapterInterface.php
@@ -92,6 +92,16 @@ interface AdapterInterface
* Does the implemented service support mms sending
*/
public static function meta_support_mms_sending(): bool;
+
+ /**
+ * Does the implemented service support inbound call callback
+ */
+ public static function meta_support_inbound_call_callback(): bool;
+
+ /**
+ * Does the implemented service support end call callback
+ */
+ public static function meta_support_end_call_callback(): bool;
/**
* Method called to send a SMS to a number.
@@ -162,4 +172,35 @@ interface AdapterInterface
* ]
*/
public static function reception_callback(): array;
+
+ /**
+ * Method called on reception of an inbound_call notification
+ *
+ * @return array : [
+ * bool 'error' => false on success, true on error
+ * ?string 'error_message' => null on success, error message else
+ * array 'call' => array [
+ * string 'uid' : Uid of the call on the adapter plateform
+ * string 'start' : Start of the call date format Y-m-d H:i:s,
+ * ?string 'end' : End of the call date format Y-m-d H:i:s. If no known end, NULL
+ * string 'origin' : Emitter phone call number. International format.
+ * ]
+ * ]
+ */
+ public function inbound_call_callback(): array;
+
+
+ /**
+ * Method called on reception of a end call notification
+ *
+ * @return array : [
+ * bool 'error' => false on success, true on error
+ * ?string 'error_message' => null on success, error message else
+ * array 'call' => array [
+ * string 'uid' : Uid of the call on the adapter plateform. Used to find the raspisms local call to update.
+ * string 'end' : End of the call date format Y-m-d H:i:s.
+ * ]
+ * ]
+ */
+ public function end_call_callback(): array;
}
diff --git a/adapters/BenchmarkAdapter.php b/adapters/BenchmarkAdapter.php
index 45b7ef6..c872782 100644
--- a/adapters/BenchmarkAdapter.php
+++ b/adapters/BenchmarkAdapter.php
@@ -141,6 +141,16 @@ namespace adapters;
{
return false;
}
+
+ public static function meta_support_inbound_call_callback(): bool
+ {
+ return false;
+ }
+
+ public static function meta_support_end_call_callback(): bool
+ {
+ return false;
+ }
public function send(string $destination, string $text, bool $flash = false, bool $mms = false, array $medias = []) : array
{
@@ -220,4 +230,14 @@ namespace adapters;
{
return true;
}
+
+ public function inbound_call_callback(): array
+ {
+ return [];
+ }
+
+ public function end_call_callback(): array
+ {
+ return [];
+ }
}
diff --git a/adapters/GammuAdapter.php b/adapters/GammuAdapter.php
index 4829ab1..3fd8a45 100644
--- a/adapters/GammuAdapter.php
+++ b/adapters/GammuAdapter.php
@@ -151,6 +151,16 @@ namespace adapters;
{
return false;
}
+
+ public static function meta_support_inbound_call_callback(): bool
+ {
+ return false;
+ }
+
+ public static function meta_support_end_call_callback(): bool
+ {
+ return false;
+ }
public function send(string $destination, string $text, bool $flash = false, bool $mms = false, array $medias = []) : array
{
@@ -296,6 +306,16 @@ namespace adapters;
{
return [];
}
+
+ public function inbound_call_callback(): array
+ {
+ return [];
+ }
+
+ public function end_call_callback(): array
+ {
+ return [];
+ }
/**
* Function to unlock pin.
diff --git a/adapters/OctopushShortcodeAdapter.php b/adapters/OctopushShortcodeAdapter.php
index 04e54bc..e2d4240 100644
--- a/adapters/OctopushShortcodeAdapter.php
+++ b/adapters/OctopushShortcodeAdapter.php
@@ -188,6 +188,16 @@ class OctopushShortcodeAdapter implements AdapterInterface
{
return false;
}
+
+ public static function meta_support_inbound_call_callback(): bool
+ {
+ return false;
+ }
+
+ public static function meta_support_end_call_callback(): bool
+ {
+ return false;
+ }
public function send(string $destination, string $text, bool $flash = false, bool $mms = false, array $medias = []) : array
{
@@ -407,4 +417,14 @@ class OctopushShortcodeAdapter implements AdapterInterface
return $response;
}
+
+ public function inbound_call_callback(): array
+ {
+ return [];
+ }
+
+ public function end_call_callback(): array
+ {
+ return [];
+ }
}
diff --git a/adapters/OctopushVirtualNumberAdapter.php b/adapters/OctopushVirtualNumberAdapter.php
index d0d7cdc..d38eb29 100644
--- a/adapters/OctopushVirtualNumberAdapter.php
+++ b/adapters/OctopushVirtualNumberAdapter.php
@@ -193,6 +193,16 @@ class OctopushVirtualNumberAdapter implements AdapterInterface
{
return false;
}
+
+ public static function meta_support_inbound_call_callback(): bool
+ {
+ return false;
+ }
+
+ public static function meta_support_end_call_callback(): bool
+ {
+ return false;
+ }
public function send(string $destination, string $text, bool $flash = false, bool $mms = false, array $medias = []) : array
{
@@ -407,4 +417,14 @@ class OctopushVirtualNumberAdapter implements AdapterInterface
return $response;
}
+
+ public function inbound_call_callback(): array
+ {
+ return [];
+ }
+
+ public function end_call_callback(): array
+ {
+ return [];
+ }
}
diff --git a/adapters/OvhSmsShortcodeAdapter.php b/adapters/OvhSmsShortcodeAdapter.php
index aa98bbd..9c6137b 100644
--- a/adapters/OvhSmsShortcodeAdapter.php
+++ b/adapters/OvhSmsShortcodeAdapter.php
@@ -185,6 +185,16 @@ namespace adapters;
{
return false;
}
+
+ public static function meta_support_inbound_call_callback(): bool
+ {
+ return false;
+ }
+
+ public static function meta_support_end_call_callback(): bool
+ {
+ return false;
+ }
public function send(string $destination, string $text, bool $flash = false, bool $mms = false, array $medias = []) : array
{
@@ -359,4 +369,14 @@ namespace adapters;
{
return [];
}
+
+ public function inbound_call_callback(): array
+ {
+ return [];
+ }
+
+ public function end_call_callback(): array
+ {
+ return [];
+ }
}
diff --git a/adapters/OvhSmsVirtualNumberAdapter.php b/adapters/OvhSmsVirtualNumberAdapter.php
index 1f8995f..35925a3 100644
--- a/adapters/OvhSmsVirtualNumberAdapter.php
+++ b/adapters/OvhSmsVirtualNumberAdapter.php
@@ -196,6 +196,16 @@ namespace adapters;
{
return false;
}
+
+ public static function meta_support_inbound_call_callback(): bool
+ {
+ return false;
+ }
+
+ public static function meta_support_end_call_callback(): bool
+ {
+ return false;
+ }
public function send(string $destination, string $text, bool $flash = false, bool $mms = false, array $medias = []) : array
{
@@ -358,4 +368,14 @@ namespace adapters;
{
return [];
}
+
+ public function inbound_call_callback(): array
+ {
+ return [];
+ }
+
+ public function end_call_callback(): array
+ {
+ return [];
+ }
}
diff --git a/adapters/TestAdapter.php b/adapters/TestAdapter.php
index 6a20baf..8a318a4 100644
--- a/adapters/TestAdapter.php
+++ b/adapters/TestAdapter.php
@@ -146,6 +146,16 @@ namespace adapters;
{
return true;
}
+
+ public static function meta_support_inbound_call_callback(): bool
+ {
+ return false;
+ }
+
+ public static function meta_support_end_call_callback(): bool
+ {
+ return false;
+ }
public function send(string $destination, string $text, bool $flash = false, bool $mms = false, array $medias = []) : array
{
@@ -272,4 +282,58 @@ namespace adapters;
{
return [];
}
+
+ public function inbound_call_callback(): array
+ {
+ $response = [
+ 'error' => false,
+ 'error_message' => null,
+ 'call' => [],
+ ];
+
+ $uid = $_POST['uid'] ?? false;
+ $start = $_POST['start'] ?? false;
+ $end = $_POST['end'] ?? null;
+ $origin = $_POST['origin'] ?? false;
+
+ if (!$uid || !$start || !$origin)
+ {
+ $response['error'] = true;
+ $response['error_message'] = 'Missing required argument.';
+
+ return $response;
+ }
+
+ $response['call']['uid'] = $uid;
+ $response['call']['start'] = $start;
+ $response['call']['end'] = $end;
+ $response['call']['origin'] = $origin;
+
+ return $response;
+ }
+
+ public function end_call_callback(): array
+ {
+ $response = [
+ 'error' => false,
+ 'error_message' => null,
+ 'call' => [],
+ ];
+
+ $uid = $_POST['uid'] ?? false;
+ $end = $_POST['end'] ?? null;
+
+ if (!$uid || !$end)
+ {
+ $response['error'] = true;
+ $response['error_message'] = 'Missing required argument.';
+
+ return $response;
+ }
+
+ $response['call']['uid'] = $uid;
+ $response['call']['end'] = $end;
+
+ return $response;
+ }
}
diff --git a/adapters/TwilioVirtualNumberAdapter.php b/adapters/TwilioVirtualNumberAdapter.php
index b5065e7..a753d9e 100644
--- a/adapters/TwilioVirtualNumberAdapter.php
+++ b/adapters/TwilioVirtualNumberAdapter.php
@@ -190,6 +190,16 @@ class TwilioVirtualNumberAdapter implements AdapterInterface
{
return false;
}
+
+ public static function meta_support_inbound_call_callback(): bool
+ {
+ return false;
+ }
+
+ public static function meta_support_end_call_callback(): bool
+ {
+ return false;
+ }
public function send(string $destination, string $text, bool $flash = false, bool $mms = false, array $medias = []) : array
{
@@ -336,4 +346,14 @@ class TwilioVirtualNumberAdapter implements AdapterInterface
{
return [];
}
+
+ public function inbound_call_callback(): array
+ {
+ return [];
+ }
+
+ public function end_call_callback(): array
+ {
+ return [];
+ }
}
diff --git a/controllers/publics/Call.php b/controllers/internals/Call.php
similarity index 62%
rename from controllers/publics/Call.php
rename to controllers/internals/Call.php
index 55acbb2..5e3dcf7 100644
--- a/controllers/publics/Call.php
+++ b/controllers/internals/Call.php
@@ -16,7 +16,7 @@ namespace controllers\internals;
protected $model;
/**
- * Create a media.
+ * Create a call.
*
* @param int $id_user : Id of the user
* @param int $id_phone : Id of the phone that emitted (outbound) or received (inbound) the call
@@ -50,11 +50,11 @@ namespace controllers\internals;
switch ($direction)
{
case \models\Call::DIRECTION_OUTBOUND :
- null === $destination ?: return false;
+ if (null === $destination) { return false; }
break;
case \models\Call::DIRECTION_INBOUND :
- null === $origin ?: return false;
+ if (null === $origin) { return false; }
break;
default :
@@ -78,4 +78,50 @@ namespace controllers\internals;
return $this->get_model()->insert($call);
}
+
+
+ /**
+ * End a call
+ *
+ * @param int $id_user : Id of the user to end call for
+ * @param int $id_phone : If of the phone to end call for
+ * @param string $uid : Uid of the call to end
+ * @param string $end : End date of the call, format Y-m-d H:i:s
+ *
+ * @return bool : False if cannot end phone call, true else
+ */
+ public function end(int $id_user, int $id_phone, string $uid, string $end)
+ {
+ if (!\controllers\internals\Tool::validate_date($end, 'Y-m-d H:i:s'))
+ {
+ return false;
+ }
+
+ $call = $this->get_model()->get_by_uid_and_phone_for_user($id_user, $id_phone, $uid);
+ if (!$call)
+ {
+ return false;
+ }
+
+ if (new \DateTime($end) < new \DateTime($call['start']))
+ {
+ return false;
+ }
+
+ $datas = [
+ 'end' => $end,
+ ];
+
+ return (bool) $this->get_model()->update_for_user($id_user, $call['id'], $datas);
+ }
+
+ /**
+ * Get the model for the Controller.
+ */
+ protected function get_model(): \descartes\Model
+ {
+ $this->model = $this->model ?? new \models\Call($this->bdd);
+
+ return $this->model;
+ }
}
diff --git a/controllers/publics/Callback.php b/controllers/publics/Callback.php
index 5c6e32e..4f2160c 100644
--- a/controllers/publics/Callback.php
+++ b/controllers/publics/Callback.php
@@ -27,6 +27,7 @@ use Monolog\Logger;
private $internal_adapter;
private $internal_media;
private $internal_phone;
+ private $internal_call;
public function __construct()
{
@@ -37,7 +38,8 @@ use Monolog\Logger;
$this->internal_received = new \controllers\internals\Received($bdd);
$this->internal_media = new \controllers\internals\Media($bdd);
$this->internal_adapter = new \controllers\internals\Adapter();
- $this->internal_phone = new \controllers\internals\Phone();
+ $this->internal_phone = new \controllers\internals\Phone($bdd);
+ $this->internal_call = new \controllers\internals\Call($bdd);
//Logger
$this->logger = new Logger('Callback ' . uniqid());
@@ -283,73 +285,81 @@ use Monolog\Logger;
$response = $phone['adapter']::inbound_call_callback();
if ($response['error'])
{
- $this->logger->error('Callback reception with adapter ' . $adapter_uid . ' failed : ' . $response['error_message']);
+ $this->logger->error('Callback inbound_call failed : ' . $response['error_message']);
return false;
}
- $sms = $response['sms'];
- $mms = !empty($sms['mms']);
- $medias = empty($sms['medias']) ? [] : $sms['medias'];
- $media_ids = [];
+ $call = $response['call'];
+ $result = $this->internal_call->create($this->user['id'], $id_phone, $call['uid'], \models\Call::DIRECTION_INBOUND, $call['start'], $call['end'] ?? null, $call['origin']);
- //We create medias to link to the sms
- if ($mms)
+ if (!$result)
{
- foreach ($medias as $media)
- {
- try
- {
- $media['mimetype'] = empty($media['mimetype']) ? mime_content_type($media['filepath']) : $media['mimetype'];
+ $this->logger->error('Callback inbound_call failed because cannot create call ' . json_encode($call));
- $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;
- }
- }
+ return false;
}
- $response = $this->internal_received->receive($this->user['id'], $id_phone, $sms['text'], $sms['origin'], $sms['at'], \models\Received::STATUS_UNREAD, $mms, $media_ids);
+ $this->logger->info('Callback inbound_call successfully received inbound call : ' . json_encode($call));
+
+ return true;
+ }
+
+
+ /**
+ * Function call on end call notification
+ * We return nothing, and we let the adapter do his things.
+ *
+ * @param int $id_phone : Phone id
+ *
+ * @return bool : true on success, false on error
+ */
+ public function end_call(int $id_phone)
+ {
+ $this->logger->info('Callback end call with phone : ' . $id_phone);
+ $phone = $this->internal_phone->get_for_user($this->user['id'], $id_phone);
+
+ if (!$phone)
+ {
+ $this->logger->error('Callback end call use non existing phone : ' . $id_phone);
+
+ return false;
+ }
+
+ if (!class_exists($phone['adapter']))
+ {
+ $this->logger->error('Callback end call use non existing adapter : ' . $phone['adapter']);
+
+ return false;
+ }
+
+ if (!$phone['adapter']::meta_support_end_call_callback())
+ {
+ $this->logger->error('Callback end call use adapter ' . $phone['adapter'] . ' which does not support end call callback.');
+
+ return false;
+ }
+
+ $response = $phone['adapter']::end_call_callback();
if ($response['error'])
{
- $this->logger->error('Failed receive message : ' . json_encode($sms) . ' with error : ' . $response['error_message']);
+ $this->logger->error('Callback end call failed : ' . $response['error_message']);
return false;
}
- $this->logger->info('Callback reception successfully received message : ' . json_encode($sms));
+ $call = $response['call'];
+ $result = $this->internal_call->end($this->user['id'], $id_phone, $call['uid'], $call['end']);
+ if (!$result)
+ {
+ $this->logger->error('Callback end call failed because cannot update call ' . json_encode($call));
+
+ return false;
+ }
+
+ $this->logger->info('Callback end call successfully update call : ' . json_encode($call));
+
return true;
}
}
diff --git a/models/Call.php b/models/Call.php
index fee82b3..dd3e8a9 100644
--- a/models/Call.php
+++ b/models/Call.php
@@ -19,6 +19,26 @@ namespace models;
const DIRECTION_INBOUND = 'inbound';
const DIRECTION_OUTBOUND = 'outbound';
+ /**
+ * Get a call for a user by his phone and uid
+ *
+ * @param int $id_user : user id
+ * @param int $id_phone : phone id
+ * @param int $uid : call uid
+ *
+ * @return array : the call or an empty array
+ */
+ public function get_by_uid_and_phone_for_user($id_user, $id_phone, $uid)
+ {
+ $where = [
+ 'id_user' => $id_user,
+ 'id_phone' => $id_phone,
+ 'uid' => $uid,
+ ];
+
+ return $this->_select_one($this->get_table_name(), $where);
+ }
+
/**
* Return table name.
*/
diff --git a/models/StandardModel.php b/models/StandardModel.php
index 32b9686..45bf967 100644
--- a/models/StandardModel.php
+++ b/models/StandardModel.php
@@ -158,9 +158,9 @@ namespace models;
*
* @return int : number of modified rows
*/
- public function update_for_user(int $id_user, int $id, array $entry)
+ public function update_for_user(int $id_user, int $id, array $data)
{
- return $this->_update($this->get_table_name(), $entry, ['id_user' => $id_user, 'id' => $id]);
+ return $this->_update($this->get_table_name(), $data, ['id_user' => $id_user, 'id' => $id]);
}
/**
From c63f3bebba5732791a94f450cf6fc5e179228188 Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Wed, 24 Mar 2021 00:22:17 +0100
Subject: [PATCH 12/40] add page for calls
---
adapters/TestAdapter.php | 4 +-
controllers/publics/Call.php | 93 +++++++++++++++++
controllers/publics/Phone.php | 48 ++-------
models/Call.php | 42 ++++++++
routes.php | 12 ++-
templates/call/list.php | 145 +++++++++++++++++++++++++++
templates/conditional_group/list.php | 2 +-
templates/incs/nav.php | 5 +-
templates/phone/list.php | 17 ++++
9 files changed, 323 insertions(+), 45 deletions(-)
create mode 100644 controllers/publics/Call.php
create mode 100644 templates/call/list.php
diff --git a/adapters/TestAdapter.php b/adapters/TestAdapter.php
index 8a318a4..4ddb9c6 100644
--- a/adapters/TestAdapter.php
+++ b/adapters/TestAdapter.php
@@ -149,12 +149,12 @@ namespace adapters;
public static function meta_support_inbound_call_callback(): bool
{
- return false;
+ return true;
}
public static function meta_support_end_call_callback(): bool
{
- return false;
+ return true;
}
public function send(string $destination, string $text, bool $flash = false, bool $mms = false, array $medias = []) : array
diff --git a/controllers/publics/Call.php b/controllers/publics/Call.php
new file mode 100644
index 0000000..fb05bc2
--- /dev/null
+++ b/controllers/publics/Call.php
@@ -0,0 +1,93 @@
+
+ *
+ * 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 des calls.
+ */
+ class Call extends \descartes\Controller
+ {
+ private $internal_call;
+
+ /**
+ * Cette fonction est appelée avant toute les autres :
+ * Elle vérifie que l'utilisateur est bien connecté.
+ *
+ * @return void;
+ */
+ public function __construct()
+ {
+ $bdd = \descartes\Model::_connect(DATABASE_HOST, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD);
+
+ $this->internal_call = new \controllers\internals\Call($bdd);
+
+ \controllers\internals\Tool::verifyconnect();
+ }
+
+ /**
+ * Page for showing calls list
+ */
+ public function list()
+ {
+ $this->render('call/list');
+ }
+
+ /**
+ * Return calls list as json.
+ */
+ public function list_json()
+ {
+ $entities = $this->internal_call->list_for_user($_SESSION['user']['id']);
+ foreach ($entities as &$entity)
+ {
+ switch ($entity['direction'])
+ {
+ case \models\Call::DIRECTION_INBOUND :
+ $entity['origin_formatted'] = \controllers\internals\Tool::phone_link($entity['origin']);
+ break;
+
+ case \models\Call::DIRECTION_OUTBOUND :
+ $entity['destination_formatted'] = \controllers\internals\Tool::phone_link($entity['destination']);
+ break;
+ }
+ }
+
+ header('Content-Type: application/json');
+ echo json_encode(['data' => $entities]);
+ }
+
+ /**
+ * Delete a list of calls
+ *
+ * @param array int $_GET['ids'] : Ids of calls to delete
+ * @param string $csrf : csrf token
+ *
+ * @return boolean;
+ */
+ public function delete(string $csrf)
+ {
+ if (!$this->verify_csrf($csrf))
+ {
+ \FlashMessage\FlashMessage::push('danger', 'Jeton CSRF invalid !');
+
+ return $this->redirect(\descartes\Router::url('Call', 'list'));
+ }
+
+ $ids = $_GET['ids'] ?? [];
+ foreach ($ids as $id)
+ {
+ $this->internal_call->delete_for_user($_SESSION['user']['id'], $id);
+ }
+
+ return $this->redirect(\descartes\Router::url('Call', 'list'));
+ }
+ }
diff --git a/controllers/publics/Phone.php b/controllers/publics/Phone.php
index 329572a..81d6249 100644
--- a/controllers/publics/Phone.php
+++ b/controllers/publics/Phone.php
@@ -33,43 +33,7 @@ class Phone extends \descartes\Controller
*/
public function list()
{
- $id_user = $_SESSION['user']['id'];
- $api_key = $_SESSION['user']['api_key'];
- $phones = $this->internal_phone->list_for_user($id_user);
-
- $adapters = [];
- $adapters = $this->internal_adapter->list_adapters();
- foreach ($adapters as $key => $adapter)
- {
- unset($adapters[$key]);
- $adapters[$adapter['meta_classname']] = $adapter;
- }
-
- foreach ($phones as &$phone)
- {
- $adapter = $adapters[$phone['adapter']] ?? false;
-
- if (!$adapter)
- {
- $phone['adapter'] = 'Inconnu';
-
- continue;
- }
-
- $phone['adapter'] = $adapter['meta_name'];
-
- if ($adapter['meta_support_reception'])
- {
- $phone['callback_reception'] = \descartes\Router::url('Callback', 'reception', ['adapter_uid' => $adapter['meta_uid'], 'id_phone' => $phone['id']], ['api_key' => $api_key]);
- }
-
- if ($adapter['meta_support_status_change'])
- {
- $phone['callback_status'] = \descartes\Router::url('Callback', 'update_sended_status', ['adapter_uid' => $adapter['meta_uid']], ['api_key' => $api_key]);
- }
- }
-
- $this->render('phone/list', ['phones' => $phones]);
+ $this->render('phone/list');
}
/**
@@ -111,6 +75,16 @@ class Phone extends \descartes\Controller
{
$phone['callback_status'] = \descartes\Router::url('Callback', 'update_sended_status', ['adapter_uid' => $adapter['meta_uid']], ['api_key' => $api_key]);
}
+
+ if ($adapter['meta_support_inbound_call_callback'])
+ {
+ $phone['callback_inbound_call'] = \descartes\Router::url('Callback', 'inbound_call', ['id_phone' => $phone['id']], ['api_key' => $api_key]);
+ }
+
+ if ($adapter['meta_support_end_call_callback'])
+ {
+ $phone['callback_end_call'] = \descartes\Router::url('Callback', 'end_call', ['id_phone' => $phone['id']], ['api_key' => $api_key]);
+ }
}
header('Content-Type: application/json');
diff --git a/models/Call.php b/models/Call.php
index dd3e8a9..e8e08ce 100644
--- a/models/Call.php
+++ b/models/Call.php
@@ -18,6 +18,48 @@ namespace models;
{
const DIRECTION_INBOUND = 'inbound';
const DIRECTION_OUTBOUND = 'outbound';
+
+ /**
+ * Return a list of call for a user.
+ * Add a column contact_name and phone_name when available.
+ *
+ * @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 `call`.*, contact.name as contact_name, phone.name as phone_name
+ FROM `call`
+ LEFT JOIN contact
+ ON contact.number = `call`.destination
+ OR contact.number = `call`.origin
+ LEFT JOIN phone
+ ON phone.id = `call`.id_phone
+ WHERE `call`.id_user = :id_user
+ ';
+
+ 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);
+ }
/**
* Get a call for a user by his phone and uid
diff --git a/routes.php b/routes.php
index bf66579..0384728 100644
--- a/routes.php
+++ b/routes.php
@@ -58,10 +58,7 @@
],
'Event' => [
- 'list' => [
- '/event/',
- '/event/p/{page}/',
- ],
+ 'list' => '/event/',
'list_json' => '/event/json/',
'delete' => '/event/delete/{csrf}/',
],
@@ -155,6 +152,12 @@
'delete' => '/phone/delete/{csrf}/',
],
+ 'Call' => [
+ 'list' => '/call/',
+ 'list_json' => '/call/json/',
+ 'delete' => '/call/delete/{csrf}/',
+ ],
+
'Webhook' => [
'list' => '/webhook/',
'list_json' => '/webhook/json/',
@@ -169,6 +172,7 @@
'update_sended_status' => '/callback/status/{adapter_uid}/',
'reception' => '/callback/reception/{adapter_uid}/{id_phone}/',
'inbound_call' => '/callback/inbound_call/{id_phone}/',
+ 'end_call' => '/callback/end_call/{id_phone}/',
],
'Api' => [
diff --git a/templates/call/list.php b/templates/call/list.php
new file mode 100644
index 0000000..7fed56a
--- /dev/null
+++ b/templates/call/list.php
@@ -0,0 +1,145 @@
+render('incs/head', ['title' => 'Appels - Show All'])
+?>
+
+render('incs/nav', ['page' => 'calls'])
+?>
+
+
+
+
+
+
+
+
+
+
+
Liste des appels
+
+
+
+
+
+
+
+
+
+render('incs/footer');
diff --git a/templates/conditional_group/list.php b/templates/conditional_group/list.php
index ecb104e..b8ce2ad 100644
--- a/templates/conditional_group/list.php
+++ b/templates/conditional_group/list.php
@@ -1,7 +1,7 @@
render('incs/head', ['title' => 'ConditionalGroupes Conditionnels - Show All'])
+ $this->render('incs/head', ['title' => 'Groupes Conditionnels - Show All'])
?>
Logs
-
+
>
SMS STOP
+ >
+ Appels
+
>
Évènements
diff --git a/templates/phone/list.php b/templates/phone/list.php
index c72c6a1..2d93d65 100644
--- a/templates/phone/list.php
+++ b/templates/phone/list.php
@@ -108,7 +108,24 @@ jQuery(document).ready(function ()
} else {
html += 'Non disponible.
';
}
+
+ html += ' ';
+ html += 'Notification d\'appel entrant :
';
+ if (row.callback_inbound_call) {
+ html += '' + row.callback_inbound_call + '
';
+ } else {
+ html += 'Non disponible.
';
+ }
+
+ html += ' ';
+ html += 'Notification de fin d\'appel :
';
+ console.log(row);
+ if (row.callback_end_call) {
+ html += '' + row.callback_end_call + '
';
+ } else {
+ html += 'Non disponible.
';
+ }
return html;
},
From 86c6d65fe4c6532dba0b744bff20d10d678e7bf3 Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Fri, 26 Mar 2021 17:31:09 +0100
Subject: [PATCH 13/40] Update callback to check valid call return
---
composer.json | 3 ++-
controllers/publics/Callback.php | 17 ++++++++++++++++-
2 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/composer.json b/composer.json
index 4173614..7522544 100644
--- a/composer.json
+++ b/composer.json
@@ -13,7 +13,8 @@
"twilio/sdk": "^6.1",
"symfony/yaml": "^5.0",
"phpmailer/phpmailer": "^6.1",
- "ralouphie/mimey": "^2.1"
+ "ralouphie/mimey": "^2.1",
+ "kreait/firebase-php": "^5.14"
},
"require-dev": {
}
diff --git a/controllers/publics/Callback.php b/controllers/publics/Callback.php
index 4f2160c..550a4c7 100644
--- a/controllers/publics/Callback.php
+++ b/controllers/publics/Callback.php
@@ -258,7 +258,7 @@ use Monolog\Logger;
*/
public function inbound_call(int $id_phone)
{
- $this->logger->info('Callback reception call with phone : ' . $id_phone);
+ $this->logger->info('Callback inbound_call call with phone : ' . $id_phone);
$phone = $this->internal_phone->get_for_user($this->user['id'], $id_phone);
if (!$phone)
@@ -291,6 +291,14 @@ use Monolog\Logger;
}
$call = $response['call'];
+
+ if (empty($call) || empty($call['uid']) || empty($call['start']) || empty($call['origin']))
+ {
+ $this->logger->error('Callback inbound_call failed : missing required param in call return');
+
+ return false;
+ }
+
$result = $this->internal_call->create($this->user['id'], $id_phone, $call['uid'], \models\Call::DIRECTION_INBOUND, $call['start'], $call['end'] ?? null, $call['origin']);
if (!$result)
@@ -349,6 +357,13 @@ use Monolog\Logger;
}
$call = $response['call'];
+ if (empty($call) || empty($call['uid']) || empty($call['end']))
+ {
+ $this->logger->error('Callback end call failed : missing required param in call return');
+
+ return false;
+ }
+
$result = $this->internal_call->end($this->user['id'], $id_phone, $call['uid'], $call['end']);
if (!$result)
From a3a672daf8ba0ae347c868e9e6b5d5f083e4fb71 Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Fri, 26 Mar 2021 17:32:36 +0100
Subject: [PATCH 14/40] ignore descartes env changes
---
.gitignore | 1 +
1 file changed, 1 insertion(+)
diff --git a/.gitignore b/.gitignore
index 7438c69..2dd1406 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@ scripts/
composer.lock
env.*
phinx.*
+descartes/env.php
data/test_write_sms.json
data/test_read_sms.json
From 62d140d98703268ac5c533acd5efc33653abf56f Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Fri, 26 Mar 2021 17:39:16 +0100
Subject: [PATCH 15/40] fix bad invisible char callback
---
controllers/publics/Callback.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/controllers/publics/Callback.php b/controllers/publics/Callback.php
index 550a4c7..66de202 100644
--- a/controllers/publics/Callback.php
+++ b/controllers/publics/Callback.php
@@ -292,7 +292,7 @@ use Monolog\Logger;
$call = $response['call'];
- if (empty($call) || empty($call['uid']) || empty($call['start']) || empty($call['origin']))
+ if (empty($call) || empty($call['uid']) || empty($call['start']) || empty($call['origin']))
{
$this->logger->error('Callback inbound_call failed : missing required param in call return');
@@ -357,7 +357,7 @@ use Monolog\Logger;
}
$call = $response['call'];
- if (empty($call) || empty($call['uid']) || empty($call['end']))
+ if (empty($call) || empty($call['uid']) || empty($call['end']))
{
$this->logger->error('Callback end call failed : missing required param in call return');
From fcbb44df9821bbafdd1bf83fca488cfb76a58f7d Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Fri, 26 Mar 2021 17:44:51 +0100
Subject: [PATCH 16/40] fix bad foreign key id_phone pointing to user
---
db/migrations/20210322223812_create_call.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/db/migrations/20210322223812_create_call.php b/db/migrations/20210322223812_create_call.php
index dda93d0..6060c09 100644
--- a/db/migrations/20210322223812_create_call.php
+++ b/db/migrations/20210322223812_create_call.php
@@ -41,7 +41,7 @@ class CreateCall extends AbstractMigration
->addColumn('origin', 'string', ['limit' => 20, 'null' => true])
->addColumn('destination', 'string', ['limit' => 20, 'null' => true])
->addForeignKey('id_user', 'user', 'id', ['delete' => 'CASCADE', 'update' => 'CASCADE'])
- ->addForeignKey('id_phone', 'user', 'id', ['delete' => 'SET_NULL', 'update' => 'CASCADE'])
+ ->addForeignKey('id_phone', 'phone', 'id', ['delete' => 'SET_NULL', 'update' => 'CASCADE'])
->create();
}
}
From 6ce6a85f13e7f4eee57d2cb61ec6f6a24381df8e Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Fri, 26 Mar 2021 17:55:47 +0100
Subject: [PATCH 17/40] Add webhook trigger
---
controllers/internals/Call.php | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/controllers/internals/Call.php b/controllers/internals/Call.php
index 5e3dcf7..c04e198 100644
--- a/controllers/internals/Call.php
+++ b/controllers/internals/Call.php
@@ -75,8 +75,17 @@ namespace controllers\internals;
{
return false;
}
+
+ $new_call_id = $this->get_model()->insert($call);
+ if (!$new_call_id)
+ {
+ return false;
+ }
+
+ $internal_webhook = new Webhook($this->bdd);
+ $internal_webhook->trigger($id_user, \models\Webhook::TYPE_INBOUND_CALL, $call);
- return $this->get_model()->insert($call);
+ return $new_call_id;
}
From 626b59080ead24d15d44a28569aaf1645c185270 Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Fri, 26 Mar 2021 17:59:09 +0100
Subject: [PATCH 18/40] add id to call webhook
---
controllers/internals/Call.php | 2 ++
1 file changed, 2 insertions(+)
diff --git a/controllers/internals/Call.php b/controllers/internals/Call.php
index c04e198..4852c72 100644
--- a/controllers/internals/Call.php
+++ b/controllers/internals/Call.php
@@ -81,6 +81,8 @@ namespace controllers\internals;
{
return false;
}
+
+ $call['id'] = $new_call_id;
$internal_webhook = new Webhook($this->bdd);
$internal_webhook->trigger($id_user, \models\Webhook::TYPE_INBOUND_CALL, $call);
From 92146ba8e1c529ddb1afd9ad4faa1747df660acd Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Fri, 26 Mar 2021 18:53:20 +0100
Subject: [PATCH 19/40] Add tool to clean unused medias
---
controllers/internals/Console.php | 19 +++++++++++++++++++
controllers/internals/Media.php | 25 +++++++++++++++++++++++--
models/Media.php | 23 +++++++++++++++++++++++
3 files changed, 65 insertions(+), 2 deletions(-)
diff --git a/controllers/internals/Console.php b/controllers/internals/Console.php
index ccb48b6..f2f3986 100644
--- a/controllers/internals/Console.php
+++ b/controllers/internals/Console.php
@@ -151,4 +151,23 @@ namespace controllers\internals;
exit($success ? 0 : 1);
}
+
+
+ /**
+ * Delete medias that are no longer usefull
+ */
+ public function clean_unused_medias()
+ {
+ $bdd = \descartes\Model::_connect(DATABASE_HOST, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD, 'UTF8');
+ $internal_media = new \controllers\internals\Media($bdd);
+
+ $medias = $internal_media->gets_unused();
+
+ foreach ($medias as $media)
+ {
+ $success = $internal_media->delete_for_user($media['id_user'], $media['id']);
+
+ echo ($success === false ? '[KO]' : '[OK]') . ' - ' . $media['path'] . "\n";
+ }
+ }
}
diff --git a/controllers/internals/Media.php b/controllers/internals/Media.php
index 2f11b76..5e33498 100644
--- a/controllers/internals/Media.php
+++ b/controllers/internals/Media.php
@@ -174,7 +174,7 @@ namespace controllers\internals;
* @param int $id_user : User id
* @param int $id : Entry id
*
- * @return int : Number of removed rows
+ * @return mixed bool|int : False on error, else number of removed rows
*/
public function delete_for_user(int $id_user, int $id_media): bool
{
@@ -184,7 +184,19 @@ namespace controllers\internals;
return false;
}
- unlink($media['path']);
+ //Delete file
+ try
+ {
+ $filepath = PWD_DATA . '/' . $media['path'];
+ if (file_exists($filepath))
+ {
+ unlink($filepath);
+ }
+ }
+ catch (\Throwable $t)
+ {
+ return false;
+ }
return $this->get_model()->delete_for_user($id_user, $id_media);
}
@@ -225,6 +237,15 @@ namespace controllers\internals;
return $this->get_model()->gets_for_received($id_received);
}
+ /**
+ * Find medias that are not used
+ * @return array
+ */
+ public function gets_unused()
+ {
+ return $this->get_model()->gets_unused();
+ }
+
/**
* Get the model for the Controller.
*/
diff --git a/models/Media.php b/models/Media.php
index 827bcc1..830acd7 100644
--- a/models/Media.php
+++ b/models/Media.php
@@ -245,6 +245,29 @@ namespace models;
return $this->_delete('media_sended', $where);
}
+ /**
+ * Find all unused medias
+ * @return array
+ */
+ public function gets_unused ()
+ {
+ $query = '
+ SELECT `media`.*
+ FROM `media`
+ LEFT JOIN `media_sended`
+ ON `media`.id = `media_sended`.id_media
+ LEFT JOIN `media_received`
+ ON `media`.id = `media_received`.id_media
+ LEFT JOIN `media_scheduled`
+ ON `media`.id = `media_scheduled`.id_media
+ WHERE `media_sended`.id IS NULL
+ AND `media_received`.id IS NULL
+ AND `media_scheduled`.id IS NULL
+ ';
+
+ return $this->_run_query($query);
+ }
+
/**
* Return table name.
*/
From 04a40049ce0e7a39fe8400acf4a91f2d3a865bab Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Fri, 26 Mar 2021 23:32:29 +0100
Subject: [PATCH 20/40] refactor all media create to centralise copy of medias
and save, etc.
---
.gitignore | 2 +-
.htaccess | 2 +-
adapters/TestAdapter.php | 45 ++-
controllers/internals/Media.php | 471 +++++++++++++++--------------
controllers/internals/Received.php | 29 +-
controllers/internals/Sended.php | 4 +-
controllers/internals/Tool.php | 109 +++----
controllers/internals/User.php | 1 +
controllers/publics/Api.php | 9 +-
controllers/publics/Callback.php | 52 +---
controllers/publics/Discussion.php | 15 +-
controllers/publics/Scheduled.php | 16 +-
daemons/Phone.php | 2 +-
templates/discussion/show.php | 2 +-
templates/received/list.php | 2 +-
templates/scheduled/edit.php | 2 +-
templates/scheduled/list.php | 2 +-
templates/sended/list.php | 2 +-
18 files changed, 400 insertions(+), 367 deletions(-)
diff --git a/.gitignore b/.gitignore
index 2dd1406..78ab9f5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,6 @@ descartes/env.php
data/test_write_sms.json
data/test_read_sms.json
-data/medias/
+data/public/
!*.dist
diff --git a/.htaccess b/.htaccess
index ad6df7b..ce35d68 100644
--- a/.htaccess
+++ b/.htaccess
@@ -1,5 +1,5 @@
RewriteEngine on
RewriteRule ^assets - [L]
RewriteRule ^.well-known - [L]
-RewriteRule ^data/medias - [L]
+RewriteRule ^data/public/ - [L]
RewriteRule . index.php
diff --git a/adapters/TestAdapter.php b/adapters/TestAdapter.php
index 4ddb9c6..b92d78c 100644
--- a/adapters/TestAdapter.php
+++ b/adapters/TestAdapter.php
@@ -184,6 +184,20 @@ namespace adapters;
return $response;
}
+ /**
+ * Read from a files to simulate sms reception.
+ * In the file we expect a json string representing an array of sms of format :
+ * {
+ * "at" : "2021-03-26 11:21:48",
+ * "medias" : [
+ * "https://unsplash.com/photos/q4DJVtxES0w/download?force=true&w=640",
+ * "/tmp/somelocalfile.jpg"
+ * ],
+ * "mms" : true,
+ * "origin" : "+33612345678",
+ * "text" : "SMS Text"
+ * }
+ */
public function read(): array
{
$response = [
@@ -223,7 +237,36 @@ namespace adapters;
continue;
}
- $response['smss'][] = $decode_sms;
+ $clean_sms = [
+ 'at' => $decode_sms['at'],
+ 'text' => $decode_sms['text'],
+ 'origin' => $decode_sms['origin'],
+ 'mms' => $decode_sms['mms'],
+ 'medias' => [],
+ ];
+
+ //In medias we want a media URI or URL
+ foreach ($decode_sms['medias'] ?? [] as $media)
+ {
+ $tempfile = tempnam('/tmp', 'raspisms-media-');
+ if (!$tempfile)
+ {
+ continue;
+ }
+
+ $copy = copy($media, $tempfile);
+ if (!$copy)
+ {
+ continue;
+ }
+
+ $clean_sms['medias'][] = [
+ 'filepath' => $tempfile,
+ 'extension' => pathinfo($media, PATHINFO_EXTENSION) ?: null,
+ ];
+ }
+
+ $response['smss'][] = $clean_sms;
}
return $response;
diff --git a/controllers/internals/Media.php b/controllers/internals/Media.php
index 5e33498..e2f5dd5 100644
--- a/controllers/internals/Media.php
+++ b/controllers/internals/Media.php
@@ -11,248 +11,281 @@
namespace controllers\internals;
- class Media extends StandardController
+class Media extends StandardController
+{
+ const DEFAULT_CHMOD = 0660;
+
+ protected $model;
+
+ /**
+ * Create a media.
+ *
+ * @param int $id_user : Id of the user
+ * @param string $tmpfile_path : Path of the temporary local copy of the media
+ * @param ?string $extension : Extension to use for the media
+ *
+ * @return int : Exception on error, new media id else
+ */
+ public function create(int $id_user, string $tmpfile_path, ?string $extension = null)
{
- protected $model;
-
- /**
- * Create a media.
- *
- * @param int $id_user : Id of the user
- * @param string $path : path of the media in data dir
- *
- * @return mixed bool|int : false on error, new media id else
- */
- public function create(int $id_user, string $path)
+ $user_path = \controllers\internals\Tool::create_user_public_path($id_user);
+ if (!file_exists($tmpfile_path) || !is_readable($tmpfile_path))
{
- $data = [
- 'path' => $path,
- 'id_user' => $id_user,
- ];
-
- return $this->get_model()->insert($data);
+ throw new \Exception('File ' . $tmpfile_path . ' is not readable.');
}
- /**
- * Upload and create a media
- *
- * @param int $id_user : Id of the user
- * @param array $file : array representing uploaded file, extracted from $_FILES['yourfile']
- * @return mixed bool | int : False on error, or new media id on success
- */
- public function upload_and_create_for_user(int $id_user, array $file)
+ $mimey = new \Mimey\MimeTypes;
+ $extension = $extension ?? $mimey->getExtension(mime_content_type($tmpfile_path));
+
+ $new_file_name = \controllers\internals\Tool::random_uuid() . '.' . $extension;
+ $new_file_path = $user_path . '/' . $new_file_name;
+ $new_file_relpath = $id_user . '/' . $new_file_name;
+
+ if (!rename($tmpfile_path, $new_file_path))
{
- $user_media_path = PWD_DATA . '/medias/' . $id_user;
-
- //Create user medias dir if not exists
- if (!file_exists($user_media_path))
- {
- if (!mkdir($user_media_path, fileperms(PWD_DATA), true))
- {
- return false;
- }
- }
-
- $upload_result = \controllers\internals\Tool::save_uploaded_file($file, $user_media_path);
- if ($upload_result['success'] !== true)
- {
- return false;
- }
-
- $new_filepath = 'medias/' . $id_user . '/' . $upload_result['content'];
- return $this->create($id_user, $new_filepath);
+ throw new \Exception('Cannot create file ' . $new_file_path);
}
- /**
- * 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, string $resource_type, int $resource_id)
+ if (!chown($new_file_path, fileowner($user_path)))
{
- 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;
- }
+ throw new \Exception('Cannot give file ' . $new_file_path . ' to user : ' . fileowner($user_path));
}
-
- /**
- * 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)
+ if (!chgrp($new_file_path, filegroup($user_path)))
{
- 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(string $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;
- }
+ throw new \Exception('Cannot give file ' . $new_file_path . ' to group : ' . filegroup($user_path));
}
- /**
- * Update a media for a user.
- *
- * @param int $id_user : user id
- * @param int $id_media : Media id
- * @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, string $path): bool
+ if (!chmod($new_file_path, self::DEFAULT_CHMOD))
{
- $media = [
- 'path' => $path,
- ];
-
- return (bool) $this->get_model()->update_for_user($id_user, $id_media, $media);
+ throw new \Exception('Cannot give file ' . $new_file_path . ' rights : ' . self::DEFAULT_CHMOD);
}
- /**
- * Delete a media for a user.
- *
- * @param int $id_user : User id
- * @param int $id : Entry id
- *
- * @return mixed bool|int : False on error, else number of removed rows
- */
- public function delete_for_user(int $id_user, int $id_media): bool
+ $data = [
+ 'path' => $new_file_relpath,
+ 'id_user' => $id_user,
+ ];
+
+ $new_media_id = $this->get_model()->insert($data);
+ if (!$new_media_id)
{
- $media = $this->get_model()->get_for_user($id_user, $id_media);
- if (!$media)
- {
- return false;
- }
-
- //Delete file
- try
- {
- $filepath = PWD_DATA . '/' . $media['path'];
- if (file_exists($filepath))
- {
- unlink($filepath);
- }
- }
- catch (\Throwable $t)
- {
- return false;
- }
-
- return $this->get_model()->delete_for_user($id_user, $id_media);
+ throw new \Exception('Cannot insert media in database.');
}
- /**
- * Find medias for a scheduled.
- *
- * @param int $id_scheduled : Scheduled id to fin medias for
- *
- * @return mixed : Medias || false
- */
- public function gets_for_scheduled(int $id_scheduled)
+ return $new_media_id;
+ }
+
+ /**
+ * Upload and create a media
+ *
+ * @param int $id_user : Id of the user
+ * @param array $file : array representing uploaded file, extracted from $_FILES['yourfile']
+ * @return int : Raise exception on error or return new media id on success
+ */
+ public function create_from_uploaded_file_for_user(int $id_user, array $file)
+ {
+ $upload_result = \controllers\internals\Tool::read_uploaded_file($file);
+ if ($upload_result['success'] !== true)
{
- return $this->get_model()->gets_for_scheduled($id_scheduled);
- }
-
- /**
- * Find medias for a sended and a user.
- *
- * @param int $id_sended : Scheduled id to fin medias for
- *
- * @return mixed : Medias || false
- */
- public function gets_for_sended(int $id_sended)
- {
- return $this->get_model()->gets_for_sended($id_sended);
- }
-
- /**
- * Find medias for a received and a user.
- *
- * @param int $id_received : Scheduled id to fin medias for
- *
- * @return mixed : Medias || false
- */
- public function gets_for_received(int $id_received)
- {
- return $this->get_model()->gets_for_received($id_received);
+ throw new \Exception($upload_result['content']);
}
- /**
- * Find medias that are not used
- * @return array
- */
- public function gets_unused()
- {
- return $this->get_model()->gets_unused();
- }
+ return $this->create($id_user, $upload_result['tmp_name'], $upload_result['extension']);
- /**
- * Get the model for the Controller.
- */
- protected function get_model(): \descartes\Model
- {
- $this->model = $this->model ?? new \models\Media($this->bdd);
+ $new_filepath = 'medias/' . $id_user . '/' . $upload_result['content'];
+ return $this->create($id_user, $new_filepath);
+ }
- return $this->model;
+ /**
+ * 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, string $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(string $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;
+ }
+ }
+
+ /**
+ * Update a media for a user.
+ *
+ * @param int $id_user : user id
+ * @param int $id_media : Media id
+ * @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, string $path): bool
+ {
+ $media = [
+ 'path' => $path,
+ ];
+
+ return (bool) $this->get_model()->update_for_user($id_user, $id_media, $media);
+ }
+
+ /**
+ * Delete a media for a user.
+ *
+ * @param int $id_user : User id
+ * @param int $id : Entry id
+ *
+ * @return mixed bool|int : False on error, else number of removed rows
+ */
+ public function delete_for_user(int $id_user, int $id_media): bool
+ {
+ $media = $this->get_model()->get_for_user($id_user, $id_media);
+ if (!$media)
+ {
+ return false;
+ }
+
+ //Delete file
+ try
+ {
+ $filepath = PWD_DATA . '/' . $media['path'];
+ if (file_exists($filepath))
+ {
+ unlink($filepath);
+ }
+ }
+ catch (\Throwable $t)
+ {
+ return false;
+ }
+
+ return $this->get_model()->delete_for_user($id_user, $id_media);
+ }
+
+ /**
+ * Find medias for a scheduled.
+ *
+ * @param int $id_scheduled : Scheduled id to fin medias for
+ *
+ * @return mixed : Medias || false
+ */
+ public function gets_for_scheduled(int $id_scheduled)
+ {
+ return $this->get_model()->gets_for_scheduled($id_scheduled);
+ }
+
+ /**
+ * Find medias for a sended and a user.
+ *
+ * @param int $id_sended : Scheduled id to fin medias for
+ *
+ * @return mixed : Medias || false
+ */
+ public function gets_for_sended(int $id_sended)
+ {
+ return $this->get_model()->gets_for_sended($id_sended);
+ }
+
+ /**
+ * Find medias for a received and a user.
+ *
+ * @param int $id_received : Scheduled id to fin medias for
+ *
+ * @return mixed : Medias || false
+ */
+ public function gets_for_received(int $id_received)
+ {
+ return $this->get_model()->gets_for_received($id_received);
+ }
+
+ /**
+ * Find medias that are not used
+ * @return array
+ */
+ public function gets_unused()
+ {
+ return $this->get_model()->gets_unused();
+ }
+
+ /**
+ * Get the model for the Controller.
+ */
+ protected function get_model(): \descartes\Model
+ {
+ $this->model = $this->model ?? new \models\Media($this->bdd);
+
+ return $this->model;
+ }
+}
diff --git a/controllers/internals/Received.php b/controllers/internals/Received.php
index c685adb..285a8ca 100644
--- a/controllers/internals/Received.php
+++ b/controllers/internals/Received.php
@@ -244,14 +244,17 @@ namespace controllers\internals;
* @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
+ * @param array $medias : Empty array if no medias, or medias to create and link to the received message. Format : [[
+ * string 'filepath' => local path to a readable copy of the media,
+ * ?string 'extension' => extension to use for the file or null
+ * ], ...]
*
* @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, bool $mms = false, array $media_ids = []): 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 $medias = []): array
{
$return = [
'error' => false,
@@ -269,6 +272,26 @@ namespace controllers\internals;
$is_command = true;
$text = $response;
}
+
+ //We create medias to link to the sms
+ $internal_media = new Media($this->bdd);
+ $media_ids = [];
+ if ($mms)
+ {
+ foreach ($medias as $media)
+ {
+ try
+ {
+ $new_media_id = $internal_media->create($id_user, $media['filepath'], $media['extension']);
+ $media_ids[] = $new_media_id;
+ }
+ catch (\Throwable $t)
+ {
+ $return['error_message'] = $t->getMessage();
+ continue; //Better loose the media than the message
+ }
+ }
+ }
$received_id = $this->create($id_user, $id_phone, $at, $text, $origin, $status, $is_command, $mms, $media_ids);
if (!$received_id)
@@ -287,7 +310,7 @@ namespace controllers\internals;
'origin' => $origin,
'command' => $is_command,
'mms' => $mms,
- 'medias' => $this->get_model()->gets_in_for_user($id_user, $media_ids),
+ 'medias' => $internal_media->gets_in_for_user($id_user, $media_ids),
];
$internal_webhook = new Webhook($this->bdd);
diff --git a/controllers/internals/Sended.php b/controllers/internals/Sended.php
index ee3ea04..c9575c9 100644
--- a/controllers/internals/Sended.php
+++ b/controllers/internals/Sended.php
@@ -211,8 +211,8 @@ namespace controllers\internals;
foreach ($medias as $media)
{
$media_uris[] = [
- 'http_url' => HTTP_PWD_DATA . '/' . $media['path'],
- 'local_uri' => PWD_DATA . '/' . $media['path'],
+ 'http_url' => HTTP_PWD_DATA_PUBLIC . '/' . $media['path'],
+ 'local_uri' => PWD_DATA_PUBLIC . '/' . $media['path'],
];
}
diff --git a/controllers/internals/Tool.php b/controllers/internals/Tool.php
index 0f42360..b1a795e 100644
--- a/controllers/internals/Tool.php
+++ b/controllers/internals/Tool.php
@@ -205,7 +205,7 @@ namespace controllers\internals;
*
* @param array $file : The array extracted from $_FILES['file']
*
- * @return array : ['success' => bool, 'content' => file handler | error message, 'error_code' => $file['error']]
+ * @return array : ['success' => bool, 'content' => file handler | error message, 'error_code' => $file['error'], 'mime_type' => server side calculated mimetype, 'extension' => original extension, 'tmp_name' => name of the tmp_file]
*/
public static function read_uploaded_file(array $file)
{
@@ -213,8 +213,9 @@ namespace controllers\internals;
'success' => false,
'content' => 'Une erreur inconnue est survenue.',
'error_code' => $file['error'] ?? 99,
- 'mime_type' => false,
- 'extension' => false,
+ 'mime_type' => null,
+ 'extension' => null,
+ 'tmp_name' => null,
];
if (UPLOAD_ERR_OK !== $file['error'])
@@ -277,73 +278,6 @@ namespace controllers\internals;
return $result;
}
- /**
- * 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' => new file name | error message, 'error_code' => $file['error']]
- */
- 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,
- 'content' => 'Une erreur inconnue est survenue.',
- 'error_code' => $file['error'] ?? 99,
- ];
-
- $upload_info = self::read_uploaded_file($file);
- if (!$upload_info['success'])
- {
- $result['content'] = $upload_info['content'];
- return $result;
- }
-
- if ($extension === null)
- {
- $extension = $upload_info['extension'];
- if ($extension === '' || $use_mimetype)
- {
- $mimey = new \Mimey\MimeTypes;
- $extension = $mimey->getExtension($upload_info['mime_type']);
- }
- }
-
- if ($filename === null)
- {
- $filename = self::random_uuid();
- }
-
- $filename = $filename . '.' . $extension;
- $filepath = $dirpath . '/' . $filename;
-
- if (file_exists($filepath) && !$override)
- {
- $result['content'] = 'Le fichier ' . $filepath . ' existe déjà.';
-
- return $result;
- }
-
- $success = move_uploaded_file($upload_info['tmp_name'], $filepath);
- if (!$success)
- {
- $result['content'] = 'Impossible de délplacer le fichier vers ' . $filepath;
-
- return $result;
- }
-
- $result['success'] = true;
- $result['content'] = $filename;
-
- return $result;
- }
-
-
/**
* Generate a highly random uuid based on timestamp and strong cryptographic random
*
@@ -354,4 +288,39 @@ namespace controllers\internals;
$bytes = random_bytes(16);
return time() . '-' . bin2hex($bytes);
}
+
+
+ /**
+ * Create a user data public path
+ * @param int $id_user : The user id
+ *
+ * @return string : The created path
+
+ * @exception Raise exception on error
+ */
+ public static function create_user_public_path (int $id_user)
+ {
+ $new_dir = PWD_DATA_PUBLIC . '/' . $id_user;
+ if (file_exists($new_dir))
+ {
+ return $new_dir;
+ }
+
+ if (!mkdir($new_dir, fileperms(PWD_DATA_PUBLIC)))
+ {
+ throw new \Exception('Cannot create dir ' . $new_dir);
+ }
+
+ if (!chown($new_dir, fileowner(PWD_DATA_PUBLIC)))
+ {
+ throw new \Exception('Cannot give dir ' . $new_dir . ' to user : ' . fileowner(PWD_DATA));
+ }
+
+ if (!chgrp($new_dir, filegroup(PWD_DATA_PUBLIC)))
+ {
+ throw new \Exception('Cannot give dir ' . $new_dir . ' to group : ' . filegroup(PWD_DATA));
+ }
+
+ return $new_dir;
+ }
}
diff --git a/controllers/internals/User.php b/controllers/internals/User.php
index 99a47cf..b0b8eeb 100644
--- a/controllers/internals/User.php
+++ b/controllers/internals/User.php
@@ -264,6 +264,7 @@ namespace controllers\internals;
* string 'text' => sms content,
* string 'destination' => id of phone the sms was sent to
* string 'origin' => phone number that sent the sms
+ * bool 'mms' => is the sms a mms
* ]
*
* @return bool : False if no transfer, true else
diff --git a/controllers/publics/Api.php b/controllers/publics/Api.php
index a726345..626764a 100644
--- a/controllers/publics/Api.php
+++ b/controllers/publics/Api.php
@@ -362,12 +362,15 @@ namespace controllers\publics;
{
foreach ($files_arrays as $file)
{
- $new_media_id = $this->internal_media->upload_and_create_for_user($this->user['id'], $file);
- if (!$new_media_id)
+ try
+ {
+ $new_media_id = $this->internal_media->upload_and_create_for_user($this->user['id'], $file);
+ }
+ catch (\Exception $e)
{
$return = self::DEFAULT_RETURN;
$return['error'] = self::ERROR_CODES['CANNOT_CREATE'];
- $return['message'] = self::ERROR_MESSAGES['CANNOT_CREATE'] . ' : Cannot upload and create media file ' . $file['name'];
+ $return['message'] = self::ERROR_MESSAGES['CANNOT_CREATE'] . ' : Cannot upload and create media file ' . $file['name'] . ' : ' . $e->getMessage();
$this->auto_http_code(false);
return $this->json($return);
diff --git a/controllers/publics/Callback.php b/controllers/publics/Callback.php
index 66de202..d97a2e6 100644
--- a/controllers/publics/Callback.php
+++ b/controllers/publics/Callback.php
@@ -183,58 +183,10 @@ use Monolog\Logger;
}
$sms = $response['sms'];
- $mms = !empty($sms['mms']);
+ $mms = (bool) $sms['mms'] ?? false;
$medias = empty($sms['medias']) ? [] : $sms['medias'];
- $media_ids = [];
- //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);
+ $response = $this->internal_received->receive($this->user['id'], $id_phone, $sms['text'], $sms['origin'], $sms['at'], \models\Received::STATUS_UNREAD, $mms, $medias);
if ($response['error'])
{
$this->logger->error('Failed receive message : ' . json_encode($sms) . ' with error : ' . $response['error_message']);
diff --git a/controllers/publics/Discussion.php b/controllers/publics/Discussion.php
index 42cc010..b8fae9a 100644
--- a/controllers/publics/Discussion.php
+++ b/controllers/publics/Discussion.php
@@ -120,7 +120,7 @@ namespace controllers\publics;
$medias = $this->internal_media->gets_for_sended($sended['id']);
foreach ($medias as &$media)
{
- $media = HTTP_PWD_DATA . '/' . $media['path'];
+ $media = HTTP_PWD_DATA_PUBLIC . '/' . $media['path'];
}
}
@@ -149,7 +149,7 @@ namespace controllers\publics;
$medias = $this->internal_media->gets_for_received($received['id']);
foreach ($medias as &$media)
{
- $media = HTTP_PWD_DATA . '/' . $media['path'];
+ $media = HTTP_PWD_DATA_PUBLIC . '/' . $media['path'];
}
}
@@ -170,7 +170,7 @@ namespace controllers\publics;
$medias = $this->internal_media->gets_for_scheduled($scheduled['id']);
foreach ($medias as &$media)
{
- $media = HTTP_PWD_DATA . '/' . $media['path'];
+ $media = HTTP_PWD_DATA_PUBLIC . '/' . $media['path'];
}
}
@@ -288,11 +288,14 @@ namespace controllers\publics;
{
foreach ($files_arrays as $file)
{
- $new_media_id = $this->internal_media->upload_and_create_for_user($_SESSION['user']['id'], $file);
- if (!$new_media_id)
+ try
+ {
+ $new_media_id = $this->internal_media->create_from_uploaded_file_for_user($_SESSION['user']['id'], $file);
+ }
+ catch (\Exception $e)
{
$return['success'] = false;
- $return['message'] = 'Impossible d\'upload et d\'enregistrer le fichier ' . $file['name'];
+ $return['message'] = $e->getMessage();
echo json_encode($return);
return false;
diff --git a/controllers/publics/Scheduled.php b/controllers/publics/Scheduled.php
index 677da5e..b96c58d 100644
--- a/controllers/publics/Scheduled.php
+++ b/controllers/publics/Scheduled.php
@@ -332,10 +332,13 @@ namespace controllers\publics;
{
foreach ($files_arrays as $file)
{
- $new_media_id = $this->internal_media->upload_and_create_for_user($_SESSION['user']['id'], $file);
- if (!$new_media_id)
+ try
{
- \FlashMessage\FlashMessage::push('danger', 'Impossible d\'upload et d\'enregistrer le fichier ' . $file['name']);
+ $new_media_id = $this->internal_media->create_from_uploaded_file_for_user($_SESSION['user']['id'], $file);
+ }
+ catch (\Exception $e)
+ {
+ \FlashMessage\FlashMessage::push('danger', 'Impossible d\'upload et d\'enregistrer le fichier ' . $file['name'] . ':' . $e->getMessage());
return $this->redirect(\descartes\Router::url('Scheduled', 'add'));
}
@@ -459,8 +462,11 @@ namespace controllers\publics;
{
foreach ($files_arrays as $file)
{
- $new_media_id = $this->internal_media->upload_and_create_for_user($_SESSION['user']['id'], $file);
- if (!$new_media_id)
+ try
+ {
+ $new_media_id = $this->internal_media->create_from_uploaded_file_for_user($_SESSION['user']['id'], $file);
+ }
+ catch (\Exception $e)
{
continue 2;
}
diff --git a/daemons/Phone.php b/daemons/Phone.php
index 69ef0f2..48b99e9 100644
--- a/daemons/Phone.php
+++ b/daemons/Phone.php
@@ -181,7 +181,7 @@ class Phone extends AbstractDaemon
foreach ($response['smss'] as $sms)
{
$this->logger->info('Receive message : ' . json_encode($sms));
- $response = $internal_received->receive($this->phone['id_user'], $this->phone['id'], $sms['text'], $sms['origin']);
+ $response = $internal_received->receive($this->phone['id_user'], $this->phone['id'], $sms['text'], $sms['origin'], $sms['at'], \models\Received::STATUS_UNREAD, $sms['mms'] ?? false, $sms['medias'] ?? []);
if ($response['error'])
{
diff --git a/templates/discussion/show.php b/templates/discussion/show.php
index 2dbaa47..674474d 100644
--- a/templates/discussion/show.php
+++ b/templates/discussion/show.php
@@ -87,7 +87,7 @@
}
else
{
- return '';
+ return '';
}
});
var medias_html = '' + medias.join('') + '
';
diff --git a/templates/received/list.php b/templates/received/list.php
index 763a28e..bab2b53 100644
--- a/templates/received/list.php
+++ b/templates/received/list.php
@@ -110,7 +110,7 @@ jQuery(document).ready(function ()
if (row.mms == 1) {
var medias = [];
for (i = 0; i < row.medias.length; i++) {
- medias.push('Fichier ' + (i + 1) + ' ');
+ medias.push('Fichier ' + (i + 1) + ' ');
}
html = data + ' ' + medias.join(' - ');
return html;
diff --git a/templates/scheduled/edit.php b/templates/scheduled/edit.php
index bfd788d..669d1a7 100644
--- a/templates/scheduled/edit.php
+++ b/templates/scheduled/edit.php
@@ -75,7 +75,7 @@
$media) { ?>
- Fichier = $key + 1 ?> : Voir le média Supprimer le média
+ Fichier = $key + 1 ?> : Voir le média Supprimer le média
diff --git a/templates/scheduled/list.php b/templates/scheduled/list.php
index ecfd708..63576e2 100644
--- a/templates/scheduled/list.php
+++ b/templates/scheduled/list.php
@@ -93,7 +93,7 @@ jQuery(document).ready(function ()
if (row.mms == 1) {
var medias = [];
for (i = 0; i < row.medias.length; i++) {
- medias.push('
Fichier ' + (i + 1) + ' ');
+ medias.push('
Fichier ' + (i + 1) + ' ');
}
html = data + '
' + medias.join(' - ');
return html;
diff --git a/templates/sended/list.php b/templates/sended/list.php
index d8c6289..15e837c 100644
--- a/templates/sended/list.php
+++ b/templates/sended/list.php
@@ -100,7 +100,7 @@ jQuery(document).ready(function ()
if (row.mms == 1) {
var medias = [];
for (i = 0; i < row.medias.length; i++) {
- medias.push('
Fichier ' + (i + 1) + ' ');
+ medias.push('
Fichier ' + (i + 1) + ' ');
}
html = data + '
' + medias.join(' - ');
return html;
From 6acf28e3ceb9df8adb9527a4917da5990f1104e4 Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Sat, 27 Mar 2021 01:15:09 +0100
Subject: [PATCH 21/40] Properly working mms reception with public dir in data
and valid rights, etc.
---
adapters/TestAdapter.php | 2 +-
controllers/internals/Media.php | 26 ++++++++++++++++++++++----
controllers/internals/Tool.php | 15 +++++++++++----
controllers/publics/Discussion.php | 2 +-
templates/discussion/show.php | 4 ++--
5 files changed, 37 insertions(+), 12 deletions(-)
diff --git a/adapters/TestAdapter.php b/adapters/TestAdapter.php
index b92d78c..07abfcf 100644
--- a/adapters/TestAdapter.php
+++ b/adapters/TestAdapter.php
@@ -186,7 +186,7 @@ namespace adapters;
/**
* Read from a files to simulate sms reception.
- * In the file we expect a json string representing an array of sms of format :
+ * In the file we expect a series of lines, each line beeing a SMS as a json string of format :
* {
* "at" : "2021-03-26 11:21:48",
* "medias" : [
diff --git a/controllers/internals/Media.php b/controllers/internals/Media.php
index e2f5dd5..3056e57 100644
--- a/controllers/internals/Media.php
+++ b/controllers/internals/Media.php
@@ -29,7 +29,12 @@ class Media extends StandardController
public function create(int $id_user, string $tmpfile_path, ?string $extension = null)
{
$user_path = \controllers\internals\Tool::create_user_public_path($id_user);
- if (!file_exists($tmpfile_path) || !is_readable($tmpfile_path))
+ if (!file_exists($tmpfile_path))
+ {
+ throw new \Exception('File ' . $tmpfile_path . ' does not exists.');
+ }
+
+ if (!is_readable($tmpfile_path))
{
throw new \Exception('File ' . $tmpfile_path . ' is not readable.');
}
@@ -41,6 +46,11 @@ class Media extends StandardController
$new_file_path = $user_path . '/' . $new_file_name;
$new_file_relpath = $id_user . '/' . $new_file_name;
+ if (!file_put_contents($new_file_path, 'a'))
+ {
+ throw new \Exception('pute de merde');
+ }
+
if (!rename($tmpfile_path, $new_file_path))
{
throw new \Exception('Cannot create file ' . $new_file_path);
@@ -90,10 +100,18 @@ class Media extends StandardController
throw new \Exception($upload_result['content']);
}
- return $this->create($id_user, $upload_result['tmp_name'], $upload_result['extension']);
+ //Move uploaded file to a tmp file
+ if (!$tmp_file = tempnam('/tmp', 'raspisms-media-'))
+ {
+ throw new \Exception('Cannot create tmp file in /tmp to store the uploaded file.');
+ }
- $new_filepath = 'medias/' . $id_user . '/' . $upload_result['content'];
- return $this->create($id_user, $new_filepath);
+ if (!move_uploaded_file($upload_result['tmp_name'], $tmp_file))
+ {
+ throw new \Exception('Cannot move uploaded file to : ' . $tmp_file);
+ }
+
+ return $this->create($id_user, $tmp_file, $upload_result['extension']);
}
/**
diff --git a/controllers/internals/Tool.php b/controllers/internals/Tool.php
index b1a795e..e5b71f9 100644
--- a/controllers/internals/Tool.php
+++ b/controllers/internals/Tool.php
@@ -306,19 +306,26 @@ namespace controllers\internals;
return $new_dir;
}
- if (!mkdir($new_dir, fileperms(PWD_DATA_PUBLIC)))
+ clearstatcache();
+ if (!mkdir($new_dir))
{
throw new \Exception('Cannot create dir ' . $new_dir);
}
- if (!chown($new_dir, fileowner(PWD_DATA_PUBLIC)))
+ //We do chmod in two times because else umask fuck mkdir permissions
+ if (!chmod($new_dir, fileperms(PWD_DATA_PUBLIC) & 0777)) //Fileperms return garbage in addition to perms. Perms are only in weak bytes. We must use an octet notation with 0
{
- throw new \Exception('Cannot give dir ' . $new_dir . ' to user : ' . fileowner(PWD_DATA));
+ throw new \Exception('Cannot give dir ' . $new_dir . ' rights : ' . decoct(fileperms(PWD_DATA_PUBLIC) & 0777)); //Show error in dec
+ }
+
+ if (posix_getuid() === 0 && !chown($new_dir, fileowner(PWD_DATA_PUBLIC))) //If we are root, try to give the file to a proper user
+ {
+ throw new \Exception('Cannot give dir ' . $new_dir . ' to user : ' . fileowner(PWD_DATA_PUBLIC));
}
if (!chgrp($new_dir, filegroup(PWD_DATA_PUBLIC)))
{
- throw new \Exception('Cannot give dir ' . $new_dir . ' to group : ' . filegroup(PWD_DATA));
+ throw new \Exception('Cannot give dir ' . $new_dir . ' to group : ' . filegroup(PWD_DATA_PUBLIC));
}
return $new_dir;
diff --git a/controllers/publics/Discussion.php b/controllers/publics/Discussion.php
index b8fae9a..6f26b80 100644
--- a/controllers/publics/Discussion.php
+++ b/controllers/publics/Discussion.php
@@ -144,7 +144,7 @@ namespace controllers\publics;
}
$medias = [];
- if ($sended['mms'])
+ if ($received['mms'])
{
$medias = $this->internal_media->gets_for_received($received['id']);
foreach ($medias as &$media)
diff --git a/templates/discussion/show.php b/templates/discussion/show.php
index 674474d..72636fc 100644
--- a/templates/discussion/show.php
+++ b/templates/discussion/show.php
@@ -87,7 +87,7 @@
}
else
{
- return '
';
+ return '
';
}
});
var medias_html = '
' + medias.join('') + '
';
@@ -99,7 +99,7 @@
'
' +
'
' +
'
' + message.text + '
' +
- medias.html +
+ medias_html +
'
' + message.date + '
' +
'
' +
'
';
From c326e27babc41e7980655525d408b59aa41fd766 Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Sat, 27 Mar 2021 01:16:24 +0100
Subject: [PATCH 22/40] add data/public dir
---
data/public/.tokeep | 0
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 data/public/.tokeep
diff --git a/data/public/.tokeep b/data/public/.tokeep
new file mode 100644
index 0000000..e69de29
From d29424ff0c5aa4f32692bddae99a33f44d603b96 Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Sat, 27 Mar 2021 01:18:02 +0100
Subject: [PATCH 23/40] fix delete to use proper dir
---
controllers/internals/Media.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/controllers/internals/Media.php b/controllers/internals/Media.php
index 3056e57..2722aab 100644
--- a/controllers/internals/Media.php
+++ b/controllers/internals/Media.php
@@ -238,7 +238,7 @@ class Media extends StandardController
//Delete file
try
{
- $filepath = PWD_DATA . '/' . $media['path'];
+ $filepath = PWD_DATA_PUBLIC . '/' . $media['path'];
if (file_exists($filepath))
{
unlink($filepath);
From e06079e6af093c7824802fadddf8bfd8a3f1de96 Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Sat, 3 Apr 2021 18:19:08 +0200
Subject: [PATCH 24/40] add mms files to mail transfert
---
controllers/internals/Mailer.php | 4 +++-
controllers/internals/User.php | 10 +++++++++-
daemons/Mailer.php | 2 +-
env.php.dist | 4 +++-
templates/email/transfer-sms.php | 2 +-
5 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/controllers/internals/Mailer.php b/controllers/internals/Mailer.php
index 2df5d3c..04d7204 100755
--- a/controllers/internals/Mailer.php
+++ b/controllers/internals/Mailer.php
@@ -101,10 +101,11 @@ class Mailer extends \descartes\Controller
* @param string $destination : email address to send email to
* @param array $settings : Email settings
* @param array $data : Data to inject into email template
+ * @param array $attachments : List of paths of files to attach to the mail
*
* @return bool : true on success, false on error
*/
- public function enqueue(string $destination, array $settings, array $data): bool
+ public function enqueue(string $destination, array $settings, array $data, array $attachments = []): bool
{
$response = $this->generate_body($settings, $data);
@@ -113,6 +114,7 @@ class Mailer extends \descartes\Controller
'subject' => $settings['subject'],
'body' => $response['body'],
'alt_body' => $response['alt_body'],
+ 'attachments' => $attachments,
];
$error_code = null;
diff --git a/controllers/internals/User.php b/controllers/internals/User.php
index b0b8eeb..427cceb 100644
--- a/controllers/internals/User.php
+++ b/controllers/internals/User.php
@@ -291,12 +291,20 @@ namespace controllers\internals;
}
$mailer = new Mailer();
+
+ $attachments = [];
+
+ foreach ($received['medias'] ?? [] as $media)
+ {
+ $attachments[] = PWD_DATA_PUBLIC . '/' . $media['path'];
+ }
return $mailer->enqueue($user['email'], EMAIL_TRANSFER_SMS, [
'at' => $received['at'],
'origin' => $received['origin'],
'destination' => $phone['name'],
'text' => $received['text'],
- ]);
+ 'mms' => $received['mms'] ?? false,
+ ], $attachments);
}
}
diff --git a/daemons/Mailer.php b/daemons/Mailer.php
index c9f1ac9..a25b253 100644
--- a/daemons/Mailer.php
+++ b/daemons/Mailer.php
@@ -74,7 +74,7 @@ class Mailer extends AbstractDaemon
$this->logger->info('Try sending email : ' . json_encode($message));
$mailer = new \controllers\internals\Mailer();
- $success = $mailer->send($message['destinations'], $message['subject'], $message['body'], $message['alt_body']);
+ $success = $mailer->send($message['destinations'], $message['subject'], $message['body'], $message['alt_body'], $message['attachments']);
if (!$success)
{
$this->logger->error('Failed sending email');
diff --git a/env.php.dist b/env.php.dist
index dd77747..5aa3260 100644
--- a/env.php.dist
+++ b/env.php.dist
@@ -17,6 +17,8 @@
'PWD_ADAPTERS' => PWD . '/adapters',
'PWD_DATA' => PWD . '/data',
'HTTP_PWD_DATA' => HTTP_PWD . '/data',
+ 'PWD_DATA_PUBLIC' => PWD . '/data/public',
+ 'HTTP_PWD_DATA_PUBLIC' => HTTP_PWD . '/data/public',
'PWD_LOGS' => '/var/log/raspisms',
'PWD_PID' => '/var/run/raspisms',
'APP_SECRET' => '%APP_SECRET%',
@@ -67,7 +69,7 @@
'preferred_phone_country' => 'fr,be,ca',
'default_phone_country' => 'fr',
'authorized_phone_country' => 'fr,be,ca',
- 'mms' => 0,
+ 'mms' => 1,
],
];
diff --git a/templates/email/transfer-sms.php b/templates/email/transfer-sms.php
index e75a0a9..9b5612d 100644
--- a/templates/email/transfer-sms.php
+++ b/templates/email/transfer-sms.php
@@ -1,4 +1,4 @@
-Vous avez reçu un nouveau SMS :
+Vous avez reçu un nouveau = $mms ? 'MMS' : 'SMS'; ?> :
Date : = $at ?>
Origine : = $origin ?>
From 7c85753bfa5716271f40b860ddfb9ba2be04bc26 Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Sat, 3 Apr 2021 18:30:05 +0200
Subject: [PATCH 25/40] only root try to give another group to file
---
controllers/internals/Tool.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/controllers/internals/Tool.php b/controllers/internals/Tool.php
index e5b71f9..a207185 100644
--- a/controllers/internals/Tool.php
+++ b/controllers/internals/Tool.php
@@ -323,7 +323,7 @@ namespace controllers\internals;
throw new \Exception('Cannot give dir ' . $new_dir . ' to user : ' . fileowner(PWD_DATA_PUBLIC));
}
- if (!chgrp($new_dir, filegroup(PWD_DATA_PUBLIC)))
+ if (posix_getuid() === 0 && !chgrp($new_dir, filegroup(PWD_DATA_PUBLIC))) //If we are root, try to give the file to a proper group
{
throw new \Exception('Cannot give dir ' . $new_dir . ' to group : ' . filegroup(PWD_DATA_PUBLIC));
}
From f992fe21bc0db16d2b9de4cc3fdf8228f9522111 Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Tue, 6 Apr 2021 20:52:07 +0200
Subject: [PATCH 26/40] show media input on discussion only if mms is enabled
---
templates/discussion/show.php | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/templates/discussion/show.php b/templates/discussion/show.php
index 72636fc..eed789e 100644
--- a/templates/discussion/show.php
+++ b/templates/discussion/show.php
@@ -42,7 +42,9 @@
-
+
+
+
Envoyer
From 78fce650cc2ed78b53e7e9ddc43a2749583bcfc5 Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Tue, 6 Apr 2021 21:42:30 +0200
Subject: [PATCH 27/40] add env.descartes.php.dist
---
env.descartes.php.dist | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
create mode 100644 env.descartes.php.dist
diff --git a/env.descartes.php.dist b/env.descartes.php.dist
new file mode 100644
index 0000000..803c5cf
--- /dev/null
+++ b/env.descartes.php.dist
@@ -0,0 +1,21 @@
+ $http_pwd,
+ ];
From 2918087856a2fc3939eb7e732000f4720b2e4eaf Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Tue, 6 Apr 2021 21:44:30 +0200
Subject: [PATCH 28/40] revert
---
env.descartes.php.dist | 21 ---------------------
1 file changed, 21 deletions(-)
delete mode 100644 env.descartes.php.dist
diff --git a/env.descartes.php.dist b/env.descartes.php.dist
deleted file mode 100644
index 803c5cf..0000000
--- a/env.descartes.php.dist
+++ /dev/null
@@ -1,21 +0,0 @@
- $http_pwd,
- ];
From 48e8eacc3496f54e7a341747c3a38d5f18d9076a Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Tue, 6 Apr 2021 23:20:00 +0200
Subject: [PATCH 29/40] Change http_url for medias
---
controllers/internals/Sended.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/controllers/internals/Sended.php b/controllers/internals/Sended.php
index c9575c9..3847185 100644
--- a/controllers/internals/Sended.php
+++ b/controllers/internals/Sended.php
@@ -211,7 +211,7 @@ namespace controllers\internals;
foreach ($medias as $media)
{
$media_uris[] = [
- 'http_url' => HTTP_PWD_DATA_PUBLIC . '/' . $media['path'],
+ 'path' => $media['path'],
'local_uri' => PWD_DATA_PUBLIC . '/' . $media['path'],
];
}
From fb919ff59f6532a77182126493bc8836b6bd384c Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Tue, 13 Apr 2021 02:01:03 +0200
Subject: [PATCH 30/40] highly improve discussion perfs + fix initial sound
playing and erratic scrolling
---
assets/js/custom.js | 11 +++---
controllers/internals/Received.php | 13 +++++++
controllers/internals/Scheduled.php | 14 +++++++
controllers/internals/Sended.php | 13 +++++++
controllers/publics/Discussion.php | 51 +++++++++++++++++++++-----
models/Received.php | 28 ++++++++++++++
models/Scheduled.php | 57 +++++++++++++++++++++++++++++
models/Sended.php | 29 +++++++++++++++
routes.php | 5 ++-
templates/discussion/show.php | 49 ++++++++++++++++++-------
10 files changed, 242 insertions(+), 28 deletions(-)
diff --git a/assets/js/custom.js b/assets/js/custom.js
index eb2b6f9..b25910a 100644
--- a/assets/js/custom.js
+++ b/assets/js/custom.js
@@ -45,8 +45,9 @@ function verifReceived()
/**
* Cette fonction permet de scroller au dernier message
+ * @param force: bool : should we force the scroll
*/
-function scrollDownDiscussion()
+function scrollDownDiscussion(force)
{
var discussion_height = jQuery('.discussion-container').innerHeight();
var discussion_scroll_height = jQuery('.discussion-container')[0].scrollHeight;
@@ -54,7 +55,7 @@ function scrollDownDiscussion()
var scroll_before_end = discussion_scroll_height - (discussion_scroll_top + discussion_height);
//On scroll uniquement si on a pas remonté plus haut que la moitié de la fenetre de discussion
- if (scroll_before_end <= discussion_height / 2)
+ if (force || scroll_before_end <= discussion_height / 2)
{
jQuery('.discussion-container').animate({scrollTop: 1000000});
}
@@ -115,8 +116,8 @@ jQuery(document).ready(function()
var form = jQuery(this);
var message = form.find('textarea').val();
var formData = new FormData(form[0]);
- jQuery('.discussion-container').find('#send-message-spiner').remove();
- jQuery('.discussion-container').append('
');
+ jQuery('.discussion-container').find('#send-message-spinner').remove();
+ jQuery('.discussion-container').append('
');
scrollDownDiscussion();
jQuery.ajax({
url: form.attr('action'),
@@ -130,7 +131,7 @@ jQuery(document).ready(function()
if (!data.success)
{
showMessage(data.message.replace(//g, ">"), 0);
- jQuery('.discussion-container').find('#send-message-spiner').remove();
+ jQuery('.discussion-container').find('#send-message-spinner').remove();
}
}
}).done(function()
diff --git a/controllers/internals/Received.php b/controllers/internals/Received.php
index 285a8ca..c7c2b79 100644
--- a/controllers/internals/Received.php
+++ b/controllers/internals/Received.php
@@ -161,6 +161,19 @@ namespace controllers\internals;
return $this->get_model()->gets_by_origin_and_user($id_user, $origin);
}
+ /**
+ * Return receiveds for an origin and a user since a date.
+ *
+ * @param int $id_user : User id
+ * @param string $since : Date we want messages since format Y-m-d H:i:s
+ * @param string $origin : Number who sent the message
+ * @return array
+ */
+ public function gets_since_date_by_origin_and_user(int $id_user, string $since, string $origin)
+ {
+ return $this->get_model()->gets_since_date_by_origin_and_user($id_user, $since, $origin);
+ }
+
/**
* Get number of sended SMS for every date since a date for a specific user.
*
diff --git a/controllers/internals/Scheduled.php b/controllers/internals/Scheduled.php
index b45c6ce..da65da2 100644
--- a/controllers/internals/Scheduled.php
+++ b/controllers/internals/Scheduled.php
@@ -254,6 +254,20 @@ namespace controllers\internals;
{
return $this->get_model()->gets_before_date_for_number_and_user($id_user, $date, $number);
}
+
+ /**
+ * Get messages scheduled after a date for a number and a user.
+ *
+ * @param int $id_user : User id
+ * @param $date : Date after which we want messages
+ * @param string $number : Number for which we want messages
+ *
+ * @return array
+ */
+ public function gets_after_date_for_number_and_user(int $id_user, $date, string $number)
+ {
+ return $this->get_model()->gets_after_date_for_number_and_user($id_user, $date, $number);
+ }
/**
* Get all messages to send and the number to use to send theme.
diff --git a/controllers/internals/Sended.php b/controllers/internals/Sended.php
index 3847185..8b8d2d6 100644
--- a/controllers/internals/Sended.php
+++ b/controllers/internals/Sended.php
@@ -132,6 +132,19 @@ namespace controllers\internals;
{
return $this->get_model()->gets_by_destination_and_user($id_user, $origin);
}
+
+ /**
+ * Return sendeds for a destination and a user since a date.
+ *
+ * @param int $id_user : User id
+ * @param string $since : Date we want messages since format Y-m-d H:i:s
+ * @param string $origin : Number who sent the message
+ * @return array
+ */
+ public function gets_since_date_by_destination_and_user(int $id_user, string $since, string $origin)
+ {
+ return $this->get_model()->gets_since_date_by_destination_and_user($id_user, $since, $origin);
+ }
/**
* Return sended for an uid and an adapter.
diff --git a/controllers/publics/Discussion.php b/controllers/publics/Discussion.php
index 6f26b80..13d2031 100644
--- a/controllers/publics/Discussion.php
+++ b/controllers/publics/Discussion.php
@@ -98,17 +98,34 @@ namespace controllers\publics;
*
* @param string $number : Le numéro cible
* @param string $transaction_id : Le numéro unique de la transaction ajax (sert à vérifier si la requete doit être prise en compte)
+ * @param ?int $since : La date à partir de laquelle on veut les messages, sous forme de timestamp
*/
- public function get_messages($number, $transaction_id)
+ public function get_messages($number, $transaction_id, ?int $since = null)
{
$now = new \DateTime();
$now = $now->format('Y-m-d H:i:s');
$id_user = $_SESSION['user']['id'];
- $sendeds = $this->internal_sended->gets_by_destination_and_user($id_user, $number);
- $receiveds = $this->internal_received->gets_by_origin_and_user($id_user, $number);
- $scheduleds = $this->internal_scheduled->gets_before_date_for_number_and_user($id_user, $now, $number);
+ if ($since && !($since = date('Y-m-d H:i:s', $since)))
+ {
+ echo json_encode(['transaction_id' => $transaction_id, 'messages' => [], 'error' => true, 'error_message' => 'Not a valid date.']);
+
+ return false;
+ }
+
+ if ($since)
+ {
+ $sendeds = $this->internal_sended->gets_since_date_by_destination_and_user($id_user, $since, $number);
+ $receiveds = $this->internal_received->gets_since_date_by_origin_and_user($id_user, $since, $number);
+ $scheduleds = $this->internal_scheduled->gets_after_date_for_number_and_user($id_user, $since, $number);
+ }
+ else
+ {
+ $sendeds = $this->internal_sended->gets_by_destination_and_user($id_user, $number);
+ $receiveds = $this->internal_received->gets_by_origin_and_user($id_user, $number);
+ $scheduleds = $this->internal_scheduled->gets_before_date_for_number_and_user($id_user, $now, $number);
+ }
$messages = [];
@@ -125,6 +142,7 @@ namespace controllers\publics;
}
$message = [
+ 'uid' => 'sended-' . $sended['id'],
'date' => htmlspecialchars($sended['at']),
'text' => htmlspecialchars($sended['text']),
'type' => 'sended',
@@ -154,10 +172,10 @@ namespace controllers\publics;
}
$messages[] = [
+ 'uid' => 'received-' . $received['id'],
'date' => htmlspecialchars($received['at']),
'text' => htmlspecialchars($received['text']),
'type' => 'received',
- 'md5' => md5($received['at'] . $received['text']),
'medias' => $medias,
];
}
@@ -165,7 +183,7 @@ namespace controllers\publics;
foreach ($scheduleds as $scheduled)
{
$medias = [];
- if ($sended['mms'])
+ if ($scheduled['mms'])
{
$medias = $this->internal_media->gets_for_scheduled($scheduled['id']);
foreach ($medias as &$media)
@@ -175,6 +193,7 @@ namespace controllers\publics;
}
$messages[] = [
+ 'uid' => 'scheduled-' . $scheduled['id'],
'date' => htmlspecialchars($scheduled['at']),
'text' => htmlspecialchars($scheduled['text']),
'type' => 'inprogress',
@@ -188,10 +207,24 @@ namespace controllers\publics;
return strtotime($a['date']) - strtotime($b['date']);
});
- //On récupère uniquement les 25 derniers messages sur l'ensemble
- $messages = \array_slice($messages, -25);
+ //Sans limite de temps, on récupère uniquement les 25 derniers messages sur l'ensemble pour limiter la charge
+ if (!$since)
+ {
+ $messages = \array_slice($messages, -25);
+ }
- echo json_encode(['transaction_id' => $transaction_id, 'messages' => $messages]);
+ $response = [
+ 'transaction_id' => $transaction_id,
+ 'messages' => $messages,
+ ];
+
+ if ($messages)
+ {
+ $new_limit_date = (new \DateTime($messages[count($messages) - 1]['date']))->getTimestamp(); //Use latest message as the new limit date to search
+ $response['new_limit_date'] = $new_limit_date;
+ }
+
+ echo json_encode($response);
return true;
}
diff --git a/models/Received.php b/models/Received.php
index 23a8c05..28fabef 100644
--- a/models/Received.php
+++ b/models/Received.php
@@ -178,6 +178,34 @@ namespace models;
return $this->_run_query($query, $params);
}
+
+ /**
+ * Return sendeds for an origin and a user since a date.
+ *
+ * @param int $id_user : User id
+ * @param string $since : Date we want messages since
+ * @param string $origin : Number who sent the message
+ *
+ * @return array
+ */
+ public function gets_since_date_by_origin_and_user(int $id_user, string $since, string $origin)
+ {
+ $query = '
+ SELECT *
+ FROM received
+ WHERE id_user = :id_user
+ AND origin = :origin
+ AND at > :since
+ ';
+
+ $params = [
+ 'id_user' => $id_user,
+ 'origin' => $origin,
+ 'since' => $since,
+ ];
+
+ return $this->_run_query($query, $params);
+ }
/**
* Get number of sended SMS for every date since a date for a specific user.
diff --git a/models/Scheduled.php b/models/Scheduled.php
index 7d7bc90..f1b2cdd 100644
--- a/models/Scheduled.php
+++ b/models/Scheduled.php
@@ -233,6 +233,63 @@ namespace models;
return $this->_run_query($query, $params);
}
+
+
+ /**
+ * Get messages scheduled after a date for a number and a user.
+ *
+ * @param int $id_user : User id
+ * @param $date : Date after which we want messages
+ * @param string $number : Number for which we want messages
+ *
+ * @return array
+ */
+ public function gets_after_date_for_number_and_user(int $id_user, $date, string $number)
+ {
+ $query = '
+ SELECT *
+ FROM scheduled
+ WHERE at > :date
+ AND id_user = :id_user
+ AND (
+ id IN (
+ SELECT id_scheduled
+ FROM scheduled_number
+ WHERE number = :number
+ )
+ OR id IN (
+ SELECT id_scheduled
+ FROM scheduled_contact
+ WHERE id_contact IN (
+ SELECT id
+ FROM contact
+ WHERE number = :number
+ )
+ )
+ OR id IN (
+ SELECT id_scheduled
+ FROM scheduled_group
+ WHERE id_group IN (
+ SELECT id_group
+ FROM `group_contact`
+ WHERE id_contact IN (
+ SELECT id
+ FROM contact
+ WHERE number = :number
+ )
+ )
+ )
+ )
+ ';
+
+ $params = [
+ 'id_user' => $id_user,
+ 'date' => $date,
+ 'number' => $number,
+ ];
+
+ return $this->_run_query($query, $params);
+ }
/**
* Get scheduleds before a date.
diff --git a/models/Sended.php b/models/Sended.php
index f0235ba..fa91346 100644
--- a/models/Sended.php
+++ b/models/Sended.php
@@ -112,6 +112,35 @@ namespace models;
return $this->_run_query($query, $params);
}
+
+
+ /**
+ * Return sendeds for an destination and a user since a date.
+ *
+ * @param int $id_user : User id
+ * @param string $since : Date we want messages since
+ * @param string $destination : Number who sent the message
+ *
+ * @return array
+ */
+ public function gets_since_date_by_destination_and_user(int $id_user, string $since, string $destination)
+ {
+ $query = '
+ SELECT *
+ FROM sended
+ WHERE id_user = :id_user
+ AND destination = :destination
+ AND at > :since
+ ';
+
+ $params = [
+ 'id_user' => $id_user,
+ 'destination' => $destination,
+ 'since' => $since,
+ ];
+
+ return $this->_run_query($query, $params);
+ }
/**
* Return sended for an uid and an adapter.
diff --git a/routes.php b/routes.php
index 0384728..db8f771 100644
--- a/routes.php
+++ b/routes.php
@@ -54,7 +54,10 @@
'list_json' => '/discussion/json/',
'show' => '/discussion/show/{number}/',
'send' => '/discussion/send/{csrf}/',
- 'get_messages' => '/discussion/getmessage/{number}/{transaction_id}/',
+ 'get_messages' => [
+ '/discussion/getmessage/{number}/{transaction_id}/',
+ '/discussion/getmessage/{number}/{transaction_id}/{since}/',
+ ],
],
'Event' => [
diff --git a/templates/discussion/show.php b/templates/discussion/show.php
index eed789e..be4b21f 100644
--- a/templates/discussion/show.php
+++ b/templates/discussion/show.php
@@ -32,7 +32,7 @@
';
- if (alreadyReceivedMessages.indexOf(message.md5) == -1)
+ if (alreadyReceivedMessages.indexOf(message.uid) == -1 && !first_load) //If new message received and not first time loading
{
playReceptionSound();
- alreadyReceivedMessages.push(message.md5);
+ alreadyReceivedMessages.push(message.uid);
}
break;
@@ -140,8 +157,17 @@
}
jQuery('.discussion-container').append(texte);
- });
- scrollDownDiscussion();
+ });
+
+ //Update limit_date if set
+ if (data.new_limit_date !== undefined)
+ {
+ limit_date = data.new_limit_date;
+ }
+
+ if (data.messages.length) {
+ scrollDownDiscussion(first_load);
+ }
});
}
@@ -155,9 +181,6 @@
var messageInputContainer = jQuery('.message-input-container').outerHeight();
var footerHeight = jQuery('footer').outerHeight();
- console.log(windowHeight);
- console.log(containerPosition.top);
-
var containerHeight = Math.floor(windowHeight - (containerPosition.top + messageInputContainer) - 20); //-20 px for aesthetic
jQuery('.discussion-container').outerHeight(containerHeight);
From 697300b5755e90eda7c9c3324a5e5f40a5ccc305 Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Tue, 13 Apr 2021 02:07:47 +0200
Subject: [PATCH 31/40] add audio balise
---
templates/discussion/show.php | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/templates/discussion/show.php b/templates/discussion/show.php
index be4b21f..31f15d6 100644
--- a/templates/discussion/show.php
+++ b/templates/discussion/show.php
@@ -100,10 +100,14 @@
{
return '
';
}
- else if (['webm', 'ogg', 'ogv', 'mp4'].includes(extension))
+ else if (['webm', 'ogv', 'mp4'].includes(extension))
{
return '
';
}
+ else if (['wav', 'ogg', 'mp3'].includes(extension))
+ {
+ return '
';
+ }
else
{
return '
';
From b9641345a1ecd27225a26f6b8daac0a2ef36bf58 Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Tue, 13 Apr 2021 02:33:30 +0200
Subject: [PATCH 32/40] do not duplicatd in progress msg
---
controllers/publics/Discussion.php | 14 ++++++++++++--
templates/discussion/show.php | 5 ++++-
2 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/controllers/publics/Discussion.php b/controllers/publics/Discussion.php
index 13d2031..7d4f215 100644
--- a/controllers/publics/Discussion.php
+++ b/controllers/publics/Discussion.php
@@ -220,8 +220,18 @@ namespace controllers\publics;
if ($messages)
{
- $new_limit_date = (new \DateTime($messages[count($messages) - 1]['date']))->getTimestamp(); //Use latest message as the new limit date to search
- $response['new_limit_date'] = $new_limit_date;
+ for ($i = count($messages); $i > 0; $i--)
+ {
+ $message = $messages[$i - 1];
+ if ($message['type'] == 'inprogress')
+ {
+ continue;
+ }
+
+ $new_limit_date = (new \DateTime($message['date']))->getTimestamp(); //Use latest not inprogress message as the new limit date to search
+ $response['new_limit_date'] = $new_limit_date;
+ break;
+ }
}
echo json_encode($response);
diff --git a/templates/discussion/show.php b/templates/discussion/show.php
index 31f15d6..caae5c6 100644
--- a/templates/discussion/show.php
+++ b/templates/discussion/show.php
@@ -87,6 +87,9 @@
jQuery('.discussion-container #load-message-spinner').remove();
jQuery('.discussion-container #send-message-spinner').remove();
+ //We also remove all in-progress messages because they are added again in the new response if not sended yet, and if sended they should not appear in double
+ jQuery('.discussion-container .message-in-progress-container').remove();
+
$.each(data.messages, function(key, message) {
@@ -146,7 +149,7 @@
break;
case 'inprogress' :
var texte = '' +
- '
' +
+ '
' +
'
' +
'
' +
'
' + message.text + '
' +
From 4cd52ae9ec3ca8aac945648c77d7df2ac59cee11 Mon Sep 17 00:00:00 2001
From: osaajani <>
Date: Fri, 16 Apr 2021 17:11:24 +0200
Subject: [PATCH 33/40] fix discussion to correctly update status whithout
replacing the all conversation for each sms
---
controllers/publics/Discussion.php | 41 +++------------------
templates/discussion/show.php | 59 ++++++++++++++++--------------
2 files changed, 37 insertions(+), 63 deletions(-)
diff --git a/controllers/publics/Discussion.php b/controllers/publics/Discussion.php
index 7d4f215..fd27fd6 100644
--- a/controllers/publics/Discussion.php
+++ b/controllers/publics/Discussion.php
@@ -98,9 +98,8 @@ namespace controllers\publics;
*
* @param string $number : Le numéro cible
* @param string $transaction_id : Le numéro unique de la transaction ajax (sert à vérifier si la requete doit être prise en compte)
- * @param ?int $since : La date à partir de laquelle on veut les messages, sous forme de timestamp
*/
- public function get_messages($number, $transaction_id, ?int $since = null)
+ public function get_messages($number, $transaction_id)
{
$now = new \DateTime();
$now = $now->format('Y-m-d H:i:s');
@@ -114,18 +113,9 @@ namespace controllers\publics;
return false;
}
- if ($since)
- {
- $sendeds = $this->internal_sended->gets_since_date_by_destination_and_user($id_user, $since, $number);
- $receiveds = $this->internal_received->gets_since_date_by_origin_and_user($id_user, $since, $number);
- $scheduleds = $this->internal_scheduled->gets_after_date_for_number_and_user($id_user, $since, $number);
- }
- else
- {
- $sendeds = $this->internal_sended->gets_by_destination_and_user($id_user, $number);
- $receiveds = $this->internal_received->gets_by_origin_and_user($id_user, $number);
- $scheduleds = $this->internal_scheduled->gets_before_date_for_number_and_user($id_user, $now, $number);
- }
+ $sendeds = $this->internal_sended->gets_by_destination_and_user($id_user, $number);
+ $receiveds = $this->internal_received->gets_by_origin_and_user($id_user, $number);
+ $scheduleds = $this->internal_scheduled->gets_before_date_for_number_and_user($id_user, $now, $number);
$messages = [];
@@ -207,33 +197,14 @@ namespace controllers\publics;
return strtotime($a['date']) - strtotime($b['date']);
});
- //Sans limite de temps, on récupère uniquement les 25 derniers messages sur l'ensemble pour limiter la charge
- if (!$since)
- {
- $messages = \array_slice($messages, -25);
- }
+ //Récupère uniquement les 25 derniers messages sur l'ensemble pour limiter la charge
+ $messages = \array_slice($messages, -25);
$response = [
'transaction_id' => $transaction_id,
'messages' => $messages,
];
- if ($messages)
- {
- for ($i = count($messages); $i > 0; $i--)
- {
- $message = $messages[$i - 1];
- if ($message['type'] == 'inprogress')
- {
- continue;
- }
-
- $new_limit_date = (new \DateTime($message['date']))->getTimestamp(); //Use latest not inprogress message as the new limit date to search
- $response['new_limit_date'] = $new_limit_date;
- break;
- }
- }
-
echo json_encode($response);
return true;
diff --git a/templates/discussion/show.php b/templates/discussion/show.php
index caae5c6..dd7134a 100644
--- a/templates/discussion/show.php
+++ b/templates/discussion/show.php
@@ -56,26 +56,18 @@