src/Services/LogTools.php line 36

Open in your IDE?
  1. <?php
  2. //----------------------------------------------------------------------
  3. // src/Services/LogTools.php
  4. //----------------------------------------------------------------------
  5. namespace App\Services;
  6. use DateTime;
  7. use Doctrine\Persistence\ManagerRegistry;
  8. use Psr\Log\LoggerInterface;
  9. use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken;
  10. use Symfony\Contracts\Translation\TranslatorInterface;
  11. use Symfony\Component\Security\Core\Security;
  12. use App\DTO\Log\LogCsvRecord;
  13. use App\Entity\Access;
  14. use App\Entity\AccessClient\AccessClient;
  15. use App\Entity\APIRest\AccessAPI;
  16. use App\Entity\Client\Client;
  17. use App\Entity\HR\HumanResource;
  18. use App\Entity\Ikea\ServiceOrder;
  19. use App\Entity\Mission\Mission;
  20. use App\Entity\OnlinePayment\PaymentType as OnlinePaymentType;
  21. use App\Entity\Planning\Task;
  22. use App\Entity\Platform\Devis\Devis;
  23. use App\Entity\Platform\Invoice\Invoice;
  24. use App\Entity\ProjectManager\ProjectNotebook;
  25. use App\Entity\SocietyGroup;
  26. class LogTools
  27. {
  28.     public function __construct(ManagerRegistry $doctrineTranslatorInterface $translatorLoggerInterface $ouchLoggerSecurity $security)
  29.     {
  30.         $this->em $doctrine->getManager();
  31.         $this->translator $translator;
  32.         $this->security $security;
  33.         $this->ouchLogger $ouchLogger;
  34.     }
  35.     public function getRootDir()
  36.     {
  37.         $home dirname(__FILE__3);
  38.         return $home;
  39.     }
  40.     public function getPublicDir()
  41.     {
  42.         $home dirname(__FILE__3);
  43.         $home .= "/public/";
  44.         return $home;
  45.     }
  46.     public function getCustomLogsDir()
  47.     {
  48.         $home dirname(__FILE__3);
  49.         $home .= "/custom_logs/";
  50.         return $home;
  51.     }
  52.     public function getCustomDataDir()
  53.     {
  54.         $home dirname(__FILE__3);
  55.         $home .= "/custom_data/";
  56.         return $home;
  57.     }
  58.     public function getDMSRootDir()
  59.     {
  60.         $home dirname(__FILE__3);
  61.         $home .= "/DMS/";
  62.         return $home;
  63.     }
  64.     // Plan.io Task #4661
  65.     // ROOT/DMS/Ikea/logs/{society_group_id}/
  66.     public function getIkeaDMSLogsFolder(SocietyGroup $societyGroup)
  67.     {
  68.         $home $this->getDMSRootDir();
  69.         if (!is_dir($home))    $do mkdir($home0755true);
  70.         $home .= "Ikea/logs/";
  71.         if (!is_dir($home))    $do mkdir($home0755true);
  72.         $home .= $societyGroup->getId();
  73.         $home .= "/";
  74.         if (!is_dir($home))    $do mkdir($home0755true);
  75.         return $home;
  76.     }
  77.     // Plan.io Task #4661
  78.     // ROOT/DMS/Ikea/logs/{society_group_id}/
  79.     public function getIkeaDMSLogFile(ServiceOrder $serviceOrder)
  80.     {
  81.         if ($serviceOrder->getSocietyGroup() !== null)
  82.         {
  83.             $path $this->getIkeaDMSLogsFolder($serviceOrder->getSocietyGroup());
  84.             $logFile $path $serviceOrder->getId() . ".log";
  85.             return $logFile;
  86.         }
  87.         return null;
  88.     }
  89.     // Plan.io Task #4661
  90.     public function logNewXml(ServiceOrder $serviceOrderstring $data)
  91.     {
  92.         $logFile $this->getIkeaDMSLogFile($serviceOrder);
  93.         if ($logFile !== null)
  94.         {
  95.             $today = new \DateTime();
  96.             $timestamp $today->format("Y-m-d H:i:s");
  97.             error_log("\n[$timestamp$data"3$logFile);
  98.         }
  99.     }
  100.     // Plan.io Task #4448    
  101.     // ROOT/DMS/IkeaBP/logs/{society_group_id}/
  102.     public function getIkeaBpFolder(SocietyGroup $societyGroup)
  103.     {
  104.         $home $this->getDMSRootDir();
  105.         if (!is_dir($home))    $do mkdir($home0755true);
  106.         $home .= "IkeaBP/logs/";
  107.         if (!is_dir($home))    $do mkdir($home0755true);
  108.         $home .= $societyGroup->getId();
  109.         $home .= "/";
  110.         if (!is_dir($home))    $do mkdir($home0755true);
  111.         return $home;
  112.     }
  113.     public function getIkeaBpLogFileName(SocietyGroup $societyGroup)
  114.     {        
  115.         $today = new \DateTime();
  116.         $timestamp $today->format("Ymd_His");
  117.         $logFileName "IkeaBP_".$societyGroup->getId()."_".$timestamp.".log";
  118.         return $logFileName;
  119.     }
  120.     public function getIkeaBpErrorFileName(SocietyGroup $societyGroup)
  121.     {        
  122.         $today = new \DateTime();
  123.         $timestamp $today->format("Ymd_His");
  124.         $logFileName "IkeaBP_".$societyGroup->getId()."_".$timestamp.".err";
  125.         return $logFileName;
  126.     }
  127.     public function getIkeaBpLogFile(SocietyGroup $societyGroup)
  128.     {
  129.         $path $this->getIkeaBpFolder($societyGroup);        
  130.         $logFileName $this->getIkeaBpLogFileName($societyGroup);
  131.         $logFile $path.$logFileName;
  132.         return $logFile;
  133.     }
  134.     public function getIkeaBpErrorFile(SocietyGroup $societyGroup)
  135.     {
  136.         $path $this->getIkeaBpFolder($societyGroup);        
  137.         $logFileName $this->getIkeaBpErrorFileName($societyGroup);
  138.         $logFile $path.$logFileName;
  139.         return $logFile;
  140.     }
  141.     public function logToBpFile_andTask($logFileTask $task$msg)
  142.     {
  143.         $today = new \DateTime();
  144.         $timestamp $today->format("Y-m-d H:i:s");
  145.         error_log("\n[$timestamp$msg"3$logFile);
  146.         $ikeaBpProcessInfo $task->getIkeaBpProcessInfo();
  147.         $ikeaBpProcessInfo .= $msg;
  148.         $task->setIkeaBpProcessInfo($ikeaBpProcessInfo);
  149.         
  150.         // error_log("\n[$timestamp] $msg", 3, "ploop.log");
  151.     }
  152.     public function logToBpFile($logFile$msg)
  153.     {
  154.         $today = new \DateTime();
  155.         $timestamp $today->format("Y-m-d H:i:s");
  156.         error_log("\n[$timestamp$msg"3$logFile);
  157.         
  158.         // error_log("\n[$timestamp] $msg", 3, "ploop.log");
  159.     }
  160.     // ROOT/DMS/Export/CostAccount/society_group_id/
  161.     public function getCostAutoAccountExportFolder($societyGroup null)
  162.     {
  163.         $home $this->getDMSRootDir();
  164.         if (!is_dir($home))    $do mkdir($home0755true);
  165.         $home .= "Export/";
  166.         if (!is_dir($home))    $do mkdir($home0755true);
  167.         $home .= "CostAccount/";
  168.         if (!is_dir($home))    $do mkdir($home0755true);
  169.         if ($societyGroup !== null)
  170.         {
  171.             $home .= $societyGroup->getId();
  172.             $home .= "/";
  173.             if (!is_dir($home))    $do mkdir($home0755true);
  174.         }
  175.         return $home;
  176.     }
  177.     
  178.     // ROOT/DMS/Export/CostAccount/society_group_id/logs/
  179.     public function getCostAutoAccountExportLogsFolder(SocietyGroup $societyGroup)
  180.     {
  181.         $home $this->getDMSRootDir();
  182.         if (!is_dir($home))    $do mkdir($home0755true);
  183.         $home .= "Export/";
  184.         if (!is_dir($home))    $do mkdir($home0755true);
  185.         $home .= "CostAccount/";
  186.         if (!is_dir($home))    $do mkdir($home0755true);
  187.         $home .= $societyGroup->getId();
  188.         $home .= "/";
  189.         if (!is_dir($home))    $do mkdir($home0755true);
  190.         $home .= "logs/";
  191.         if (!is_dir($home))    $do mkdir($home0755true);
  192.         return $home;
  193.     }
  194.     
  195.     // ROOT/DMS/Export/InvoiceAccount/society_group_id/
  196.     public function getInvoiceAutoAccountExportFolder($societyGroup null)
  197.     {
  198.         $home $this->getDMSRootDir();
  199.         if (!is_dir($home))    $do mkdir($home0755true);
  200.         $home .= "Export/";
  201.         if (!is_dir($home))    $do mkdir($home0755true);
  202.         $home .= "InvoiceAccount/";
  203.         if (!is_dir($home))    $do mkdir($home0755true);
  204.         if ($societyGroup !== null)
  205.         {
  206.             $home .= $societyGroup->getId();
  207.             $home .= "/";
  208.             if (!is_dir($home))    $do mkdir($home0755true);
  209.         }
  210.         return $home;
  211.     }
  212.     
  213.     // ROOT/DMS/Export/InstallmentAccount/society_group_id/
  214.     public function getInstallmentAutoAccountExportFolder($societyGroup null)
  215.     {
  216.         $home $this->getDMSRootDir();
  217.         if (!is_dir($home))    $do mkdir($home0755true);
  218.         $home .= "Export/";
  219.         if (!is_dir($home))    $do mkdir($home0755true);
  220.         $home .= "InstallmentAccount/";
  221.         if (!is_dir($home))    $do mkdir($home0755true);
  222.         if ($societyGroup !== null)
  223.         {
  224.             $home .= $societyGroup->getId();
  225.             $home .= "/";
  226.             if (!is_dir($home))    $do mkdir($home0755true);
  227.         }
  228.         return $home;
  229.     }
  230.     
  231.     // ROOT/DMS/Export/InstallmentAccount/society_group_id/logs/
  232.     public function getInstallmentAutoAccountExportLogsFolder(SocietyGroup $societyGroup)
  233.     {
  234.         $home $this->getDMSRootDir();
  235.         if (!is_dir($home))    $do mkdir($home0755true);
  236.         $home .= "Export/";
  237.         if (!is_dir($home))    $do mkdir($home0755true);
  238.         $home .= "InstallmentAccount/";
  239.         if (!is_dir($home))    $do mkdir($home0755true);
  240.         $home .= $societyGroup->getId();
  241.         $home .= "/";
  242.         if (!is_dir($home))    $do mkdir($home0755true);
  243.         $home .= "logs/";
  244.         if (!is_dir($home))    $do mkdir($home0755true);
  245.         return $home;
  246.     }
  247.     
  248.     // ROOT/DMS/Export/InvoiceAccount/society_group_id/logs/
  249.     public function getInvoiceAutoAccountExportLogsFolder(SocietyGroup $societyGroup)
  250.     {
  251.         $home $this->getDMSRootDir();
  252.         if (!is_dir($home))    $do mkdir($home0755true);
  253.         $home .= "Export/";
  254.         if (!is_dir($home))    $do mkdir($home0755true);
  255.         $home .= "InvoiceAccount/";
  256.         if (!is_dir($home))    $do mkdir($home0755true);
  257.         $home .= $societyGroup->getId();
  258.         $home .= "/";
  259.         if (!is_dir($home))    $do mkdir($home0755true);
  260.         $home .= "logs/";
  261.         if (!is_dir($home))    $do mkdir($home0755true);
  262.         return $home;
  263.     }
  264.     /**
  265.      * Exports Logs to ROOT/DMS/Export/InvoiceAccount/society_group_id/logs/
  266.      * File Name : { yyyymmdd }_Export_QCompta_error.log
  267.      */
  268.     public function logError_invoiceAutoAccount(SocietyGroup $societyGroup$error)
  269.     {
  270.         $exportPath $this->getInvoiceAutoAccountExportLogsFolder($societyGroup);
  271.         $today = new \DateTime();
  272.         $ymd $today->format("Ymd");
  273.         $timestamp $today->format("Y-m-d H:i:s");
  274.         $fileName $ymd."_Export_QCompta_Factures_error".".log";
  275.         $errorFile $exportPath.$fileName;
  276.         error_log("\n[$timestamp$error"3$errorFile);
  277.     }
  278.     /**
  279.      * Exports Logs to ROOT/DMS/Export/CostAccount/society_group_id/logs/
  280.      * File Name : { yyyymmdd }_Export_Achats_QCompta_error.log
  281.      */
  282.     public function logError_costAutoAccount(SocietyGroup $societyGroup$error)
  283.     {
  284.         $exportPath $this->getCostAutoAccountExportLogsFolder($societyGroup);
  285.         $today = new \DateTime();
  286.         $ymd $today->format("Ymd");
  287.         $timestamp $today->format("Y-m-d H:i:s");
  288.         $fileName $ymd."_Export_QCompta_Achats_error".".log";
  289.         $errorFile $exportPath.$fileName;
  290.         error_log("\n[$timestamp$error"3$errorFile);
  291.     }
  292.     /**
  293.      * Exports Logs to ROOT/DMS/Export/InstallmentAccount/society_group_id/logs/
  294.      * File Name : { yyyymmdd }_Export_Reglements_QCompta_error.log
  295.      */
  296.     public function logError_installmentAutoAccount(SocietyGroup $societyGroup$error)
  297.     {
  298.         $exportPath $this->getInstallmentAutoAccountExportLogsFolder($societyGroup);
  299.         $today = new \DateTime();
  300.         $ymd $today->format("Ymd");
  301.         $timestamp $today->format("Y-m-d H:i:s");
  302.         $fileName $ymd."_Export_QCompta_Reglements_error".".log";
  303.         $errorFile $exportPath.$fileName;
  304.         error_log("\n[$timestamp$error"3$errorFile);
  305.     }
  306.     //--------------------------------------------------------------------------
  307.     // TO BE USED IN COMMANDS (OneShot) ONLY
  308.     //--------------------------------------------------------------------------
  309.     public function getCommandLogsDir()
  310.     {
  311.         $home $this->getCustomLogsDir()."command_logs/";
  312.         return $home;
  313.     }
  314.     public function command_log($filename$msg$newLine true)
  315.     {
  316.         $home $this->getCommandLogsDir();
  317.         $logFile $home $filename;
  318.         if ($newLine)
  319.             $msg "\n".$msg;
  320.         error_log($msg,3,$logFile);
  321.     }
  322.     //--------------------------------------------------------------------------
  323.     public function plooplog($msg)
  324.     {
  325.         error_log("\n".$msg."\n",3,"ploop.log");
  326.     }
  327.     // public function ploopWrite($msg)
  328.     // {
  329.     //     $filename = "ploop.log";
  330.     //     $msg = "\n".$msg."\n";
  331.     //     if (is_writable($filename)) 
  332.     //     {
  333.     //         if ($fp = fopen($filename, 'a')) 
  334.     //         {
  335.     //             fwrite($fp, $msg);
  336.     //         }
  337.     //     }        
  338.     // }
  339.     public function ploop_debug($active$msg)
  340.     {
  341.         if (!$active)
  342.             return false;
  343.         error_log("\n".$msg."\n",3,"ploop.log");
  344.     }
  345.     public function getGuardConfigDir()
  346.     {
  347.         $home $this->getRootDir();
  348.         $home .= "/src/Guardian/config/";
  349.         return $home;
  350.     }
  351.     public function getPlanningOptimisationLogsDir()
  352.     {
  353.         $home $this->getCustomLogsDir()."planning_optimisation_logs/";
  354.         return $home;
  355.     }
  356.     public function getIkeaKitchenPlannerLogsDir()
  357.     {
  358.         $home $this->getCustomLogsDir()."ikea_kitchen_planner_logs/";
  359.         return $home;
  360.     }
  361.     public function getApiIcodLogsDir()
  362.     {
  363.         $home $this->getCustomLogsDir()."api_icod/";
  364.         return $home;
  365.     }
  366.     public function getCynclyLogsDir()
  367.     {
  368.         $home $this->getCustomLogsDir()."cyncly_logs/";
  369.         return $home;
  370.     }
  371.     // NOTE: This folder is used for WIP subscription system not for the onlinePayment one
  372.     public function getStripeLogsDir()
  373.     {
  374.         $home $this->getCustomLogsDir()."stripe/";
  375.         return $home;
  376.     }
  377.     public function getGuestSuiteLogsDir()
  378.     {
  379.         $home $this->getCustomLogsDir()."guest_suite_log/";
  380.         return $home;
  381.     }
  382.     public function getGuardLogsDir()
  383.     {
  384.         $home $this->getCustomLogsDir()."guard_logs/";
  385.         return $home;
  386.     }
  387.     public function getGuardGlobalLogsDir()
  388.     {
  389.         $home $this->getCustomLogsDir()."guard_logs/global/";
  390.         return $home;
  391.     }
  392.     public function getIkeaOSLogsDir()
  393.     {
  394.         $home $this->getCustomLogsDir()."ikea_os_logs/";
  395.         return $home;
  396.     }
  397.     public function getIkeaOS_statusLogsDir()
  398.     {
  399.         $home $this->getCustomLogsDir()."ikea_os_logs/status_logs/";
  400.         return $home;
  401.     }
  402.     public function getIkeaHotlineCallLogsDir(SocietyGroup $societyGroup): string
  403.     {
  404.         $home $this->getCustomLogsDir()."ikea_hotline_call/";
  405.         if (!is_dir($home))
  406.         {
  407.             mkdir($home0755true);
  408.         }
  409.         $home .= $societyGroup->getId()."/";
  410.         if (!is_dir($home))
  411.         {
  412.             mkdir($home0755true);
  413.         }
  414.         return $home;
  415.     }
  416.     public function getIkeaSendLogsDir($societyGroup)
  417.     {
  418.         $home $this->getCustomLogsDir();
  419.         $home .= "ikea_send_logs/";
  420.         if (!is_dir($home))
  421.         {
  422.             $do mkdir($home0755true);
  423.             if ($do === false)
  424.             {
  425.                 return null;
  426.             }
  427.         }
  428.         if ($societyGroup !== null)
  429.         {
  430.             $id $societyGroup->getId();
  431.             $home .= $id."/";
  432.             if (!is_dir($home))
  433.             {
  434.                 $do mkdir($home0755true);
  435.                 if ($do === false)
  436.                 {
  437.                     $this->errorLog("Folder $home cannot be created.");
  438.                     return null;
  439.                 }
  440.             }
  441.         }
  442.         return $home;
  443.     }
  444.     
  445.     public function getPlanningLogsDir()
  446.     {
  447.         $home $this->getCustomLogsDir()."planning/";
  448.         if (!is_dir($home))    $do mkdir($home0755true);
  449.         return $home;
  450.     }
  451.     
  452.     public function getCostLogsDir()
  453.     {
  454.         $home $this->getCustomLogsDir()."cost_logs/";
  455.         if (!is_dir($home))    $do mkdir($home0755true);
  456.         return $home;
  457.     }
  458.     
  459.     public function getInstallmentLogsDir()
  460.     {
  461.         $home $this->getCustomLogsDir()."installment_logs/";
  462.         if (!is_dir($home))    $do mkdir($home0755true);
  463.         return $home;
  464.     }
  465.     
  466.     public function getInvoiceLogsDir()
  467.     {
  468.         $home $this->getCustomLogsDir()."invoice_logs/";
  469.         if (!is_dir($home))    $do mkdir($home0755true);
  470.         return $home;
  471.     }
  472.     
  473.     // custom_logs/invoice_logs/missing_devis_product/
  474.     public function getInvoiceMissingDevisProductsLogsDir()
  475.     {
  476.         $home $this->getInvoiceLogsDir();
  477.         $home .= "missing_devis_product/";
  478.         if (!is_dir($home))
  479.         {
  480.             $do mkdir($home0755true);
  481.             if ($do === false)
  482.             {
  483.                 return null;
  484.             }
  485.         }
  486.         return $home;
  487.     }
  488.     
  489.     // custom_logs/cost_logs/export/{society_group_id}/
  490.     public function getCostExportLogsDir($societyGroup)
  491.     {
  492.         $home $this->getCostLogsDir();
  493.         $home .= "export/";
  494.         if (!is_dir($home))
  495.         {
  496.             $do mkdir($home0755true);
  497.             if ($do === false)
  498.             {
  499.                 return null;
  500.             }
  501.         }
  502.         if ($societyGroup !== null)
  503.         {
  504.             $id $societyGroup->getId();
  505.             $home .= $id."/";
  506.             if (!is_dir($home))
  507.             {
  508.                 $do mkdir($home0755true);
  509.                 if ($do === false)
  510.                 {
  511.                     $this->errorLog("Folder $home cannot be created.");
  512.                     return null;
  513.                 }
  514.             }
  515.         }
  516.         return $home;
  517.     }
  518.     
  519.     // custom_logs/installment_logs/export/{society_group_id}/
  520.     public function getInstallmentExportLogsDir($societyGroup)
  521.     {
  522.         $home $this->getInstallmentLogsDir();
  523.         $home .= "export/";
  524.         if (!is_dir($home))
  525.         {
  526.             $do mkdir($home0755true);
  527.             if ($do === false)
  528.             {
  529.                 return null;
  530.             }
  531.         }
  532.         if ($societyGroup !== null)
  533.         {
  534.             $id $societyGroup->getId();
  535.             $home .= $id."/";
  536.             if (!is_dir($home))
  537.             {
  538.                 $do mkdir($home0755true);
  539.                 if ($do === false)
  540.                 {
  541.                     $this->errorLog("Folder $home cannot be created.");
  542.                     return null;
  543.                 }
  544.             }
  545.         }
  546.         return $home;
  547.     }
  548.     
  549.     // custom_logs/invoice_logs/export/{society_group_id}/
  550.     public function getInvoiceExportLogsDir(?SocietyGroup $societyGroup)
  551.     {
  552.         $home $this->getInvoiceLogsDir();
  553.         $home .= "export/";
  554.         if (!is_dir($home))
  555.         {
  556.             $do mkdir($home0755true);
  557.             if ($do === false)
  558.             {
  559.                 return null;
  560.             }
  561.         }
  562.         if ($societyGroup !== null)
  563.         {
  564.             $id $societyGroup->getId();
  565.             $home .= $id."/";
  566.             if (!is_dir($home))
  567.             {
  568.                 $do mkdir($home0755true);
  569.                 if ($do === false)
  570.                 {
  571.                     $this->errorLog("Folder $home cannot be created.");
  572.                     return null;
  573.                 }
  574.             }
  575.         }
  576.         return $home;
  577.     }
  578.     public function getITaskBaseLogsDir()
  579.     {
  580.         $home $this->getCustomLogsDir()."itask_logs/";
  581.         return $home;
  582.     }
  583.     public function getITaskLogsDir($societyGroup null)
  584.     {
  585.         $home $this->getITaskBaseLogsDir() . "auto/";
  586.         if ($societyGroup !== null)
  587.         {
  588.             $id $societyGroup->getId();
  589.             $home .= $id."/";
  590.             if (!is_dir($home))
  591.             {
  592.                 $do mkdir($home0755true);
  593.                 if ($do === false)
  594.                 {
  595.                     $this->errorLog("Folder $home cannot be created.");
  596.                     return null;
  597.                 }
  598.             }
  599.         }
  600.         return $home;
  601.     }
  602.     public function getITaskDebugDir()
  603.     {
  604.         $home $this->getITaskBaseLogsDir()."debug/";
  605.         return $home;
  606.     }
  607.     public function getLoginLogsDir()
  608.     {
  609.         $home $this->getCustomLogsDir()."login_logs/";
  610.         return $home;
  611.     }
  612.     public function getProductLogsDir()
  613.     {
  614.         $home $this->getCustomLogsDir()."product_logs/";
  615.         return $home;
  616.     }
  617.     public function getChargeLogsDir()
  618.     {
  619.         $home $this->getCustomLogsDir()."charge_logs/";
  620.         return $home;
  621.     }
  622.     public function getSmsLogsDir()
  623.     {
  624.         $home $this->getCustomLogsDir()."sms_logs/";
  625.         return $home;
  626.     }
  627.     public function getEmailLogsDir()
  628.     {
  629.         $home $this->getCustomLogsDir()."email_logs/";
  630.         return $home;
  631.     }
  632.     // Plan.io Task #4624
  633.     public function getProjectManagerLogsDir()
  634.     {
  635.         $home $this->getCustomLogsDir()."project_manager/";
  636.         return $home;
  637.     }
  638.     // Plan.io Task #4518
  639.     public function getNotifierLogsDir()
  640.     {
  641.         $home $this->getCustomLogsDir()."notifier/";
  642.         return $home;
  643.     }
  644.     public function getTemplateLogsDir()
  645.     {
  646.         $home $this->getCustomLogsDir()."template/";
  647.         return $home;
  648.     }
  649.     //--------------------------------------------------------------------------
  650.     // Plan.io Task #3922 
  651.     // DMS Global, Email and Object Logs
  652.     //--------------------------------------------------------------------------
  653.     // Old path : ROOT/custom_logs/global_logs
  654.     // New path : ROOT/DMS/Logs/Global/
  655.     public function getDMS_GlobalLogsDir()
  656.     {
  657.         $home dirname(__FILE__3);
  658.         $home .= "/DMS/Logs/Global/";
  659.         return $home;
  660.     }
  661.     
  662.     // DMS/Logs/Email/
  663.     public function getDMS_EmailLogsDir()
  664.     {        
  665.         $home dirname(__FILE__3);
  666.         $home .= "/DMS/Logs/Email/";
  667.         return $home;
  668.     }
  669.     // DMS/Logs/Mission/
  670.     public function getDMS_MissionLogsDir()
  671.     {        
  672.         $home dirname(__FILE__3);
  673.         $home .= "/DMS/Logs/Mission/";
  674.         return $home;
  675.     }
  676.     // DMS/Logs/Client/
  677.     public function getDMS_ClientLogsDir()
  678.     {        
  679.         $home dirname(__FILE__3);
  680.         $home .= "/DMS/Logs/Client/";
  681.         return $home;
  682.     }
  683.     // DMS/Logs/HumanResource/
  684.     public function getDMS_HumanResourceLogsDir()
  685.     {        
  686.         $home dirname(__FILE__3);
  687.         $home .= "/DMS/Logs/HumanResource/";
  688.         return $home;
  689.     }
  690.     // DMS/Logs/ProjectManager/Notebook/
  691.     public function getDMS_ProjectManagerNotebookLogsDir()
  692.     {        
  693.         $home dirname(__FILE__3);
  694.         $home .= "/DMS/Logs/ProjectManager/Notebook/";
  695.         return $home;
  696.     }
  697.     //--------------------------------------------------------------------------
  698.     public function getSecurityLogsDir()
  699.     {
  700.         $home $this->getCustomLogsDir()."security/";
  701.         return $home;
  702.     }
  703.     // Plan.io Task #4410
  704.     public function getWebappLogsDir()
  705.     {
  706.         $home $this->getCustomLogsDir()."webapp/";
  707.         return $home;
  708.     }
  709.     // Plan.io Task #4518
  710.     // Used in 
  711.     //    src/Services/Platform/NoteTools.php
  712.     //    src/Services/Communication/Email/NotifierEmails.php
  713.     //    src/Services/Ding/Notifier.php
  714.     public function notifier_debug($msg$active true)
  715.     {
  716.         if ($active)
  717.         {
  718.             $home $this->getNotifierLogsDir();
  719.             if (!is_dir($home))    $do mkdir($home0755true);
  720.     
  721.             $today = new \DateTime();
  722.             $log "notifier_debug_".$today->format("Y").$today->format("m").".log";
  723.             $logFile $home $log;
  724.     
  725.             $timestamp $today->format("[Y-m-d H:i:s] ");
  726.     
  727.             error_log("\n".$timestamp.$msg."\n",3,$logFile);
  728.             
  729.             error_log("\n".$timestamp.$msg."\n",3,"ploop.log");
  730.         }               
  731.     }
  732.     // Plan.io Task #4624
  733.     public function project_manager_debug($msg$active true)
  734.     {
  735.         if ($active)
  736.         {
  737.             $home $this->getProjectManagerLogsDir();
  738.             if (!is_dir($home))    $do mkdir($home0755true);
  739.             $today = new \DateTime();
  740.             $log "project_manager_debug_".$today->format("Y").$today->format("m").".log";
  741.             $logFile $home $log;
  742.             $timestamp $today->format("[Y-m-d H:i:s] ");
  743.             error_log("\n".$timestamp.$msg."\n",3,$logFile);            
  744.             
  745.             // error_log("\n".$timestamp.$msg."\n",3,"ploop.log");            
  746.         }        
  747.     }
  748.     // Plan.io Task #4483
  749.     public function analyzePlanning($isActive$msg)
  750.     {
  751.         if (!$isActive) return true;
  752.         $home $this->getPlanningLogsDir();
  753.         if (!is_dir($home))    $do mkdir($home0755true);
  754.         $today = new \DateTime();
  755.         $log "global_planning_execution_time_".$today->format("Y").$today->format("m").".log";
  756.         $logFile $home $log;
  757.         $timestamp $today->format("[Y-m-d H:i:s] ");
  758.         error_log("\n".$timestamp.$msg."\n",3,$logFile);
  759.     }
  760.     // Plan.io Task #4410
  761.     public function webapp_debug($msg)
  762.     {
  763.         $home $this->getWebappLogsDir();
  764.         if (!is_dir($home))    $do mkdir($home0755true);
  765.         $today = new \DateTime();
  766.         $log "webapp_debug_".$today->format("Y").$today->format("m").".log";
  767.         $logFile $home $log;
  768.         $timestamp $today->format("[Y-m-d H:i:s] ");
  769.         error_log("\n".$timestamp.$msg."\n",3,$logFile);
  770.     }
  771.     // Plan.io Task #4410
  772.     public function webapp_image_debug($msg)
  773.     {
  774.         $home $this->getWebappLogsDir();
  775.         if (!is_dir($home))    $do mkdir($home0755true);
  776.         $today = new \DateTime();
  777.         $log "webapp_image_debug_".$today->format("Y").$today->format("m").".log";
  778.         $logFile $home $log;
  779.         $timestamp $today->format("[Y-m-d H:i:s]");
  780.         error_log("\n".$timestamp.$msg."\n",3,$logFile);
  781.     }
  782.     public function getTakeOutExportDir()
  783.     {
  784.         $home $this->getCustomDataDir()."take_out_export/";
  785.         return $home;
  786.     }
  787.     public function getCssDir()
  788.     {
  789.         $home $this->getRootDir()."/public/theme/css/";
  790.         return $home;
  791.     }
  792.     public function getJohnDoeDir()
  793.     {
  794.         $home $this->getRootDir()."/public/assets/john_doe/";
  795.         return $home;
  796.     }
  797.     // custom_logs/cost/auto_accounting/
  798.     public function getCostAutoAccountingDebugDir()
  799.     {
  800.         $home $this->getCustomLogsDir()."cost/";
  801.         if (!is_dir($home))
  802.         {
  803.             $do mkdir($home0755true);
  804.             if ($do === false)
  805.             {
  806.                 $this->errorLog("Folder $home cannot be created.");
  807.                 return null;
  808.             }
  809.         }
  810.         $home .= 'auto_accounting/';
  811.         if (!is_dir($home))
  812.         {
  813.             $do mkdir($home0755true);
  814.             if ($do === false)
  815.             {
  816.                 $this->errorLog("Folder $home cannot be created.");
  817.                 return null;
  818.             }
  819.         }
  820.         return $home;
  821.     }
  822.     // custom_logs/invoice/auto_accounting/
  823.     public function getInvoiceAutoAccountingDebugDir()
  824.     {
  825.         $home $this->getCustomLogsDir()."invoice/";
  826.         if (!is_dir($home))
  827.         {
  828.             $do mkdir($home0755true);
  829.             if ($do === false)
  830.             {
  831.                 $this->errorLog("Folder $home cannot be created.");
  832.                 return null;
  833.             }
  834.         }
  835.         $home .= 'auto_accounting/';
  836.         if (!is_dir($home))
  837.         {
  838.             $do mkdir($home0755true);
  839.             if ($do === false)
  840.             {
  841.                 $this->errorLog("Folder $home cannot be created.");
  842.                 return null;
  843.             }
  844.         }
  845.         return $home;
  846.     }
  847.     // custom_logs/zone/{society_group_id}
  848.     public function getZoneDir($societyGroup null)
  849.     {
  850.         $home $this->getCustomLogsDir()."zone/";
  851.         if ($societyGroup !== null)
  852.         {
  853.             $id $societyGroup->getId();
  854.             $home .= $id."/";
  855.             if (!is_dir($home))
  856.             {
  857.                 $do mkdir($home0755true);
  858.                 if ($do === false)
  859.                 {
  860.                     $this->errorLog("Folder $home cannot be created.");
  861.                     return null;
  862.                 }
  863.             }
  864.         }
  865.         return $home;
  866.     }
  867.     // custom_logs/zone/debug/
  868.     public function getZoneDebugDir($societyGroup null)
  869.     {
  870.         $home $this->getZoneDir()."debug/";
  871.         return $home;
  872.     }
  873.     // custom_logs/availability/
  874.     public function getAvailablityDir()
  875.     {
  876.         $home $this->getCustomLogsDir()."availability/";
  877.         return $home;
  878.     }
  879.     // custom_logs/financial/
  880.     // Plan.io Task #4386
  881.     public function getFinancialDir()
  882.     {
  883.         $home $this->getCustomLogsDir()."financial/";
  884.         return $home;
  885.     }
  886.     // custom_logs/financial/scan_financial/
  887.     // Plan.io Task #4386
  888.     public function getScanFinancialDir()
  889.     {
  890.         $home $this->getFinancialDir()."scan_financial/";
  891.         return $home;
  892.     }
  893.     // custom_logs/financial/duplicate_code/
  894.     // Plan.io Task #4386
  895.     public function getDuplicateCodeFinancialDir()
  896.     {
  897.         $home $this->getFinancialDir()."duplicate_code/";
  898.         return $home;
  899.     }
  900.     // Plan.Io Task #3747
  901.     // custom_logs/individual/
  902.     public function getIndividualDir()
  903.     {
  904.         $home $this->getCustomLogsDir()."individual/";
  905.         if (!is_dir($home))
  906.         {
  907.             $do mkdir($home0755true);
  908.             if ($do === false)
  909.             {
  910.                 $this->errorLog("Folder $home cannot be created.");
  911.                 return null;
  912.             }
  913.         }
  914.         return $home;
  915.     }
  916.     // Plan.Io Task #3747
  917.     // custom_logs/individual/dump/{society_group_id}
  918.     public function getIndividualDumpDir($societyGroup null)
  919.     {
  920.         $home $this->getIndividualDir() . "dump/";
  921.         if ($societyGroup !== null)
  922.         {
  923.             $id $societyGroup->getId();
  924.             $home .= $id."/";
  925.             if (!is_dir($home))
  926.             {
  927.                 $do mkdir($home0755true);
  928.                 if ($do === false)
  929.                 {
  930.                     $this->errorLog("Folder $home cannot be created.");
  931.                     return null;
  932.                 }
  933.             }
  934.         }
  935.         return $home;
  936.     }
  937.     // Plan.io Task #3747
  938.     // custom_logs/individual/merge/{society_group_id}
  939.     public function getIndividualMergeDir($societyGroup null)
  940.     {
  941.         $home $this->getIndividualDir() . "merge/";
  942.         if ($societyGroup !== null)
  943.         {
  944.             $id $societyGroup->getId();
  945.             $home .= $id."/";
  946.             if (!is_dir($home))
  947.             {
  948.                 $do mkdir($home0755true);
  949.                 if ($do === false)
  950.                 {
  951.                     $this->errorLog("Folder $home cannot be created.");
  952.                     return null;
  953.                 }
  954.             }
  955.         }
  956.         return $home;
  957.     }
  958.     // Plan.io Task #4247
  959.     // custom_logs/mission/
  960.     public function getMissionDir()
  961.     {
  962.         $home $this->getCustomLogsDir()."mission/";
  963.         if (!is_dir($home))
  964.         {
  965.             $do mkdir($home0755true);
  966.             if ($do === false)
  967.             {
  968.                 $this->errorLog("Folder $home cannot be created.");
  969.                 return null;
  970.             }
  971.         }
  972.         return $home;
  973.     }
  974.     // Plan.io Task #4247
  975.     // custom_logs/mission/dump/{society_group_id}
  976.     public function getMissionDumpDir($societyGroup null)
  977.     {
  978.         $home $this->getMissionDir() . "dump/";
  979.         if ($societyGroup !== null)
  980.         {
  981.             $id $societyGroup->getId();
  982.             $home .= $id."/";
  983.             if (!is_dir($home))
  984.             {
  985.                 $do mkdir($home0755true);
  986.                 if ($do === false)
  987.                 {
  988.                     $this->errorLog("Folder $home cannot be created.");
  989.                     return null;
  990.                 }
  991.             }
  992.         }
  993.         return $home;
  994.     }
  995.     // Plan.io Task #4247
  996.     // custom_logs/mission/merge/{society_group_id}
  997.     public function getMissionMergeDir($societyGroup null)
  998.     {
  999.         $home $this->getMissionDir() . "merge/";
  1000.         if ($societyGroup !== null)
  1001.         {
  1002.             $id $societyGroup->getId();
  1003.             $home .= $id."/";
  1004.             if (!is_dir($home))
  1005.             {
  1006.                 $do mkdir($home0755true);
  1007.                 if ($do === false)
  1008.                 {
  1009.                     $this->errorLog("Folder $home cannot be created.");
  1010.                     return null;
  1011.                 }
  1012.             }
  1013.         }
  1014.         return $home;
  1015.     }
  1016.     // Plan.io Task #4331
  1017.     // custom_logs/payment_logs/YYYY/MM/
  1018.     public function getPaymentLogDir()
  1019.     {
  1020.         $home $this->getCustomLogsDir()."payment_logs/";
  1021.         return $home;
  1022.     }
  1023.     public function getPaymentErrorLogDir()
  1024.     {
  1025.         $home $this->getPaymentLogDir() . "error/";
  1026.         $now = new \DateTime();
  1027.         $home .= $now->format('Y') . "/" $now->format('m') . "/";
  1028.         return $home;
  1029.     }
  1030.     // Plan.io Task #4383
  1031.     public function getBookingLogDir()
  1032.     {
  1033.         $home $this->getCustomLogsDir()."booking/";
  1034.         return $home;
  1035.     }
  1036.     // Plan.io Task #4383
  1037.     public function getBookingSavSlotLogDir()
  1038.     {
  1039.         $home $this->getBookingLogDir()."booking_sav_slot/";
  1040.         return $home;
  1041.     }
  1042.     // Plan.io Task #4383
  1043.     public function getCleanBookingSavSlotLogDir()
  1044.     {
  1045.         $home $this->getBookingLogDir()."clean_booking_sav_slot/";
  1046.         return $home;
  1047.     }
  1048.     public function getGuardLogPath(SocietyGroup $societyGroup)
  1049.     {
  1050.         // Get the path to the log folder
  1051.         // If the folder does not exits, this creates it
  1052.         $logPath $this->getGuardLogsDir();
  1053.         // Try to get the name of the log file from the entity
  1054.         $logName $societyGroup->getGuardLog();
  1055.         // If empty, craft it
  1056.         if (empty($logName))
  1057.         {
  1058.             $today = new \DateTime();
  1059.             $logName "guard_log_".$today->format('Y_m_d')."_SG_".$societyGroup->getInternalRef().".log";
  1060.             // Update the object
  1061.             $societyGroup->setGuardLog($logName);
  1062.         }
  1063.         // Craft the path to the file
  1064.         $logFile $logPath $logName;
  1065.         // If the file does not exist, create it
  1066.         if (!file_exists($logFile))
  1067.         {
  1068.             error_log("",3,$logFile);
  1069.         }
  1070.         return $logFile;
  1071.     }
  1072.     public function addTimestampToGuardLog($logFile)
  1073.     {
  1074.         $today = new \DateTime();
  1075.         error_log("\n",3,$logFile);
  1076.         error_log("\n",3,$logFile);
  1077.         error_log($today->format("Y/m/d H:i:s"),3,$logFile);
  1078.         error_log("\n",3,$logFile);
  1079.     }
  1080.     public function mobileAppLogCall($message null)
  1081.     {
  1082.         // Global switch to enable/disable mobile app logging
  1083.         $mobileAppLoggingActive true;
  1084.         if(!$mobileAppLoggingActive)
  1085.             return false;
  1086.         // Returns User object or null if not authenticated
  1087.         $currentUser $this->getCurrentUser();
  1088.         if($currentUser === null// Do not permit log if user is not authenticated, should not happen
  1089.             return false;
  1090.         // Check that current user is not an AccessClient
  1091.         if(    ($currentUser instanceof AccessClient) )
  1092.             return false;
  1093.         $logPath $this->getApiIcodLogsDir();
  1094.         $now = new DateTime();
  1095.         $logPath $logPath 'api_rest_log/mobileAppCall_' $now->format('Y_m') . '.log';
  1096.         $today = new \DateTime();
  1097.         $currentDate $today->format('Y-m-d H:i:s.v');
  1098.         $trace debug_backtrace();
  1099.         $func $trace[1]["function"];
  1100.         $line "" $currentDate "," $currentUser->display() . "," $currentUser->getId() . "," $currentUser->getSocietyGroup()?->getRef() . "," $currentUser->getSocietyGroup()?->getId() . "," $func;
  1101.         if($message !== null)
  1102.             $line .= "," $message;
  1103.         $line .= "\n";
  1104.         error_log($line3$logPath);
  1105.     }
  1106.     public function getApiRestRateLimitLogPath(): string
  1107.     {
  1108.         $baseDir $this->getApiIcodLogsDir() . "api_rest_log/";
  1109.         if (!is_dir($baseDir))
  1110.         {
  1111.             if (!mkdir($baseDir0755true))
  1112.             {
  1113.                 $now = new \DateTime();
  1114.                 return $baseDir "rate_limit_" $now->format("Y") . "_" $now->format("m") . ".csv";
  1115.             }
  1116.         }
  1117.         $now = new \DateTime();
  1118.         $fileName "rate_limit_" $now->format("Y") . "_" $now->format("m") . ".csv";
  1119.         return $baseDir $fileName;
  1120.     }
  1121.     public function apiRestRateLimitLog(array $args): void
  1122.     {
  1123.         $scope array_key_exists('scope'$args) ? (string) $args['scope'] : '';
  1124.         $ip array_key_exists('ip'$args) ? (string) $args['ip'] : '';
  1125.         $accessApi array_key_exists('accessApi'$args) && $args['accessApi'] instanceof AccessAPI $args['accessApi'] : null;
  1126.         $route array_key_exists('route'$args) ? (string) $args['route'] : '';
  1127.         $maxRequests array_key_exists('max_requests'$args) ? $args['max_requests'] : '';
  1128.         $accessApiId '';
  1129.         $accessApiUsername '';
  1130.         $societyGroupId '';
  1131.         $societyGroupRef '';
  1132.         if ($accessApi instanceof AccessAPI)
  1133.         {
  1134.             $accessApiId = (string) $accessApi->getId();
  1135.             $accessApiUsername = (string) $accessApi->getUsername();
  1136.             $societyGroup $accessApi->getSocietyGroup();
  1137.             if ($societyGroup instanceof SocietyGroup)
  1138.             {
  1139.                 $societyGroupId = (string) $societyGroup->getId();
  1140.                 $societyGroupRef = (string) $societyGroup->getRef();
  1141.             }
  1142.         }
  1143.         $path $this->getApiRestRateLimitLogPath();
  1144.         $fileExists file_exists($path);
  1145.         $stream fopen($path$fileExists "a" "w");
  1146.         if ($stream === false)
  1147.         {
  1148.             return;
  1149.         }
  1150.         if (!$fileExists)
  1151.         {
  1152.             $header = [
  1153.                 "timestamp",
  1154.                 "scope",
  1155.                 "ip",
  1156.                 "access_api.id",
  1157.                 "access_api.username",
  1158.                 "society_group.id",
  1159.                 "society_group.ref",
  1160.                 "route",
  1161.                 "max_requests",
  1162.             ];
  1163.             fputcsv($stream$header';');
  1164.         }
  1165.         $now = new \DateTime();
  1166.         $timestamp $now->format("Y-m-d H:i:s");
  1167.         $line = [
  1168.             $timestamp,
  1169.             $scope,
  1170.             $ip,
  1171.             $accessApiId,
  1172.             $accessApiUsername,
  1173.             $societyGroupId,
  1174.             $societyGroupRef,
  1175.             $route,
  1176.             $maxRequests,
  1177.         ];
  1178.         fputcsv($stream$line';');
  1179.         fclose($stream);
  1180.     }
  1181.     public function mobileAppLog(array $args)
  1182.     {
  1183.         if (empty($args['info_error'])) return false;
  1184.         // Get data
  1185.         $errorCode $args['info_error']['errorCode'];
  1186.         $errorMessage $args['info_error']['errorMessage'];
  1187.         $message array_key_exists('message'$args) ? $args['message'] : null;
  1188.         // Optional
  1189.         $author array_key_exists('author'$args) ? $args['author'] : null;
  1190.         if ($author === null)
  1191.         {
  1192.             if ($this->security->getUser() instanceof Access)
  1193.             {
  1194.                 $author $this->security->getUser();
  1195.             }
  1196.             else
  1197.             {
  1198.                 $author $this->em
  1199.                     ->getRepository(Access::class)
  1200.                     ->findOneByUsername(Access::API_ACCESS);
  1201.             }
  1202.         }
  1203.         $authorId '';
  1204.         $authorUsername '';
  1205.         $authorSocietyGroup '';
  1206.         $authorSocietyGroupId '';
  1207.         if ($author !== null)
  1208.         {
  1209.             $authorId $author->getId();
  1210.             $authorUsername $author->getUsername();
  1211.             $authorSocietyGroup $author->getSocietyGroup();
  1212.             if ($authorSocietyGroup !== null) {
  1213.                 $authorSocietyGroupId $authorSocietyGroup->getId();
  1214.             }
  1215.         }
  1216.         // Handle OriginalUser
  1217.         $originalUser $this->getOriginalUser();
  1218.         $originalUserId '';
  1219.         $originalUserName '';
  1220.         if ($originalUser !== null)
  1221.         {
  1222.             $originalUserId $originalUser->getId();
  1223.             $originalUserName $originalUser->getUserIdentifier();
  1224.         }
  1225.         // Error
  1226.         $error false;
  1227.         if (array_key_exists('error'$args) && ($args['error'] == false || $args['error'] == true))
  1228.         {
  1229.             $error $args['error'];
  1230.         }
  1231.         $status $error 'ERROR' 'DONE';
  1232.         $logPath $this->getApiIcodLogsDir();
  1233.         $now = new DateTime();
  1234.         $logPath $logPath 'api_rest_log/mobile_app_' $now->format('Y_m') . '.log';
  1235.         $path $logPath;
  1236.         // Backtrace
  1237.         $backTrace debug_backtrace()[1];
  1238.         $backTraceMethod $backTrace['function'];
  1239.         $backTraceClass $backTrace['class'];
  1240.         // Date
  1241.         $now = new \DateTime();
  1242.         $nowDate $now->format('Y-m-d H:i:s');
  1243.         $line "[ " $nowDate " ][ " $status " ][ " $errorCode " ][ " $errorMessage " ][ id societyGroup : " $authorSocietyGroupId " ][ id access : " $authorId " ][ " $authorUsername " ][ " $originalUserId " ][ " $originalUserName " ][ " $backTraceClass " ][ " $backTraceMethod " ]\t";
  1244.         if(!empty($message)) $line .= " : " $message;
  1245.         $line .= "\n";
  1246.         error_log($line3$logPath);
  1247.     }
  1248.     /**
  1249.      * @deprecated Use LogMobileAppTools::logInternal() instead.
  1250.      */
  1251.     public function mobileAppLogInternal(array $args): bool
  1252.     {
  1253.         return false;
  1254.     }
  1255.     /**
  1256.      * @deprecated Use LogMobileAppTools::hasLogGuid() instead.
  1257.      */
  1258.     public function hasMobileAppLogGuid($guid): bool
  1259.     {
  1260.         return false;
  1261.     }
  1262.     public function planning_optimisation_error_log($args)
  1263.     {
  1264.         $home $this->getPlanningOptimisationLogsDir();
  1265.         if (!is_dir($home))    $do mkdir($home0755true);
  1266.         $home .= "errors/";
  1267.         if (!is_dir($home))    $do mkdir($home0755true);
  1268.         $today = new \DateTime();
  1269.         $log "planning_errors_".$today->format("Ymd").".log";
  1270.         $logFile $home $log;
  1271.         $jsonString json_encode($argsJSON_PRETTY_PRINT);
  1272.         $fp fopen($logFile'a');
  1273.         // Is it empty ?
  1274.         if (filesize($logFile))
  1275.         {
  1276.             // Not empty
  1277.             // Remove last char "}" of file and add ,
  1278.             $stat fstat($fp);
  1279.             ftruncate($fp$stat['size']-1);
  1280.             fwrite($fp",\n");
  1281.         }
  1282.         else
  1283.         {
  1284.             // Empty
  1285.             // Write begining of json object
  1286.             fwrite($fp"{\n");
  1287.         }
  1288.         fwrite($fp'"'.uniqid().'"' " : ");
  1289.         fwrite($fp$jsonString);
  1290.         fwrite($fp"}");
  1291.         fclose($fp);
  1292.     }
  1293.     public function kitchenPlannerErrorlog($msg)
  1294.     {
  1295.         $home $this->getIkeaKitchenPlannerLogsDir();
  1296.         if (!is_dir($home))    
  1297.             mkdir($home0755true);
  1298.         $home .= "errors/";
  1299.         if (!is_dir($home))
  1300.             mkdir($home0755true);
  1301.         $today = new \DateTime();
  1302.         $log "kitchen_planner_errors_".$today->format("Ymd").".log";
  1303.         $logFile $home $log;
  1304.         $timestamp $today->format("[Y-m-d H:i:s] ");
  1305.         $backTrace debug_backtrace()[1];
  1306.         $method $backTrace['function'];
  1307.         $class $backTrace['class'];
  1308.         $trace $class."::".$method;
  1309.         $trace str_replace("\\""::"$trace);
  1310.         $content "[".$trace "] " $msg;
  1311.         error_log("\n".$timestamp.$content."\n",3,$logFile);
  1312.     }
  1313.     public function planning_optimisation_debug($active$msg)
  1314.     {
  1315.         if (!$active)
  1316.             return false;
  1317.         $home $this->getPlanningOptimisationLogsDir();
  1318.         if (!is_dir($home))    $do mkdir($home0755true);
  1319.         $today = new \DateTime();
  1320.         $ym $today->format('Y_m');
  1321.         $log "planning_optimisation_debug_$ym.log";
  1322.         $logFile $home $log;
  1323.         $timestamp $today->format("[Y-m-d H:i:s] ");
  1324.         $backTrace debug_backtrace()[1];
  1325.         $method $backTrace['function'];
  1326.         $class $backTrace['class'];
  1327.         $trace $class."::".$method;
  1328.         $trace str_replace("\\""::"$trace);
  1329.         $content "[".$trace "] " $msg;
  1330.         error_log("\n".$timestamp.$content."\n",3,$logFile);
  1331.     }
  1332.     // Logs Errors to root/custom_logs/stripe/errors/stripe_errors_yyyymmdd.log
  1333.     // One single log each day
  1334.     public function stripe_error_log($args)
  1335.     {
  1336.         $home $this->getStripeLogsDir();
  1337.         if (!is_dir($home))    $do mkdir($home0755true);
  1338.         $home .= "errors/";
  1339.         if (!is_dir($home))    $do mkdir($home0755true);
  1340.         $today = new \DateTime();
  1341.         $log "stripe_errors_".$today->format("Ymd").".log";
  1342.         $logFile $home $log;
  1343.         $jsonString json_encode($argsJSON_PRETTY_PRINT);
  1344.         $fp fopen($logFile'a');
  1345.         // Is it empty ?
  1346.         if (filesize($logFile))
  1347.         {
  1348.             // Not empty
  1349.             // Remove last char "}" of file and add ,
  1350.             $stat fstat($fp);
  1351.             ftruncate($fp$stat['size']-1);
  1352.             fwrite($fp",\n");
  1353.         }
  1354.         else
  1355.         {
  1356.             // Empty
  1357.             // Write begining of json object
  1358.             fwrite($fp"{\n");
  1359.         }
  1360.         fwrite($fp'"'.uniqid().'"' " : ");
  1361.         fwrite($fp$jsonString);
  1362.         fwrite($fp"}");
  1363.         fclose($fp);
  1364.     }
  1365.     // Logs Errors to root/custom_logs/guest_suite_log/guest_suite_yyyymmdd.log
  1366.     // One single log each day
  1367.     public function guest_suite_log($args)
  1368.     {
  1369.         $home $this->getGuestSuiteLogsDir();
  1370.         if (!is_dir($home))    $do mkdir($home0755true);
  1371.         $today = new \DateTime();
  1372.         $log "guest_suite_".$today->format("Ymd").".log";
  1373.         $logFile $home $log;
  1374.         $jsonString json_encode($argsJSON_PRETTY_PRINT);
  1375.         $fp fopen($logFile'a');
  1376.         // Is it empty ?
  1377.         if (filesize($logFile))
  1378.         {
  1379.             // Not empty
  1380.             // Remove last char "}" of file and add ,
  1381.             $stat fstat($fp);
  1382.             ftruncate($fp$stat['size']-1);
  1383.             fwrite($fp",\n");
  1384.         }
  1385.         else
  1386.         {
  1387.             // Empty
  1388.             // Write begining of json object
  1389.             fwrite($fp"{\n");
  1390.         }
  1391.         fwrite($fp'"'.uniqid().'"' " : ");
  1392.         fwrite($fp$jsonString);
  1393.         fwrite($fp"}");
  1394.         fclose($fp);
  1395.     }
  1396.     // Logs to root/custom_logs/sms_logs/sms_reminder_task_yyyy_mm.log
  1397.     // Used by CRON command reminder
  1398.     public function sms_reminder_debug($active$msg)
  1399.     {
  1400.         if (!$active)
  1401.             return false;
  1402.         $home $this->getSmsLogsDir();
  1403.         if (!is_dir($home))    $do mkdir($home0755true);
  1404.         $today = new \DateTime();
  1405.         $ym $today->format('Y_m');
  1406.         $log "sms_reminder_task_$ym.log";
  1407.         $logFile $home $log;
  1408.         $timestamp $today->format("[Y-m-d H:i:s] ");
  1409.         $content $timestamp $msg;
  1410.         error_log("\n".$content,3,$logFile);
  1411.     }
  1412.     // Logs to root/custom_logs/sms_logs/sms_debug_yyyy_mm.log
  1413.     // Used to debug info SMS
  1414.     // For errors check LogTools::smsLogDebug method/file
  1415.     public function sms_debug($active$msg)
  1416.     {
  1417.         if (!$active)
  1418.             return false;
  1419.         $home $this->getSmsLogsDir();
  1420.         if (!is_dir($home))    $do mkdir($home0755true);
  1421.         $today = new \DateTime();
  1422.         $ym $today->format('Y_m');
  1423.         $log "sms_debug_$ym.log";
  1424.         $logFile $home $log;
  1425.         $timestamp $today->format("[Y-m-d H:i:s] ");
  1426.         $backTrace debug_backtrace()[1];
  1427.         $method $backTrace['function'];
  1428.         $class $backTrace['class'];
  1429.         $trace $class."::".$method;
  1430.         $trace str_replace("\\""::"$trace);
  1431.         $content "[".$trace "] " $msg;
  1432.         error_log("\n".$timestamp.$content."\n",3,$logFile);
  1433.     }
  1434.     // Logs to root/custom_logs/email_logs/email_debug_yyyy_mm.log
  1435.     // Handles Backtrace info
  1436.     public function email_debug($active$msg)
  1437.     {
  1438.         if (!$active)
  1439.             return false;
  1440.         $home $this->getEmailLogsDir();
  1441.         if (!is_dir($home))    $do mkdir($home0755true);
  1442.         $today = new \DateTime();
  1443.         $ym $today->format('Y_m');
  1444.         $log "email_debug_$ym.log";
  1445.         $logFile $home $log;
  1446.         $timestamp $today->format("[Y-m-d H:i:s] ");
  1447.         $backTrace debug_backtrace()[1];
  1448.         $method $backTrace['function'];
  1449.         $class $backTrace['class'];
  1450.         $trace $class."::".$method;
  1451.         $trace str_replace("\\""::"$trace);
  1452.         $content "[".$trace "] " $msg;
  1453.         error_log("\n".$timestamp.$content."\n",3,$logFile);
  1454.     }
  1455.     // Logs to root/custom_logs/stripe/stripe_yyyymmdd.log
  1456.     // One single log each day
  1457.     public function stripe_debug($active$msg)
  1458.     {
  1459.         if (!$active)
  1460.             return false;
  1461.         $home $this->getStripeLogsDir();
  1462.         if (!is_dir($home))    $do mkdir($home0755true);
  1463.         $today = new \DateTime();
  1464.         $log "stripe_".$today->format("Ymd").".log";
  1465.         $logFile $home $log;
  1466.         $timestamp $today->format("[Y-m-d H:i:s] ");
  1467.         error_log("\n".$timestamp.$msg."\n",3,$logFile);
  1468.     }
  1469.     // OLD : Logs to root/custom_logs/ikea_debug/ikea_os_yyyymm.log
  1470.     // NEW : Logs to root/custom_logs/ikea_debug/ikea_os_yyyy_week_{weekNumber}.log
  1471.     // One single log each week - Log file created on Monday
  1472.     public function ikea_debug($active$msg)
  1473.     {
  1474.         if (!$active)
  1475.             return false;
  1476.         $home $this->getCustomLogsDir()."ikea_debug/";
  1477.         if (!is_dir($home))    $do mkdir($home0755true);
  1478.         $today = new \DateTime();
  1479.         
  1480.         // Old format : Each month
  1481.         // $log = "ikea_os_".$today->format("Y").$today->format("m").".log";
  1482.         // New format : Each week on Mondays
  1483.         // ikea_os_2026_week_21.log
  1484.         $log "ikea_os_".$today->format("o")."_week_".$today->format("W").".log";
  1485.         $logFile $home $log;
  1486.         $timestamp $today->format("[Y-m-d H:i:s] ");
  1487.         error_log("\n".$timestamp.$msg."\n",3,$logFile);
  1488.         
  1489.         $this->plooplog("\n".$timestamp.$msg."\n");
  1490.     }
  1491.     // Logs to root/custom_logs/ikea_debug_status/ikea_os_status_yyyymmdd.log
  1492.     // One single log each day
  1493.     public function ikea_debug_status($active$msg)
  1494.     {
  1495.         if (!$active)
  1496.             return false;
  1497.         $home $this->getCustomLogsDir()."ikea_debug_status/";
  1498.         if (!is_dir($home))    $do mkdir($home0755true);
  1499.         $today = new \DateTime();
  1500.         $log "ikea_os_status_".$today->format("Ymd").".log";
  1501.         $logFile $home $log;
  1502.         $timestamp $today->format("[Y-m-d H:i:s] ");
  1503.         error_log("\n".$timestamp.$msg."\n",3,$logFile);
  1504.     }
  1505.     // Logs to root/custom_logs/ikea_debug_status/ikea_os_cancelled_yyyymm.log
  1506.     // One single log each month
  1507.     public function ikea_debug_cancelled($active$msg)
  1508.     {
  1509.         if (!$active)
  1510.             return false;
  1511.         $home $this->getCustomLogsDir()."ikea_debug_status/";
  1512.         if (!is_dir($home))    $do mkdir($home0755true);
  1513.         $today = new \DateTime();
  1514.         $log "ikea_os_cancelled_".$today->format("Ym").".log";
  1515.         $logFile $home $log;
  1516.         $timestamp $today->format("[Y-m-d H:i:s] ");
  1517.         error_log("\n".$timestamp.$msg."\n",3,$logFile);
  1518.     }
  1519.     public function ikea_send($args)
  1520.     {
  1521.         //Returns User object or null if not authenticated
  1522.         $currentUser $this->getCurrentUser();
  1523.         if ($currentUser === null)
  1524.         {
  1525.             return false;
  1526.         }
  1527.         $home $this->getIkeaSendLogsDir($currentUser->getSocietyGroup());
  1528.         if ($home === null)
  1529.         {
  1530.             return false;
  1531.         }
  1532.         $now = new \DateTime();
  1533.         $log "ikea_object_send_".$now->format("Ym").".csv";
  1534.         $logFile $home $log;
  1535.         // $user = array_key_exists('user', $args) ? (string) $args['user'] : '';
  1536.         $filename array_key_exists('filename'$args) ? (string) $args['filename'] : '';
  1537.         $targetPath array_key_exists('targetPath'$args) ? (string) $args['targetPath'] : '';
  1538.         $ikeaOrderNumber array_key_exists('ikeaOrderNumber'$args) ? (string) $args['ikeaOrderNumber'] : '';
  1539.         $action array_key_exists('action'$args) ? (string) $args['action'] : '';
  1540.         $result array_key_exists('result'$args) ? (string) $args['result'] : '';
  1541.         $fileExists file_exists($logFile);
  1542.         $stream fopen($logFile$fileExists "a" "w");
  1543.         if ($stream === false)
  1544.         {
  1545.             return false;
  1546.         }
  1547.         if (!$fileExists)
  1548.         {
  1549.             $header = [
  1550.                 "timestamp",
  1551.                 "user",
  1552.                 "filename",
  1553.                 "targetPath",
  1554.                 "ikeaOrderNumber",
  1555.                 "action",
  1556.                 "result",
  1557.             ];
  1558.             fputcsv($stream$header';');
  1559.         }
  1560.         $timestamp $now->format("Y-m-d H:i:s");
  1561.         $line = [
  1562.             $timestamp,
  1563.             $currentUser->getUsername(),
  1564.             $filename,
  1565.             $targetPath,
  1566.             $ikeaOrderNumber,
  1567.             $action,
  1568.             $result,
  1569.         ];
  1570.         fputcsv($stream$line';');
  1571.         fclose($stream);
  1572.         return true;
  1573.     }
  1574.     
  1575.     // Logs to root/custom_logs/cost/auto_accounting/cost_auto_accounting_debug_yyyy_mm_dd.log
  1576.     // Handles Backtrace info
  1577.     public function cost_auto_accounting_debug($active$msg)
  1578.     {
  1579.         if (!$active)
  1580.             return false;
  1581.         $home $this->getCostAutoAccountingDebugDir();
  1582.         if ($home === null) return false;
  1583.         $today = new \DateTime();
  1584.         $ym $today->format('Y_m_d');
  1585.         $log "cost_auto_accounting_debug_$ym.log";
  1586.         $logFile $home $log;
  1587.         $timestamp $today->format("[Y-m-d H:i:s] ");
  1588.         $backTrace debug_backtrace()[1];
  1589.         $method $backTrace['function'];
  1590.         $class $backTrace['class'];
  1591.         $trace $class."::".$method;
  1592.         $trace str_replace("\\""::"$trace);
  1593.         $content "[".$trace "] " $msg;
  1594.         error_log("\n".$timestamp.$content."\n",3,$logFile);
  1595.         return true;
  1596.     }
  1597.     
  1598.     // Logs to root/custom_logs/invoice/auto_accounting/invoice_auto_accounting_debug_yyyy_mm_dd.log
  1599.     // Handles Backtrace info
  1600.     public function invoice_auto_accounting_debug($active$msg)
  1601.     {
  1602.         if (!$active)
  1603.             return false;
  1604.         $home $this->getInvoiceAutoAccountingDebugDir();
  1605.         if ($home === null) return false;
  1606.         $today = new \DateTime();
  1607.         $ym $today->format('Y_m_d');
  1608.         $log "invoice_auto_accounting_debug_$ym.log";
  1609.         $logFile $home $log;
  1610.         $timestamp $today->format("[Y-m-d H:i:s] ");
  1611.         $backTrace debug_backtrace()[1];
  1612.         $method $backTrace['function'];
  1613.         $class $backTrace['class'];
  1614.         $trace $class."::".$method;
  1615.         $trace str_replace("\\""::"$trace);
  1616.         $content "[".$trace "] " $msg;
  1617.         error_log("\n".$timestamp.$content."\n",3,$logFile);
  1618.         return true;
  1619.     }
  1620.     // Logs to root/custom_logs/zone/debug/zone_debug_yyyy_mm_dd.log
  1621.     // Handles Backtrace info
  1622.     public function zone_debug($active$msg)
  1623.     {
  1624.         if (!$active)
  1625.             return false;
  1626.         $home $this->getZoneDebugDir();
  1627.         if (!is_dir($home))    $do mkdir($home0755true);
  1628.         $today = new \DateTime();
  1629.         $ym $today->format('Y_m_d');
  1630.         $log "zone_debug_$ym.log";
  1631.         $logFile $home $log;
  1632.         $timestamp $today->format("[Y-m-d H:i:s] ");
  1633.         $backTrace debug_backtrace()[1];
  1634.         $method $backTrace['function'];
  1635.         $class $backTrace['class'];
  1636.         $trace $class."::".$method;
  1637.         $trace str_replace("\\""::"$trace);
  1638.         $content "[".$trace "] " $msg;
  1639.         error_log("\n".$timestamp.$content."\n",3,$logFile);
  1640.     }
  1641.     // Logs to root/custom_logs/itask_logs/debug/debug_yyyy_mm_dd.log
  1642.     public function itask_debug($active$msg)
  1643.     {
  1644.         if (!$active)
  1645.             return false;
  1646.         $home $this->getITaskDebugDir();
  1647.         if (!is_dir($home))    $do mkdir($home0755true);
  1648.         $today = new \DateTime();
  1649.         $ym $today->format('Y_m_d');
  1650.         $log "debug_$ym.log";
  1651.         $logFile $home $log;
  1652.         $timestamp $today->format("[Y-m-d H:i:s] ");
  1653.         $content $msg;
  1654.         // $backTrace = debug_backtrace()[1];
  1655.         // $method = $backTrace['function'];
  1656.         // $class = $backTrace['class'];
  1657.         // $trace = $class."::".$method;
  1658.         // $trace = str_replace("\\", "::", $trace);
  1659.         //
  1660.         // $content = "[".$trace . "] " . $msg;
  1661.         error_log("\n".$timestamp.$content."\n",3,$logFile);
  1662.     }
  1663.     // Logs to root/custom_logs/pra/pra_os_yyyymm.log
  1664.     // One single log each month
  1665.     public function pra_debug($active$msg)
  1666.     {
  1667.         if (!$active)
  1668.             return false;
  1669.         $home $this->getCustomLogsDir()."pra/";
  1670.         if (!is_dir($home))    $do mkdir($home0755true);
  1671.         $today = new \DateTime();
  1672.         $log "pra_".$today->format("Y").$today->format("m").".log";
  1673.         $logFile $home $log;
  1674.         $timestamp $today->format("[Y-m-d H:i:s] ");
  1675.         error_log("\n".$timestamp.$msg."\n",3,$logFile);
  1676.     }
  1677.     // Used to trace SMS related errors
  1678.     // For happy path check LogTools::sms_debug method/file
  1679.     public function smsLogDebug($msg$activeProvider null)
  1680.     {
  1681.         $today = new \DateTime();
  1682.         $ym $today->format('Y_m');
  1683.         $backTrace debug_backtrace()[1];
  1684.         $method $backTrace['function'];
  1685.         $class $backTrace['class'];
  1686.         $trace $class."::".$method;
  1687.         $traceLevelOne str_replace("\\""::"$trace);
  1688.         $backTrace debug_backtrace()[2];
  1689.         $method $backTrace['function'];
  1690.         $class $backTrace['class'];
  1691.         $trace $class."::".$method;
  1692.         $traceLevelTwo str_replace("\\""::"$trace);
  1693.         // $backTrace = debug_backtrace()[3];
  1694.         // $method = $backTrace['function'];
  1695.         // $class = $backTrace['class'];
  1696.         // $trace = $class."::".$method;
  1697.         // $traceLevelThree = str_replace("\\", "::", $trace);
  1698.         // $content = $traceLevelOne . " : " . $traceLevelTwo . " : " . $traceLevelThree . " : " . $msg;
  1699.         $content $traceLevelOne " : " $traceLevelTwo " : " $msg;
  1700.         if ($activeProvider !== null)
  1701.         {
  1702.             if ($activeProvider->isSmsMode())
  1703.             {
  1704.                 $file $this->getSmsLogsDir() . "sms_mode_$ym.log";
  1705.             }
  1706.             else
  1707.             {
  1708.                 if ($activeProvider->isSmsTwilio())
  1709.                 {
  1710.                     $file $this->getSmsLogsDir() . "sms_twilio_$ym.log";
  1711.                 }
  1712.             }
  1713.         }
  1714.         else
  1715.         {
  1716.             $file $this->getSmsLogsDir() . "sms_unknown_provider_$ym.log";
  1717.         }
  1718.         $timestamp $today->format("[Y-m-d H:i:s] ");
  1719.         error_log("\n".$timestamp.$content."\n",3,$file);
  1720.     }
  1721.     // Task plan.io #4024
  1722.     // Logs to root/custom_logs/availability/availability_clean_{Y_m_d}.log
  1723.     public function availabilityClean_log($msg)
  1724.     {
  1725.         $home $this->getAvailablityDir();
  1726.         if (!is_dir($home))    $do mkdir($home0755true);
  1727.         $today = new \DateTime();
  1728.         $log "availability_clean_" $today->format("Y") . "_" $today->format("m") . "_" $today->format("d") . ".log";
  1729.         $logFile $home $log;
  1730.         $timestamp $today->format("[Y-m-d H:i:s] ");
  1731.         error_log("\n".$timestamp.$msg."\n",3,$logFile);
  1732.     }
  1733.     // Task plan.io #4386
  1734.     // Logs to root/custom_logs/financial/scan_financial/scan_financial_{Y_m_d}.log
  1735.     public function scanFinancialCommand_log($msg)
  1736.     {
  1737.         $home $this->getScanFinancialDir();
  1738.         if (!is_dir($home))    $do mkdir($home0755true);
  1739.         $today = new \DateTime();
  1740.         $log "scan_financial_" $today->format("Y_m_d") . ".log";
  1741.         $logFile $home $log;
  1742.         $timestamp $today->format("[Y-m-d H:i:s] ");
  1743.         error_log($timestamp.$msg."\n",3,$logFile);
  1744.     }
  1745.     // Task plan.io #4386
  1746.     // Logs to root/custom_logs/financial/duplicate_code/duplicate_code_{Y_m_d}.log
  1747.     public function logDuplicateFinancialCode(Client $client, array $clientsSameAccount)
  1748.     {
  1749.         $home $this->getDuplicateCodeFinancialDir();
  1750.         if (!is_dir($home))    $do mkdir($home0755true);
  1751.         $today = new \DateTime();
  1752.         $log "duplicate_code_" $today->format("Y_m_d") . ".log";
  1753.         $logFile $home $log;
  1754.         // Build log message
  1755.         $societyGroup $client->getSocietyGroup();
  1756.         $financial $client->getFinancial();
  1757.         $societyGroupDisplay $societyGroup !== null $societyGroup->displayForLog() : '';
  1758.         $financialDisplay $financial !== null $financial->displayForLog() : '';
  1759.         $timestamp $today->format("[Y-m-d H:i:s] ");
  1760.         $msg "-------------------";
  1761.         $msg .= "\n" $timestamp;
  1762.         $msg .= "\nSocietyGroup : " $societyGroupDisplay;
  1763.         $msg .= "\nClient : " $client->displayForLog();
  1764.         $msg .= "\nIndividual : " $client->displayChildrenForLog();
  1765.         $msg .= "\nFinancial : " $financialDisplay;
  1766.         $msg .= "\n";
  1767.         $msg .= "\nOther client/financial concerned\n";
  1768.         foreach($clientsSameAccount as $clientSameAccount)
  1769.         {
  1770.             $financialSameAccount $clientSameAccount->getFinancial();
  1771.             if ($financialSameAccount === null) continue;
  1772.             $msg .= "\nClient : " $clientSameAccount->displayForLog();
  1773.             $msg .= ", Individual : " $clientSameAccount->displayChildrenForLog();
  1774.             $msg .= ", Financial : " $financialSameAccount->displayForLog();
  1775.         }
  1776.         error_log("\n" $msg,3,$logFile);
  1777.     }
  1778.     // Task plan.io #4331
  1779.     // Logs to root/custom_logs/payment_logs/error
  1780.     public function payment_log_error($msg$args)
  1781.     {
  1782.         // Setup
  1783.         $today = new \DateTime();
  1784.         $backTrace debug_backtrace()[1];
  1785.         $method $backTrace['function'];
  1786.         $class $backTrace['class'];
  1787.         $trace $class."::".$method;
  1788.         // Build logFile
  1789.         $home $this->getPaymentErrorLogDir();
  1790.         if (!is_dir($home))    mkdir($home0755true);
  1791.         // Build args
  1792.         $societyGroupDisplay "no_society_group";
  1793.         if (array_key_exists('society_group'$args) && $args['society_group'] instanceof SocietyGroup)
  1794.         {
  1795.             $societyGroupDisplay $args['society_group']->displayForLog();
  1796.         }
  1797.         $type = !empty($args['type']) ? $args['type'] : "";
  1798.         switch($type)
  1799.         {
  1800.             case OnlinePaymentType::CODE_STRIPE:
  1801.             {
  1802.                 $prefixLog "STRIPE";
  1803.                 break;
  1804.             }
  1805.             case OnlinePaymentType::CODE_PAYPAL:
  1806.             {
  1807.                 $prefixLog "PAYPAL";
  1808.                 break;
  1809.             }
  1810.             default:
  1811.             {
  1812.                 $prefixLog "PAYMENT";
  1813.                 break;
  1814.             }
  1815.         }
  1816.         $logFilename "payment_error_" $today->format("Y-m-d") . ".log";
  1817.         $logFilename $home $logFilename;
  1818.         if (!file_exists($logFilename))
  1819.         {
  1820.             // Create header
  1821.             $header "payment_type,timestamp,society_group,trace,message";
  1822.             error_log($header "\n"3$logFilename);
  1823.         }
  1824.         // Log
  1825.         $logMessage $prefixLog ",";
  1826.         $logMessage .= $today->format("Y-m-d H:i:s") . ",";
  1827.         $logMessage .= $societyGroupDisplay ",";
  1828.         $logMessage .= $trace ",";
  1829.         $logMessage .= $msg "\n";
  1830.         error_log($logMessage3$logFilename);
  1831.     }
  1832.     // Plan.io Task #4327
  1833.     // Logs to root/custom_logs/booking/booking_sav_slot
  1834.     public function bookingSavSlot_log($msg$args)
  1835.     {
  1836.         $home $this->getBookingSavSlotLogDir();
  1837.         if (!is_dir($home))    mkdir($home0755true);
  1838.         $today = new \DateTime();
  1839.         $logFile "booking-log-" $today->format("Y-m-d") . ".log";
  1840.         $logFile $home $logFile;
  1841.         $devis = !empty($args['devis']) ? $args['devis'] : null;
  1842.         $timestamp $today->format("[Y-m-d H:i:s]");
  1843.         $log $timestamp;
  1844.         if ($devis !== null)
  1845.         {
  1846.             $log .= "[".$devis->getId()."-".$devis->getRef()."]";
  1847.         }
  1848.         $log .= " " $msg;
  1849.         error_log($log."\n",3,$logFile);
  1850.     }
  1851.     // Plan.io Task #4327
  1852.     // Logs to root/custom_logs/booking/clean_booking_sav_slot
  1853.     public function cleanBookingSavSlot_log($msg)
  1854.     {
  1855.         $home $this->getCleanBookingSavSlotLogDir();
  1856.         if (!is_dir($home))    mkdir($home0755true);
  1857.         $today = new \DateTime();
  1858.         $logFile "clean-booking-log-" $today->format("Y-m-d") . ".log";
  1859.         $logFile $home $logFile;
  1860.         error_log($msg."\n",3,$logFile);
  1861.     }
  1862.     public function errorlog_db($e)
  1863.     {
  1864.         if (!is_object($e))
  1865.         {
  1866.             return null;
  1867.         }
  1868.         if (!($e instanceof \Exception))
  1869.         {
  1870.             return null;
  1871.         }
  1872.         $sql null;
  1873.         $params null;
  1874.         $msg null;
  1875.         if (method_exists($e'getMessage') &&
  1876.             method_exists($e'getQuery') &&
  1877.             method_exists($e->getQuery(), 'getSql') &&
  1878.             method_exists($e->getQuery(), 'getParams'))
  1879.         {
  1880.             $sql $e->getQuery()->getSql();
  1881.             $params print_r($e->getQuery()->getParams(), true);
  1882.             $msg $e->getMessage();
  1883.         }
  1884.         $backTrace debug_backtrace()[1];
  1885.         $method $backTrace['function'];
  1886.         $class $backTrace['class'];
  1887.         // Access handling
  1888.         $access $this->getCurrentUser();
  1889.         $originalUser $this->getOriginalUser();
  1890.         $accessData "";
  1891.         $originalUserData "";
  1892.         if ($access !== null)
  1893.         {
  1894.             $accessData $access->displayForLog();
  1895.         }
  1896.         if ($originalUser !== null)
  1897.         {
  1898.             $originalUserData $originalUser->displayForLog();
  1899.         }
  1900.         $args = array(
  1901.             'recorded_by'        =>    "LogTools::errorlog_db",
  1902.             'class'                =>    $class,
  1903.             'method'            =>    $method,
  1904.             'access'            =>    $accessData,
  1905.             'original_access'    =>    $originalUserData,
  1906.             'msg'                =>    $msg,
  1907.             'query'                =>    $sql,
  1908.             'params'          =>    $params,
  1909.         );
  1910.         $this->plooplog(print_r($argstrue));
  1911.         $this->ouchLogger->error(''$args);
  1912.     }
  1913.     public function errorlog($msg)
  1914.     {
  1915.         $backTrace debug_backtrace()[1];
  1916.         $method $backTrace['function'];
  1917.         $class $backTrace['class'];
  1918.         // Access handling
  1919.         $access $this->getCurrentUser();
  1920.         $originalUser $this->getOriginalUser();
  1921.         $accessData "";
  1922.         $originalUserData "";
  1923.         if ($access !== null)
  1924.         {
  1925.             $accessData $access->displayForLog();
  1926.         }
  1927.         if ($originalUser !== null)
  1928.         {
  1929.             $originalUserData $originalUser->displayForLog();
  1930.         }
  1931.         $args = array(
  1932.             'recorded_by'        =>    "LogTools::errorlog",
  1933.             'class'                =>    $class,
  1934.             'method'            =>    $method,
  1935.             'original_access'    =>    $originalUserData,
  1936.             'msg'                =>    $msg,
  1937.         );
  1938.         if ($access instanceof Access)
  1939.         {
  1940.             $args['access'] = $accessData;
  1941.         }
  1942.         else
  1943.         {
  1944.             if ($access instanceof AccessClient)
  1945.             {
  1946.                 $args['accessClient'] = $accessData;
  1947.             }
  1948.             else
  1949.             {
  1950.                 if ($access instanceof AccessAPI)
  1951.                 {
  1952.                     $args['accessApi'] = $accessData;
  1953.                 }
  1954.             }
  1955.         }
  1956.         $this->plooplog(print_r($argstrue));
  1957.         $this->ouchLogger->error(''$args);
  1958.     }
  1959.     // plan.io #3716
  1960.     public function errorlogApi($msg$accessApi)
  1961.     {
  1962.         $backTrace debug_backtrace()[1];
  1963.         $method $backTrace['function'];
  1964.         $class $backTrace['class'];
  1965.         // Access handling
  1966.         $accessData "";
  1967.         if ($accessApi !== null)
  1968.         {
  1969.             $accessData $accessApi->displayForLog();
  1970.         }
  1971.         $args = array(
  1972.             'class'                =>    $class,
  1973.             'method'            =>    $method,
  1974.             'access'            =>    $accessData,
  1975.             'msg'                =>    $msg,
  1976.         );
  1977.         $this->ouchLogger->error(''$args);
  1978.     }
  1979.     public function getCurrentUser()
  1980.     {
  1981.         $emptyAccess $this->em->getRepository(Access::class)->findOneByEmail(Access::EMPTY_ACCESS);
  1982.         // Get the Access|AccessAPI|AccessClient object returned by the security
  1983.         $access $this->security->getUser();
  1984.         if (empty($access))
  1985.         {
  1986.             // This occurs when the code is execuded by a Command (CRON Task for example)
  1987.             // In this case there is no firewall, and thus no Access|AccessAPI
  1988.             $access $emptyAccess;
  1989.         }
  1990.         else
  1991.         {
  1992.             // The firewall has been bypassed => We have an user provider,
  1993.             // either Access or AccessAPI
  1994.             // Plan.io Task #4327 ... or an AccessClient :)
  1995.             if (!($access instanceof Access) && !($access instanceof AccessAPI) && !($access instanceof AccessClient))
  1996.             {
  1997.                 // This should not happen, but hey, life is weird, who knows
  1998.                 // Even VSCode proved to be worse than Atom ;)
  1999.                 $access $emptyAccess;
  2000.             }
  2001.         }
  2002.         // Plan.io Task #3946
  2003.         if ($access instanceof AccessAPI)
  2004.         {
  2005.             // Almost all AccessAPI have a corresponding Access, so retrive it
  2006.             $access $access->getAccess();
  2007.         }
  2008.         return $access;
  2009.     }
  2010.     // https://symfony.com/doc/5.4/security/impersonating_user.html
  2011.     // This is also in AdminVoter
  2012.     // This is also in Logger
  2013.     // This is also in LogTools
  2014.     // This is also in AccessTools
  2015.     // This is also in AlertTools
  2016.     public function getOriginalUser()
  2017.     {
  2018.         $originalUser null;
  2019.         // #3251 Fix to avoid infinity loop
  2020.         $currentUser $this->getCurrentUser();
  2021.         if ($currentUser === null || $currentUser->getEmail() == Access::EMPTY_ACCESS)
  2022.         {
  2023.             return null;
  2024.         }
  2025.         $token $this->security->getToken();
  2026.         if ($token instanceof SwitchUserToken)
  2027.         {
  2028.             $originalUser $token->getOriginalToken()->getUser();
  2029.         }
  2030.         return $originalUser;
  2031.     }
  2032.     public function generateAlphaNumCode($length)
  2033.     {
  2034.         $lowercase "qwertyuiopasdfghjklzxcvbnm";
  2035.         $numbers "1234567890";
  2036.         $randomCode "";
  2037.         $length abs($length);
  2038.         $lengthSlice $length 2;
  2039.         $max strlen($lowercase) - 1;
  2040.         for ($x 0$x $lengthSlice$x++)
  2041.         {
  2042.             $i random_int(0$max);
  2043.             $randomCode .= $lowercase[$i];
  2044.         }
  2045.         $max strlen($numbers) - 1;
  2046.         for ($x 0$x $lengthSlice$x++)
  2047.         {
  2048.             $i random_int(0$max);
  2049.             $randomCode .= $numbers[$i];
  2050.         }
  2051.         return str_shuffle($randomCode);
  2052.     }
  2053.     public function generateUniqueGuardLogFileName()
  2054.     {
  2055.         $today = new \DateTime();
  2056.         $prefix "guard_log_".$today->format("Y_m_d")."_";
  2057.         $code $this->generateAlphaNumCode(30);
  2058.         $logFileName $prefix $code ".log";
  2059.         return $logFileName;
  2060.     }
  2061.     public function fetchGuardLogFile($logFileName)
  2062.     {
  2063.         $logPath $this->getGuardGlobalLogsDir();
  2064.         if (!file_exists($logPath))
  2065.         {
  2066.             if (!mkdir($logPath))
  2067.             {
  2068.                 return null;
  2069.             }
  2070.         }
  2071.         $logFile $logPath.$logFileName;
  2072.         return $logFile;
  2073.     }
  2074.     public function formatGuardLogFile($logFile)
  2075.     {
  2076.         if (!file_exists($logFile))
  2077.         {
  2078.             return "";
  2079.         }
  2080.         $log file_get_contents($logFile);
  2081.         if ($log)
  2082.         {
  2083.             $log str_replace("\n""<br>"$log);
  2084.             $log str_replace("not found""<span class='text-danger'>not found</span>"$log);
  2085.             $log str_replace("inactive""<span class='text-danger'>inactive</span>"$log);
  2086.             $log str_replace("error""<span class='text-danger'><strong>error</strong></span>"$log);
  2087.             $log str_replace("ADD""<span style='color:#39c300'><strong>ADD</strong></span>"$log);
  2088.             $log str_replace("DENY""<span style='color:#ff731d'><strong>DENY</strong></span>"$log);
  2089.             $log str_replace("ERROR""<span style='color:#da0000'><strong>ERROR</strong></span>"$log);
  2090.             $log str_replace("UPDATE""<span style='color:#af0076'><strong>UPDATE</strong></span>"$log);
  2091.             $log str_replace("SYNC""<span style='color:#d800a6'><strong>SYNC</strong></span>"$log);
  2092.             $log str_replace("DEACTIVATE""<span style='color:#645caa'><strong>DEACTIVATE</strong></span>"$log);
  2093.             $log str_replace("ACTIVATE""<span style='color:#645caa'><strong>ACTIVATE</strong></span>"$log);
  2094.             $log str_replace("REMOVE""<span style='color:#F94A29'><strong>REMOVE</strong></span>"$log);
  2095.             $log str_replace("DELETE""<span style='color:#F94A29'><strong>DELETE</strong></span>"$log);
  2096.         }
  2097.         return $log;
  2098.     }
  2099.     public function loadGuardFile($logFile$fileName)
  2100.     {
  2101.         $log null;
  2102.         $matrix null;
  2103.         // Get the configuration file
  2104.         $webPath $this->getGuardConfigDir();
  2105.         $matrixFile $webPath $fileName;
  2106.         if (!file_exists($matrixFile))
  2107.         {
  2108.             $log = array(
  2109.                 "status"        =>    false,
  2110.                 "error"            =>    "config_file_does_not_exist",
  2111.                 "info"            =>    $matrixFile,
  2112.             );
  2113.         }
  2114.         else
  2115.         {
  2116.             // Read its contents
  2117.             $matrix file_get_contents($matrixFile);
  2118.             if (!$matrix)
  2119.             {
  2120.                 $log = array(
  2121.                     "status"        =>    false,
  2122.                     "error"            =>    "config_file_could_not_be_read",
  2123.                     "info"            =>    $matrixFile,
  2124.                 );
  2125.             }
  2126.             else
  2127.             {
  2128.                 // If we are here it means that all went well
  2129.                 return $matrix;
  2130.             }
  2131.         }
  2132.         if ($log !== null)
  2133.         {
  2134.             // Something went wrong
  2135.             // Get the error and translate it
  2136.             $error $this->translator->trans($log["error"]);
  2137.             $error .= " : ";
  2138.             $error .= $log["info"];
  2139.             // Save it to the log file, if any
  2140.             if ($logFile !== null)
  2141.             {
  2142.                 error_log("\n".$error3$logFile);
  2143.             }
  2144.             // Log it to global logs also
  2145.             $this->errorlog($error);
  2146.             return null;
  2147.         }
  2148.         // If we are here it means that all went well
  2149.         return $matrix;
  2150.     }
  2151.     public function loadGuardConfigFile($logFile$fileName)
  2152.     {
  2153.         $log null;
  2154.         $matrix null;
  2155.         // Get the configuration file
  2156.         $webPath $this->getGuardConfigDir();
  2157.         $matrixFile $webPath $fileName;
  2158.         if (!file_exists($matrixFile))
  2159.         {
  2160.             $log = array(
  2161.                 "status"        =>    false,
  2162.                 "error"            =>    "config_file_does_not_exist",
  2163.                 "info"            =>    $matrixFile,
  2164.             );
  2165.         }
  2166.         else
  2167.         {
  2168.             // Read its contents
  2169.             $matrix file_get_contents($matrixFile);
  2170.             if (!$matrix)
  2171.             {
  2172.                 $log = array(
  2173.                     "status"        =>    false,
  2174.                     "error"            =>    "config_file_could_not_be_read",
  2175.                     "info"            =>    $matrixFile,
  2176.                 );
  2177.             }
  2178.             else
  2179.             {
  2180.                 // Parse it
  2181.                 $yamlparser = new \Symfony\Component\Yaml\Parser();
  2182.                 $matrix $yamlparser->parse($matrix);
  2183.                 if (!$matrix || empty($matrix))
  2184.                 {
  2185.                     $log = array(
  2186.                         "status"        =>    false,
  2187.                         "error"            =>    "config_file_could_not_be_parsed",
  2188.                         "info"            =>    $matrixFile,
  2189.                     );
  2190.                 }
  2191.             }
  2192.         }
  2193.         if ($log !== null)
  2194.         {
  2195.             // Something went wrong
  2196.             // Get the error and translate it
  2197.             $error $this->translator->trans($log["error"]);
  2198.             $error .= " : ";
  2199.             $error .= $log["info"];
  2200.             // Save it to the log file, if any
  2201.             if ($logFile !== null)
  2202.             {
  2203.                 error_log("\n".$error3$logFile);
  2204.             }
  2205.             // Log it to global logs also
  2206.             $this->errorlog($error);
  2207.             return null;
  2208.         }
  2209.         // If we are here it means that all went well
  2210.         return $matrix;
  2211.     }
  2212.     // Plan.io Task #4105
  2213.     public function loadSocietyGroupDeleteScript(SocietyGroup $societyGroup)
  2214.     {
  2215.         $matrix null;
  2216.         $fileName "society_group_delete.sql";
  2217.         // Get the configuration file
  2218.         $webPath $this->getGuardConfigDir();
  2219.         $matrixFile $webPath $fileName;
  2220.         if (!file_exists($matrixFile))
  2221.         {
  2222.             return array(
  2223.                 "status"        =>    false,
  2224.                 "error"            =>    "config_file_does_not_exist",
  2225.                 "info"            =>    $matrixFile,
  2226.             );
  2227.         }
  2228.         else
  2229.         {
  2230.             // Read its contents
  2231.             $matrix file_get_contents($matrixFile);
  2232.             if (!$matrix)
  2233.             {
  2234.                 return array(
  2235.                     "status"        =>    false,
  2236.                     "error"            =>    "config_file_could_not_be_read",
  2237.                     "info"            =>    $matrixFile,
  2238.                 );
  2239.             }
  2240.             else
  2241.             {
  2242.                 // If we are here it means that all went well
  2243.                 // Replace SGID with $societyGroup->id
  2244.                 $matrix str_replace("SGID"$societyGroup->getId(), $matrix);
  2245.                 return array(
  2246.                     "status"        =>    true,
  2247.                     "data"            =>    $matrix,
  2248.                 );
  2249.             }
  2250.         }
  2251.         return array(
  2252.             "status"        =>    false,
  2253.             "error"            =>    "unknown_error",
  2254.             "info"            =>    $matrixFile,
  2255.         );
  2256.     }
  2257.     // Types : add, deny, error, update, sync, activate, deactivate
  2258.     // add : #39c300 rgb(57,195,0)
  2259.     // deny : #ff731d rgb(255,115,29)
  2260.     // error : #da0000 rgb(218,0,0)
  2261.     // update : #af0076 rgb(175,0,118)
  2262.     // sync : #d800a6 rgb(216,0,166)
  2263.     // activate : #645caa rgb(100,92,170)
  2264.     // deactivate : #645caa rgb(160,132,202)
  2265.     public function guardLog($logFile$msg$type null)
  2266.     {
  2267.         if ($logFile === null)
  2268.         {            
  2269.             return false;
  2270.         }
  2271.         $backTrace debug_backtrace();
  2272.         // When the logging is done by the SocietyGroupGenerator
  2273.         // Allow the deny statements
  2274.         // Skip them otherwise
  2275.         $allowDeny false;
  2276.         if (in_array($type, array("deny")))
  2277.         {
  2278.             if (isset($backTrace[2]))
  2279.             {
  2280.                 $method $backTrace[2]['function'];
  2281.                 // $this->plooplog("method(2) = ".$method);
  2282.                 if ($method == 'createSocietyGroupWithGuard')
  2283.                 {
  2284.                     $allowDeny true;
  2285.                 }
  2286.             }
  2287.         }
  2288.         if (in_array($type, array("deny")) && !$allowDeny)
  2289.         {
  2290.             return false;
  2291.         }
  2292.         $backTrace debug_backtrace()[1];
  2293.         $method $backTrace['function'];
  2294.         $class $backTrace['class'];
  2295.         $class str_replace("App\\Guardian\\"""$class);
  2296.         $class str_replace("App\\Services\\"""$class);
  2297.         $class str_replace("App\\Controller\\"""$class);
  2298.         if ($type !== null)
  2299.         {
  2300.             $msg "\n" strtoupper($type) . " ; " $class " ; " $method " ; " $msg;
  2301.         }
  2302.         else
  2303.         {
  2304.             $msg "\n" $class " ; " $method " ; " $msg;
  2305.         }
  2306.         error_log($msg3$logFile);
  2307.         return true;
  2308.     }
  2309.     public function logInvoiceMissingDevisProduct(Invoice $invoiceDevis $devis$msg)
  2310.     {
  2311.         $logFile $this->getInvoiceMissingDevisProductsLogsDir().$invoice->getSocietyGroup()->getId().".log";
  2312.         $today = new \DateTime();
  2313.         $timestamp $today->format("Y-m-d H:i:s");
  2314.         $invoiceId $invoice->getId();
  2315.         $invoiceRef $invoice->getRef();
  2316.         $devisId $devis->getId();
  2317.         $devisRef $devis->getRef();
  2318.         $output "\n[$timestamp][$invoiceId][$invoiceRef][$devisId][$devisRef]$msg";
  2319.         error_log($output3$logFile);
  2320.     }
  2321.     //--------------------------------------------------------------------------
  2322.     // Plan.io Task #3922
  2323.     public function camelToSnakeCase($text)
  2324.     {
  2325.         return strtolower(preg_replace(['/([a-z\d])([A-Z])/''/([^_])([A-Z][a-z])/'], '$1_$2'$text));
  2326.     }
  2327.     public function handleLoggingData($entity)
  2328.     {
  2329.         $info null;
  2330.         $specialAuthor null;
  2331.         $ignore false;
  2332.         $action null;
  2333.         if (method_exists($entity"getLoggingData"))
  2334.         {
  2335.             $loggingData $entity->getLoggingData();
  2336.             // $this->ploopLog("[LogTools][handleLoggingData] --------------------------------------------------");
  2337.             // $this->ploopLog("[LogTools][handleLoggingData] entity = ".$entity->displayForLog());
  2338.             // $this->ploopLog("[LogTools][handleLoggingData] loggingData = ".print_r($loggingData, true));
  2339.             if (!empty($loggingData) && is_array($loggingData))
  2340.             {
  2341.                 if (array_key_exists('action'$loggingData))
  2342.                 {
  2343.                     $action $loggingData['action'];
  2344.                 }
  2345.                 if (array_key_exists('info'$loggingData))
  2346.                 {
  2347.                     $info $loggingData['info'];
  2348.                 }
  2349.                 if (array_key_exists('special_author'$loggingData))
  2350.                 {
  2351.                     $specialAuthor $loggingData['special_author'];
  2352.                 }
  2353.                 if (array_key_exists('ignore'$loggingData))
  2354.                 {
  2355.                     $ignore boolval($loggingData['ignore']);
  2356.                 }
  2357.             }
  2358.         }
  2359.         // $this->ploopLog("[LogTools][handleLoggingData] info = ".$info);
  2360.         // if ($ignore) $this->ploopLog("[LogTools][handleLoggingData] ignore = true");
  2361.         // else $this->ploopLog("[LogTools][handleLoggingData] ignore = false");
  2362.         // $this->ploopLog("[LogTools][handleLoggingData] --------------------------------------------------");
  2363.         return array(
  2364.             'action'            => $action,
  2365.             'info'              => $info,
  2366.             'special_author'    => $specialAuthor,
  2367.             'ignore'            => $ignore,
  2368.         );
  2369.     }
  2370.     //--------------------------------------------------------------------------
  2371.     // Plan.io Task #3922
  2372.     public function getLogPathForMission(Mission $mission)
  2373.     {
  2374.         $originalLogPath $this->getDMS_MissionLogsDir();
  2375.         $societyGroup $mission->getSocietyGroupAuthor();
  2376.         if ($societyGroup === null)
  2377.         {
  2378.             return null;
  2379.         }
  2380.         $societyGroupId $societyGroup->getId();
  2381.         $societyGroupRef $societyGroup->getInternalRef();
  2382.         if (empty($societyGroupId) || empty($societyGroupRef))
  2383.         {
  2384.             return null;
  2385.         }            
  2386.         // Craft folder name
  2387.         $logFolderName $societyGroupId."_".$societyGroupRef;
  2388.         $logPath $originalLogPath.$logFolderName."/";
  2389.         // Craft file name
  2390.         $id $mission->getId();
  2391.         $logFileName "mission_log_".$id.".csv";        
  2392.         
  2393.         $logFile $logPath.$logFileName;
  2394.         if (file_exists($logFile))
  2395.         {
  2396.             return $logFile;
  2397.         }
  2398.         return null;
  2399.     }
  2400.     // Plan.io Task #3922
  2401.     public function getLogPathForClient(Client $client)
  2402.     {
  2403.         $originalLogPath $this->getDMS_ClientLogsDir();
  2404.         $societyGroup $client->getSocietyGroup();
  2405.         if ($societyGroup === null)
  2406.         {
  2407.             return null;
  2408.         }
  2409.         $societyGroupId $societyGroup->getId();
  2410.         $societyGroupRef $societyGroup->getInternalRef();
  2411.         if (empty($societyGroupId) || empty($societyGroupRef))
  2412.         {
  2413.             return null;
  2414.         }            
  2415.         // Craft folder name
  2416.         $logFolderName $societyGroupId."_".$societyGroupRef;
  2417.         $logPath $originalLogPath.$logFolderName."/";
  2418.         // Craft file name
  2419.         $id $client->getId();
  2420.         $logFileName "client_log_".$id.".csv";        
  2421.         
  2422.         $logFile $logPath.$logFileName;
  2423.         if (file_exists($logFile))
  2424.         {
  2425.             return $logFile;
  2426.         }
  2427.         return null;
  2428.     }
  2429.     /**
  2430.      * Returns the path to the CSV log file for the given human resource, or null if not found.
  2431.      * Path pattern: DMS/Logs/HumanResource/{societyGroupId}_{societyGroupRef}/human_resource_log_{id}.csv
  2432.      */
  2433.     public function getLogPathForHumanResource(HumanResource $humanResource)
  2434.     {
  2435.         $originalLogPath $this->getDMS_HumanResourceLogsDir();
  2436.         $societyGroup $humanResource->getSocietyGroup();
  2437.         if ($societyGroup === null)
  2438.         {
  2439.             return null;
  2440.         }
  2441.         $societyGroupId $societyGroup->getId();
  2442.         $societyGroupRef $societyGroup->getInternalRef();
  2443.         if (empty($societyGroupId) || empty($societyGroupRef))
  2444.         {
  2445.             return null;
  2446.         }
  2447.         $logFolderName $societyGroupId."_".$societyGroupRef;
  2448.         $logPath $originalLogPath.$logFolderName."/";
  2449.         $id $humanResource->getId();
  2450.         $logFileName "human_resource_log_".$id.".csv";
  2451.         $logFile $logPath.$logFileName;
  2452.         if (file_exists($logFile))
  2453.         {
  2454.             return $logFile;
  2455.         }
  2456.         return null;
  2457.     }
  2458.     // Plan.io Task #4624
  2459.     public function getLogPathForProjectNotebook(ProjectNotebook $notebook)
  2460.     {
  2461.         $originalLogPath $this->getDMS_ProjectManagerNotebookLogsDir();
  2462.         $societyGroup $notebook->getSocietyGroup();
  2463.         if ($societyGroup === null)
  2464.         {
  2465.             return null;
  2466.         }
  2467.         $societyGroupId $societyGroup->getId();
  2468.         $societyGroupRef $societyGroup->getInternalRef();
  2469.         if (empty($societyGroupId) || empty($societyGroupRef))
  2470.         {
  2471.             return null;
  2472.         }            
  2473.         // Craft folder name
  2474.         $logFolderName $societyGroupId."_".$societyGroupRef;
  2475.         $logPath $originalLogPath.$logFolderName."/";
  2476.         // Craft file name
  2477.         $id $notebook->getId();
  2478.         $logFileName "project_notebook_log_".$id.".csv";            
  2479.         
  2480.         $logFile $logPath.$logFileName;
  2481.         if (file_exists($logFile))
  2482.         {
  2483.             return $logFile;
  2484.         }
  2485.         return null;
  2486.     }
  2487.     
  2488.     /**
  2489.      * Reads the logging CSV produced by Logging::logToFile() into an array of LogCsvRecord DTOs.
  2490.      * File format: semicolon-delimited, first line may be blank, then header row, then data rows.
  2491.      *
  2492.      * #3922 : This was generated by Cursor => Review and Clean
  2493.      * 
  2494.      * @return LogCsvRecord[]
  2495.      */
  2496.     public function readLoggingCsv(string $filePath): array
  2497.     {
  2498.         // Ensure the file exists and is readable before attempting to load it
  2499.         if (!is_readable($filePath))
  2500.         {
  2501.             return [];
  2502.         }
  2503.         // Load the entire file content (CSV may be written with mixed line endings by error_log)
  2504.         $raw file_get_contents($filePath);
  2505.         if ($raw === false)
  2506.         {
  2507.             return [];
  2508.         }
  2509.         // Split into lines using any common newline sequence (Unix, Windows, legacy Mac)
  2510.         $allLines preg_split('/\r\n|\r|\n/'$raw);
  2511.         if ($allLines === false)
  2512.         {
  2513.             return [];
  2514.         }
  2515.         // Drop empty lines so the first non-empty line is the header (Logging writes a leading newline)
  2516.         $nonEmptyLines array_values(array_filter($allLines, static function (string $line): bool
  2517.         {
  2518.             return trim($line) !== '';
  2519.         }));
  2520.         if (count($nonEmptyLines) < 2)
  2521.         {
  2522.             return [];
  2523.         }
  2524.         // First non-empty line is the semicolon-separated header row; normalize and remove empty cells
  2525.         $headerLine $nonEmptyLines[0];
  2526.         $headers array_values(array_filter(array_map('trim'explode(';'$headerLine)), static function (string $h): bool {
  2527.             return $h !== '';
  2528.         }));
  2529.         // Parse each remaining line as a data row and map to a LogCsvRecord DTO
  2530.         $records = [];
  2531.         for ($i 1$n count($nonEmptyLines); $i $n$i++)
  2532.         {
  2533.             $values explode(';'$nonEmptyLines[$i]);
  2534.             $records[] = LogCsvRecord::fromRow($headers$values);
  2535.         }
  2536.         return $records;
  2537.     }
  2538.     /**
  2539.      * Streams the logging CSV produced by Logging::logToFile() as LogCsvRecord DTOs.
  2540.      * Use this for large files (e.g. tens of MB) to avoid loading the whole file into memory.
  2541.      * File format: semicolon-delimited, first line may be blank, then header row, then data rows.
  2542.      * 
  2543.      * #3922 : This was generated by Cursor => Review and Clean
  2544.      * Working, but still too much data to output
  2545.      * DOM loading is extremly slow ... and becomes not responsive
  2546.      *
  2547.      * @return \Generator<int, LogCsvRecord, void, void>
  2548.      *
  2549.      * Example:
  2550.      *   foreach ($logTools->readLoggingCsvXXL('/path/to/file.csv') as $record) {
  2551.      *       // process $record
  2552.      *   }
  2553.      */
  2554.     public function readLoggingCsvXXL(string $filePath): \Generator
  2555.     {
  2556.         // Ensure the file exists and is readable before opening
  2557.         if (!is_readable($filePath))
  2558.         {
  2559.             return;
  2560.         }
  2561.         $handle fopen($filePath'rb');
  2562.         if ($handle === false)
  2563.         {
  2564.             return;
  2565.         }
  2566.         try
  2567.         {
  2568.             // Read line by line (fgets stops at \n; \r\n or \r are included and trimmed below)
  2569.             // so only one line is in memory at a time.
  2570.             $headers null;
  2571.             while (($line fgets($handle)) !== false)
  2572.             {
  2573.                 $line trim($line);
  2574.                 if ($line === '')
  2575.                 {
  2576.                     continue;
  2577.                 }
  2578.                 // First non-empty line is the header; parse and store for mapping data rows
  2579.                 if ($headers === null)
  2580.                 {
  2581.                     $headers array_values(array_filter(
  2582.                         array_map('trim'explode(';'$line)),
  2583.                         static function (string $h): bool {
  2584.                             return $h !== '';
  2585.                         }
  2586.                     ));
  2587.                     continue;
  2588.                 }
  2589.                 // Subsequent non-empty lines are data rows: parse and yield one record
  2590.                 $values explode(';'$line);
  2591.                 yield LogCsvRecord::fromRow($headers$values);
  2592.             }
  2593.         }
  2594.         finally
  2595.         {
  2596.             // Always close the handle when the generator is exhausted or the consumer stops iterating
  2597.             fclose($handle);
  2598.         }
  2599.     }
  2600. }