/home/ivoiecob/email.hirewise-va.com/dev/migrate-contacts-to-dav-cards.php
<?php
/**
 * This code is licensed under AGPLv3 license or Afterlogic Software License
 * if commercial version of the product was purchased.
 * For full statements of the licenses see LICENSE-AFTERLOGIC and LICENSE-AGPL3 files.
 */

namespace Aurora\Modules\Calendar;

use Afterlogic\DAV\Constants;
use Aurora\Modules\Contacts\Classes\Contact;
use Aurora\Modules\Contacts\Enums\StorageType;
use Aurora\Modules\Contacts\Models\ContactCard;
use Aurora\Modules\Core\Module as CoreModule;
use Aurora\Modules\Contacts\Module as ContactsModule;
use Aurora\System\Api;
use Illuminate\Database\Capsule\Manager as Capsule;

if (PHP_SAPI !== 'cli') {
    exit("Use the console for running this script");
}

require_once \dirname(__file__) . "/../system/autoload.php";
Api::Init(true);

class MigrationContact extends Contact
{
    public function getExtendedProp($prop)
    {
        $result = null;

        if (isset($this->Properties[$prop])) {
            $result = $this->Properties[$prop];
        }

        return $result;
    }

    public function setExtendedProp($prop, $value)
    {
        $this->Properties[$prop] = $value;
    }

    public function populate($Contact)
    {
        parent::populate($Contact);
        if (is_string($this->Properties)) {
            $this->Properties = \json_decode($this->Properties, true);
        }
        $uid = $this->getExtendedProp('DavContacts::VCardUID');
        if (!isset($uid)) {
            $this->setExtendedProp('DavContacts::VCardUID', $this->UUID);
        }
    }
}

class Helper
{
    public static $InitialisedUserId = null;
    public static $Principal = null;

    public static function getStorage($sStorage)
    {
        $sResult = Constants::ADDRESSBOOK_DEFAULT_NAME;
        if ($sStorage === StorageType::Personal) {
            $sResult = Constants::ADDRESSBOOK_DEFAULT_NAME;
        } elseif ($sStorage === StorageType::Shared) {
            $sResult = Constants::ADDRESSBOOK_SHARED_WITH_ALL_NAME;
        } elseif ($sStorage === StorageType::Collected) {
            $sResult = Constants::ADDRESSBOOK_COLLECTED_NAME;
        } elseif ($sStorage === StorageType::Team) {
            $sResult = 'gab';
        }

        return $sResult;
    }

    /**
    * @param int $iUserId
    */
    public static function init($iUserId)
    {
        $bInitialized = false;
        if (self::$InitialisedUserId !== $iUserId) {
            $oUser = \Aurora\Api::getUserById($iUserId);
            self::$InitialisedUserId = $iUserId;
            if ($oUser) {
                $aPrincipalProperties = \Afterlogic\DAV\Backend::Principal()->getPrincipalByPath(Constants::PRINCIPALS_PREFIX . $oUser->PublicId);
                if ($aPrincipalProperties) {
                    if (isset($aPrincipalProperties['uri'])) {
                        self::$Principal = $aPrincipalProperties['uri'];
                    }
                }
                $bInitialized = true;
            }
        } else {
            $bInitialized = true;
        }

        return $bInitialized;
    }

    public static function getAddressBook($iUserId, $sName)
    {
        $oAddressBook = false;
        if (self::init($iUserId)) {
            $oUserAddressBooks = new \Afterlogic\DAV\CardDAV\AddressBookRoot(
                \Afterlogic\DAV\Backend::Carddav(),
                self::$Principal
            );

            if ($oUserAddressBooks->childExists($sName)) {
                $oAddressBook = $oUserAddressBooks->getChild($sName);
            }
        }

        return $oAddressBook;
    }

    public static function getDBAddressBook($UserId, $AddressBookId)
    {
        return Capsule::connection()->table('contacts_addressbooks')
            ->where('Id', $AddressBookId)
            ->where('UserId', $UserId)
            ->first();
    }

    public static function getCard($oAddressBook, $sId)
    {
        $bResult = false;
        if ($oAddressBook) {
            if ($oAddressBook->childExists($sId)) {
                $bResult = $oAddressBook->getChild($sId);
            }
        }
        return $bResult;
    }

    public static function getDavContactById($iUserId, $sAddressBookName, $mContactId)
    {
        $oContact = false;
        if (self::init($iUserId)) {
            $oAddressBook = self::getAddressBook($iUserId, $sAddressBookName);
            $oContactItem = self::getCard($oAddressBook, $mContactId . '.vcf');

            if ($oContactItem) {
                $sVCardData = $oContactItem->get();
                if ($sVCardData) {
                    $oContact = new MigrationContact();
                    $oContact->InitFromVCardStr($iUserId, $sVCardData);
                    $oContact->ETag = trim($oContactItem->getETag(), '"');
                }
            }

            return $oContact;
        }
    }

    public static function updateContactCardProperties($oCard, $oContact)
    {
        $result = false;

        $cardData = \Closure::bind(
            function ($oCard) {
                return $oCard->cardData;
            },
            null,
            \Sabre\CardDAV\Card::class
        )($oCard);
        if ($cardData) {
            $properties = null;
            if ($oContact && isset($oContact->Properties)) {
                $properties = $oContact->Properties;
                if (isset($properties['DavContacts::UID'])) {
                    unset($properties['DavContacts::UID']);
                }
                if (isset($properties['DavContacts::VCardUID'])) {
                    unset($properties['DavContacts::VCardUID']);
                }
            }

            if (isset($properties) && is_array($properties) && count($properties) > 0) {
                $result = !!ContactCard::where('CardId', $cardData['id'])->update(['Properties' => \json_encode($properties)]);
            }
        }

        return $result;
    }

    public static function updateDavContact($oContact)
    {
        $bResult = false;

        $oAddressBook = null;
        if ($oContact->Storage === StorageType::Personal) {
            $oAddressBook = self::getAddressBook($oContact->IdUser, Constants::ADDRESSBOOK_DEFAULT_NAME);
        } elseif ($oContact->Storage === StorageType::Shared) {
            $oAddressBook = self::getAddressBook($oContact->IdUser, Constants::ADDRESSBOOK_SHARED_WITH_ALL_NAME);
        } elseif ($oContact->Storage === StorageType::Team) {
            $bResult = true;
        } elseif ($oContact->Storage === StorageType::AddressBook) {

            $oAddressBook = self::getDBAddressBook($oContact->IdUser, $oContact->IdUser);

            if ($oAddressBook) {
                $oAddressBook = self::getAddressBook($oContact->IdUser, $oAddressBook->UUID);
            }
        }

        $sDavContactsUID = $oContact->UUID;
        if ($sDavContactsUID) {
            $oContactItem = $oAddressBook ? self::getCard($oAddressBook, $sDavContactsUID . '.vcf') : null;

            if ($oContactItem) {
                $sData = $oContactItem->get();

                $oVCard = \Sabre\VObject\Reader::read($sData);
                if ($oVCard) {
                    \Aurora\Modules\Contacts\Classes\VCard\Helper::UpdateVCardFromContact($oContact, $oVCard);
                    $oContactItem->put($oVCard->serialize());

                    self::updateContactCardProperties($oContactItem, $oContact);

                    $bResult = true;
                }
                unset($oVCard);
            }
        }

        return $bResult;
    }

    public static function createDavContact($oContact)
    {
        $bResult = false;
        if ($oContact) {
            self::init($oContact->IdUser);
            $oAddressBook = null;
            if ($oContact->Storage === StorageType::Personal) {
                if (!$oContact->Auto) {
                    $oAddressBook = self::getAddressBook($oContact->IdUser, Constants::ADDRESSBOOK_DEFAULT_NAME);
                } else {
                    $oAddressBook = self::getAddressBook($oContact->IdUser, Constants::ADDRESSBOOK_COLLECTED_NAME);
                }
            } elseif ($oContact->Storage === StorageType::Shared) {
                $oAddressBook = self::getAddressBook($oContact->IdUser, Constants::ADDRESSBOOK_SHARED_WITH_ALL_NAME);
            } elseif ($oContact->Storage === StorageType::AddressBook) {

                $oAddressBook = self::getDBAddressBook($oContact->IdUser, $oContact->AddressBookId);

                if ($oAddressBook) {
                    $oAddressBook = self::getAddressBook($oContact->IdUser, $oAddressBook->UUID);
                }
            }
            if ($oAddressBook) {
                $sDavContactsUID = $oContact->UUID . '.vcf';

                $oVCard = new \Sabre\VObject\Component\VCard();
                \Aurora\Modules\Contacts\Classes\VCard\Helper::UpdateVCardFromContact($oContact, $oVCard);
                $oAddressBook->createFile($sDavContactsUID, $oVCard->serialize());

                $oContactItem = self::getCard($oAddressBook, $sDavContactsUID);
                if ($oContactItem) {
                    self::updateContactCardProperties($oContactItem, $oContact);
                }

                $bResult = true;
            }
        }

        return $bResult;
    }

    public static function getContactsIdsFromUUIDs($UserId, $UUIDs)
    {
        $Uris = array_map(function ($item) {
            return $item . '.vcf';
        }, $UUIDs);

        $contactsIds = Capsule::connection()->table('adav_cards')
            ->join('adav_addressbooks', 'adav_cards.addressbookid', '=', 'adav_addressbooks.id')
            ->select('adav_cards.id as card_id')
            ->where('principaluri', Constants::PRINCIPALS_PREFIX . Api::getUserPublicIdById($UserId))
            ->whereIn('adav_cards.uri', $Uris)->get()->all();

        return array_map(function ($item) {
            return $item->card_id;
        }, $contactsIds);
    }

    public static function log($message)
    {
        Api::Log($message, \Aurora\System\Enums\LogLevel::Error, 'contacts-migration-');
    }
}

function UpdateContact($aContact)
{
    Helper::log('Process contact: ' . $aContact['UUID']);
    if (is_array($aContact)) {

        $Contact = new MigrationContact();
        $Contact->populate($aContact);

        $UserId = $Contact->IdUser;
        $sContactStorage = $Contact->Storage;
        if ($sContactStorage === StorageType::Personal && isset($Contact->Auto) && $Contact->Auto === true) {
            $sContactStorage = StorageType::Collected;
        }
        $sStorage = Helper::getStorage($sContactStorage);

        $aStorageParts = \explode('-', $sContactStorage);
        if (isset($aStorageParts[0]) && $aStorageParts[0] === StorageType::AddressBook) {
            $oAddressBook = Helper::getDBAddressBook($Contact->IdUser, $Contact->IdUser);
            if ($oAddressBook) {
                $sStorage =  $oAddressBook->UUID;
            } else {
                $sUserPrincipalUri = Constants::PRINCIPALS_PREFIX . Api::getUserPublicIdById($UserId);
                $dBPrefix = Api::GetSettings()->DBPrefix;
                $stmt = Api::GetPDO()->prepare("select sa.* from " . $dBPrefix . "adav_shared_addressbooks sa 
                left join " . $dBPrefix . "adav_addressbooks da on sa.addressbook_id = da.id 
                right join " . $dBPrefix . "contacts_addressbooks ca on da.uri = ca.UUID where ca.Id = ? and sa.principaluri = ?");
                $stmt->execute([$Contact->AddressBookId, $sUserPrincipalUri]);
                $res = $stmt->fetchAll(\PDO::FETCH_ASSOC);
                if (is_array($res) && count($res) > 0) {
                    $sStorage = $res[0]['addressbookuri'];
                }
            }
        }
        $oDavContact = Helper::getDavContactById(
            $UserId,
            $sStorage,
            $Contact->UUID
        );

        if ($oDavContact) {
            Helper::log('Contact is already exists: ' . $Contact->UUID);
            // $aStorageParts = \explode('-', $Contact->Storage);
            // if (isset($aStorageParts[0]) && $aStorageParts[0] === StorageType::AddressBook) {
            //     $Contact->Storage = StorageType::AddressBook;
            // }
            // if (Helper::updateDavContact($Contact)) {
            //     Helper::log('Contact updated successfully: ' . $Contact->UUID);
            // }
        } else {
            if (Helper::createDavContact($Contact)) {
                Helper::log('Contact created successfully: ' . $Contact->UUID);
            }
        }
    }
}

$usersCount = -1;

$offset = 0;
$limit = 100;
while ($offset < $usersCount || $usersCount === -1) {

    // getting users list
    $result = CoreModule::Decorator()->GetUsers(0, $offset, $limit);

    if ($usersCount < 0) {
        Helper::log('Users count: ' . $result['Count']);
    }

    $usersCount = $result['Count'];
    $offset = $offset + $limit - 1;

    foreach ($result['Items'] as $user) {
        Helper::log('');
        Helper::log('===> Process user: ' . $user['Id']);
        // getting user contacts
        $contactsQuery = Capsule::connection()->table('contacts')->where('IdUser', $user['Id']);
        $contactsCount = $contactsQuery->count();
        Helper::log('Contacts count: ' . $contactsCount);

        $contactsOffset = 0;

        while ($contactsOffset < $contactsCount) {
            $contacts = $contactsQuery->offset($contactsOffset)->limit($limit)->get();

            foreach ($contacts as $contact) {
                $contact = json_decode(json_encode($contact), true);
                // update contact
                UpdateContact($contact);
            }
            $contactsOffset = $contactsOffset + $limit - 1;
        }

        // getting groups
        $groups = Capsule::connection()->table('contacts_groups')
            ->where('IdUser', $user['Id'])
            ->get();

        Helper::log('Groups count: ' . count($groups));

        foreach ($groups as $group) {
            $group = json_decode(json_encode($group), true);
            Helper::log('Process group: ' . $group['UUID']);
            $groupIds = Helper::getContactsIdsFromUUIDs($user['Id'], [$group['UUID']]);
            $DavGroup = false;
            if (isset($groupIds[0])) {
                $DavGroup = ContactsModule::Decorator()->GetGroup($user['Id'], $groupIds[0]);
            }
            // if the group does not exist
            if (!$DavGroup) {
                $groupContacts = Capsule::connection()->table('contacts_group_contact')
                    ->leftJoin('contacts', 'contacts.Id', '=', 'contacts_group_contact.ContactId')
                    ->where('contacts.IdUser', $user['Id'])
                    ->where('contacts_group_contact.GroupId', $group['Id'])
                    ->select('contacts.UUID as DavUid')
                    ->pluck('DavUid')->toArray();

                Helper::log('Group contacts count: ' . count($groupContacts));
                $group['Contacts'] = Helper::getContactsIdsFromUUIDs($user['Id'], $groupContacts);

                // creating the group
                if (!!ContactsModule::Decorator()->CreateGroup($group, $user['Id'])) {
                    Helper::log('Group created successfully: ' . $group['UUID']);
                } else {
                    Helper::log('An error occurred while creating the group: ' . $group['UUID']);
                }
            } else {
                Helper::log('Group already exists: ' . $group['UUID']);
            }
        }
    }
}