<?php
namespace App\Controller\Admin;
use App\Api\SalesforceRest\Controllers\ApiAccountController;
use App\Dto\DtoRepository;
use App\Entity\Account;
use App\Entity\AppPushNotification;
use App\Entity\Article;
use App\Entity\Category;
use App\Entity\Company;
use App\Entity\Configuration;
use App\Entity\Contact;
use App\Entity\InAppNotification;
use App\Entity\InAppNotificationItem;
use App\Entity\Invoice;
use App\Entity\InvoiceMail;
use App\Entity\PushNotification;
use App\Entity\Repository;
use App\Entity\RepositoryFamily;
use App\Entity\Resource;
use App\Entity\ResourceCategory;
use App\Entity\ResourceGroup;
use App\Entity\ResourceWorkflow;
use App\Entity\Segmentation;
use App\Entity\SfCampaign;
use App\Entity\SfCampaignAllocation;
use App\Entity\SpecialMailText;
use App\Helper\DateHelper;
use App\Helper\ObjectConversionHelper;
use App\Helper\StringHelper;
use App\Service\ConfigurationManager;
use App\Service\ContactManager;
use App\Service\InvoiceMailManager;
use App\Service\InvoiceManager;
use App\Service\MailManager;
use App\Service\MobileApplicationService;
use App\Service\NotificationManager;
use DateTime;
use Doctrine\ORM\EntityManagerInterface;
use DoctrineExtensions\Query\Mysql\Date;
use EasyCorp\Bundle\EasyAdminBundle\Config\Dashboard;
use EasyCorp\Bundle\EasyAdminBundle\Config\MenuItem;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractDashboardController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
class DashboardController extends AbstractDashboardController
{
private InvoiceManager $invoiceManager;
private ContactManager $contactManager;
private MailManager $mailManager;
private EntityManagerInterface $entityManager;
private NotificationManager $notificationManager;
private ConfigurationManager $configurationManager;
private InvoiceMailManager $invoiceMailManager;
private MobileApplicationService $mobileApplicationService;
public function __construct(EntityManagerInterface $entityManager,
NotificationManager $notificationManager,
InvoiceManager $invoiceManager,
ContactManager $contactManager,
MailManager $mailManager,
ConfigurationManager $configurationManager,
InvoiceMailManager $invoiceMailManager,
MobileApplicationService $mobileApplicationService
)
{
$this->invoiceManager = $invoiceManager;
$this->contactManager = $contactManager;
$this->mailManager = $mailManager;
$this->entityManager = $entityManager;
$this->notificationManager = $notificationManager;
$this->configurationManager = $configurationManager;
$this->invoiceMailManager = $invoiceMailManager;
$this->mobileApplicationService = $mobileApplicationService;
}
/**
* Show admin interface
*
* @return Response
*/
public function index(): Response
{
try{
// get number of contacts in DB
$contactsNumber = $this->contactManager->getNumberOfContacts();
// get number of send mails in DB
$mailsNumber = $this->mailManager->getNumberOfSentMails();
// get number of sync invoices in DB
$invoicesNumber = $this->invoiceManager->getNumberOfInvoices();
// Nombre total d’installation de l’application (tous les fcmTokens)
// $totalInstallationsNumber = $this->mobileApplicationService->getTotalInstallationsNumber();
// Nombre de contacts uniques ayant installé l'application (unique fcmToken)
$uniqueContactInstallationsNumber = $this->mobileApplicationService->getUniqueContactInstallationsNumber();
// Nombre de contacts qui se sont connectés au moins une fois sur l'application mobile (via la table analytic)
$uniqueContactLoggedNumber = $this->mobileApplicationService->getContactsLoggedNumber();
// Nombre de contacts qui se sont connectés au moins une fois sur l'application mobile pour la période donnée (graphique -> prend le mois en cours si aucun parametre)
$uniqueContactLoggedForPeriod = $this->mobileApplicationService->getContactsLoggedForPeriod();
$currentDate = new \DateTime();
$currentMonthStartDate = $currentDate->format('Y-m-01');
$currentMonthEndDate = $currentDate->format('Y-m-t');
}
catch (\Doctrine\DBAL\Driver\Exception $e){
$this->notificationManager::addError($e->getMessage());
$contactsNumber = 0;
$mailsNumber = 0;
$invoicesNumber = 0;
// $totalInstallationsNumber = 0;
$uniqueContactInstallationsNumber = 0;
$uniqueContactLoggedNumber = 0;
$uniqueContactLoggedForPeriod = [];
$currentMonthStartDate = "";
$currentMonthEndDate = "";
}
catch (\Doctrine\DBAL\Exception $e){
$this->notificationManager::addError($e->getMessage());
$contactsNumber = 0;
$mailsNumber = 0;
$invoicesNumber = 0;
// $totalInstallationsNumber = 0;
$uniqueContactInstallationsNumber = 0;
$uniqueContactLoggedNumber = 0;
$uniqueContactLoggedForPeriod = [];
$currentMonthStartDate = "";
$currentMonthEndDate = "";
}
finally {
return $this->render('admin/index.html.twig',
[
"contactsNumber" => $contactsNumber,
"mailsNumber" => $mailsNumber,
"invoicesNumber" => $invoicesNumber,
// "totalInstallationsNumber" => $totalInstallationsNumber,
"uniqueContactInstallationsNumber" => $uniqueContactInstallationsNumber,
"uniqueContactLoggedNumber" => $uniqueContactLoggedNumber,
"uniqueContactLoggedForPeriod" => $uniqueContactLoggedForPeriod,
"currentMonthStartDate" => $currentMonthStartDate,
"currentMonthEndDate" => $currentMonthEndDate,
]);
}
}
/**
* To handle all contacts
* @param Request $request
* @return Response
*/
public function viewAdminContacts(Request $request) : Response
{
$syncContacts = $request->request->get('syncContacts');
$addContact = $request->request->get('addContact');
$updateContactPassword = $request->request->get('updateContactPassword');
// If the user wants to synchronize contacts
if(!is_null($syncContacts)) {
$insertedFromSalesforce = $this->contactManager->syncContactsFromSF();
$this->notificationManager::addInfo(strval($insertedFromSalesforce) . ' nouveaux contacts reçus et insérés de Salesforce');
}
// If the user wants to add a contact (+ instructor or club)
elseif(!is_null($addContact)) {
$contactEmail = $request->request->get('contactEmail', '');
// If the email address is filled and is correct
if($contactEmail !== '' && filter_var($contactEmail,FILTER_VALIDATE_EMAIL)){
// if the contact doesn't already exist
if(!$this->contactManager->isContactByMail($contactEmail)){
$contact = $this->contactManager->createContact($contactEmail);
$this->entityManager->persist($contact);
$this->entityManager->flush();
$this->notificationManager::addInfo('Profil de ' . $contactEmail . ' créé');
// todo : create instructor or club on salesforce
}
// if the contact already exist
else{
$this->notificationManager::addError('Le contact ' . $contactEmail . ' existe déjà en base de données');
}
}
// if the email address is not correct
else{
$this->notificationManager::addError('Le champs email renseigné n\'est pas correct');
}
}
// update contact password
elseif(!is_null($updateContactPassword)) {
$contactEmail = $request->request->get('contactEmail');
$contactPassword = $request->request->get('contactPassword', '');
if(!is_null($contactEmail) && strlen($contactPassword) > 3){
$contact = $this->contactManager->getContactByMail($contactEmail);
// if the contact exists
if(!is_null($contact)){
$this->contactManager->updatePassword($contact, $contactPassword);
$this->notificationManager::addInfo('Mot de passe de ' . $contactEmail . ' mis à jour.');
}
// If the mail is bad and contact doesnt exist
else{
$this->notificationManager::addError('Le contact n\'existe pas');
}
}
// If the email / password are not corrects
else{
$this->notificationManager::addError('Le mail ou le mot de passe ne sont pas valdie');
}
}
$this->setLogs();
$this->createFlashs();
return $this->render('admin/contacts.html.twig');
}
/**
* Get all the invoices from the sage folder,
* Ask Salesforce information about them + their linked account
* Create the accounts (id + hash) and the companies in database if they don't exist and create account folders
* Copy the invoices files from sage folder to account folders,
* Send information to Salesforce that processed invoices are processed
* Move invoice file to trash folder
* @return Response
*/
public function viewAdminDocuments(Request $request) : Response
{
$this->notificationManager::addInfo('Le dossier où se situent les factures de Sage est : ' . $_ENV['INVOICES_FOLDER']);
$this->notificationManager::addInfo('Le dossier où seront copiées les factures est : ' . $_ENV['INVOICE_PUBLIC_FOLDER']);
$this->notificationManager::addInfo('Le dossier où seront déplacées les factures une fois copiées est : ' . $_ENV['INVOICE_TRASH_FOLDER']);
$this->notificationManager::addInfo('Le nombre de factures traitées par lot est de : ' . $_ENV['MAX_INVOICE_PER_BATCH']);
// If the user wants to synchronize all invoices
if(isset($_POST['synchroInvoices']) && $_POST['synchroInvoices'] == 'synchroInvoices') {
$this->invoiceManager->executeSynchronizeInvoices();
}
// If the user wants to rename all invoices
if(isset($_POST['renameInvoices']) && $_POST['renameInvoices'] == 'renameInvoices') {
$badNamesInvoices = $this->invoiceManager->getBadNameInvoices($_ENV['INVOICES_FOLDER']);
$treated = $this->invoiceManager->renameInvoices($badNamesInvoices);
$badNamesInvoicesNumber = count($badNamesInvoices);
$this->notificationManager::addInfo(strval($badNamesInvoicesNumber) . ' documents à renommer.');
$this->notificationManager::addInfo(strval($treated). ' documents renommés.');
if($badNamesInvoicesNumber !== $treated){
$this->notificationManager::addError(strval($badNamesInvoicesNumber) . ' factures avec nom incorrect, ' . $treated . ' effectués.');
}
}
$this->setLogs();
$this->createFlashs();
return $this->render('admin/documents.html.twig');
}
/**
* To handle the mails sent to contacts
* @param Request $request
* @return Response
*/
public function viewAdminMails(Request $request) : Response
{
$companies = $this->entityManager->getRepository(Company::class)->findAll();
$sendMail = $request->request->get('sendMail');
$configureMail = $request->request->get('configureMail');
$this->notificationManager::addInfo('Le nombre de mails envoyés par lot est de : ' . $_ENV['MAX_EMAIL_SENT_PER_BATCH']);
$specialMailOnPriceReduction = $this->configurationManager->findByName(Configuration::SPECIAL_MAIL_ON_PRICE_REDUCTION)->getValue();
// If the user wants to send all not sent mail to contacts linked to accounts of invoices
if(!is_null($configureMail)) {
$specialMailsSelected = []; // a key (company id) => ?value (template mail id, null if not selected) array
// get every special mail texts template IDs
foreach ($companies as $company){
$specialCompanyMailText = $this->entityManager->find(SpecialMailText::class, intval($request->request->get('mailTemplateId-' . $company->getId())));
// if we configured one special text for the company, null or not
$company->setSpecialMailText($specialCompanyMailText);
// saving in database
$this->entityManager->persist($company);
}
// value of "do we send special emails based on price reduction ?"
$crisisBasedOnPriceReductionValue = $request->request->get(Configuration::SPECIAL_MAIL_ON_PRICE_REDUCTION) == 'on' ? '1' : '0';
$crisisBasedOnPriceReductionConfObject = $this->configurationManager->findByName(Configuration::SPECIAL_MAIL_ON_PRICE_REDUCTION);
if(is_null($crisisBasedOnPriceReductionConfObject)){
$crisisBasedOnPriceReductionConfObject = (new Configuration())
->setName(Configuration::SPECIAL_MAIL_ON_PRICE_REDUCTION);
}
$crisisBasedOnPriceReductionConfObject->setValue($crisisBasedOnPriceReductionValue);
$this->entityManager->persist($crisisBasedOnPriceReductionConfObject);
$specialMailOnPriceReduction = $this->configurationManager->findByName(Configuration::SPECIAL_MAIL_ON_PRICE_REDUCTION)->getValue();
// Save companies default mail + configuration
$this->entityManager->flush();
}
// for mail sending
if(!is_null($sendMail)) {
$this->invoiceMailManager->executeSendInvoiceMails();
$this->setLogs();
}
$this->createFlashs();
// todo : return the good template + mailtexts
return $this->render('admin/mails.html.twig', [
'companies' => $companies,
Configuration::SPECIAL_MAIL_ON_PRICE_REDUCTION => $specialMailOnPriceReduction,
]);
}
/**
* To add CECI and fullname in old accounts
* @param Request $request
* @return Response
*/
public function viewAdminAccounts(Request $request) : Response
{
// $accounts = $this->entityManager->getRepository(Account::class)->findAll();
$limit = 1000;
$accounts = $this->entityManager->getRepository(Account::class)->findBy(array(
"fullName" => null,
"CECI" => null
), ["id" => "ASC"], $limit);
$addAccountFields = $request->request->get('addAccountFields');
// If the account hasn't CE/CI or fullName property
if(!is_null($addAccountFields)) {
$countEditedAccounts = 0;
foreach ($accounts as $account){
// if(is_null($account->getCECI())) {
$account->setCECI(ApiAccountController::getAccount($account->getSalesforceId())->ID_CE_CI__c);
// $this->entityManager->persist($account);
// }
// if(is_null($account->getFullName())) {
$account->setFullName(ApiAccountController::getAccount($account->getSalesforceId())->Name);
$this->entityManager->persist($account);
// }
// saving in database
$this->entityManager->flush();
$countEditedAccounts++;
}
$this->addFlash("success", $countEditedAccounts." compte(s) modifié(s)");
$this->addFlash("success", "Limite de ".$limit." comptes par itération.");
}
$this->createFlashs();
// todo : return the good template
return $this->render('admin/accounts.html.twig', [
]);
}
/**
* Set all logs in admin logs files
* @return void
*/
protected function setLogs(): void
{
$dirError = false;
// Get logs from NotificationManager
$logs = $this->notificationManager->getMessages();
$finalLogs = [];
$infos = [];
$errors = [];
// Write logs with current datetime in arrays
foreach($logs['info'] as $info) {
$date = new DateTime();
$infos[] = '[' .$date->format("d-m-YTH:i:s.vP"). '] app.ERROR: ' . $info;
}
foreach($logs['error'] as $error) {
$date = new DateTime();
$errors[] = '[' .$date->format("d-m-YTH:i:s.vP"). '] app.ERROR: ' . $error;
}
$finalLogs[] = $infos;
$finalLogs[] = $errors;
// Check if customLogs directory exist, if not create it
if(!is_dir($_ENV['CUSTOM_LOGS_FOLDER'])) {
if (!mkdir($_ENV['CUSTOM_LOGS_FOLDER'])) {
$dirError = true;
$this->notificationManager::addError('Erreur lors de la création du dossier ' . $_ENV['CUSTOM_LOGS_FOLDER']);
}
}
// If customLogs directory exist create/update the log file of the day
if(!$dirError) {
$date = new DateTime();
foreach ($finalLogs as $logs) {
foreach ($logs as $log) {
file_put_contents($_ENV['CUSTOM_LOGS_FOLDER'].'/'.$date->format("d-m-Y").'.log', $log. PHP_EOL,FILE_APPEND);
}
}
}
}
/**
* Show all customLogs files
*/
public function showLogs(): Response
{
// dd(file_get_contents($_ENV['CUSTOM_LOGS_FOLDER'].'/30-12-2020.log'));
$results_array = array();
if (is_dir($_ENV['CUSTOM_LOGS_FOLDER']))
{
if ($handle = opendir($_ENV['CUSTOM_LOGS_FOLDER']))
{
//Notice the parentheses I added:
while(($file = readdir($handle)) !== FALSE)
{
$results_array[] = $file;
}
closedir($handle);
}
}
//Output findings
unset($results_array[0]);
unset($results_array[1]);
// dd($results_array);
return $this->render('admin/logs.html.twig', [
'logs' => $results_array,
]);
}
/**
* Download the log $logName in CUSTOM_LOGS_FOLDER
*/
public function downloadLog($logName)
{
$response = new Response();
$response->headers->set('Content-type', 'application/octet-stream');
$response->headers->set('Content-Disposition', sprintf('attachment; filename="%s"', $logName));
$response->setContent(file_get_contents($_ENV['CUSTOM_LOGS_FOLDER'].'/'.$logName));
$response->setStatusCode(200);
$response->headers->set('Content-Transfer-Encoding', 'binary');
$response->headers->set('Pragma', 'no-cache');
$response->headers->set('Expires', '0');
return $response;
}
/**
* Parse all errors and messages to create flash messages
*/
protected function createFlashs() : void{
$messages = $this->getMessages();
foreach ($messages['error'] as $error){
$this->addFlash('danger', $error);
}
foreach ($messages['info'] as $message){
$this->addFlash('success', $message);
}
$this->notificationManager::eraseMessages();
}
/**
* @return array
*/
public function getMessages() : array
{
return $this->notificationManager::getMessages();
}
public function configureDashboard(): Dashboard
{
return Dashboard::new()
->setTitle('Administration');
}
public function configureMenuItems(): iterable
{
yield MenuItem::section('Actions');
yield MenuItem::linktoRoute('Retour vers le site', 'fa fa-home', 'Index');
yield MenuItem::linktoDashboard('Dashboard', 'fas fa-chart-line')
->setPermission('ROLE_ADMIN');
yield MenuItem::linkToRoute('Documents', 'fas fa-file-invoice', 'AdminDocuments')
->setPermission('ROLE_ADMIN');
yield MenuItem::linkToRoute('Mails', 'fa fa-envelope', 'AdminMails')
->setPermission('ROLE_ADMIN');
yield MenuItem::linkToRoute('Contacts', 'fas fa-id-card', 'AdminContacts')
->setPermission('ROLE_ADMIN');
yield MenuItem::linkToRoute('Comptes', 'fas fa-id-card', 'AdminAccounts')
->setPermission('ROLE_ADMIN');
yield MenuItem::linkToRoute('Logs', 'fa fa-history', 'AdminLogs')
->setPermission('ROLE_ADMIN');
yield MenuItem::section('Base de données')
->setPermission('ROLE_SALE');
yield MenuItem::linkToCrud('Messages de Crise', 'fa fa-envelope', SpecialMailText::class)
->setPermission('ROLE_ADMIN');
yield MenuItem::linkToCrud('Liste contacts', 'fas fa-id-card', Contact::class)
->setPermission('ROLE_ADMIN');
yield MenuItem::linkToCrud('Liste factures', 'fas fa-file-invoice', Invoice::class)
->setPermission('ROLE_BILLING');
yield MenuItem::linkToCrud('Liste mails factures', 'fas fa-envelope', InvoiceMail::class)
->setPermission('ROLE_ADMIN');
yield MenuItem::section('Blog / Communication')
->setPermission('ROLE_WRITER');
yield MenuItem::linkToCrud('Articles', 'fas fa-newspaper', Article::class)
->setPermission('ROLE_WRITER');
yield MenuItem::linkToCrud('Catégories', 'fas fa-tags', Category::class)
->setPermission('ROLE_WRITER');
yield MenuItem::linkToCrud("Notes d'information", 'fas fa-info-circle', PushNotification::class)
->setPermission('ROLE_WRITER');
yield MenuItem::section('Ressources')
->setPermission('ROLE_WRITER');
yield MenuItem::linkToCrud('Ressources', 'fas fa-newspaper', Resource::class)
->setPermission('ROLE_WRITER');
yield MenuItem::linkToCrud('Catégories', 'fas fa-tags', ResourceCategory::class)
->setPermission('ROLE_WRITER');
yield MenuItem::linkToCrud('Processus', 'fa fa-cogs', ResourceWorkflow::class)
->setPermission('ROLE_WRITER');
yield MenuItem::linkToCrud('Etapes', 'fa fa-cog', ResourceGroup::class)
->setPermission('ROLE_WRITER');
yield MenuItem::section('Leads')->setPermission('ROLE_ADMIN');
yield MenuItem::linkToRoute('Map Leads', 'fas fa-map-marked', 'AdminMapLeads')
->setPermission('ROLE_SALE');
yield MenuItem::section('Marketing')->setPermission('ROLE_ADMIN');
yield MenuItem::linkToCrud('Segmentations', 'fa fa-filter', Segmentation::class)
->setPermission('ROLE_ADMIN');
yield MenuItem::linkToCrud('Enveloppe budgétaire', 'fa fa-tags', SfCampaignAllocation::class)
->setPermission('ROLE_ADMIN');
yield MenuItem::linkToCrud('Campagnes Salesforce', 'fa fa-tags', SfCampaign::class)
->setPermission('ROLE_ADMIN');
yield MenuItem::linkToUrl("Coûts", "fa fa-eur", "adminpf/marketing-costs")
->setPermission('ROLE_ADMIN');
yield MenuItem::section('Application mobile')->setPermission('ROLE_ADMIN');
yield MenuItem::linkToCrud('Notifications Push', 'fa fa-bell', AppPushNotification::class)
->setPermission('ROLE_ADMIN');
yield MenuItem::linkToCrud('Notifications InApp', 'fa fa-bell', InAppNotification::class)
->setPermission('ROLE_ADMIN');
yield MenuItem::linkToCrud('Slides pour notifications InApp', 'fa fa-bell', InAppNotificationItem::class)
->setPermission('ROLE_ADMIN');
}
public function configureCrud(): Crud
{
return Crud::new()
// the first argument is the "template name", which is the same as the
// Twig path but without the `@EasyAdmin/` prefix
->overrideTemplate('layout', 'bundles/EasyAdminBundle/layout.html.twig');
}
/**
* @return Response
* @throws \JsonException
*/
public function mapLeadsByClub(): Response
{
$data = file_get_contents($_ENV['DASHBOARD_CLUB_LEAD_API_URL']);
$json = json_decode($data, true, 512, JSON_THROW_ON_ERROR);
if(is_array($json) && count($json) > 1) {
foreach($json as $jsonData) {
$json['datas'][] = [
'id' => $jsonData['user'],
'text' => $jsonData['user'].' - '.$jsonData['name']
];
}
return $this->render('admin/map-leads.html.twig', [ 'json' => $json['datas'] ]);
}
return $this->render('admin/map-leads.html.twig', [ 'json' => '' ]);
}
}