HEX
Server: Microsoft-IIS/8.5
System: Windows NT YDAWBH120 6.3 build 9600 (Windows Server 2012 R2 Standard Edition) AMD64
User: tentjecom_web (0)
PHP: 7.4.14
Disabled: NONE
Upload Files
File: D:/HostingSpaces/SBogers10/zelfverkopen.komma.pro/app/KommaApp/Realworks/Kms/RealWorksService.php
<?php
namespace App\KommaApp\Realworks\Kms;


use App\KommaApp\Kms\Core\Attributes\Attribute;
use App\KommaApp\Kms\Core\NestedSets\Nodes\EloquentNode;
use App\KommaApp\Kms\Core\Sections\SectionService;
use App\KommaApp\Kms\Core\Sections\SectionTabItem;
use App\KommaApp\Languages\Models\Language;
use App\KommaApp\Pages\Models\Page;
use App\KommaApp\Realworks\Models\Huur;
use App\KommaApp\Realworks\Models\HuurTranslation;
use App\KommaApp\Realworks\Models\Koop;
use App\KommaApp\Realworks\Models\KoopTranslation;
use App\KommaApp\Realworks\Models\RealworkObject;
use App\KommaApp\Realworks\Models\ObjectDetails;
use App\KommaApp\Realworks\Models\ObjectDetailsTranslation;
use App\Mail\RealworksFailMail;
use Carbon\Carbon;
use function GuzzleHttp\Psr7\str;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Support\Facades\Log;
use ZipArchive;

class RealWorksService extends SectionService
{
    protected $sortable = true;

    protected static $DOWNLOAD_FOLDER_NAME = 'download'; //without trailing slash
    protected static $XML_FILE_NAME = 'voorbeeld.wonen'; //without extension

    function __construct()
    {
        $this->forModelName = Page::class; //TODO Change me

        parent::__construct();
    }

    /**
     * Downloads the zipped xml from Realworks.
     *
     * Warning. Strict regulations by Realworks apply. Please check their documentation for the latest info on those
     * regulations. At the time of writing they are:
     *
     * - Only call the api once a day after 08:30 in the morning.
     * - The media that is defined in the xml must be downloaded for displaying.
     */
    public function DownloadZipWithXML()
    {
        $storageFolder = $this->createAndGetRealworksStorageFolder(self::$DOWNLOAD_FOLDER_NAME);

        $url = $this->getUrl();

        try {
            $file = file_get_contents($url);
        }
        catch (\Exception $exception)
        {
            $this->mailOrShowErrorAndDie('Could not save downloaded realworks zip');
        }


        $zipPath = $storageFolder.config('realworks.zip_name', 'realworks').'.zip';
        if(file_put_contents($zipPath,$file) === false) $this->mailOrShowErrorAndDie('Could not save downloaded realworks zip ("'.$zipPath.'")');
    }

    /**
     * Extracts the realworks zip and deletes the zip file
     */
    public function ProcessZip()
    {
        $storageFolder = $this->createAndGetRealworksStorageFolder(self::$DOWNLOAD_FOLDER_NAME);
        $zipPath = $storageFolder.config('realworks.zip_name', 'realworks').'.zip';

        $zip = new ZipArchive;
        if ($zip->open($zipPath) === true) {
            $zip->extractTo($storageFolder);
            $zip->close();

            if(unlink($zipPath) === false) $this->mailOrShowErrorAndDie('Could delete realworks zip file after extraction: '.$zipPath);

        } else {
            $this->mailOrShowErrorAndDie('Could not extract realworks zip file: '.$zipPath);
        }
    }

    /**
     * Clears the storage folder in which the downloaded zip and its extracted data is placed
     */
    public function ClearDownloadFolder()
    {
        RealWorksService::rrmdir($this->createAndGetRealworksStorageFolder(self::$DOWNLOAD_FOLDER_NAME), false);
    }

    /**
     * MUST be triggered by App\Console\Kernel to update Realworks api stuff.
     */
    public function executeSchedule()
    {
        $this->ClearDownloadFolder();
        $this->DownloadZipWithXML(); //WARNING! Only runnable once a day. Else we will get fined!
        $this->ProcessZip();
        $xml = $this->DownloadedXMLToArray();
        $this->DownloadMediaFromXMLToPublicFolders($xml); //WARNING! Only runnable once a day. Else we will get fined!
        $this->storeObjectsInDatabase($xml);
        $this->deleteObjectsFromDatabaseThatArentListedInXML($xml);
        $this->deletePublicFoldersFromNonExistingObjects();

        // Only for test purpose this weekend instead of the real
//        \Mail::send('site.emails.propertyDataError', [
//            'propertyId'  => 0,
//            'dataTail'    => 'Schedular test',
//            'errorType'   => 0,
//            'errorString' => 'Schedular test',
//            'variable'    => 'Schedular test'
//        ], function ($message){
//            $message->from(\Config::get('mail.from.address'), \Config::get('mail.from.name'));
//            $message->to(\Config::get('mail.admin.address'));
//
//            // Set subject
//            $message->subject('Schedular test');
//        });

    }

    /**
     * Stores the properties in the database by using the xml
     *
     * @param array $xml
     */
    public function storeObjectsInDatabase(array $xml)
    {
        if(!array_key_exists('Object', $xml)) return; //Empty

        foreach($xml['Object'] as $objectArray)
        {
            $object = $this->createObjectFromXML($objectArray);
        }
    }

    /**
     * Creates an object model from the input xml data
     *
     * @param $xml
     * @return RealworkObject|null
     */
    private function createObjectFromXML(array $xml): ?RealworkObject
    {
        if(!isset($xml['ObjectSystemID'])) $this->mailOrShowErrorAndDie('An object did not have the required ObjectSystemID key. Stopped processing');
        $dutchLanguage = Language::where('native_name', '=', 'Nederlands')->first();
        if(!$dutchLanguage) $this->mailOrShowErrorAndDie('The dutch language did not exists. The object details could not be created. Stopped processing');

        $object = RealworkObject::where('system_id', '=', $xml['ObjectSystemID'])->first();
        if(!$object) $object = new RealworkObject();

        $object->raw_json = json_encode($xml);
        if(isset($xml['NVMVestigingNR'])) $object->nvm_vestiging_nummer = $xml['NVMVestigingNR'];
        if(isset($xml['ObjectCompany'])) $object->company = $xml['ObjectCompany'];
        if(isset($xml['ObjectAfdeling'])) $object->afdeling = $xml['ObjectAfdeling'];
        if(isset($xml['ObjectTiaraID'])) $object->tiara_id = $xml['ObjectTiaraID'];
        if(isset($xml['ObjectSystemID'])) $object->system_id = $xml['ObjectSystemID'];
        if(isset($xml['ObjectCode'])) $object->code = $xml['ObjectCode'];
        if(!isset($xml['ObjectDetails'])) $this->mailOrShowErrorAndDie('An object did not contain object details. Stopped processing');
        $object->save();

        /** @var ObjectDetailsTranslation $objectDetailsTranslation */
        $objectDetailsTranslation = $this->createObjectDetailsFromXML($xml['ObjectDetails'], $object, $dutchLanguage)->translations->first();
        $object->public_path = $this->getPublicFolderPathForObject($object->system_id, $objectDetailsTranslation->woonplaats, $objectDetailsTranslation->straatnaam, $objectDetailsTranslation->huisnummer);
        $object->public_path = str_replace(public_path(), '', $object->public_path);
        $object->public_path = str_replace('\\', '/', $object->public_path);

        $object->save();

        return $object;
    }

    /**
     * Creates an object details model from the input xml data
     *
     * @param array $xml
     * @param RealworkObject $object
     * @param Language $language
     * @return ObjectDetails
     */
    private function createObjectDetailsFromXML(array $xml, RealworkObject $object, Language $language): ObjectDetails {
        /** @var ObjectDetails $objectDetails */
        $objectDetails = $object->objectDetails()->first();

        if($objectDetails) {
            //Check if the object was updated yesterday. If not we are going to skip it
            $updateDate = Carbon::createFromFormat('Y-m-d', $xml['DatumWijziging']);
            $now = Carbon::now();
            $now = Carbon::create($now->year, $now->month, $now->day);
            $needsUpdate = ($updateDate && $now->subDay()->equalTo($updateDate)) ? true: false;
            if(!$needsUpdate) return $objectDetails;
        }

        if(!$objectDetails) {
            $objectDetails = new ObjectDetails();
            $objectDetails->object()->associate($object);
            $objectDetails->save();
        }

        $objectDetailsTranslation = $objectDetails->translations()->where('language_id', '=', $language->id)->first();
        if(!$objectDetailsTranslation) {
            $objectDetailsTranslation = new ObjectDetailsTranslation();
            $objectDetailsTranslation->translatable()->associate($objectDetails);
            $objectDetailsTranslation->language()->associate($language);
        }

        if(isset($xml['Adres']) && isset($xml['Adres']['Nederlands'])) {
            $adresData = $xml['Adres']['Nederlands'];

            if (isset($adresData['Straatnaam'])) $objectDetailsTranslation->straatnaam = $adresData['Straatnaam'];
            if (isset($adresData['Huisnummer'])) $objectDetailsTranslation->huisnummer = $adresData['Huisnummer'];
            if (isset($adresData['HuisnummerToevoeging'])) $objectDetailsTranslation->huisnummer_toevoeging = $adresData['HuisnummerToevoeging'];
            if (isset($adresData['Postcode'])) $objectDetailsTranslation->postcode = $adresData['Postcode'];
            if (isset($adresData['Woonplaats'])) $objectDetailsTranslation->woonplaats = $adresData['Woonplaats'];
            if (isset($adresData['Land'])) $objectDetailsTranslation->land = $adresData['Land'];
        }
        if (isset($xml['Aanvaarding'])) $objectDetailsTranslation->aanvaarding = $xml['Aanvaarding'];
        if (isset($xml['DatumAanvaarding'])) $objectDetailsTranslation->toelichting_aanvaarding = $xml['DatumAanvaarding'];
        if (isset($xml['ToelichtingAanvaarding'])) $objectDetailsTranslation->toelichting_aanvaarding = $xml['ToelichtingAanvaarding'];
        if (isset($xml['DatumAanvaarding'])) $objectDetailsTranslation->datum_aanvaarding = Carbon::createFromFormat('Y-m-d', $xml['DatumAanvaarding']);
        if (isset($xml['ObjectAanmelding'])) $objectDetailsTranslation->object_aanmelding = $xml['ObjectAanmelding'];
        if (isset($xml['DatumInvoer'])) $objectDetailsTranslation->datum_invoer = Carbon::createFromFormat('Y-m-d', $xml['DatumInvoer']);
        if (isset($xml['DatumWijziging'])) $objectDetailsTranslation->datum_wijziging = Carbon::createFromFormat('Y-m-d', $xml['DatumWijziging']);
        if (isset($xml['DatumVeiling'])) $objectDetailsTranslation->datum_veiling = Carbon::createFromFormat('Y-m-d', $xml['DatumVeiling']);
        if (isset($xml['StatusBeschikbaarheid'])) {
            $statusBeschikbaarheid = $xml['StatusBeschikbaarheid'];
            if (isset($statusBeschikbaarheid['Status'])) $objectDetailsTranslation->status_beschikbaarheid = $statusBeschikbaarheid['Status'];
            if (isset($statusBeschikbaarheid['VerkochtOnderVoorbehoud'])) {
                $verkochtOnderVoorbehoud = $statusBeschikbaarheid['VerkochtOnderVoorbehoud'];
                if(isset($verkochtOnderVoorbehoud['Datum'])) $objectDetailsTranslation->status_verkocht_onder_voorbehoud_datum = Carbon::createFromFormat('Y-m-d', $verkochtOnderVoorbehoud['Datum']);
                if(isset($verkochtOnderVoorbehoud['DatumVoorbehoudTot'])) $objectDetailsTranslation->status_verkocht_onder_voorbehoud_datum_tot = Carbon::createFromFormat('Y-m-d', $verkochtOnderVoorbehoud['DatumVoorbehoudTot']);
            }
            if (isset($statusBeschikbaarheid['TransactieDatum'])) $objectDetailsTranslation->status_transactie_datum = Carbon::createFromFormat('Y-m-d', $statusBeschikbaarheid['TransactieDatum']);
        }
        if (isset($xml['Bouwvorm'])) $objectDetailsTranslation->bouwvorm = $xml['Bouwvorm'];
        if (isset($xml['Aanbiedingstekst'])) $objectDetailsTranslation->aanbiedingstekst = $xml['Aanbiedingstekst'];


        $objectDetailsTranslation->save();

        if(isset($xml['Koop'])) $this->createKoopFromXML($xml['Koop'], $objectDetails, $language);
        if(isset($xml['Huur'])) $this->createHuurFromXml($xml['Huur'], $objectDetails, $language);

        return $objectDetails;
    }

    private function createKoopFromXML(array $xml, ObjectDetails $objectDetails, Language $language): Koop
    {
        $koop = $objectDetails->koop()->first();
        if(!$koop) {
            $koop = new Koop();
            $koop->objectDetails()->associate($objectDetails);
            $koop->save();
        }

        $koopTranslation = $koop->translations()->where('language_id', '=', $language->id)->first();
        if(!$koopTranslation) {
            $koopTranslation = new KoopTranslation();
            $koopTranslation->translatable()->associate($koop);
            $koopTranslation->language()->associate($language);
        }

        if (isset($xml['Prijsvoorvoegsel'])) $koopTranslation->prijs_voorvoegsel = $xml['Prijsvoorvoegsel'];
        if (isset($xml['Koopprijs'])) $koopTranslation->koop_prijs = $xml['Koopprijs'];
        if (isset($xml['KoopConditie'])) $koopTranslation->koop_conditie = $xml['KoopConditie'];
        if (isset($xml['KoopSpecificatie'])) $koopTranslation->koop_specificatie = $xml['KoopSpecificatie'];
        if (isset($xml['WOZ'])) {
            $woz = $xml['WOZ'];
            if (isset($woz['WOZWaarde'])) $koopTranslation->woz_waarde = $woz['WOZWaarde'];
            if (isset($woz['WOZWaardePeildatum'])) $koopTranslation->woz_waarde_peildatum = Carbon::createFromFormat('Y-m-d', $woz['WOZWaardePeildatum']);
        }
        $koopTranslation->save();

        return $koop;
    }

    private function createHuurFromXml(array $xml, ObjectDetails $objectDetails, Language $language): Huur
    {
        $huur = $objectDetails->huur()->first();
        if(!$huur) {
            $huur = new Huur();
            $huur->objectDetails()->associate($objectDetails);
            $huur->save();
        }

        $huurTranslation = $huur->translations()->where('language_id', '=', $language->id)->first();
        if(!$huurTranslation) {
            $huurTranslation = new HuurTranslation();
            $huurTranslation->translatable()->associate($huur);
            $huurTranslation->language()->associate($language);
        }

        if (isset($xml['Huurprijs'])) $huurTranslation->huur_prijs = $xml['Huurprijs'];
        if (isset($xml['HuurConditie'])) $huurTranslation->huur_conditie = $xml['HuurConditie'];
        if (isset($xml['HuurSpecificatie'])) $huurTranslation->huur_specificatie = $xml['HuurSpecificatie'];
        $huurTranslation->save();

        return $huur;
    }

    /**
     * Parses the XML into an PHP array that we can use for further processing
     *
     * @return array
     */
    public function DownloadedXMLToArray():array
    {
        $storageFolder = $this->createAndGetRealworksStorageFolder(self::$DOWNLOAD_FOLDER_NAME);
        $exampleFileName = self::$XML_FILE_NAME.'.xml';
        $realFileName = $this->getTodaysXMLFileName();
        $fileName = (file_exists($storageFolder.DIRECTORY_SEPARATOR.$realFileName)) ? $realFileName : $exampleFileName;

        $xmlFile = file_get_contents($storageFolder.DIRECTORY_SEPARATOR.$fileName);
        $xml = simplexml_load_string($xmlFile, "SimpleXMLElement", LIBXML_NOCDATA);
        $json = json_encode($xml);
        $array = json_decode($json,true);

//        Log::debug($array);

        return $array;
    }

    private function getTodaysXMLFileName():string
    {
        return 'WONEN_'.date('Y').date('m').date('d').'.xml';
    }

    /**
     * Downloads all media from the given xml and passes it into a public folder
     * @param array $xml
     */
    private function DownloadMediaFromXMLToPublicFolders(array $xml)
    {
        $this->createAndGetPublicRealworksFolder();

        if(!array_key_exists('Object', $xml)) return; //Empty

        foreach($xml['Object'] as $objectArray)
        {
            $this->downloadMediaListIfNotAlreadyFromObjectAndReturnMediaArray($objectArray);
        }
    }

    /**
     * @param string $objectSystemId
     * @param string $place
     * @param string $street
     * @param string $houseNumber
     * @return string
     */
    public function getPublicFolderPathForObject(string $objectSystemId, string $place, string $street, string $houseNumber)
    {

        $folder = \Str::slug( $place.'-'.$street.'-'.$houseNumber.'-'.$objectSystemId);
        $rootFolder = $this->createAndGetPublicRealworksFolder($folder);
        return $rootFolder;
    }

    /**
     * Downloads every file from the MediaLijst array inside the given object and returns the MediaLijst array after that
     *
     * @param array $object
     * @return array|mixed
     */
    private function downloadMediaListIfNotAlreadyFromObjectAndReturnMediaArray(array $object)
    {
        $mediaReturnArray = [];

        if(isset($object['MediaLijst']) && isset($object['ObjectSystemID']) && isset($object['ObjectDetails']))
        {
            //Validation
            if(!is_array($object['MediaLijst'])) $this->mailOnError('At least one MediaLijst entry in the Realworks xml wasn\'t an array. Needs direct fix');
            if(!is_array($object['ObjectDetails'])) $this->mailOnError('At least one ObjectDetails entry in the Realworks xml wasn\'t an array. Needs direct fix');
            if(!is_string($object['ObjectSystemID'])) $this->mailOnError('At least one ObjectSystemID entry in the Realworks xml wasn\'t a string. Needs direct fix');

            if(!isset($object['ObjectDetails']['Adres']) || !isset($object['ObjectDetails']['Adres']['Nederlands']))
                $this->mailOnError('At least one ObjectDetails entry in the Realworks xml wasn\'t properly formatted. Expecting keys ObjectDetails > Adres > Nederlands. Needs direct fix');

            $addressArray = $object['ObjectDetails']['Adres']['Nederlands'];
            if(!is_array($addressArray)) $this->mailOnError('At least one ObjectDetails > Adres > Nederlands entry in the Realworks xml wasn\'t an array. Needs direct fix');

//            dd(array_keys($addressArray));
            if(count(array_diff(['Straatnaam', 'Huisnummer', 'Postcode', 'Woonplaats', 'Land'], array_keys($addressArray))) > 0)
                $this->mailOnError("At least one ObjectDetails > Adres > Nederlands entry in the Realworks xml did not contain all keys: 'Straatnaam', 'Huisnummer', 'Postcode', 'Woonplaats', 'Land'. Needs direct fix ".json_encode($addressArray));

//            dd($object);
//            dd(array_keys($object));
//            dd(array_values($object));

            foreach($object['MediaLijst'] as $index => $mediaArrays) {
                foreach($mediaArrays as $mediaArray)
                {
                    if(!isset($mediaArray['Groep'])) $this->mailOnError("At least one MediaLijst item did not have 'Groep' set. ".json_encode($mediaArray));

                    $propertyFolder = $this->getPublicFolderPathForObject($object['ObjectSystemID'], $addressArray['Woonplaats'], $addressArray['Straatnaam'], $addressArray['Huisnummer']);
                    $groupFolder = self::createFolderIfNotExitsOrFail($propertyFolder.DIRECTORY_SEPARATOR.$mediaArray['Groep']);

                    $url = trim($mediaArray['URL']);
                    $filenameWithExtension = self::getFilenameFromUrl($url);
                    $filePath = $groupFolder.DIRECTORY_SEPARATOR.$index.'_'.$filenameWithExtension;

                    //Check if the media was updated yesterday. If so we also need to refresh the image
                    $mediaUpdate = Carbon::createFromFormat('Y-m-d', $mediaArray['MediaUpdate']);
                    $now = Carbon::now();
                    $now = Carbon::create($now->year, $now->month, $now->day);
                    $needsUpdate = ($mediaUpdate && $now->subDay()->equalTo($mediaUpdate)) ? true: false;
                    if($needsUpdate) Log::debug('Going to download the following media item again since it was was updated yesterday: '.$url);

                    if(!file_exists($filePath) || $needsUpdate) {
                        try {
                            $file = file_get_contents($url);
                        } catch(\Exception $exception) {
                            Log::debug('Tried to download a media file defined in the realworks xml but failed ('.$exception->getMessage().'): '.$url);
                            continue;
                        }
                        if(file_put_contents($filePath, $file) === false) $this->mailOnError('Could not store downloaded media file to path: '.$filePath);
                    }
                }
            }

            return $object['MediaLijst'];
        }
        return [];
    }

    private function getUrl()
    {
        $base = config('realworks.api_url');

        $queryParameters = [
            'koppeling' => 'WEBSITE',
            'user' => config('realworks.api_username'),
            'password' => config('realworks.api_password'),
            'og' => config('realworks.api_og_value'),
        ];

        if(config('realworks.api_documentation') == "true") $queryParameters['documentatie'] = "true";
        if(config('realworks.api_connected') == "true") $queryParameters['connected'] = "true";
        if(config('realworks.api_version')) $queryParameters['versie'] = config('realworks.api_version');
        if(config('realworks.api_office')) $queryParameters['kantoor'] = config('realworks.api_office');

        //String "true" or "false" from config is changed into a real true or false. Putting that in the http_build_query causes it to change to 0 or 1. While realworks expects "true" or "false" for boolean
        foreach($queryParameters as $index => $parameter)
        {
            if($parameter === true) $queryParameters[$index] = "true";
            else if($parameter === false) $queryParameters[$index] = "false";
        }

        $url = $base.'?'.http_build_query($queryParameters);

        return $url;
    }

    /**
     * @param string $url
     * @return mixed
     */
    public static function getFilenameFromUrl(string $url)
    {
        $path = parse_url($url, PHP_URL_PATH);
        $exploded = explode('/', $path);
        return last($exploded);
    }

    /**
     * Creates a folder for the realworks zip and it's contents in laravels storage folder if needed and returns its absolute path
     *
     * @param string|null $subFolder
     * @return string
     */
    private static function createAndGetRealworksStorageFolder(string $subFolder = null):string
    {
        $folder = storage_path().DIRECTORY_SEPARATOR.config('realworks.storagefolder_name', 'realworks').DIRECTORY_SEPARATOR;
        self::createFolderIfNotExitsOrFail($folder);

        if($subFolder)
        {
            $absoluteSubFolder = self::createFolderIfNotExitsOrFail($folder.$subFolder);
            return $absoluteSubFolder;
        }

        return $folder;
    }

    /**
     * Creates a public folder for the frontend
     *
     * @param string|null $subFolder
     * @return string
     */
    private static function createAndGetPublicRealworksFolder(string $subFolder = null):string
    {
        $folder = public_path().DIRECTORY_SEPARATOR.config('realworks.storagefolder_name', 'realworks').DIRECTORY_SEPARATOR;
        self::createFolderIfNotExitsOrFail($folder);

        if($subFolder)
        {
            $absoluteSubFolder = $folder.$subFolder.DIRECTORY_SEPARATOR;
            self::createFolderIfNotExitsOrFail($absoluteSubFolder);
            return $absoluteSubFolder;
        }

        return $folder;
    }

    /**
     * @param $folder
     * @return mixed
     */
    private static function createFolderIfNotExitsOrFail($folder)
    {
        if(!file_exists($folder)) {
            if(!mkdir($folder, 0777, true)) throw new \RuntimeException('The realworks folder could not be created. '.$folder);
        }

        return $folder;
    }

    /**
     * Recursively deletes a the contents in a directory and if you specify the boolean argument to true the directory itself too
     *
     * @param $dir
     * @param bool $deleteDir
     */
    private static function rrmdir($dir, $deleteDir = true) {
        if (is_dir($dir)) {
            $objects = scandir($dir);
            foreach ($objects as $object) {
                if ($object != "." && $object != "..") {
                    if (filetype($dir.DIRECTORY_SEPARATOR.$object) == "dir")
                        self::rrmdir($dir.DIRECTORY_SEPARATOR.$object);
                    else {
                        if(!unlink($dir.DIRECTORY_SEPARATOR.$object)) throw new \RuntimeException('Could not empty the temporary folder to store the exported results in. Please contact your website builder '.$dir."/".$object);
                    }
                }
            }
            reset($objects);
            if($deleteDir) rmdir($dir);
        }
    }

    /**
     * @param string $message
     */
    private function mailOrShowErrorAndDie(string $message)
    {
        Log::error($message);

        $mail = (new RealworksFailMail( $message));
        if(!\App::environment('local')) {
            \Mail::send($mail);
//            die();
        } else {
//            echo 'ERROR: '.$message.PHP_EOL;
//            die();

        }
    }

    /**
     * @param string $message
     */
    private function mailOnError(string $message)
    {
        Log::error($message);

        $mail = (new RealworksFailMail( $message));
        if(!\App::environment('local')) {
            \Mail::send($mail);
        }

    }

    private function deletePublicFoldersFromNonExistingObjects()
    {
        //Get all realworks data property / object folders in the wwwroot
        $publicRealworksFolder = $this->createAndGetPublicRealworksFolder();
        $filesAndDirs = scandir($publicRealworksFolder);
        $dirs = array_filter($filesAndDirs, function($item) use ($publicRealworksFolder) {
            $absolute = $publicRealworksFolder.$item;
            return is_dir($absolute) && $item != '.' && $item != '..';
        });

        //Get the system ids from the property / object folders in the wwwroot
        $objectSystemIdsFromStoredFiles = array_map(function($dir) {
            return last(explode('-', $dir));
        }, $dirs);

        //Get all system ids from objects in the database
        $systemIdsPresentInDatabase = RealworkObject::get(['id', 'system_id'])->map(function(RealworkObject $object) {
            return $object->system_id;
        });

        //Create al list of system ids which have a folder in wwwroot property / object folder but don't have a database entry
        $systemIdsToRemoveFromPublicFolder = array_filter($objectSystemIdsFromStoredFiles, function($systemId) use($systemIdsPresentInDatabase) {
            if($systemIdsPresentInDatabase->contains($systemId)) {
                return false;
            } else {
                return true;
            }
        });

        //Delete the wwwroot property / object folders which don't have a databse entry
        foreach($systemIdsToRemoveFromPublicFolder as $systemIdToRemoveFromPublicFolder)
        {
            foreach($dirs as $dir)
            {
                $systemIdFromFolder = last(explode('-', $dir));
                if($systemIdToRemoveFromPublicFolder == $systemIdFromFolder) {
                    $folderToDelete = $absolute = $publicRealworksFolder.$dir;
//                    echo $folderToDelete.PHP_EOL;
                    self::rrmdir($folderToDelete, true);
                }
            }
        }
    }

    /**
     * Delete al Objects that are not in the xml anymore
     *
     * @param array $xml
     */
    private function deleteObjectsFromDatabaseThatArentListedInXML(array $xml)
    {
        $this->createAndGetPublicRealworksFolder();

        if(!array_key_exists('Object', $xml)) return; //Empty

        $allObjects = RealworkObject::all(['id', 'system_id']);

        $xmlSystemIds = [];
        foreach($xml['Object'] as $objectArray)
        {
            if(!isset($objectArray['ObjectSystemID'])) $this->mailOrShowErrorAndDie('An object in the XML did not have a required ObjectSystemID. Stopped processing.');
            $xmlSystemIds[] = $objectArray['ObjectSystemID'];
        }

        $idsToDelete = $allObjects->map(function(RealworkObject $object) use ($xmlSystemIds) {
            if(!in_array((string) $object->system_id, $xmlSystemIds, true)) return $object->id;
            return null;
        })->filter(function($systemId) { return $systemId !== null; })->toArray();

        RealworkObject::destroy($idsToDelete);
    }
}