<commit_before><?php
/*********************************************************************************
 * SugarCRM Community Edition is a customer relationship management program developed by
 * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
 *
 * SuiteCRM is an extension to SugarCRM Community Edition developed by Salesagility Ltd.
 * Copyright (C) 2011 - 2016 Salesagility Ltd.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by the
 * Free Software Foundation with the addition of the following permission added
 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along with
 * this program; if not, see http://www.gnu.org/licenses or write to the Free
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA.
 *
 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
 * SW2-130, Cupertino, CA 95014, USA. or at email address <EMAIL>.
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
 * these Appropriate Legal Notices must retain the display of the "Powered by
 * SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not
 * reasonably feasible for  technical reasons, the Appropriate Legal Notices must
 * display the words  "Powered by SugarCRM" and "Supercharged by SuiteCRM".
 ********************************************************************************/

/**
 * Reminder class
 *
 */
class Reminder extends Basic
{

    const UPGRADE_VERSION = '7.4.3';

    var $name;

    var $new_schema = true;
    var $module_dir = 'Reminders';
    var $object_name = 'Reminder';
    var $table_name = 'reminders';
    var $tracker_visibility = false;
    var $importable = false;
    var $disable_row_level_security = true;

    var $popup;
    var $email;
    var $email_sent = false;
    var $timer_popup;
    var $timer_email;
    var $related_event_module;
    var $related_event_module_id;

    private static $remindersData = array();

    public function __construct()
    {
        parent::Basic();
    }

    public function bean_implements($interface)
    {
        switch ($interface) {
            case 'ACL':
                return true;
        }
        return false;
    }

    // ---- save and load remainders on EditViews

    /**
     * Save multiple reminders data from clients Meetings/Calls EditView.
     * Call this static function in save action.
     *
     * @param string $eventModule Event Bean module name (e.g. Meetings, Calls)
     * @param string $eventModuleId Event Bean GUID
     * @param string $remindersDataJson Remainders data as Json string from POST data.
     * @throws Exception throw an Exception if json format is invalid.
     */
    public static function saveRemindersDataJson($eventModule, $eventModuleId, $remindersDataJson)
    {
        $reminderData = json_decode($remindersDataJson);
        if (!json_last_error()) {
            Reminder::saveRemindersData($eventModule, $eventModuleId, $reminderData);
        } else {
            throw new Exception(json_last_error_msg());
        }
    }

    private static function saveRemindersData($eventModule, $eventModuleId, $remindersData)
    {
        $savedReminderIds = array();
        foreach ($remindersData as $reminderData) {
            if (isset($_POST['isDuplicate']) && $_POST['isDuplicate']) $reminderData->id = '';
            $reminderBean = BeanFactory::getBean('Reminders', $reminderData->id);
            $reminderBean->popup = $reminderData->popup;
            $reminderBean->email = $reminderData->email;
            $reminderBean->timer_popup = $reminderData->timer_popup;
            $reminderBean->timer_email = $reminderData->timer_email;
            $reminderBean->related_event_module = $eventModule;
            $reminderBean->related_event_module_id = $eventModuleId;
            $reminderBean->save();
            $savedReminderIds[] = $reminderBean->id;
            $reminderId = $reminderBean->id;
            Reminder_Invitee::saveRemindersInviteesData($reminderId, $reminderData->invitees);
        }
        $reminders = BeanFactory::getBean('Reminders')->get_full_list("", "reminders.related_event_module = '$eventModule' AND reminders.related_event_module_id = '$eventModuleId'");
        if ($reminders) {
            foreach ($reminders as $reminder) {
                if (!in_array($reminder->id, $savedReminderIds)) {
                    Reminder_Invitee::deleteRemindersInviteesMultiple($reminder->id);
                    $reminder->mark_deleted($reminder->id);
                    $reminder->save();
                }
            }
        }
        unset(self::$remindersData[$eventModule][$eventModuleId]);
    }

    /**
     * Load multiple reminders JSON data for related Event module EditViews.
     * Call this function in module display function.
     *
     * @param string $eventModule Related event module name (Meetings/Calls)
     * @param string $eventModuleId Related event GUID
     * @return string JSON string contains the remainders
     * @throws Exception
     */
    public static function loadRemindersDataJson($eventModule, $eventModuleId, $isDuplicate = false)
    {
        $remindersData = self::loadRemindersData($eventModule, $eventModuleId, $isDuplicate);
        $remindersDataJson = json_encode($remindersData);
        if (!$remindersDataJson && json_last_error()) {
            throw new Exception(json_last_error_msg());
        }
        return $remindersDataJson;
    }

    /**
     * Load multiple reminders data for related Event module EditViews.
     * Call this function in module display function.
     *
     * @param string $eventModule Related event module name (Meetings/Calls)
     * @param string $eventModuleId Related event GUID
     * @return array contains the remainders
     * @throws Exception
     */
    public static function loadRemindersData($eventModule, $eventModuleId, $isDuplicate = false)
    {
        if (!isset(self::$remindersData[$eventModule][$eventModuleId]) || !$eventModuleId || $isDuplicate) {
            $ret = array();
            $reminders = BeanFactory::getBean('Reminders')->get_full_list("reminders.date_entered", "reminders.related_event_module = '$eventModule' AND reminders.related_event_module_id = '$eventModuleId'");
            if ($reminders) {
                foreach ($reminders as $reminder) {
                    $ret[] = array(
                        'id' => $isDuplicate ? null : $reminder->id,
                        'popup' => $reminder->popup,
                        'email' => $reminder->email,
                        'timer_popup' => $reminder->timer_popup,
                        'timer_email' => $reminder->timer_email,
                        'invitees' => Reminder_Invitee::loadRemindersInviteesData($reminder->id, $isDuplicate),
                    );
                }
            }
            self::$remindersData[$eventModule][$eventModuleId] = $ret;
        }
        return self::$remindersData[$eventModule][$eventModuleId];
    }

    // ---- sending email reminders

    /**
     * Sending multiple email reminders.
     * Call in EmainReminder and use original EmailRemainder class for sending.
     *
     * @param EmailReminder $emailReminder Caller EmailReminder
     * @param Administration $admin Administration module for EmailRemainder->sendReminders() function
     * @param boolean $checkDecline (optional) Send email if user accept status is not decline. Default is TRUE.
     */
    public static function sendEmailReminders(EmailReminder $emailReminder, Administration $admin, $checkDecline = true)
    {
        if ($reminders = self::getUnsentEmailReminders()) {
            foreach ($reminders as $reminderId => $reminder) {
                $recipients = self::getEmailReminderInviteesRecipients($reminderId, $checkDecline);
                $eventBean = BeanFactory::getBean($reminder->related_event_module, $reminder->related_event_module_id);
                if ($eventBean && $emailReminder->sendReminders($eventBean, $admin, $recipients)) {
                    $reminder->email_sent = 1;
                    $reminder->save();
                }
            }
        }
    }

    private static function getEmailReminderInviteesRecipients($reminderId, $checkDecline = true)
    {
        $emails = array();
        $reminder = BeanFactory::getBean('Reminders', $reminderId);
        $eventModule = $reminder->related_event_module;
        $eventModuleId = $reminder->related_event_module_id;
        $event = BeanFactory::getBean($eventModule, $eventModuleId);
        if ($event && (!isset($event->status) || $event->status != 'Held')) {
            $invitees = BeanFactory::getBean('Reminders_Invitees')->get_full_list('', "reminders_invitees.reminder_id = '$reminderId'");
            foreach ($invitees as $invitee) {
                $inviteeModule = $invitee->related_invitee_module;
                $inviteeModuleId = $invitee->related_invitee_module_id;
                $personBean = BeanFactory::getBean($inviteeModule, $inviteeModuleId);
                // The original email reminders check the accept_status field in related users/leads/contacts etc. and filtered these users who not decline this event.
                if ($checkDecline && !self::isDecline($event, $personBean)) {
                    if (!empty($personBean->email1)) {
                        $arr = array(
                            'type' => $inviteeModule,
                            'name' => $personBean->full_name,
                            'email' => $personBean->email1,
                        );
                        $emails[] = $arr;
                    }
                }
            }
        }
        return $emails;
    }

    private static function getUnsentEmailReminders()
    {
        global $db;
        $reminders = array();
        $reminderBeans = BeanFactory::getBean('Reminders')->get_full_list('', "reminders.email = 1 AND reminders.email_sent = 0");
        if (!empty($reminderBeans)) {
            foreach ($reminderBeans as $reminderBean) {
                $eventBean = BeanFactory::getBean($reminderBean->related_event_module, $reminderBean->related_event_module_id);
                $dateStart = $eventBean->date_start;
                $time = strtotime($db->fromConvert($dateStart, 'datetime'));
                $dateStart = date(TimeDate::DB_DATETIME_FORMAT, $time);
                $remind_ts = $GLOBALS['timedate']->fromDb($db->fromConvert($dateStart, 'datetime'))->modify("-{$reminderBean->timer_email} seconds")->ts;
                $now_ts = $GLOBALS['timedate']->getNow()->ts;
                if ($now_ts >= $remind_ts) {
                    $reminders[$reminderBean->id] = $reminderBean;
                }
            }
        }
        return $reminders;
    }

    // ---- popup and alert reminders

    /**
     * Show a popup and/or desktop notification alert for related users with related Event information.
     * Call in jsAlerts class and use original jsAlerts for show notifications.
     *
     * @global ??? $current_user
     * @global ??? $timedate
     * @global ??? $app_list_strings
     * @global ??? $db
     * @global ??? $sugar_config
     * @global ??? $app_strings<commit_msg>Fixed #1279 Reminder emails still going out later than it should dependant on timezone<commit_after><?php
/*********************************************************************************
 * SugarCRM Community Edition is a customer relationship management program developed by
 * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
 *
 * SuiteCRM is an extension to SugarCRM Community Edition developed by Salesagility Ltd.
 * Copyright (C) 2011 - 2016 Salesagility Ltd.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by the
 * Free Software Foundation with the addition of the following permission added
 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along with
 * this program; if not, see http://www.gnu.org/licenses or write to the Free
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA.
 *
 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
 * SW2-130, Cupertino, CA 95014, USA. or at email address <EMAIL>.
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
 * these Appropriate Legal Notices must retain the display of the "Powered by
 * SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not
 * reasonably feasible for  technical reasons, the Appropriate Legal Notices must
 * display the words  "Powered by SugarCRM" and "Supercharged by SuiteCRM".
 ********************************************************************************/

/**
 * Reminder class
 *
 */
class Reminder extends Basic
{

    const UPGRADE_VERSION = '7.4.3';

    var $name;

    var $new_schema = true;
    var $module_dir = 'Reminders';
    var $object_name = 'Reminder';
    var $table_name = 'reminders';
    var $tracker_visibility = false;
    var $importable = false;
    var $disable_row_level_security = true;

    var $popup;
    var $email;
    var $email_sent = false;
    var $timer_popup;
    var $timer_email;
    var $related_event_module;
    var $related_event_module_id;

    private static $remindersData = array();

    public function __construct()
    {
        parent::Basic();
    }

    public function bean_implements($interface)
    {
        switch ($interface) {
            case 'ACL':
                return true;
        }
        return false;
    }

    // ---- save and load remainders on EditViews

    /**
     * Save multiple reminders data from clients Meetings/Calls EditView.
     * Call this static function in save action.
     *
     * @param string $eventModule Event Bean module name (e.g. Meetings, Calls)
     * @param string $eventModuleId Event Bean GUID
     * @param string $remindersDataJson Remainders data as Json string from POST data.
     * @throws Exception throw an Exception if json format is invalid.
     */
    public static function saveRemindersDataJson($eventModule, $eventModuleId, $remindersDataJson)
    {
        $reminderData = json_decode($remindersDataJson);
        if (!json_last_error()) {
            Reminder::saveRemindersData($eventModule, $eventModuleId, $reminderData);
        } else {
            throw new Exception(json_last_error_msg());
        }
    }

    private static function saveRemindersData($eventModule, $eventModuleId, $remindersData)
    {
        $savedReminderIds = array();
        foreach ($remindersData as $reminderData) {
            if (isset($_POST['isDuplicate']) && $_POST['isDuplicate']) $reminderData->id = '';
            $reminderBean = BeanFactory::getBean('Reminders', $reminderData->id);
            $reminderBean->popup = $reminderData->popup;
            $reminderBean->email = $reminderData->email;
            $reminderBean->timer_popup = $reminderData->timer_popup;
            $reminderBean->timer_email = $reminderData->timer_email;
            $reminderBean->related_event_module = $eventModule;
            $reminderBean->related_event_module_id = $eventModuleId;
            $reminderBean->save();
            $savedReminderIds[] = $reminderBean->id;
            $reminderId = $reminderBean->id;
            Reminder_Invitee::saveRemindersInviteesData($reminderId, $reminderData->invitees);
        }
        $reminders = BeanFactory::getBean('Reminders')->get_full_list("", "reminders.related_event_module = '$eventModule' AND reminders.related_event_module_id = '$eventModuleId'");
        if ($reminders) {
            foreach ($reminders as $reminder) {
                if (!in_array($reminder->id, $savedReminderIds)) {
                    Reminder_Invitee::deleteRemindersInviteesMultiple($reminder->id);
                    $reminder->mark_deleted($reminder->id);
                    $reminder->save();
                }
            }
        }
        unset(self::$remindersData[$eventModule][$eventModuleId]);
    }

    /**
     * Load multiple reminders JSON data for related Event module EditViews.
     * Call this function in module display function.
     *
     * @param string $eventModule Related event module name (Meetings/Calls)
     * @param string $eventModuleId Related event GUID
     * @return string JSON string contains the remainders
     * @throws Exception
     */
    public static function loadRemindersDataJson($eventModule, $eventModuleId, $isDuplicate = false)
    {
        $remindersData = self::loadRemindersData($eventModule, $eventModuleId, $isDuplicate);
        $remindersDataJson = json_encode($remindersData);
        if (!$remindersDataJson && json_last_error()) {
            throw new Exception(json_last_error_msg());
        }
        return $remindersDataJson;
    }

    /**
     * Load multiple reminders data for related Event module EditViews.
     * Call this function in module display function.
     *
     * @param string $eventModule Related event module name (Meetings/Calls)
     * @param string $eventModuleId Related event GUID
     * @return array contains the remainders
     * @throws Exception
     */
    public static function loadRemindersData($eventModule, $eventModuleId, $isDuplicate = false)
    {
        if (!isset(self::$remindersData[$eventModule][$eventModuleId]) || !$eventModuleId || $isDuplicate) {
            $ret = array();
            $reminders = BeanFactory::getBean('Reminders')->get_full_list("reminders.date_entered", "reminders.related_event_module = '$eventModule' AND reminders.related_event_module_id = '$eventModuleId'");
            if ($reminders) {
                foreach ($reminders as $reminder) {
                    $ret[] = array(
                        'id' => $isDuplicate ? null : $reminder->id,
                        'popup' => $reminder->popup,
                        'email' => $reminder->email,
                        'timer_popup' => $reminder->timer_popup,
                        'timer_email' => $reminder->timer_email,
                        'invitees' => Reminder_Invitee::loadRemindersInviteesData($reminder->id, $isDuplicate),
                    );
                }
            }
            self::$remindersData[$eventModule][$eventModuleId] = $ret;
        }
        return self::$remindersData[$eventModule][$eventModuleId];
    }

    // ---- sending email reminders

    /**
     * Sending multiple email reminders.
     * Call in EmainReminder and use original EmailRemainder class for sending.
     *
     * @param EmailReminder $emailReminder Caller EmailReminder
     * @param Administration $admin Administration module for EmailRemainder->sendReminders() function
     * @param boolean $checkDecline (optional) Send email if user accept status is not decline. Default is TRUE.
     */
    public static function sendEmailReminders(EmailReminder $emailReminder, Administration $admin, $checkDecline = true)
    {
        if ($reminders = self::getUnsentEmailReminders()) {
            foreach ($reminders as $reminderId => $reminder) {
                $recipients = self::getEmailReminderInviteesRecipients($reminderId, $checkDecline);
                $eventBean = BeanFactory::getBean($reminder->related_event_module, $reminder->related_event_module_id);
                if ($eventBean && $emailReminder->sendReminders($eventBean, $admin, $recipients)) {
                    $reminder->email_sent = 1;
                    $reminder->save();
                }
            }
        }
    }

    private static function getEmailReminderInviteesRecipients($reminderId, $checkDecline = true)
    {
        $emails = array();
        $reminder = BeanFactory::getBean('Reminders', $reminderId);
        $eventModule = $reminder->related_event_module;
        $eventModuleId = $reminder->related_event_module_id;
        $event = BeanFactory::getBean($eventModule, $eventModuleId);
        if ($event && (!isset($event->status) || $event->status != 'Held')) {
            $invitees = BeanFactory::getBean('Reminders_Invitees')->get_full_list('', "reminders_invitees.reminder_id = '$reminderId'");
            foreach ($invitees as $invitee) {
                $inviteeModule = $invitee->related_invitee_module;
                $inviteeModuleId = $invitee->related_invitee_module_id;
                $personBean = BeanFactory::getBean($inviteeModule, $inviteeModuleId);
                // The original email reminders check the accept_status field in related users/leads/contacts etc. and filtered these users who not decline this event.
                if ($checkDecline && !self::isDecline($event, $personBean)) {
                    if (!empty($personBean->email1)) {
                        $arr = array(
                            'type' => $inviteeModule,
                            'name' => $personBean->full_name,
                            'email' => $personBean->email1,
                        );
                        $emails[] = $arr;
                    }
                }
            }
        }
        return $emails;
    }

    private static function getUnsentEmailReminders()
    {
        global $timedate;
        $reminders = array();
        $reminderBeans = BeanFactory::getBean('Reminders')->get_full_list('', "reminders.email = 1 AND reminders.email_sent = 0");
        if (!empty($reminderBeans)) {
            foreach ($reminderBeans as $reminderBean) {
                $eventBean = BeanFactory::getBean($reminderBean->related_event_module, $reminderBean->related_event_module_id);
                $remind_ts = $timedate->fromUser($eventBean->date_start)->modify("-{$reminderBean->timer_email} seconds")->ts;
                $now_ts = $timedate->getNow()->ts;
                if ($now_ts >= $remind_ts) {
                    $reminders[$reminderBean->id] = $reminderBean;
                }
            }
        }
        return $reminders;
    }

    // ---- popup and alert reminders

    /**
     * Show a popup and/or desktop notification alert for related users with related Event information.
     * Call in jsAlerts class and use original jsAlerts for show notifications.
     *
     * @global ??? $current_user
     * @global ??? $timedate
     * @global ??? $app_list_strings
     * @global ??? $db
     * @global ??? $sugar_config
     * @global ??? $app_strings
{'fraction_non_alphanumeric': 0.1157548231176299, 'fraction_numerical': 0.004916871536314013, 'mean_word_length': 2.6686028737389176, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 141, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4015395', 'n_tokens_mistral': 6799, 'n_tokens_neox': 6625, 'n_words': 1775}