add import/Export contacts system
This commit is contained in:
parent
6503eb62ec
commit
cfde77a0c1
|
@ -23,6 +23,11 @@
|
|||
font-style: italic;
|
||||
}
|
||||
|
||||
.float-right
|
||||
{
|
||||
float: right;
|
||||
}
|
||||
|
||||
/** POPUPS ALERT **/
|
||||
.popup-alerts-container
|
||||
{
|
||||
|
@ -276,6 +281,17 @@ footer
|
|||
|
||||
|
||||
/* CONTACT */
|
||||
#import-modal input:valid + label .valid-icon
|
||||
{
|
||||
color: #4cae4c;
|
||||
display: inline-block !important;
|
||||
}
|
||||
|
||||
#import-modal input:invalid + label .invalid-icon
|
||||
{
|
||||
display: inline-block !important;
|
||||
}
|
||||
|
||||
.contact-datas-container input
|
||||
{
|
||||
display: inline-block;
|
||||
|
|
|
@ -109,4 +109,229 @@ namespace controllers\internals;
|
|||
|
||||
return $this->get_model()->update_for_user($id_user, $id, $contact);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Import a list of contacts as csv
|
||||
* @param resource $file_handler : File handler to import contacts from
|
||||
* @param int $id_user : User id
|
||||
* @return mixed : False on error, number of inserted contacts else
|
||||
*/
|
||||
public function import_csv (int $id_user, $file_handler)
|
||||
{
|
||||
if (!is_resource($file_handler))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$nb_insert = 0;
|
||||
|
||||
$head = null;
|
||||
while ($line = fgetcsv($file_handler))
|
||||
{
|
||||
if ($head === null)
|
||||
{
|
||||
$head = $line;
|
||||
continue;
|
||||
}
|
||||
|
||||
$line = array_combine($head, $line);
|
||||
if ($line === false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($line['name'], $line['number']))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$datas = [];
|
||||
foreach ($line as $key => $value)
|
||||
{
|
||||
if ($key == 'name' || $key == 'number')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($value === '')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$key = mb_ereg_replace('[\W]', '', $key);
|
||||
$datas[$key] = $value;
|
||||
}
|
||||
$datas = json_encode($datas);
|
||||
|
||||
$success = $this->create($id_user, $line['number'], $line['name'], $datas);
|
||||
if ($success)
|
||||
{
|
||||
$nb_insert ++;
|
||||
}
|
||||
}
|
||||
|
||||
return $nb_insert;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Import a list of contacts as json
|
||||
* @param resource $file_handler : File handler to import contacts from
|
||||
* @param int $id_user : User id
|
||||
* @return mixed : False on error, number of inserted contacts else
|
||||
*/
|
||||
public function import_json (int $id_user, $file_handler)
|
||||
{
|
||||
if (!is_resource($file_handler))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$file_content = '';
|
||||
while ($line = fgets($file_handler))
|
||||
{
|
||||
$file_content .= $line;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$contacts = json_decode($file_content, true);
|
||||
|
||||
if (!is_array($contacts))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$nb_insert = 0;
|
||||
foreach ($contacts as $contact)
|
||||
{
|
||||
if (!is_array($contact))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($contact['name'], $contact['number']))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$datas = $contact['datas'] ?? [];
|
||||
$datas = json_encode($datas);
|
||||
|
||||
$success = $this->create($id_user, $contact['number'], $contact['name'], $datas);
|
||||
if ($success)
|
||||
{
|
||||
$nb_insert ++;
|
||||
}
|
||||
}
|
||||
|
||||
return $nb_insert;
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Export the contacts of a user as csv
|
||||
* @param int $id_user : User id
|
||||
* @return array : ['headers' => array of headers to return, 'content' => the generated file]
|
||||
*/
|
||||
public function export_csv (int $id_user) : array
|
||||
{
|
||||
$contacts = $this->get_model()->gets_for_user($id_user);
|
||||
|
||||
$columns = [
|
||||
'name',
|
||||
'number',
|
||||
];
|
||||
|
||||
foreach ($contacts as $contact)
|
||||
{
|
||||
$datas = json_decode($contact['datas'], true);
|
||||
foreach ($datas as $key => $value)
|
||||
{
|
||||
$columns[] = $key;
|
||||
}
|
||||
}
|
||||
$columns = array_unique($columns);
|
||||
|
||||
$lines = [];
|
||||
foreach ($contacts as $contact)
|
||||
{
|
||||
$datas = json_decode($contact['datas'], true);
|
||||
|
||||
$line = [];
|
||||
foreach ($columns as $column)
|
||||
{
|
||||
if (isset($contact[$column]))
|
||||
{
|
||||
$line[] = $contact[$column];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($datas[$column]))
|
||||
{
|
||||
$line[] = $datas[$column];
|
||||
continue;
|
||||
}
|
||||
|
||||
$line[] = null;
|
||||
}
|
||||
$lines[] = $line;
|
||||
}
|
||||
|
||||
//Php only support csv formatting to file. To get it in string we need to create a tmp in memory file, write in it, and then read the file into a var
|
||||
// output up to 5MB is kept in memory, if it becomes bigger it will automatically be written to a temporary file
|
||||
$csv_tmp_file = fopen('php://temp/maxmemory:'. (5*1024*1024), 'r+');
|
||||
fputcsv($csv_tmp_file, $columns);
|
||||
foreach ($lines as $line)
|
||||
{
|
||||
fputcsv($csv_tmp_file, $line);
|
||||
}
|
||||
rewind($csv_tmp_file);
|
||||
|
||||
$csv_string = stream_get_contents($csv_tmp_file);
|
||||
|
||||
return [
|
||||
'headers' => [
|
||||
'Content-Disposition: attachment; filename=contacts.csv',
|
||||
'Content-Type: text/csv',
|
||||
'Content-Length: ' . strlen($csv_string),
|
||||
],
|
||||
'content' => $csv_string,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Export the contacts of a user as json
|
||||
* @param int $id_user : User id
|
||||
* @return array : ['headers' => array of headers to return, 'content' => the generated file]
|
||||
*/
|
||||
public function export_json (int $id_user) : array
|
||||
{
|
||||
$contacts = $this->get_model()->gets_for_user($id_user);
|
||||
|
||||
foreach ($contacts as &$contact)
|
||||
{
|
||||
unset($contact['id']);
|
||||
unset($contact['id_user']);
|
||||
$contact['datas'] = json_decode($contact['datas']);
|
||||
}
|
||||
$content = json_encode($contacts);
|
||||
|
||||
return [
|
||||
'headers' => [
|
||||
'Content-Disposition: attachment; filename=contacts.json',
|
||||
'Content-Type: application/json',
|
||||
'Content-Length: ' . strlen($content),
|
||||
],
|
||||
'content' => $content,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,13 +26,24 @@ namespace controllers\internals;
|
|||
*/
|
||||
public static function parse_phone($number)
|
||||
{
|
||||
$number = preg_replace('#[^-0-9+]#', '', $number);
|
||||
if (preg_match('#^(0|\+[1-9]{1,3}|\+1\-[0-9]{3})[1-9][0-9]{8,10}$#', $number))
|
||||
try
|
||||
{
|
||||
return $number;
|
||||
}
|
||||
$phone_number_util = \libphonenumber\PhoneNumberUtil::getInstance();
|
||||
$phone_number_o = $phone_number_util->parse($number, null);
|
||||
|
||||
return false;
|
||||
$valid = $phone_number_util->isValidNumber($phone_number_o);
|
||||
|
||||
if (!$valid)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return $phone_number_util->format($phone_number_o, \libphonenumber\PhoneNumberFormat::E164);
|
||||
}
|
||||
catch(\Exception $e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -44,10 +55,17 @@ namespace controllers\internals;
|
|||
*/
|
||||
public static function phone_format($number)
|
||||
{
|
||||
$phone_number_util = \libphonenumber\PhoneNumberUtil::getInstance();
|
||||
$phone_number_o = $phone_number_util->parse($number, null);
|
||||
try
|
||||
{
|
||||
$phone_number_util = \libphonenumber\PhoneNumberUtil::getInstance();
|
||||
$phone_number_o = $phone_number_util->parse($number, null);
|
||||
|
||||
return $phone_number_util->format($phone_number_o, \libphonenumber\PhoneNumberFormat::INTERNATIONAL);
|
||||
return $phone_number_util->format($phone_number_o, \libphonenumber\PhoneNumberFormat::INTERNATIONAL);
|
||||
}
|
||||
catch(\Exception $e)
|
||||
{
|
||||
return $number;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -205,4 +223,69 @@ namespace controllers\internals;
|
|||
|
||||
return mail($to, $settings['subject'], $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow to read an uploaded file
|
||||
* @param array $file : The array extracted from $_FILES['file']
|
||||
* @return array : ['success' => bool, 'content' => file handler | error message, 'error_code' => $file['error']]
|
||||
*/
|
||||
public static function read_uploaded_file(array $file)
|
||||
{
|
||||
$result = [
|
||||
'success' => false,
|
||||
'content' => 'Une erreur inconnue est survenue.',
|
||||
'error_code' => $file['error'] ?? 99,
|
||||
'mime_type' => false,
|
||||
];
|
||||
|
||||
if ($file['error'] !== UPLOAD_ERR_OK)
|
||||
{
|
||||
switch ($file['error'])
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
$tmp_filename = $file['tmp_name'] ?? false;
|
||||
if (!$tmp_filename || !is_readable($tmp_filename))
|
||||
{
|
||||
return $result;
|
||||
}
|
||||
|
||||
$result['mime_type'] = mime_content_type($tmp_filename) == 'text/plain' ? $file['type'] : mime_content_type($tmp_filename);
|
||||
|
||||
$file_handler = fopen($tmp_filename, 'r');
|
||||
$result['success'] = true;
|
||||
$result['content'] = $file_handler;
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -243,6 +243,120 @@ namespace controllers\publics;
|
|||
return $this->redirect(\descartes\Router::url('Contact', 'list'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allow to import a contacts list
|
||||
* @param string $csrf : Csrf token
|
||||
* @param $_FILES['contacts_list_file'] : A csv file of the contacts to import
|
||||
*/
|
||||
public function import (string $csrf)
|
||||
{
|
||||
if (!$this->verify_csrf($csrf))
|
||||
{
|
||||
\FlashMessage\FlashMessage::push('danger', 'Jeton CSRF invalid !');
|
||||
return $this->redirect(\descartes\Router::url('Contact', 'list'));
|
||||
}
|
||||
|
||||
$id_user = $_SESSION['user']['id'];
|
||||
|
||||
$upload_array = $_FILES['contacts_list_file'] ?? false;
|
||||
if (!$upload_array)
|
||||
{
|
||||
\FlashMessage\FlashMessage::push('danger', 'Vous devez fournir un fichier de contacts à importer.');
|
||||
return $this->redirect(\descartes\Router::url('Contact', 'list'));
|
||||
}
|
||||
|
||||
$read_file = \controllers\internals\Tool::read_uploaded_file($upload_array);
|
||||
if (!$read_file['success'])
|
||||
{
|
||||
\FlashMessage\FlashMessage::push('danger', $read_file['content']);
|
||||
return $this->redirect(\descartes\Router::url('Contact', 'list'));
|
||||
}
|
||||
|
||||
//Try to import file
|
||||
$invalid_type = false;
|
||||
switch ($read_file['mime_type'])
|
||||
{
|
||||
case 'text/csv' :
|
||||
$result = $this->internal_contact->import_csv($id_user, $read_file['content']);
|
||||
break;
|
||||
|
||||
case 'application/json' :
|
||||
$result = $this->internal_contact->import_json($id_user, $read_file['content']);
|
||||
break;
|
||||
|
||||
default :
|
||||
$invalid_type = true;
|
||||
}
|
||||
|
||||
if ($invalid_type)
|
||||
{
|
||||
\FlashMessage\FlashMessage::push('danger', 'Le type de fichier n\'est pas valide.');
|
||||
return $this->redirect(\descartes\Router::url('Contact', 'list'));
|
||||
}
|
||||
|
||||
if ($result === false)
|
||||
{
|
||||
\FlashMessage\FlashMessage::push('danger', 'Le fichier contient des erreurs. Impossible d\'importer les contacts.');
|
||||
return $this->redirect(\descartes\Router::url('Contact', 'list'));
|
||||
}
|
||||
|
||||
$msg = $result . ' nouveau contact a été inséré.';
|
||||
if ($result > 1)
|
||||
{
|
||||
$msg = $result . ' nouveaux contacts ont été insérés.';
|
||||
}
|
||||
|
||||
\FlashMessage\FlashMessage::push('success', $msg);
|
||||
return $this->redirect(\descartes\Router::url('Contact', 'list'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allow to export a contacts list
|
||||
* @param $format : Format to export contacts to
|
||||
*/
|
||||
public function export (string $format)
|
||||
{
|
||||
$id_user = $_SESSION['user']['id'];
|
||||
|
||||
//Try to export contacts
|
||||
$invalid_type = false;
|
||||
switch ($format)
|
||||
{
|
||||
case 'csv' :
|
||||
$result = $this->internal_contact->export_csv($id_user);
|
||||
break;
|
||||
|
||||
case 'json' :
|
||||
$result = $this->internal_contact->export_json($id_user);
|
||||
break;
|
||||
|
||||
default :
|
||||
$invalid_type = true;
|
||||
}
|
||||
|
||||
if ($invalid_type)
|
||||
{
|
||||
\FlashMessage\FlashMessage::push('danger', 'Le format demandé n\'est pas supporté.');
|
||||
return $this->redirect(\descartes\Router::url('Contact', 'list'));
|
||||
}
|
||||
|
||||
if ($result === false)
|
||||
{
|
||||
\FlashMessage\FlashMessage::push('danger', 'Nous ne sommes par parveu à exporté les contacts.');
|
||||
return $this->redirect(\descartes\Router::url('Contact', 'list'));
|
||||
}
|
||||
|
||||
$result['headers'] = $result['headers'] ?? [];
|
||||
foreach ($result['headers'] as $header)
|
||||
{
|
||||
header($header);
|
||||
}
|
||||
|
||||
echo $result['content'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Cette fonction retourne la liste des contacts sous forme JSON.
|
||||
*/
|
||||
|
|
|
@ -44,6 +44,8 @@
|
|||
'delete' => '/contact/delete/{csrf}/',
|
||||
'edit' => '/contact/edit/',
|
||||
'update' => '/contact/update/{csrf}/',
|
||||
'import' => '/contact/import/{csrf}/',
|
||||
'export' => '/contact/export/{format}/',
|
||||
'json_list' => '/contacts.json/',
|
||||
],
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
<div class="col-lg-12">
|
||||
<h1 class="page-header">
|
||||
Dashboard <small>Contacts</small>
|
||||
<a class="btn btn-info float-right" id="btn-export" href="#"><span class="fa fa-upload"></span> Exporter la liste des contacts</a>
|
||||
<a class="btn btn-info float-right" id="btn-import" href="#" style="margin-right: 10px;"><span class="fa fa-download"></span> Importer une liste de contacts</a>
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li>
|
||||
|
@ -82,5 +84,60 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" tabindex="-1" id="import-modal">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<form action="<?php $this->s(\descartes\Router::url('Contact', 'import', ['csrf' => $_SESSION['csrf']])); ?>" method="POST" enctype="multipart/form-data">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">Importer une liste de contacts</h4>
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<p>Vous pouvez importer une liste aux formats suivants : CSV ou JSON.</p>
|
||||
<input id="contacts_list_file" type="file" name="contacts_list_file" class="hidden" required="required">
|
||||
<label class="btn btn-default" for="contacts_list_file"><span class="fa fa-file-text-o hidden invalid-icon"></span><span class="fa fa-check hidden valid-icon"></span> Choisir le fichier</label>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a type="button" class="btn btn-danger" data-dismiss="modal">Annuler</a>
|
||||
<input type="submit" class="btn btn-success" value="Valider" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" tabindex="-1" id="export-modal">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<form action="<?php $this->s(\descartes\Router::url('Contact', 'import', ['csrf' => $_SESSION['csrf']])); ?>" method="POST" enctype="multipart/form-data">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">Exporter la liste des contacts</h4>
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<p>Vous pouvez exporter la liste aux formats suivants.</p>
|
||||
<a target="_blank" href="<?php $this->s(\descartes\Router::url('Contact', 'export', ['format' => 'csv'])); ?>" class="btn btn-default">CSV</a>
|
||||
<a target="_blank" href="<?php $this->s(\descartes\Router::url('Contact', 'export', ['format' => 'json'])); ?>" class="btn btn-default">JSON</a>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a type="button" class="btn btn-danger" data-dismiss="modal">Annuler</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
jQuery(document).ready(function()
|
||||
{
|
||||
jQuery('body').on('click', '#btn-import', function ()
|
||||
{
|
||||
jQuery('#import-modal').modal({'keyboard': true});
|
||||
});
|
||||
|
||||
jQuery('body').on('click', '#btn-export', function ()
|
||||
{
|
||||
jQuery('#export-modal').modal({'keyboard': true});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
$this->render('incs/footer');
|
||||
|
|
|
@ -68,7 +68,7 @@
|
|||
<?php if ($_SESSION['user']['admin']) { ?>
|
||||
<div class="text-right col-xs-12 no-padding">
|
||||
<strong>Action pour la séléction :</strong>
|
||||
<button class="btn btn-default" type="submit" formaction="<?php echo \descartes\Router::url('Event', 'delete', ['csrf' => $_SESSION['csrf']]); ?>"><span class="fa fa-trash-o"></span> Supprimer</button>
|
||||
<button class="btn btn-default btn-confirm" type="submit" formaction="<?php echo \descartes\Router::url('Event', 'delete', ['csrf' => $_SESSION['csrf']]); ?>"><span class="fa fa-trash-o"></span> Supprimer</button>
|
||||
</div>
|
||||
<?php } ?>
|
||||
<ul class="pager">
|
||||
|
|
|
@ -70,7 +70,7 @@
|
|||
<?php if ($_SESSION['user']['admin']) { ?>
|
||||
<div class="text-right col-xs-12 no-padding">
|
||||
<strong>Action pour la séléction :</strong>
|
||||
<button class="btn btn-default" type="submit" formaction="<?php echo \descartes\Router::url('Received', 'delete', ['csrf' => $_SESSION['csrf']]); ?>"><span class="fa fa-trash-o"></span> Supprimer</button>
|
||||
<button class="btn btn-default btn-confirm" type="submit" formaction="<?php echo \descartes\Router::url('Received', 'delete', ['csrf' => $_SESSION['csrf']]); ?>"><span class="fa fa-trash-o"></span> Supprimer</button>
|
||||
</div>
|
||||
<?php } ?>
|
||||
<ul class="pager">
|
||||
|
|
|
@ -82,7 +82,7 @@
|
|||
<?php if ($_SESSION['user']['admin']) { ?>
|
||||
<div class="text-right col-xs-12 no-padding">
|
||||
<strong>Action pour la séléction :</strong>
|
||||
<button class="btn btn-default" type="submit" formaction="<?php echo \descartes\Router::url('Sended', 'delete', ['csrf' => $_SESSION['csrf']]); ?>"><span class="fa fa-trash-o"></span> Supprimer</button>
|
||||
<button class="btn btn-default btn-confirm" type="submit" formaction="<?php echo \descartes\Router::url('Sended', 'delete', ['csrf' => $_SESSION['csrf']]); ?>"><span class="fa fa-trash-o"></span> Supprimer</button>
|
||||
</div>
|
||||
<?php } ?>
|
||||
<ul class="pager">
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
<?php if ($_SESSION['user']['admin']) { ?>
|
||||
<div class="text-right col-xs-12 no-padding">
|
||||
<strong>Action pour la séléction :</strong>
|
||||
<button class="btn btn-default" type="submit" formaction="<?php echo \descartes\Router::url('SmsStop', 'delete', ['csrf' => $_SESSION['csrf']]); ?>"><span class="fa fa-trash-o"></span> Supprimer</button>
|
||||
<button class="btn btn-default btn-confirm" type="submit" formaction="<?php echo \descartes\Router::url('SmsStop', 'delete', ['csrf' => $_SESSION['csrf']]); ?>"><span class="fa fa-trash-o"></span> Supprimer</button>
|
||||
</div>
|
||||
<?php } ?>
|
||||
<ul class="pager">
|
||||
|
|
Loading…
Reference in New Issue