<?php

use idoit\Component\Logger;

/**
 * i-doit
 *
 * Notification module DAO
 *
 * @package     i-doit
 * @subpackage  Modules
 * @author      Benjamin Heisig <bheisig@i-doit.org>
 * @copyright   synetics GmbH
 * @license     http://www.i-doit.com/license
 * @since       0.9.9-9
 */
class isys_notifications_dao extends isys_module_dao
{
    /**
     * Notification status: deactivated
     */
    const C__STATUS__DEACTIVATED = 0;

    /**
     * Notification status: activated
     */
    const C__STATUS__ACTIVATED = 1;

    /**
     * Notification status: limit reached; no more notifications
     */
    const C__STATUS__LIMIT_REACHED = 2;

    /**
     * Notification status: incomplete notification; skip it
     */
    const C__STATUS__INCOMPLETE = 4;

    /**
     * Notification status: all together (only used for internal bitwise operations)
     */
    const C__STATUS__ALL = 7;

    /**
     * Notification domain: none
     */
    const C__DOMAIN__NONE = 0;

    /**
     * Notification domain: objects
     */
    const C__DOMAIN__OBJECTS = 1;

    /**
     * Notification domain: object types
     */
    const C__DOMAIN__OBJECT_TYPES = 2;

    /**
     * Notification domain: reports
     */
    const C__DOMAIN__REPORTS = 4;

    /**
     * Notification domain: all
     */
    const C__DOMAIN__ALL = 7;

    /**
     * Object mapping for contacts which are assigned by roles.
     *
     * @var  array
     */
    public static $m_receivers_roles_object_mapping = [];

    /**
     * Data cache.
     *
     * @var  array
     */
    protected $m_cache;

    /**
     * Domain types.
     *
     * @var  array
     */
    protected $m_domain_types;

    /**
     * Logger.
     *
     * @var  Logger
     */
    protected $m_log;

    /**
     * Data tables for properties.
     *
     * @var  array  Associative array of strings
     */
    protected $m_tables = [
        'notifications'          => 'isys_notification',
        'notification_domains'   => 'isys_notification_domain',
        'notification_types'     => 'isys_notification_type',
        'notification_roles'     => 'isys_notification_role',
        'notification_templates' => 'isys_notification_template',

        'units'        => 'isys_unit',
        'objects'      => 'isys_obj',
        'object_types' => 'isys_obj_type',
        'reports'      => 'isys_report',
        'contacts'     => 'isys_contact',
        'roles'        => 'isys_contact_tag'
    ];

    public const RECEIVER_CALC_STRATEGY__ONLY_PERSONS = 1;
    public const RECEIVER_CALC_STRATEGY__PERSONS_AND_GROUPS = 2;
    public const RECEIVER_CALC_STRATEGY__ONLY_GROUPS = 3;
    public const RECEIVER_CALC_STRATEGY__GROUPS_IF_AVAIL_OR_PERSONS = 4;

    public const NOTIFICATIONS        = 'notifications';
    public const NOTIFICATION_TYPES   = 'notification_types';
    public const NOTIFICATION_DOMAINS = 'notification_domains';
    public const NOTIFICATION_ROLES = 'notification_roles';
    public const NOTIFICATION_TEMPLATES = 'notification_templates';
    public const NOTIFICATION_UNITS = 'units';

    /**
     * @return array
     * @throws Exception
     */
    public static function getReceiverCalcStrategy()
    {
        $lang = isys_application::instance()->container->get('language');
        return [
            self::RECEIVER_CALC_STRATEGY__ONLY_PERSONS               => $lang->get('LC__NOTIFICATIONS__RECEIVER_CALC_STRATEGY__ONLY_PERSONS'),
            self::RECEIVER_CALC_STRATEGY__PERSONS_AND_GROUPS         => $lang->get('LC__NOTIFICATIONS__RECEIVER_CALC_STRATEGY__GROUPS_AND_PERSONS'),
            self::RECEIVER_CALC_STRATEGY__ONLY_GROUPS                => $lang->get('LC__NOTIFICATIONS__RECEIVER_CALC_STRATEGY__ONLY_GROUPS'),
            self::RECEIVER_CALC_STRATEGY__GROUPS_IF_AVAIL_OR_PERSONS => $lang->get('LC__NOTIFICATIONS__RECEIVER_CALC_STRATEGY__GROUPS_IF_AVAIL_OR_PERSONS')
        ];
    }

    /**
     * Get all placeholders which should be skipped as they make no sense to add into the template
     *
     * @return array[]
     */
    private function getSkippedPlaceholders()
    {
        return [
            self::NOTIFICATIONS => [
                'template_de' => true,
                'template_en' => true,
            ]
        ];
    }

    /**
     * Gets information about property types.
     *
     * @param   string $p_type (optional) Property type (e. g. 'notifications'). Defaults to null (all property types will be fetched).
     *
     * @return  array  Associative array
     * @throws Exception
     */
    public function get_property_types($p_type = null)
    {
        if ($p_type !== null && isset($this->m_types[$p_type])) {
            return $this->m_types[$p_type];
        }

        return $this->m_types;
    }

    /**
     * Gets information about property groups.
     *
     * @return  array
     * @throws Exception
     */
    public function get_property_groups()
    {
        if (!isset($this->m_groups)) {
            $this->m_groups = [
                'common_settings' => [
                    'title' => isys_application::instance()->container->get('language')
                        ->get('LC__NOTIFICATIONS__COMMON_SETTINGS')
                ],
                'domains'         => [
                    'title' => isys_application::instance()->container->get('language')
                        ->get('LC__NOTIFICATIONS__DOMAINS')
                ],
                'receivers'       => [
                    'title' => isys_application::instance()->container->get('language')
                        ->get('LC__NOTIFICATIONS__RECEIVERS')
                ]
            ];
        }

        return $this->m_groups;
    }

    /**
     * Gets information about domain types.
     *
     * @param   string $p_type (optional) Domain type (e. g. 'objects'). Defaults to null (all domain types will be fetched).
     *
     * @return  array  Associative array
     * @throws Exception
     */
    public function get_domain_types($p_type = null)
    {
        if (!isset($this->m_domain_types)) {
            $this->m_domain_types = [
                'objects'      => [
                    'title' => isys_application::instance()->container->get('language')
                        ->get('LC__NOTIFICATIONS__DOMAIN_OBJECTS')
                ],
                'object_types' => [
                    'title' => isys_application::instance()->container->get('language')
                        ->get('LC__NOTIFICATIONS__DOMAIN_OBJECT_TYPES')
                ],
                'reports'      => [
                    'title' => isys_application::instance()->container->get('language')
                        ->get('LC__NOTIFICATIONS__DOMAIN_REPORTS')
                ]
            ];
        }

        if (isset($p_type)) {
            assert(array_key_exists($p_type, $this->m_domain_types));

            return $this->m_domain_types[$p_type];
        }

        return $this->m_domain_types;
    }

    /**
     * Gets notifications status messages.
     *
     * @return  array
     * @throws Exception
     */
    public function get_status()
    {
        return [
            self::C__STATUS__DEACTIVATED   => isys_application::instance()->container->get('language')
                ->get('LC__NOTIFICATIONS__STATUS__DEACTIVATED'),
            self::C__STATUS__ACTIVATED     => isys_application::instance()->container->get('language')
                ->get('LC__NOTIFICATIONS__STATUS__ACTIVATED'),
            self::C__STATUS__LIMIT_REACHED => isys_application::instance()->container->get('language')
                ->get('LC__NOTIFICATIONS__STATUS__LIMIT_REACHED'),
            self::C__STATUS__INCOMPLETE    => isys_application::instance()->container->get('language')
                ->get('LC__NOTIFICATIONS__STATUS__INCOMPLETE')
        ];
    }

    /**
     * Fetches available languages from system database.
     *
     * @return array Associative array with language abbreviations as keys and
     * their titles as values.
     * @throws isys_exception_database
     * @throws Exception
     */
    public function get_locales()
    {
        global $g_comp_database_system;

        $l_languages = [];
        $l_dao = new isys_component_dao($g_comp_database_system);

        $l_query = "SELECT isys_language__title, isys_language__short FROM isys_language;";

        $l_result = $l_dao->retrieve($l_query);

        if ($l_result->num_rows() > 1) {
            while ($l_row = $l_result->get_row()) {
                $l_languages[$l_row['isys_language__short']] = isys_application::instance()->container->get('language')
                    ->get($l_row['isys_language__title']);
            }
        }

        return $l_languages;
    }

    /**
     * Gets available placeholders.
     *
     * @return array Indexed array of associative arrays which contains 'value',
     * 'title', and (optional) 'description'
     * @throws Exception
     */
    public function get_placeholders()
    {
        $language = isys_application::instance()->container->get('language');
        $placeholders = [];
        $l_all_properties = $this->get_properties();
        $skippedPlaceholders = $this->getSkippedPlaceholders();
        // Use properties:
        foreach ($l_all_properties as $l_property_type => $l_properties) {
            foreach ($l_properties as $l_property_tag => $l_property_info) {
                if (isset($skippedPlaceholders[$l_property_type][$l_property_tag])) {
                    continue;
                }

                $placeholders[] = [
                    'value'       => '%' . $l_property_type . '__' . $l_property_tag . '%',
                    'title'       => $l_property_info[C__PROPERTY__INFO][C__PROPERTY__INFO__TITLE],
                    'description' => isset($l_property_info[C__PROPERTY__INFO][C__PROPERTY__INFO__DESCRIPTION])
                        ? $l_property_info[C__PROPERTY__INFO][C__PROPERTY__INFO__DESCRIPTION]
                        : null
                ];
            }
        }

        $placeholders[] = ['value' => '%notification_domains__report_ids%', 'title' => $language->get('LC__NOTIFICATIONS__REPORT_IDS')];
        $placeholders[] = ['value' => '%receivers__title%', 'title' => $language->get('LC__CMDB__CATG__CONTACT_TITLE')];
        $placeholders[] = ['value' => '%receivers__email%', 'title' => $language->get('LC__CATG__CONTACT_EMAIL')];
        $placeholders[] = ['value' => '%tenant_id%', 'title' => $language->get('LC__NOTIFICATIONS__TENANT_ID')];
        $placeholders[] = ['value' => '%tenant_name%', 'title' => $language->get('LC__NOTIFICATIONS__TENANT_NAME')];

        usort($placeholders, function ($itemA, $itemB) {
            return strcmp($itemA['value'], $itemB['value']);
        });

        return $placeholders;
    }

    /**
     * Fetches notification data from database.
     *
     * @param array $p_selections    (optional) Select only these properties. If
     *                               not set (default), all properties will be selected.
     * @param array $p_conditions    (optional) Make some conditions. Associative
     *                               array of properties as keys and the destinated values as values. Defaults
     *                               to no condition.
     * @param bool  $p_raw           (optional) Returns unformatted ouput. Defaults to
     *                               false.
     * @param bool  $p_as_result_set (optional) Returns fetched data as result
     *                               set. Defaults to false.
     *
     * @return mixed Associative array or result set (isys_component_dao_result)
     */
    public function get_notifications($p_selections = null, $p_conditions = null, $p_raw = false, $p_as_result_set = false)
    {
        return $this->get_entities(self::NOTIFICATIONS, $p_selections, $p_conditions, $p_raw, $p_as_result_set);
    }

    /**
     * Fetches notification template data from database.
     *
     * @param array $p_selections    (optional) Select only these properties. If
     *                               not set (default), all properties will be selected.
     * @param array $p_conditions    (optional) Make some conditions. Associative
     *                               array of properties as keys and the destinated values as values. Defaults
     *                               to no condition.
     * @param bool  $p_raw           (optional) Returns unformatted ouput. Defaults to
     *                               false.
     * @param bool  $p_as_result_set (optional) Returns fetched data as result
     *                               set. Defaults to false.
     *
     * @return mixed Associative array or result set (isys_component_dao_result)
     */
    public function get_templates($p_selections = null, $p_conditions = null, $p_raw = false, $p_as_result_set = false)
    {
        return $this->get_entities(self::NOTIFICATION_TEMPLATES, $p_selections, $p_conditions, $p_raw, $p_as_result_set);
    }

    /**
     * Fetches notification data from database.
     *
     * @param int  $p_id            (optional) Select notification identifier. If not set
     *                              (default), all notifications will be selected.
     * @param bool $p_raw           (optional) Returns unformatted ouput. Defaults to
     *                              false.
     * @param bool $p_as_result_set (optional) Returns fetched data as result
     *                              set. Defaults to false.
     *
     * @return mixed Associative array or result set (isys_component_dao_result)
     * @throws isys_exception_database
     */
    public function get_notification($p_id = null, $p_raw = false, $p_as_result_set = false)
    {
        return $this->fetch_properties(self::NOTIFICATIONS, $p_id, $p_raw, $p_as_result_set);
    }

    /**
     * Fetches notification types from database.
     *
     * @param int  $p_id            (optional) Select notification type identifier. If
     *                              not set (default), all notification types will be selected.
     * @param bool $p_raw           (optional) Returns unformatted ouput. Defaults to
     *                              false.
     * @param bool $p_as_result_set (optional) Returns fetched data as result
     *                              set. Defaults to false.
     *
     * @return mixed Associative array or result set (isys_component_dao_result)
     * @throws isys_exception_database
     */
    public function get_type($p_id = null, $p_raw = false, $p_as_result_set = false)
    {
        return $this->fetch_properties(self::NOTIFICATION_TYPES, $p_id, $p_raw, $p_as_result_set);
    }

    /**
     * Fetches affected objects from database. All domains will be asked for
     * data: assigned objects, object types and reports.
     *
     * @param int $p_notification_id Notification identifier
     *
     * @return array Associative array
     * @throws isys_exception_database
     */
    public function get_objects($p_notification_id)
    {
        assert(is_int($p_notification_id) && $p_notification_id >= 0);

        if (isset($this->m_cache['objects'][$p_notification_id])) {
            $this->m_cache['objects'][$p_notification_id];
        }

        $this->m_cache['objects'][$p_notification_id] = [];

        $l_query = 'SELECT *
            FROM ' . $this->m_tables[self::NOTIFICATION_DOMAINS] . '
            WHERE ' . $this->m_tables[self::NOTIFICATION_DOMAINS] . '__' . $this->m_tables[self::NOTIFICATIONS] . '__id = ' . $this->convert_sql_id($p_notification_id) . ';';

        $l_notification_domains = $this->retrieve($l_query)
            ->__as_array();

        $l_object_ids = [];

        $l_cmdb_dao = new isys_cmdb_dao($this->m_db);
        $reportDao = isys_report_dao::instance($this->m_db);

        foreach ($l_notification_domains as $l_notification_domain) {
            if (isset($l_notification_domain[$this->m_tables[self::NOTIFICATION_DOMAINS] . '__' . $this->m_tables['objects'] . '__id'])) {
                $l_object_ids[] = $l_notification_domain[$this->m_tables[self::NOTIFICATION_DOMAINS] . '__' . $this->m_tables['objects'] . '__id'];
            }

            if (isset($l_notification_domain[$this->m_tables[self::NOTIFICATION_DOMAINS] . '__' . $this->m_tables['object_types'] . '__id'])) {
                $l_objects_by_type = $l_cmdb_dao->get_objects_by_type(intval($l_notification_domain[$this->m_tables[self::NOTIFICATION_DOMAINS] . '__' .
                $this->m_tables['object_types'] . '__id']))
                    ->__as_array();
                foreach ($l_objects_by_type as $l_object) {
                    $l_object_ids[] = $l_object[$this->m_tables['objects'] . '__id'];
                }
            }

            if (isset($l_notification_domain[$this->m_tables[self::NOTIFICATION_DOMAINS] . '__isys_report__id'])) {
                $l_reports = $this->get_all_reports();

                foreach ($l_reports as $l_report) {
                    if ($l_report['isys_report__id'] == $l_notification_domain[$this->m_tables[self::NOTIFICATION_DOMAINS] . '__isys_report__id']) {
                        $l_query = $reportDao->replacePlaceHolders($l_report['isys_report__query']);
                        $l_result = $this->retrieve($l_query)
                            ->__as_array();
                        // 'Smart' check:
                        if (count($l_result) == 0 || !array_key_exists('__id__', $l_result[0])) {
                            $this->m_log->notice('You\'ve selected a report that is not compatible with this notification. Object identifers are required as "__id__"');
                        } else {
                            foreach ($l_result as $l_entry) {
                                $l_object_ids[] = intval($l_entry['__id__']);
                            }
                        }
                        break;
                    }
                }
            }
        }

        if (count($l_object_ids) > 0) {
            $condition = ['ids' => $l_object_ids];

            // @see ID-4889 Check if the notification should only check for objects with status "normal".
            if ($this->onlyProcessObjectsWithStatusNormal($p_notification_id)) {
                $condition['status'] = C__RECORD_STATUS__NORMAL;
            }

            $this->m_cache['objects'][$p_notification_id] = $l_cmdb_dao->get_objects($condition)
                ->__as_array();
        }

        return $this->m_cache['objects'][$p_notification_id];
    }

    /**
     * Returns true or false, if the given notification should only contain objects with status normal
     *
     * @param integer $notificationId
     *
     * @see    ID-4889
     * @return boolean
     * @throws isys_exception_database
     */
    private function onlyProcessObjectsWithStatusNormal($notificationId)
    {
        $onlyNormalQuery = 'SELECT ' . $this->m_tables[self::NOTIFICATIONS] . '__only_normal
            FROM ' . $this->m_tables[self::NOTIFICATIONS] . '
            WHERE ' . $this->m_tables[self::NOTIFICATIONS] . '__id = ' . $this->convert_sql_id($notificationId) . ';';

        return !!$this->retrieve($onlyNormalQuery)
            ->get_row_value($this->m_tables[self::NOTIFICATIONS] . '__only_normal');
    }

    /**
     * Fetches notification domains from database.
     *
     * @param int $p_notification_id Notification identifier
     *
     * @return array Associative array with all domain types as keys
     * @throws isys_exception_database
     * @throws Exception
     */
    public function get_domains($p_notification_id)
    {
        assert(is_int($p_notification_id) && $p_notification_id >= 0);

        $p_property_type = self::NOTIFICATION_DOMAINS;

        if (is_array($this->m_cache[$p_property_type]) && isset($this->m_cache[$p_property_type][$p_notification_id])) {
            return $this->m_cache[$p_property_type][$p_notification_id];
        }

        $l_domain_types = $this->get_domain_types();

        $this->m_cache[$p_property_type][$p_notification_id] = [];
        foreach ($l_domain_types as $l_domain => $l_null) {
            $this->m_cache[$p_property_type][$p_notification_id][$l_domain] = null;
        }

        /** @noinspection SqlResolve */
        $l_query = 'SELECT * FROM ' . $this->m_tables[$p_property_type] . ' WHERE `' . $this->m_tables[$p_property_type] . '__' . $this->m_tables[self::NOTIFICATIONS] .
            '__id` = ' . $this->convert_sql_id($p_notification_id) . ';';

        $l_raw_data = $this->retrieve($l_query)
            ->__as_array();
        $l_data = [];

        foreach ($l_raw_data as $l_raw) {
            $l_data[] = $this->map_properties($p_property_type, $l_raw);
        }

        if (count($l_data) == 0) {
            return $this->m_cache[$p_property_type][$p_notification_id];
        }

        foreach ($l_data as $l_entity) {
            foreach ($l_domain_types as $l_domain => $l_null) {
                if (isset($l_entity[$l_domain])) {
                    $this->m_cache[$p_property_type][$p_notification_id][$l_domain][] = $l_entity[$l_domain];
                }
            }
        }

        return $this->m_cache[$p_property_type][$p_notification_id];
    }

    /**
     * Fetches receivers from database based on assigned roles and contacts.
     *
     * @param int $notificationId Notification identifier
     *
     * @return array Array of receivers' object identifiers ('id'), their full
     * names ('title'), email addresses ('email'), and locale ('locale') as
     * values.
     *
     * @throws isys_exception_database
     * @todo Also fetch locale settings
     * @throws Exception
     */
    public function get_receivers($notificationId)
    {
        $notifications = $this->get_notifications();
        $currentNotification = $notifications[$notificationId];
        $receiver_calc_strategy = $currentNotification['receiver_calc_strategy'];

        $this->m_log->debug('Fetching receivers from database based on assigned roles and contacts...');
        if (is_array($this->m_cache['receivers']) && array_key_exists($notificationId, $this->m_cache['receivers'])) {
            $this->m_log->debug('Cache found.');
            return $this->m_cache['receivers'][$notificationId];
        }

        $this->m_cache['receivers'][$notificationId]['contacts'] = $this->m_cache['receivers'][$notificationId]['contacts_by_roles'] = [];

        $receiversArray = [
            'contacts'          => $this->resolve_receivers_from_contacts($this->get_receivers_by_contacts($notificationId)),
            'contacts_by_roles' => $this->get_receivers_by_roles($notificationId)
        ];

        $conditions = [
            "isys_catg_mail_addresses_list__title <> ''",
            "isys_obj__title <> ''",
            "isys_obj__status = " . $this->convert_sql_int(C__RECORD_STATUS__NORMAL)
        ];

        if (count($receiversArray['contacts_by_roles']) > 0 || count($receiversArray['contacts'])) {
            $usedReceivers = [];

            switch ($receiver_calc_strategy) {
                case self::RECEIVER_CALC_STRATEGY__ONLY_GROUPS:
                    $conditions[] = 'isys_obj_type__isysgui_cats__id = ' . $this->convert_sql_id(defined_or_default('C__CATS__PERSON_GROUP'));
                    break;
                case self::RECEIVER_CALC_STRATEGY__ONLY_PERSONS:
                    $conditions[] = 'isys_obj_type__isysgui_cats__id = ' . $this->convert_sql_id(defined_or_default('C__CATS__PERSON'));
                    break;
                case self::RECEIVER_CALC_STRATEGY__PERSONS_AND_GROUPS:
                case self::RECEIVER_CALC_STRATEGY__GROUPS_IF_AVAIL_OR_PERSONS:
                default:
                    break;
            }

            foreach ($receiversArray as $type => $recArray) {
                $recArrayCount = count($recArray);

                if ($recArrayCount === 0) {
                    continue;
                }

                // Fetch identifiers (isys_obj__id), full names (isys_obj__title) and email addresses (isys_cats_person_list__mail_address) from database:
                $query = 'SELECT isys_obj__id AS id, isys_obj__title AS title, isys_catg_mail_addresses_list__title AS email,
                    (isys_obj_type__isysgui_cats__id = ' . $this->convert_sql_id(defined_or_default('C__CATS__PERSON')) . ') AS is_person
                    FROM isys_obj
                    LEFT JOIN isys_obj_type ON isys_obj_type__id = isys_obj__isys_obj_type__id
                    LEFT JOIN isys_catg_mail_addresses_list ON isys_catg_mail_addresses_list__isys_obj__id = isys_obj__id AND isys_catg_mail_addresses_list__primary = 1
                    WHERE isys_obj__id ' . $this->prepare_in_condition($recArray) . ' AND ' . implode(' AND ', $conditions) . ';';

                $receivers = $this->retrieve($query)->__as_array();
                $receiversCount = count($receivers);

                // @see ID-11203 Check if the original amount of recipients was reduced - notify the user!
                if ($recArrayCount !== $receiversCount) {
                    $delta = array_diff($recArray, array_map(fn ($contact) => $contact['id'], $receivers));

                    $this->m_log->warning(sprintf(
                        'At least one receiver can not be notified (got %d instead of %d). Please check the following object(s): #%s',
                        $receiversCount,
                        $recArrayCount,
                        implode(', #', $delta)
                    ));
                }

                if ($receiversCount === 0) {
                    continue;
                }

                // @see ID-9216 Get objects with a primary e-mail address:
                $query = 'SELECT isys_catg_mail_addresses_list__isys_obj__id AS id
                    FROM isys_catg_mail_addresses_list
                    WHERE isys_catg_mail_addresses_list__isys_obj__id ' . $this->prepare_in_condition($recArray) . '
                    AND isys_catg_mail_addresses_list__primary = 1
                    GROUP BY isys_catg_mail_addresses_list__isys_obj__id;';

                $objectsWithPrimaryEmail = array_map(fn ($contact) => (int)$contact['id'], $this->retrieve($query)->__as_array());

                foreach ($receivers as $receiver) {
                    if (isset($usedReceivers[$receiver['id']])) {
                        continue;
                    }

                    // @see ID-9215 If no receiver strategy was selected, we use 'groups if available or persons' as default see definition in ID-8680
                    if ((!$receiver_calc_strategy || $receiver_calc_strategy === self::RECEIVER_CALC_STRATEGY__GROUPS_IF_AVAIL_OR_PERSONS) && $receiver['is_person']) {
                        // check if person is in receiver groups, if true put them in already used array
                        $groupsMemberships = isys_cmdb_dao_category_s_person_assigned_groups::instance($this->m_db)->getAssignedGroupsIDs($receiver['id']);
                        foreach ($groupsMemberships as $membership) {
                            if (in_array($membership, $objectsWithPrimaryEmail)) {
                                $this->m_log->info(sprintf('Receivergroup-Membership for "%s" [%s] found. Skip receiver.', $receiver['title'], $receiver['id']));
                                continue 2;
                            }
                        }
                    }

                    $newReceiver = $receiver;

                    // Get language for receiver
                    $l_language = $this->getUserLanguage((int)$receiver['id']);

                    if (isset($l_language) && !empty($l_language)) {
                        $newReceiver['locale'] = $l_language;
                    } else {
                        $this->m_log->warning(sprintf('Language not set for user "%s" [%s]. Changing to default Language.', $receiver['title'], $receiver['id']));
                        // @See ID-6172 Use default language "en" instead of skipping mail
                        $newReceiver['locale'] = isys_tenantsettings::get('system.default-language', C__USE_BROWSER_DEFAULT);

                        // @see ID-11762 React to 'browser detection' setting.
                        if ($newReceiver['locale'] === null || $newReceiver['locale'] == C__USE_BROWSER_DEFAULT) {
                            $newReceiver['locale'] = isys_application::instance()->container->get('locales')->getPreferredLanguage();
                        }
                    }

                    if ($type === 'contacts_by_roles') {
                        // Assigned objects
                        $newReceiver['assigned_objects'] = self::$m_receivers_roles_object_mapping[$receiver['id']];
                    }

                    $this->m_cache['receivers'][$notificationId][$type][] = $newReceiver;
                    $usedReceivers[$newReceiver['id']] = true;
                }
            }
        }

        $this->m_log->debug(sprintf(
            'Amount of receivers: %s',
            count($this->m_cache['receivers'][$notificationId]['contacts']) + count($this->m_cache['receivers'][$notificationId]['contacts_by_roles'])
        ));

        return $this->m_cache['receivers'][$notificationId];
    }

    /**
     * Fetches receivers from database based on assigned contacts.
     *
     * @param int   $notificationId Notification identifier
     *
     * @return array Object identifiers (integers)
     * @throws isys_exception_database
     */
    public function get_receivers_by_contacts($notificationId)
    {
        $this->m_log->debug('Fetching receivers from database based on assigned contacts...');

        $notification = $this->get_notification($notificationId);
        if (count($notification) === 0 || !isset($notification['contacts'])) {
            return [];
        }

        return $this->get_contacts($notification['contacts']);
    }

    /**
     * Fetches receivers from database based on assigned contacts.
     *
     * @param int $p_contact_id Assigned contact identifier
     *
     * @return array Object identifiers (integers)
     * @throws isys_exception_database
     */
    public function get_contacts($p_contact_id)
    {
        assert(is_int($p_contact_id));

        $l_query = 'SELECT * FROM isys_contact_2_isys_obj ' . 'INNER JOIN isys_obj ON isys_obj__id = isys_contact_2_isys_obj__isys_obj__id ' .
            'INNER JOIN isys_obj_type ON isys_obj_type__id = isys_obj__isys_obj_type__id ' . 'WHERE isys_contact_2_isys_obj__isys_contact__id = ' .
            $this->convert_sql_id($p_contact_id) . ';';

        return $this->retrieve($l_query)
            ->__as_array();
    }

    /**
     * Fetches unit data from database.
     *
     * @param int  $p_id            (optional) Select unit identifier. If not set (default),
     *                              all units will be selected.
     * @param bool $p_raw           (optional) Returns unformatted ouput. Defaults to
     *                              false.
     * @param bool $p_as_result_set (optional) Returns fetched data as result
     *                              set. Defaults to false.
     *
     * @return mixed Associative array or result set (isys_component_dao_result)
     * @throws isys_exception_database
     */
    public function get_unit($p_id = null, $p_raw = false, $p_as_result_set = false)
    {
        return $this->fetch_properties(self::NOTIFICATION_UNITS, $p_id, $p_raw, $p_as_result_set);
    }

    /**
     * Fetches unit parameters data from database.
     *
     * @param string $table Table name
     *
     * @return array
     * @throws isys_exception_database
     */
    public function get_unit_parameters($table): array
    {
        if (!isys_application::instance()->container->get('cmdb_dao')->table_exists($table)) {
            return [];
        }

        if (isset($this->m_cache['unit_parameters']) && array_key_exists($table, $this->m_cache['unit_parameters'])) {
            return $this->m_cache['unit_parameters'][$table];
        }

        $status = $this->convert_sql_int(C__RECORD_STATUS__NORMAL);

        $this->m_cache['unit_parameters'][$table] = $this
            ->retrieve("SELECT * FROM {$table} WHERE {$table}__status = {$status};")
            ->__as_array();

        return $this->m_cache['unit_parameters'][$table];
    }

    /**
     * Fetches reports from system database.
     *
     * @global isys_component_database $g_comp_database_system
     *
     * @return array
     */
    public function get_all_reports()
    {
        if (isset($this->m_cache['reports'])) {
            return $this->m_cache['reports'];
        }

        $this->m_cache['reports'] = isys_report_dao::instance(isys_application::instance()->database_system)
            ->get_reports(null, null, null, false, false);

        return $this->m_cache['reports'];
    }

    /**
     * Fetches notification roles from database.
     *
     * @param int $p_notification_id Notification identifier
     *
     * @return array
     * @throws isys_exception_database
     */
    public function get_roles($p_notification_id)
    {
        assert(is_int($p_notification_id) && $p_notification_id >= 0);

        if (is_array($this->m_cache[self::NOTIFICATION_ROLES]) && isset($this->m_cache[self::NOTIFICATION_ROLES][$p_notification_id])) {
            return $this->m_cache[self::NOTIFICATION_ROLES][$p_notification_id];
        }

        $l_query = 'SELECT * FROM ' . $this->m_tables[self::NOTIFICATION_ROLES] . ' INNER JOIN ' . $this->m_tables['roles'] . ' ON ' . $this->m_tables['roles'] . '__id = ' .
            $this->m_tables[self::NOTIFICATION_ROLES] . '__' . $this->m_tables['roles'] . '__id WHERE ' . $this->m_tables[self::NOTIFICATION_ROLES] . '__' .
            $this->m_tables[self::NOTIFICATIONS] . '__id = ' . $this->convert_sql_id($p_notification_id) . ';';

        $this->m_cache[self::NOTIFICATION_ROLES][$p_notification_id] = $this->retrieve($l_query)
            ->__as_array();

        return $this->m_cache[self::NOTIFICATION_ROLES][$p_notification_id];
    }

    /**
     * Deletes an existing entity from database.
     *
     * @param string     $p_property_type Property type (e. g. 'notifications')
     * @param array|null $p_conditions
     *
     * @throws isys_exception_dao
     * @throws isys_exception_general
     * @internal param int $p_conditions['entity'] Entity identifier
     * @internal param bool $p_conditions['use_notification_id'] (optional) Use notification identifier
     *                                      instead of entity identifier. Note, there is not always a notification
     *                                      identifier given! Defaults to false.
     * @throws Exception
     */
    public function delete($p_property_type, $p_conditions = null)
    {
        assert(is_string($p_property_type));

        $l_types = $this->get_property_types();

        if (!array_key_exists($p_property_type, $l_types)) {
            throw new isys_exception_general(sprintf('Failed to delete entity "%s" because of unknown property type "%s"', $p_conditions['entity'], $p_property_type));
        }

        $l_notification = $this->m_tables[$p_property_type];
        if ($p_conditions['use_notification_id'] && $p_property_type !== self::NOTIFICATIONS) {
            $l_notification .= '__' . $this->m_tables[self::NOTIFICATIONS];
        }

        $l_query = 'DELETE FROM ' . $this->m_tables[$p_property_type] . ' WHERE ' . $l_notification . '__id = ' . $this->convert_sql_id($p_conditions['entity']) . ';';

        if (!$this->update($l_query)) {
            throw new isys_exception_general(sprintf(
                'Failed to delete entity "%s" for property type "%s" because an unexpected database error occured.',
                $p_conditions['entity'],
                $p_property_type
            ));
        }
    }

    /**
     * Fetches all notification data from database.
     *
     * @return array
     */
    public function get_data()
    {
        return $this->get_notification();
    }

    /**
     * Adds or updates one or more roles to a notification.
     *
     * @param int   $p_notification_id Notification identifier
     * @param mixed $p_roles           One or more user roles formatted as a comma
     *                                 separated string of numerics, a JSON array or numerics, or an array of
     *                                 numerics
     *
     * @throws isys_exception_dao
     * @throws isys_exception_general
     * @throws Exception
     */
    public function add_roles($p_notification_id, $p_roles)
    {
        $l_roles = [];

        if (isys_format_json::is_json_array($p_roles)) {
            $l_roles = isys_format_json::decode($p_roles, true);
        } elseif (is_string($p_roles)) {
            $l_roles = explode(',', $p_roles);
        } elseif (is_array($p_roles)) {
            $l_roles = $p_roles;
        } else {
            throw new isys_exception_general('Failed to add roles because format is invalid.');
        }

        foreach ($l_roles as $l_role) {
            assert(is_numeric($l_role) && $l_role >= 0);
        }

        // Delete existing notification roles:

        $l_query = 'DELETE FROM ' . $this->m_tables[self::NOTIFICATION_ROLES] . ' WHERE ' . $this->m_tables[self::NOTIFICATION_ROLES] . '__' . $this->m_tables[self::NOTIFICATIONS] .
            '__id = ' . $this->convert_sql_id($p_notification_id) . ';';

        if (!$this->update($l_query)) {
            throw new isys_exception_general('Failed to clean up notification roles because an unexpected database error occured.');
        }

        // No "new" roles? We've finished.
        if (count($l_roles) === 0) {
            return;
        }

        // Add "new" roles:

        $l_inserts = [];

        foreach ($l_roles as $l_role) {
            $l_inserts[] = '(' . $this->convert_sql_id($p_notification_id) . ', ' . $this->convert_sql_id($l_role) . ')';
        }

        /** @noinspection SqlResolve */
        $l_query = 'INSERT INTO ' . $this->m_tables[self::NOTIFICATION_ROLES] . ' (`' . $this->m_tables[self::NOTIFICATION_ROLES] . '__' . $this->m_tables[self::NOTIFICATIONS] .
            '__id`, `' . $this->m_tables[self::NOTIFICATION_ROLES] . '__' . $this->m_tables['roles'] . '__id`) VALUES ' . implode(', ', $l_inserts) . ';';

        if (!$this->update($l_query)) {
            throw new isys_exception_general('Failed to add notification roles because an unexpected database error occured.');
        }
    }

    /**
     * Fetches roles from database.
     *
     * @return array
     * @throws isys_exception_database
     */
    public function get_all_roles()
    {
        if (isset($this->m_cache['roles'])) {
            return $this->m_cache['roles'];
        }

        $l_query = 'SELECT ' . $this->m_tables['roles'] . '__id, ' . $this->m_tables['roles'] . '__title FROM ' . $this->m_tables['roles'] . ' WHERE ' .
            $this->m_tables['roles'] . '__status = \'' . C__RECORD_STATUS__NORMAL . '\';';

        $this->m_cache['roles'] = $this->retrieve($l_query)
            ->__as_array();

        return $this->m_cache['roles'];
    }

    /**
     * Adds or updates one or more contacts assigned to a notification.
     *
     * Notice: This is just a helper method for creating/updating a
     * notification. You have to insert/update manually this notification with
     * the return value of this method.
     *
     * @param int   $p_notification_id (optional) Notification identifier. If not
     *                                 set a new reference will be created. Defaults to null
     * @param mixed $p_contacts        One or more user contact identifiers formatted
     *                                 as a comma separated string or numerics, a JSON array or numerics, or an
     *                                 array of numerics
     *
     * @throws isys_exception_dao
     * @throws isys_exception_database
     * @throws isys_exception_general
     */
    public function add_contacts($p_notification_id, $p_contacts)
    {
        assert(is_int($p_notification_id));

        $l_property_type = self::NOTIFICATIONS;
        $l_properties = $this->get_properties($l_property_type);

        $l_notification_data = $this->get_notification($p_notification_id);
        $l_existing_contact_id = $l_notification_data['contacts'];

        $l_dao_ref = new isys_contact_dao_reference($this->m_db);

        $l_contact_id = null;

        if (is_array($p_contacts) && count($p_contacts) === 0) {
            $p_contacts = null;
        }

        if ($p_contacts === null && $l_existing_contact_id === null) {
            // Everthing is fine.
            return;
        } elseif ($p_contacts === null && $l_existing_contact_id !== null) {
            // Remove contacts:
            if ($l_dao_ref->delete($l_existing_contact_id) === false) {
                throw new isys_exception_general('Failed to remove old contacts.');
            }
        } else {
            $l_contact_id = $l_dao_ref->ref_contact($p_contacts, $l_existing_contact_id);

            if ($l_contact_id === false) {
                throw new isys_exception_general('Failed to add contacts.');
            }
        }

        // Update contact field for this notification:

        $l_query = 'UPDATE ' . $this->m_tables[$l_property_type] . ' SET `' . $this->m_tables[$l_property_type] . '__' . $this->m_tables['contacts'] . '__id` = ' .
            $this->convert_sql_id($l_contact_id) . ' WHERE `' . $l_properties['id'][C__PROPERTY__DATA][C__PROPERTY__DATA__FIELD] . '` = ' .
            $this->convert_sql_id($p_notification_id) . ';';

        if (!$this->update($l_query)) {
            throw new isys_exception_general('Failed to add contacts because an unexpected database error occured.');
        }
    }

    /**
     * Adds domains for a notification. Existing ones will be replaced.
     *
     * @param int   $p_notification_id Notification identifier
     * @param array $p_domains         (optional) Associative array with domain types as
     *                                 keys and the selected entities (indexed array of identifiers) as values.
     *                                 Defaults to null (all existing domains will be deleted).
     *
     * @throws isys_exception_dao
     * @throws isys_exception_general
     */
    public function add_domains($p_notification_id, $p_domains = null)
    {
        assert(is_int($p_notification_id));

        $l_property_type = self::NOTIFICATION_DOMAINS;

        // Truncate existing domain entities:
        $this->delete($l_property_type, ['entity' => $p_notification_id, 'use_notification_id' => true]);

        $l_inserts = [];

        if (isset($p_domains)) {
            assert(is_array($p_domains));

            foreach ($p_domains as $l_domain => $l_entities) {
                if (is_array($l_entities)) {
                    assert(is_array($l_entities));

                    $l_insert = [
                        'notifications' => $p_notification_id,
                        'objects'       => 'NULL',
                        'object_types'  => 'NULL',
                        'reports'       => 'NULL'
                    ];

                    foreach ($l_entities as $l_entity) {
                        assert(is_int($l_entity));
                        $l_insert[$l_domain] = $this->convert_sql_id($l_entity);
                        $l_inserts[] = '(' . implode(', ', $l_insert) . ')';
                        $l_insert[$l_domain] = 'NULL';
                    }
                }
            }
        }

        if (count($l_inserts) == 0) {
            return;
        }

        /** @noinspection SqlResolve */
        $l_query = 'INSERT INTO ' . $this->m_tables[$l_property_type] . ' (`' . $this->m_tables[$l_property_type] . '__' . $this->m_tables[self::NOTIFICATIONS] . '__id`, `' .
            $this->m_tables[$l_property_type] . '__' . $this->m_tables['objects'] . '__id`, `' . $this->m_tables[$l_property_type] . '__' . $this->m_tables['object_types'] .
            '__id`, `' . $this->m_tables[$l_property_type] . '__' . $this->m_tables['reports'] . '__id`' . ') VALUES ' . implode(', ', $l_inserts) . ';';

        if (!$this->update($l_query)) {
            throw new isys_exception_general('Failed to add notification domains because an unexpected database error occured.');
        }
    }

    /**
     * Fetches notification data from database.
     *
     * @param   integer $p_id            (optional) Select notification template identifier. If not set (default), all notification templates will be selected.
     * @param   boolean $p_raw           (optional) Returns unformatted ouput. Defaults to false.
     * @param   boolean $p_as_result_set (optional) Returns fetched data as result set. Defaults to false.
     *
     * @return  mixed  Associative array or result set (isys_component_dao_result)
     * @throws isys_exception_database
     */
    public function get_template($p_id = null, $p_raw = false, $p_as_result_set = false)
    {
        return $this->fetch_properties(self::NOTIFICATION_TEMPLATES, $p_id, $p_raw, $p_as_result_set);
    }

    /**
     * Provides information about properties.
     *
     * @throws Exception
     */
    protected function build_properties()
    {
        $l_provides_all = self::C__PROPERTY__PROVIDES__VIEW + self::C__PROPERTY__PROVIDES__CREATE + self::C__PROPERTY__PROVIDES__SAVE + self::C__PROPERTY__PROVIDES__DELETE;
        $lang = isys_application::instance()->container->get('language');
        $this->m_properties = [
            self::NOTIFICATIONS          => [
                'id'                     => [
                    C__PROPERTY__INFO     => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_ID')
                    ],
                    C__PROPERTY__DATA     => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATIONS] . '__id',
                        C__PROPERTY__DATA__TYPE  => 'int',
                        'params'                 => [
                            'primary_key',
                            'unsigned',
                            'auto_increment',
                            'unique'
                        ]
                    ],
                    C__PROPERTY__UI       => [
                        C__PROPERTY__UI__ID     => 'C__NOTIFICATIONS__NOTIFICATION_ID',
                        C__PROPERTY__UI__TYPE   => C__PROPERTY__UI__TYPE__TEXT,
                        C__PROPERTY__UI__PARAMS => [
                            'p_bInvisible' => 1
                        ]
                    ],
                    C__PROPERTY__CHECK    => [
                        C__PROPERTY__CHECK__MANDATORY  => true,
                        C__PROPERTY__CHECK__VALIDATION => [
                            FILTER_VALIDATE_INT,
                            [
                                'options' => ['min_range' => 0]
                            ]
                        ]
                    ],
                    C__PROPERTY__PROVIDES => self::C__PROPERTY__PROVIDES__VIEW
                ],
                'title'                  => [
                    C__PROPERTY__INFO     => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_TITLE'),
                        'group'                  => 'common_settings'
                    ],
                    C__PROPERTY__DATA     => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATIONS] . '__title',
                        C__PROPERTY__DATA__TYPE  => 'varchar'
                    ],
                    C__PROPERTY__UI       => [
                        C__PROPERTY__UI__ID   => 'C__NOTIFICATIONS__NOTIFICATION_TITLE',
                        C__PROPERTY__UI__TYPE => C__PROPERTY__UI__TYPE__TEXT
                    ],
                    C__PROPERTY__CHECK    => [
                        C__PROPERTY__CHECK__MANDATORY  => true,
                        C__PROPERTY__CHECK__VALIDATION => [
                            FILTER_CALLBACK,
                            [
                                'options' => [
                                    'isys_helper',
                                    'filter_text'
                                ]
                            ]
                        ]
                    ],
                    C__PROPERTY__PROVIDES => $l_provides_all
                ],
                'status'                 => [
                    C__PROPERTY__INFO     => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_STATUS'),
                        'group'                  => 'common_settings'
                    ],
                    C__PROPERTY__DATA     => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATIONS] . '__status',
                        C__PROPERTY__DATA__TYPE  => 'int',
                        'params'                 => [
                            'unsigned'
                        ],
                        'default'                => 1
                    ],
                    C__PROPERTY__UI       => [
                        C__PROPERTY__UI__ID   => 'C__NOTIFICATIONS__NOTIFICATION_STATUS',
                        C__PROPERTY__UI__TYPE => C__PROPERTY__UI__TYPE__CHECKBOX
                    ],
                    C__PROPERTY__CHECK    => [
                        C__PROPERTY__CHECK__MANDATORY  => true,
                        C__PROPERTY__CHECK__VALIDATION => [
                            FILTER_VALIDATE_INT,
                            [
                                'min_range' => 0
                            ]
                        ]
                    ],
                    C__PROPERTY__PROVIDES => $l_provides_all
                ],
                'threshold'              => [
                    C__PROPERTY__INFO     => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_THRESHOLD'),
                        'group'                  => 'common_settings'
                    ],
                    C__PROPERTY__DATA     => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATIONS] . '__threshold',
                        C__PROPERTY__DATA__TYPE  => 'float',
                        'default'                => null
                    ],
                    C__PROPERTY__UI       => [
                        C__PROPERTY__UI__ID   => 'C__NOTIFICATIONS__NOTIFICATION_THRESHOLD',
                        C__PROPERTY__UI__TYPE => C__PROPERTY__UI__TYPE__TEXT
                    ],
                    C__PROPERTY__CHECK    => [
                        C__PROPERTY__CHECK__MANDATORY  => false,
                        C__PROPERTY__CHECK__VALIDATION => [
                            FILTER_VALIDATE_FLOAT
                        ]
                    ],
                    C__PROPERTY__PROVIDES => $l_provides_all
                ],
                'threshold_unit'         => [
                    C__PROPERTY__INFO     => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_THRESHOLD_UNIT'),
                        'group'                  => 'common_settings'
                    ],
                    C__PROPERTY__DATA     => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATIONS] . '__threshold_unit',
                        C__PROPERTY__DATA__TYPE  => 'int',
                        'params'                 => [
                            'unsigned'
                        ],
                        'default'                => null
                    ],
                    C__PROPERTY__UI       => [
                        C__PROPERTY__UI__ID     => 'C__NOTIFICATIONS__NOTIFICATION_THRESHOLD_UNIT',
                        C__PROPERTY__UI__TYPE   => C__PROPERTY__UI__TYPE__DIALOG,
                        C__PROPERTY__UI__PARAMS => [
                            'p_bDbFieldNN' => '0',
                            'p_strClass' => 'input-small'
                        ]
                    ],
                    C__PROPERTY__CHECK    => [
                        C__PROPERTY__CHECK__MANDATORY  => false,
                        C__PROPERTY__CHECK__VALIDATION => [
                            FILTER_VALIDATE_INT,
                            [
                                'options' => ['min_range' => 0]
                            ]
                        ]
                    ],
                    C__PROPERTY__PROVIDES => $l_provides_all & ~self::C__PROPERTY__PROVIDES__DELETE
                ],
                'type'                   => [
                    C__PROPERTY__INFO     => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_TYPE_ID')
                    ],
                    C__PROPERTY__DATA     => [
                        C__PROPERTY__DATA__FIELD      => $this->m_tables[self::NOTIFICATIONS] . '__' . $this->m_tables[self::NOTIFICATION_TYPES] . '__id',
                        C__PROPERTY__DATA__TYPE       => 'int',
                        'params'                      => [
                            'unsigned'
                        ],
                        C__PROPERTY__DATA__REFERENCES => [
                            $this->m_tables[self::NOTIFICATION_TYPES],
                            $this->m_tables[self::NOTIFICATION_TYPES] . '__id'
                        ]
                    ],
                    C__PROPERTY__UI       => [
                        C__PROPERTY__UI__ID     => 'C__NOTIFICATIONS__NOTIFICATION_TYPE_ID',
                        C__PROPERTY__UI__TYPE   => C__PROPERTY__UI__TYPE__TEXT,
                        C__PROPERTY__UI__PARAMS => [
                            'p_bInvisible' => 1
                        ]
                    ],
                    C__PROPERTY__CHECK    => [
                        C__PROPERTY__CHECK__MANDATORY  => true,
                        C__PROPERTY__CHECK__VALIDATION => [
                            FILTER_VALIDATE_INT,
                            [
                                'options' => ['min_range' => 0]
                            ]
                        ]
                    ],
                    C__PROPERTY__PROVIDES => $l_provides_all & ~self::C__PROPERTY__PROVIDES__DELETE
                ],
                'contacts'               => [
                    C__PROPERTY__INFO     => [
                        C__PROPERTY__INFO__TITLE       => $lang->get('LC__NOTIFICATIONS__CONTACTS'),
                        C__PROPERTY__INFO__DESCRIPTION => $lang->get('LC__NOTIFICATIONS__CONTACTS__DESCRIPTION'),
                        'group'                        => 'LC__NOTIFICATIONS__RECEIVERS'
                    ],
                    C__PROPERTY__DATA     => [
                        C__PROPERTY__DATA__FIELD      => $this->m_tables[self::NOTIFICATIONS] . '__' . $this->m_tables['contacts'] . '__id',
                        C__PROPERTY__DATA__TYPE       => 'int',
                        'params'                      => [
                            'unsigned'
                        ],
                        C__PROPERTY__DATA__REFERENCES => [
                            $this->m_tables['contacts'],
                            $this->m_tables['contacts'] . '__id'
                        ],
                        'default'                     => null
                    ],
                    C__PROPERTY__UI       => [
                        C__PROPERTY__UI__ID     => 'C__NOTIFICATIONS__CONTACTS',
                        C__PROPERTY__UI__TYPE   => C__PROPERTY__UI__TYPE__POPUP,
                        C__PROPERTY__UI__PARAMS => [
                            'title'          => 'LC__BROWSER__TITLE__CONTACT',
                            'p_strPopupType' => 'browser_object_ng',
                            'catFilter'      => 'C__CATS__PERSON;C__CATS__PERSON_GROUP;C__CATS__ORGANIZATION',
                            'multiselection' => 'true'
                        ]
                    ],
                    C__PROPERTY__CHECK    => [
                        C__PROPERTY__CHECK__MANDATORY => false,
                        // @todo It's an array of unsigned integers
                        //                        C__PROPERTY__CHECK__VALIDATION => array(
                        //                            FILTER_CALLBACK,
                        //                            array('isys_helper', 'filter_json_array_of_ids')
                        //                        )
                    ],
                    C__PROPERTY__PROVIDES => $l_provides_all
                ],
                'limit'                  => [
                    C__PROPERTY__INFO     => [
                        C__PROPERTY__INFO__TITLE       => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_LIMIT'),
                        'group'                        => 'common_settings',
                        C__PROPERTY__INFO__DESCRIPTION => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_LIMIT__DESCRIPTION')
                    ],
                    C__PROPERTY__DATA     => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATIONS] . '__limit',
                        C__PROPERTY__DATA__TYPE  => 'int',
                        'params'                 => [],
                        'default'                => 1
                    ],
                    C__PROPERTY__UI       => [
                        C__PROPERTY__UI__ID   => 'C__NOTIFICATIONS__NOTIFICATION_LIMIT',
                        C__PROPERTY__UI__TYPE => C__PROPERTY__UI__TYPE__TEXT
                    ],
                    C__PROPERTY__CHECK    => [
                        C__PROPERTY__CHECK__MANDATORY  => true,
                        C__PROPERTY__CHECK__VALIDATION => [
                            FILTER_VALIDATE_INT,
                            []
                        ]
                    ],
                    C__PROPERTY__PROVIDES => $l_provides_all
                ],
                'count'                  => [
                    C__PROPERTY__INFO     => [
                        C__PROPERTY__INFO__TITLE       => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_COUNT'),
                        C__PROPERTY__INFO__DESCRIPTION => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_COUNT__DESCRIPTION'),
                        'group'                        => 'common_settings'
                    ],
                    C__PROPERTY__DATA     => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATIONS] . '__count',
                        C__PROPERTY__DATA__TYPE  => 'int',
                        'params'                 => [
                            // @todo Unsigned includes 0, doesn't it? Why is convert_sql_id turning it into NULL?!?!
                            //'unsigned'
                        ],
                        'default'                => 0
                    ],
                    C__PROPERTY__UI       => [
                        C__PROPERTY__UI__ID     => 'C__NOTIFICATIONS__NOTIFICATION_COUNT',
                        C__PROPERTY__UI__TYPE   => C__PROPERTY__UI__TYPE__TEXT,
                        C__PROPERTY__UI__PARAMS => [
                            'p_bReadonly' => 'true'
                        ]
                    ],
                    C__PROPERTY__CHECK    => [
                        C__PROPERTY__CHECK__MANDATORY  => true,
                        C__PROPERTY__CHECK__VALIDATION => [
                            FILTER_VALIDATE_INT,
                            [
                                'options' => ['min_range' => 0]
                            ]
                        ]
                    ],
                    C__PROPERTY__PROVIDES => $l_provides_all
                ],
                'template_de'            => [
                    C__PROPERTY__INFO     => [
                        C__PROPERTY__INFO__TITLE       => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_TEMPLATE_DE'),
                        C__PROPERTY__INFO__DESCRIPTION => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_TEMPLATE_DE__DESCRIPTION'),
                        'group'                        => 'common_settings'
                    ],
                    C__PROPERTY__DATA     => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATIONS] . '__isys_notification_template__id__de',
                        C__PROPERTY__DATA__TYPE  => 'int',
                        'params'                 => ['unsigned', 'force-id-conversion'], // @see ID-8494 Add 'force-if-conversion' parameter
                    ],
                    C__PROPERTY__UI       => [
                        C__PROPERTY__UI__ID     => 'C__NOTIFICATIONS__NOTIFICATION_TEMPLATE_DE',
                        C__PROPERTY__UI__TYPE   => C__PROPERTY__UI__TYPE__DIALOG,
                        C__PROPERTY__UI__PARAMS => [

                        ]
                    ],
                    C__PROPERTY__CHECK    => [
                        C__PROPERTY__CHECK__MANDATORY  => false,
                        C__PROPERTY__CHECK__VALIDATION => [
                            FILTER_VALIDATE_INT,
                            [
                                'options' => ['min_range' => -1]
                            ]
                        ]
                    ],
                    C__PROPERTY__PROVIDES => $l_provides_all
                ],
                'template_en'            => [
                    C__PROPERTY__INFO     => [
                        C__PROPERTY__INFO__TITLE       => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_TEMPLATE_EN'),
                        C__PROPERTY__INFO__DESCRIPTION => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_TEMPLATE_EN__DESCRIPTION'),
                        'group'                        => 'common_settings'
                    ],
                    C__PROPERTY__DATA     => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATIONS] . '__isys_notification_template__id__en',
                        C__PROPERTY__DATA__TYPE  => 'int',
                        'params'                 => ['unsigned', 'force-id-conversion'], // @see ID-8494 Add 'force-if-conversion' parameter
                    ],
                    C__PROPERTY__UI       => [
                        C__PROPERTY__UI__ID     => 'C__NOTIFICATIONS__NOTIFICATION_TEMPLATE_EN',
                        C__PROPERTY__UI__TYPE   => C__PROPERTY__UI__TYPE__DIALOG,
                        C__PROPERTY__UI__PARAMS => [

                        ]
                    ],
                    C__PROPERTY__CHECK    => [
                        C__PROPERTY__CHECK__MANDATORY  => false,
                        C__PROPERTY__CHECK__VALIDATION => [
                            FILTER_VALIDATE_INT,
                            [
                                'options' => ['min_range' => -1]
                            ]
                        ]
                    ],
                    C__PROPERTY__PROVIDES => $l_provides_all
                ],
                'last_run'               => [
                    C__PROPERTY__INFO     => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_LAST_RUN'),
                        'group'                  => 'common_settings'
                    ],
                    C__PROPERTY__DATA     => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATIONS] . '__last_run',
                        C__PROPERTY__DATA__TYPE  => 'datetime',
                        'default'                => null
                    ],
                    C__PROPERTY__UI       => [
                        C__PROPERTY__UI__ID     => 'C__NOTIFICATIONS__NOTIFICATION_LAST_RUN',
                        C__PROPERTY__UI__TYPE   => C__PROPERTY__UI__TYPE__POPUP,
                        C__PROPERTY__UI__PARAMS => [
                            'p_strPopupType' => 'calendar',
                            'p_bTime'        => '1',
                            'p_bReadonly'    => 'true',
                            'p_strClass'     => 'input-medium'
                        ]
                    ],
                    C__PROPERTY__CHECK    => [
                        C__PROPERTY__CHECK__MANDATORY  => false,
                        C__PROPERTY__CHECK__VALIDATION => [
                            FILTER_CALLBACK,
                            [
                                'options' => [
                                    'isys_helper',
                                    'filter_date'
                                ]
                            ]
                        ]
                    ],
                    C__PROPERTY__PROVIDES => $l_provides_all
                ],
                'interval_start'         => [
                    C__PROPERTY__INFO     => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_INTERVAL_START'),
                        'group'                  => 'common_settings'
                    ],
                    C__PROPERTY__DATA     => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATIONS] . '__interval_start',
                        C__PROPERTY__DATA__TYPE  => 'datetime',
                        'default'                => null
                    ],
                    C__PROPERTY__UI       => [
                        C__PROPERTY__UI__ID     => 'C__NOTIFICATIONS__INTERVAL_START',
                        C__PROPERTY__UI__TYPE   => C__PROPERTY__UI__TYPE__POPUP,
                        C__PROPERTY__UI__PARAMS => [
                            'p_strPopupType' => 'calendar',
                            'p_strClass'     => 'input-medium'
                        ]
                    ],
                    C__PROPERTY__CHECK    => [
                        C__PROPERTY__CHECK__MANDATORY  => false,
                        C__PROPERTY__CHECK__VALIDATION => [
                            FILTER_CALLBACK,
                            [
                                'options' => [
                                    'isys_helper',
                                    'filter_date'
                                ]
                            ]
                        ]
                    ],
                    C__PROPERTY__PROVIDES => $l_provides_all
                ],
                'interval_config'        => [
                    C__PROPERTY__INFO     => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_INTERVAL_CONFIG'),
                        'group'                  => 'common_settings'
                    ],
                    C__PROPERTY__DATA     => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATIONS] . '__interval_config',
                        C__PROPERTY__DATA__TYPE  => 'text'
                    ],
                    C__PROPERTY__UI       => [
                        C__PROPERTY__UI__ID     => 'C__NOTIFICATIONS__INTERVAL_CONFIG',
                        C__PROPERTY__UI__TYPE   => C__PROPERTY__UI__TYPE__POPUP,
                        C__PROPERTY__UI__PARAMS => [
                            'p_strPopupType' => 'interval'
                        ]
                    ],
                    C__PROPERTY__CHECK    => [
                        C__PROPERTY__CHECK__MANDATORY => false,
                        // C__PROPERTY__CHECK__VALIDATION => [
                        //     FILTER_CALLBACK,
                        //     [
                        //         'options' => [
                        //             'isys_helper',
                        //             'filter_date'
                        //         ]
                        //     ]
                        // ]
                    ],
                    C__PROPERTY__PROVIDES => $l_provides_all
                ],
                'description'            => [
                    C__PROPERTY__INFO     => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_DESCRIPTION'),
                        'group'                  => 'common_settings'
                    ],
                    C__PROPERTY__DATA     => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATIONS] . '__description',
                        C__PROPERTY__DATA__TYPE  => 'text',
                        'default'                => null
                    ],
                    C__PROPERTY__UI       => [
                        C__PROPERTY__UI__ID     => 'C__NOTIFICATIONS__NOTIFICATION_DESCRIPTION',
                        C__PROPERTY__UI__TYPE   => C__PROPERTY__UI__TYPE__TEXTAREA,
                        C__PROPERTY__UI__PARAMS => [
                            'p_nRows' => '3',
                            'p_nCols' => '55'
                        ]
                    ],
                    C__PROPERTY__CHECK    => [
                        C__PROPERTY__CHECK__MANDATORY  => false,
                        C__PROPERTY__CHECK__VALIDATION => [
                            FILTER_CALLBACK,
                            [
                                'options' => [
                                    'isys_helper',
                                    'filter_textarea'
                                ]
                            ]
                        ]
                    ],
                    C__PROPERTY__PROVIDES => $l_provides_all
                ],
                // @see  ID-4889
                'object_status_normal'   => [
                    C__PROPERTY__INFO     => [
                        C__PROPERTY__INFO__TITLE       => $lang->get('LC__NOTIFICATIONS__DOMAIN_OBJECTS_STATUS_NORMAL'),
                        C__PROPERTY__INFO__DESCRIPTION => $lang->get('LC__NOTIFICATIONS__DOMAIN_OBJECTS_STATUS_NORMAL__DESCRIPTION'),
                        'group'                        => 'LC__NOTIFICATIONS__NOTIFICATION_DOMAINS'
                    ],
                    C__PROPERTY__DATA     => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATIONS] . '__only_normal',
                        C__PROPERTY__DATA__TYPE  => 'int',
                        'params'                 => [],
                        'default'                => '0'
                    ],
                    C__PROPERTY__UI       => [
                        C__PROPERTY__UI__ID     => 'C__NOTIFICATIONS__DOMAIN_OBJECTS_STATUS_NORMAL',
                        C__PROPERTY__UI__TYPE   => C__PROPERTY__UI__TYPE__DIALOG,
                        C__PROPERTY__UI__PARAMS => [
                            'p_bDbFieldNN' => true,
                            'p_arData'     => get_smarty_arr_YES_NO()
                        ],
                        'post'                  => 'C__NOTIFICATIONS__DOMAIN_OBJECTS_STATUS_NORMAL'
                    ],
                    C__PROPERTY__CHECK    => [
                        C__PROPERTY__CHECK__MANDATORY  => false,
                        C__PROPERTY__CHECK__VALIDATION => [
                            FILTER_VALIDATE_INT,
                            [
                                'options' => ['min_range' => 0, 'max_range' => 1]
                            ]
                        ]
                    ],
                    C__PROPERTY__PROVIDES => $l_provides_all
                ],
                'receiver_calc_strategy' => [
                    C__PROPERTY__INFO     => [
                        C__PROPERTY__INFO__TITLE       => $lang->get('LC__NOTIFICATIONS__RECEIVER_CALC_STRATEGY'),
                        C__PROPERTY__INFO__DESCRIPTION => $lang->get('LC__NOTIFICATIONS__RECEIVER_CALC_STRATEGY__DESCRIPTION'),
                        'group'                        => 'common_settings'
                    ],
                    C__PROPERTY__DATA     => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATIONS] . '__receiver_calc_strategy',
                        C__PROPERTY__DATA__TYPE  => 'int',
                        'params'                 => [
                            'unsigned'
                        ],
                    ],
                    C__PROPERTY__UI       => [
                        C__PROPERTY__UI__ID     => 'C__NOTIFICATIONS__RECEIVER_CALC_STRATEGY',
                        C__PROPERTY__UI__TYPE   => C__PROPERTY__UI__TYPE__DIALOG,
                        C__PROPERTY__UI__PARAMS => [
                            'p_bDbFieldNN' => false,
                            'p_arData'     => self::getReceiverCalcStrategy()
                        ]
                    ],
                    C__PROPERTY__CHECK    => [
                        C__PROPERTY__CHECK__MANDATORY  => false,
                        C__PROPERTY__CHECK__VALIDATION => [
                            FILTER_VALIDATE_INT,
                            [
                                'options' => ['min_range' => -1]
                            ]
                        ]
                    ],
                    C__PROPERTY__PROVIDES => $l_provides_all
                ]
            ],
            self::NOTIFICATION_TYPES     => [
                'id'           => [
                    C__PROPERTY__INFO => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_TYPE_ID')
                    ],
                    C__PROPERTY__DATA => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATION_TYPES] . '__id',
                        C__PROPERTY__DATA__TYPE  => 'int'
                    ]
                ],
                'title'        => [
                    C__PROPERTY__INFO => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_TYPE_TITLE')
                    ],
                    C__PROPERTY__DATA => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATION_TYPES] . '__title',
                        C__PROPERTY__DATA__TYPE  => 'varchar'
                    ]
                ],
                'description'  => [
                    C__PROPERTY__INFO => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_TYPE_DESCRIPTION')
                    ],
                    C__PROPERTY__DATA => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATION_TYPES] . '__description',
                        C__PROPERTY__DATA__TYPE  => 'text'
                    ]
                ],
                'status'       => [
                    C__PROPERTY__INFO => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_TYPE_STATUS')
                    ],
                    C__PROPERTY__DATA => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATION_TYPES] . '__status',
                        C__PROPERTY__DATA__TYPE  => 'int'
                    ]
                ],
                'callback'     => [
                    C__PROPERTY__INFO => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_TYPE_CALLBACK')
                    ],
                    C__PROPERTY__DATA => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATION_TYPES] . '__callback',
                        C__PROPERTY__DATA__TYPE  => 'varchar'
                    ]
                ],
                'domains'      => [
                    C__PROPERTY__INFO => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_TYPE_DOMAINS')
                    ],
                    C__PROPERTY__DATA => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATION_TYPES] . '__domains',
                        C__PROPERTY__DATA__TYPE  => 'int'
                    ]
                ],
                'unit'         => [
                    C__PROPERTY__INFO => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_TYPE_UNIT')
                    ],
                    C__PROPERTY__DATA => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATION_TYPES] . '__' . $this->m_tables[self::NOTIFICATION_UNITS] . '__id',
                        C__PROPERTY__DATA__TYPE  => 'int'
                    ]
                ],
                'default_unit' => [
                    C__PROPERTY__INFO => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_TYPE_DEFAULT_UNIT')
                    ],
                    C__PROPERTY__DATA => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATION_TYPES] . '__default_unit',
                        C__PROPERTY__DATA__TYPE  => 'int'
                    ]
                ],
                'threshold'    => [
                    C__PROPERTY__INFO => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_THRESHOLD')
                    ],
                    C__PROPERTY__DATA => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATION_TYPES] . '__threshold',
                        C__PROPERTY__DATA__TYPE  => 'int'
                    ]
                ]
            ],
            self::NOTIFICATION_DOMAINS   => [
                'id'           => [
                    C__PROPERTY__INFO     => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__NOTIFICATIONS__DOMAIN_ID')
                    ],
                    C__PROPERTY__DATA     => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATION_DOMAINS] . '__id',
                        C__PROPERTY__DATA__TYPE  => 'int',
                        'params'                 => [
                            'primary_key',
                            'unsigned',
                            'auto_increment',
                            'unique'
                        ]
                    ],
                    C__PROPERTY__UI       => [
                        C__PROPERTY__UI__ID     => 'C__NOTIFICATIONS__DOMAIN_ID',
                        C__PROPERTY__UI__TYPE   => C__PROPERTY__UI__TYPE__TEXT,
                        C__PROPERTY__UI__PARAMS => [
                            'p_bInvisible' => 1
                        ]
                    ],
                    C__PROPERTY__CHECK    => [
                        C__PROPERTY__CHECK__MANDATORY  => true,
                        C__PROPERTY__CHECK__VALIDATION => [
                            FILTER_VALIDATE_INT,
                            [
                                'options' => ['min_range' => 0]
                            ]
                        ]
                    ],
                    C__PROPERTY__PROVIDES => self::C__PROPERTY__PROVIDES__VIEW
                ],
                'notification' => [
                    C__PROPERTY__INFO     => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_ID')
                    ],
                    C__PROPERTY__DATA     => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATION_DOMAINS] . '__' . $this->m_tables[self::NOTIFICATIONS] . '__id',
                        C__PROPERTY__DATA__TYPE  => 'int',
                        'params'                 => [
                            'unsigned'
                        ],
                        'default'                => null
                    ],
                    C__PROPERTY__CHECK    => [
                        C__PROPERTY__CHECK__MANDATORY  => true,
                        C__PROPERTY__CHECK__VALIDATION => [
                            FILTER_VALIDATE_INT,
                            [
                                'options' => ['min_range' => 0]
                            ]
                        ]
                    ],
                    C__PROPERTY__PROVIDES => $l_provides_all
                ],
                'objects'      => [
                    C__PROPERTY__INFO     => [
                        C__PROPERTY__INFO__TITLE       => $lang->get('LC__NOTIFICATIONS__DOMAIN_OBJECTS'),
                        C__PROPERTY__INFO__DESCRIPTION => $lang->get('LC__NOTIFICATIONS__DOMAIN_OBJECTS__DESCRIPTION'),
                        'group'                        => 'LC__NOTIFICATIONS__NOTIFICATION_DOMAINS'

                    ],
                    C__PROPERTY__DATA     => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATION_DOMAINS] . '__' . $this->m_tables['objects'] . '__id',
                        C__PROPERTY__DATA__TYPE  => 'int',
                        'params'                 => [
                            'unsigned'
                        ],
                        'default'                => null
                    ],
                    C__PROPERTY__UI       => [
                        C__PROPERTY__UI__ID     => 'C__NOTIFICATIONS__DOMAIN_OBJECTS',
                        C__PROPERTY__UI__TYPE   => C__PROPERTY__UI__TYPE__POPUP,
                        C__PROPERTY__UI__PARAMS => [
                            'title'          => 'LC__BROWSER__TITLE__CONTACT',
                            'p_strPopupType' => 'browser_object_ng',
                            'multiselection' => 'true'
                        ]
                    ],
                    C__PROPERTY__CHECK    => [
                        C__PROPERTY__CHECK__MANDATORY => false,
                        // @todo It's an array of unsigned integers
                        //                        C__PROPERTY__CHECK__VALIDATION => array(
                        //                            FILTER_CALLBACK,
                        //                            array(
                        //                                'options' => array('isys_helper', 'filter_list_of_ids')
                        //                            )
                        //                        )
                    ],
                    C__PROPERTY__PROVIDES => $l_provides_all
                ],
                'object_types' => [
                    C__PROPERTY__INFO     => [
                        C__PROPERTY__INFO__TITLE       => $lang->get('LC__NOTIFICATIONS__DOMAIN_OBJECT_TYPES'),
                        C__PROPERTY__INFO__DESCRIPTION => $lang->get('LC__NOTIFICATIONS__DOMAIN_OBJECT_TYPES__DESCRIPTION'),
                        'group'                        => 'LC__NOTIFICATIONS__NOTIFICATION_DOMAINS'
                    ],
                    C__PROPERTY__DATA     => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATION_DOMAINS] . '__' . $this->m_tables['object_types'] . '__id',
                        C__PROPERTY__DATA__TYPE  => 'int',
                        'params'                 => [
                            'unsigned'
                        ],
                        'default'                => null
                    ],
                    C__PROPERTY__UI       => [
                        C__PROPERTY__UI__ID     => 'C__NOTIFICATIONS__DOMAIN_OBJECT_TYPES',
                        C__PROPERTY__UI__TYPE   => C__PROPERTY__UI__TYPE__DIALOG_LIST,
                        C__PROPERTY__UI__PARAMS => [
                            'p_bLinklist' => '1'
                        ],
                        'post'                  => 'C__NOTIFICATIONS__DOMAIN_OBJECT_TYPES__selected_values'
                    ],
                    C__PROPERTY__CHECK    => [
                        C__PROPERTY__CHECK__MANDATORY  => false,
                        C__PROPERTY__CHECK__VALIDATION => [
                            FILTER_CALLBACK,
                            [
                                'options' => [
                                    'isys_helper',
                                    'filter_list_of_ids'
                                ]
                            ]
                        ]
                    ],
                    C__PROPERTY__PROVIDES => $l_provides_all
                ],
                'reports'      => [
                    C__PROPERTY__INFO     => [
                        C__PROPERTY__INFO__TITLE       => $lang->get('LC__NOTIFICATIONS__DOMAIN_REPORTS'),
                        C__PROPERTY__INFO__DESCRIPTION => $lang->get('LC__NOTIFICATIONS__DOMAIN_REPORTS__DESCRIPTION'),
                        'group'                        => 'LC__NOTIFICATIONS__NOTIFICATION_DOMAINS'
                    ],
                    C__PROPERTY__DATA     => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATION_DOMAINS] . '__' . $this->m_tables['reports'] . '__id',
                        C__PROPERTY__DATA__TYPE  => 'int',
                        'params'                 => [
                            'unsigned'
                        ],
                        'default'                => null
                    ],
                    C__PROPERTY__UI       => [
                        C__PROPERTY__UI__ID     => 'C__NOTIFICATIONS__DOMAIN_REPORTS',
                        C__PROPERTY__UI__TYPE   => C__PROPERTY__UI__TYPE__DIALOG_LIST,
                        C__PROPERTY__UI__PARAMS => [
                            'p_bLinklist' => '1'
                        ],
                        'post'                  => 'C__NOTIFICATIONS__DOMAIN_REPORTS__selected_values'
                    ],
                    C__PROPERTY__CHECK    => [
                        C__PROPERTY__CHECK__MANDATORY  => false,
                        C__PROPERTY__CHECK__VALIDATION => [
                            FILTER_CALLBACK,
                            [
                                'options' => [
                                    'isys_helper',
                                    'filter_list_of_ids'
                                ]
                            ]
                        ]
                    ],
                    C__PROPERTY__PROVIDES => $l_provides_all
                ]
            ],
            self::NOTIFICATION_ROLES     => [
                'id'           => [
                    C__PROPERTY__INFO     => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__NOTIFICATIONS__ROLE_ID'),
                        'group'                  => 'LC__NOTIFICATIONS__RECEIVERS'
                    ],
                    C__PROPERTY__DATA     => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATION_ROLES] . '__id',
                        C__PROPERTY__DATA__TYPE  => 'int',
                        'params'                 => [
                            'primary_key',
                            'unsigned',
                            'auto_increment',
                            'unique'
                        ]
                    ],
                    C__PROPERTY__CHECK    => [
                        C__PROPERTY__CHECK__MANDATORY  => true,
                        C__PROPERTY__CHECK__VALIDATION => [
                            FILTER_VALIDATE_INT,
                            [
                                'options' => ['min_range' => 0]
                            ]
                        ]
                    ],
                    C__PROPERTY__PROVIDES => self::C__PROPERTY__PROVIDES__VIEW
                ],
                'notification' => [
                    C__PROPERTY__INFO     => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_ID'),
                        'group'                  => 'LC__NOTIFICATIONS__RECEIVERS'
                    ],
                    C__PROPERTY__DATA     => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATION_ROLES] . '__' . $this->m_tables[self::NOTIFICATIONS] . '__id',
                        C__PROPERTY__DATA__TYPE  => 'int',
                        'params'                 => [
                            'unsigned'
                        ]
                    ],
                    C__PROPERTY__CHECK    => [
                        C__PROPERTY__CHECK__MANDATORY  => true,
                        C__PROPERTY__CHECK__VALIDATION => [
                            FILTER_VALIDATE_INT,
                            [
                                'options' => ['min_range' => 0]
                            ]
                        ]
                    ],
                    C__PROPERTY__PROVIDES => $l_provides_all
                ],
                'roles'        => [
                    C__PROPERTY__INFO     => [
                        C__PROPERTY__INFO__TITLE       => $lang->get('LC__NOTIFICATIONS__ASSIGNED_ROLES'),
                        C__PROPERTY__INFO__DESCRIPTION => $lang->get('LC__NOTIFICATIONS__ASSIGNED_ROLES__DESCRIPTION'),
                        'group'                        => 'LC__NOTIFICATIONS__RECEIVERS'
                    ],
                    C__PROPERTY__DATA     => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATION_ROLES] . '__' . $this->m_tables['roles'] . '__id',
                        C__PROPERTY__DATA__TYPE  => 'int',
                        'params'                 => [
                            'unsigned'
                        ]
                    ],
                    C__PROPERTY__UI       => [
                        C__PROPERTY__UI__ID     => 'C__NOTIFICATIONS__ASSIGNED_ROLES',
                        C__PROPERTY__UI__TYPE   => C__PROPERTY__UI__TYPE__DIALOG_LIST,
                        C__PROPERTY__UI__PARAMS => [
                            'p_bLinklist'  => '1',
                            'p_bDbFieldNN' => '1'
                        ],
                        'post'                  => 'C__NOTIFICATIONS__ASSIGNED_ROLES__selected_values'
                    ],
                    C__PROPERTY__CHECK    => [
                        C__PROPERTY__CHECK__MANDATORY  => false,
                        C__PROPERTY__CHECK__VALIDATION => [
                            FILTER_CALLBACK,
                            [
                                'options' => [
                                    'isys_helper',
                                    'filter_list_of_ids'
                                ]
                            ]
                        ]
                    ],
                    C__PROPERTY__PROVIDES => $l_provides_all
                ]
            ],
            self::NOTIFICATION_TEMPLATES => [
                'id'                => [
                    C__PROPERTY__INFO     => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__NOTIFICATIONS__TEMPLATE_ID')
                    ],
                    C__PROPERTY__DATA     => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATION_TEMPLATES] . '__id',
                        C__PROPERTY__DATA__TYPE  => 'int',
                        'params'                 => [
                            'primary_key',
                            'unsigned',
                            'auto_increment',
                            'unique'
                        ]
                    ],
                    C__PROPERTY__UI       => [
                        C__PROPERTY__UI__ID     => 'C__NOTIFICATIONS__TEMPLATE_ID',
                        C__PROPERTY__UI__TYPE   => C__PROPERTY__UI__TYPE__TEXT,
                        C__PROPERTY__UI__PARAMS => [
                            'p_bInvisible' => 1
                        ]
                    ],
                    C__PROPERTY__CHECK    => [
                        C__PROPERTY__CHECK__MANDATORY  => true,
                        C__PROPERTY__CHECK__VALIDATION => [
                            FILTER_VALIDATE_INT,
                            [
                                'options' => ['min_range' => 0]
                            ]
                        ]
                    ],
                    C__PROPERTY__PROVIDES => self::C__PROPERTY__PROVIDES__VIEW
                ],
                'notification_type' => [
                    C__PROPERTY__INFO     => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_TYPE_ID')
                    ],
                    C__PROPERTY__DATA     => [
                        C__PROPERTY__DATA__FIELD      => $this->m_tables[self::NOTIFICATION_TEMPLATES] . '__' . $this->m_tables[self::NOTIFICATION_TYPES] . '__id',
                        C__PROPERTY__DATA__TYPE       => 'int',
                        'params'                      => [
                            'unsigned'
                        ],
                        C__PROPERTY__DATA__REFERENCES => [
                            $this->m_tables[self::NOTIFICATION_TYPES],
                            $this->m_tables[self::NOTIFICATION_TYPES] . '__id'
                        ]
                    ],
                    C__PROPERTY__UI       => [
                        C__PROPERTY__UI__ID     => 'C__NOTIFICATIONS__NOTIFICATION_TYPE_ID',
                        C__PROPERTY__UI__TYPE   => C__PROPERTY__UI__TYPE__TEXT,
                        C__PROPERTY__UI__PARAMS => [
                            'p_bInvisible' => 1
                        ]
                    ],
                    C__PROPERTY__CHECK    => [
                        C__PROPERTY__CHECK__MANDATORY  => true,
                        C__PROPERTY__CHECK__VALIDATION => [
                            FILTER_VALIDATE_INT,
                            [
                                'options' => ['min_range' => 0]
                            ]
                        ]
                    ],
                    C__PROPERTY__PROVIDES => $l_provides_all & ~self::C__PROPERTY__PROVIDES__DELETE
                ],
                'locale'            => [
                    C__PROPERTY__INFO     => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_TEMPLATE_LOCALE'),
                        'group'                  => 'common_settings'
                    ],
                    C__PROPERTY__DATA     => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATION_TEMPLATES] . '__locale',
                        C__PROPERTY__DATA__TYPE  => 'varchar'
                    ],
                    C__PROPERTY__UI       => [
                        C__PROPERTY__UI__ID     => 'C__NOTIFICATIONS__NOTIFICATION_TEMPLATE_LOCALE',
                        C__PROPERTY__UI__TYPE   => C__PROPERTY__UI__TYPE__DIALOG,
                        C__PROPERTY__UI__PARAMS => [
                            'p_bDbFieldNN' => '1',
                            'p_arData'     => $this->get_locales()
                        ]
                    ],
                    C__PROPERTY__CHECK    => [
                        C__PROPERTY__CHECK__MANDATORY  => true,
                        C__PROPERTY__CHECK__VALIDATION => [
                            FILTER_CALLBACK,
                            [
                                'options' => [
                                    'isys_helper',
                                    'filter_text'
                                ]
                            ]
                        ]
                    ],
                    C__PROPERTY__PROVIDES => $l_provides_all
                ],
                'subject'           => [
                    C__PROPERTY__INFO     => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_TEMPLATE_SUBJECT'),
                        'group'                  => 'common_settings'
                    ],
                    C__PROPERTY__DATA     => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATION_TEMPLATES] . '__subject',
                        C__PROPERTY__DATA__TYPE  => 'varchar',
                        'default'                => '%notifications__title%'
                    ],
                    C__PROPERTY__UI       => [
                        C__PROPERTY__UI__ID     => 'C__NOTIFICATIONS__NOTIFICATION_TEMPLATE_SUBJECT',
                        C__PROPERTY__UI__TYPE   => C__PROPERTY__UI__TYPE__TEXT,
                        C__PROPERTY__UI__PARAMS => []
                    ],
                    C__PROPERTY__CHECK    => [
                        C__PROPERTY__CHECK__MANDATORY  => true,
                        C__PROPERTY__CHECK__VALIDATION => [
                            FILTER_CALLBACK,
                            [
                                'options' => [
                                    'isys_helper',
                                    'filter_text'
                                ]
                            ]
                        ]
                    ],
                    C__PROPERTY__PROVIDES => $l_provides_all
                ],
                'text'              => [
                    C__PROPERTY__INFO     => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_TEMPLATE_TEXT'),
                        'group'                  => 'common_settings'
                    ],
                    C__PROPERTY__DATA     => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATION_TEMPLATES] . '__text',
                        C__PROPERTY__DATA__TYPE  => 'text'
                    ],
                    C__PROPERTY__UI       => [
                        C__PROPERTY__UI__ID     => 'C__NOTIFICATIONS__NOTIFICATION_TEMPLATE_TEXT',
                        C__PROPERTY__UI__TYPE   => C__PROPERTY__UI__TYPE__TEXTAREA,
                        C__PROPERTY__UI__PARAMS => [
                            'p_nRows' => '10',
                            'p_nCols' => '55'
                        ]
                    ],
                    C__PROPERTY__CHECK    => [
                        C__PROPERTY__CHECK__MANDATORY  => true,
                        C__PROPERTY__CHECK__VALIDATION => [
                            FILTER_CALLBACK,
                            [
                                'options' => [
                                    'isys_helper',
                                    'filter_textarea'
                                ]
                            ]
                        ]
                    ],
                    C__PROPERTY__PROVIDES => $l_provides_all
                ],
                'report'            => [
                    C__PROPERTY__INFO     => [
                        C__PROPERTY__INFO__TITLE       => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_TEMPLATE_REPORT'),
                        'group'                        => 'common_settings',
                        C__PROPERTY__INFO__DESCRIPTION => $lang->get('LC__NOTIFICATIONS__NOTIFICATION_TEMPLATE_REPORT__DESCRIPTION')
                    ],
                    C__PROPERTY__DATA     => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATION_TEMPLATES] . '__report',
                        C__PROPERTY__DATA__TYPE  => 'text'
                    ],
                    C__PROPERTY__UI       => [
                        C__PROPERTY__UI__ID     => 'C__NOTIFICATIONS__NOTIFICATION_TEMPLATE_REPORT',
                        C__PROPERTY__UI__TYPE   => C__PROPERTY__UI__TYPE__PROPERTY_SELECTOR,
                        C__PROPERTY__UI__PARAMS => [
                            'provide'  => C__PROPERTY__PROVIDES__REPORT,
                            'sortable' => true,
                            'grouping' => 0
                        ],
                        'post'                  => 'C__NOTIFICATIONS__DOMAIN_REPORTS__HIDDEN'
                    ],
                    C__PROPERTY__CHECK    => [
                        C__PROPERTY__CHECK__MANDATORY  => false,
                        C__PROPERTY__CHECK__VALIDATION => [
                            FILTER_CALLBACK,
                            [
                                'options' => [
                                    'isys_helper',
                                    'filter_property_selector'
                                ]
                            ]
                        ]
                    ],
                    C__PROPERTY__PROVIDES => $l_provides_all
                ]
            ],
            self::NOTIFICATION_UNITS                  => [
                'id'          => [
                    C__PROPERTY__INFO => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__UNITS__ID')
                    ],
                    C__PROPERTY__DATA => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATION_UNITS] . '__id',
                        C__PROPERTY__DATA__TYPE  => 'int'
                    ]
                ],
                'title'       => [
                    C__PROPERTY__INFO => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__UNITS__TITLE')
                    ],
                    C__PROPERTY__DATA => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATION_UNITS] . '__title',
                        C__PROPERTY__DATA__TYPE  => 'varchar'
                    ]
                ],
                'description' => [
                    C__PROPERTY__INFO => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__UNITS__DESCRIPTION')
                    ],
                    C__PROPERTY__DATA => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATION_UNITS] . '__description',
                        C__PROPERTY__DATA__TYPE  => 'text'
                    ]
                ],
                'table'       => [
                    C__PROPERTY__INFO => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__UNITS__TABLE')
                    ],
                    C__PROPERTY__DATA => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATION_UNITS] . '__table',
                        C__PROPERTY__DATA__TYPE  => 'varchar'
                    ]
                ],
                'default'     => [
                    C__PROPERTY__INFO => [
                        C__PROPERTY__INFO__TITLE => $lang->get('LC__UNITS__DEFAULT')
                    ],
                    C__PROPERTY__DATA => [
                        C__PROPERTY__DATA__FIELD => $this->m_tables[self::NOTIFICATION_UNITS] . '__default',
                        C__PROPERTY__DATA__TYPE  => 'int'
                    ]
                ]
            ]
        ];
    }

    /**
     * Helps getter methods to fetch data from database.
     *
     * @param string $p_property_type Property type (e. g. 'notifications')
     * @param int    $p_id            (optional) Select special identifier. If not set
     *                                (default), all identifiers will be selected.
     * @param bool   $p_raw           (optional) Returns unformatted ouput. Defaults to
     *                                false.
     * @param bool   $p_as_result_set (optional) Returns fetched data as result
     *                                set. Defaults to false.
     *
     * @return mixed Associative array or result set (isys_component_dao_result)
     * @throws isys_exception_database
     */
    protected function fetch_properties($p_property_type, $p_id = null, $p_raw = false, $p_as_result_set = false)
    {
        assert(is_bool($p_raw));

        if (isset($p_id)) {
            assert(is_int($p_id) && $p_id >= 0);
        }

        if (!isset($this->m_cache[$p_property_type]) || (isset($p_id) && !isset($this->m_cache[$p_property_type][$p_id]))) {
            $l_table = $this->m_tables[$p_property_type];

            $l_query = 'SELECT * FROM ' . $l_table;

            if (isset($p_id)) {
                $l_query .= ' WHERE ' . $l_table . '__id = ' . $this->convert_sql_id($p_id);
            }

            $l_query .= ';';

            if ($p_as_result_set) {
                return $this->retrieve($l_query);
            }

            if (isset($p_id)) {
                $this->m_cache[$p_property_type][$p_id] = $this->retrieve($l_query)
                    ->__to_array();
            } else {
                $l_result = $this->retrieve($l_query)
                    ->__as_array();

                foreach ($l_result as $l_entity) {
                    $this->m_cache[$p_property_type][$l_entity[$l_table . '__id']] = $l_entity;
                }
            }
        }

        if ($p_raw) {
            if (isset($p_id)) {
                return $this->m_cache[$p_property_type][$p_id];
            }

            return $this->m_cache[$p_property_type];
        }

        if (isset($p_id)) {
            return $this->map_properties($p_property_type, $this->m_cache[$p_property_type][$p_id]);
        }

        $l_result = [];

        foreach ($this->m_cache[$p_property_type] as $l_id => $l_values) {
            $l_result[$l_id] = $this->map_properties($p_property_type, $l_values);
        }

        return $l_result;
    }

    /**
     * Fetches receivers from database based on assigned roles.
     *
     * @param int $notificationId Notification identifier
     *
     * @return array Object identifiers (integers)
     * @throws isys_exception_database
     */
    protected function get_receivers_by_roles($notificationId)
    {
        $this->m_log->debug('Fetching receivers from database based on assigned roles...');

        $roles = $this->get_roles($notificationId);
        $objects = $this->get_objects($notificationId);

        if (count($roles) == 0 || count($objects) == 0) {
            $this->m_log->debug('There are no receivers.');

            return [];
        }

        $contactDao = isys_cmdb_dao_category_g_contact::instance($this->m_db);
        $contactList = $contactDao->get_table();

        $queryObjects = [];
        foreach ($objects as $object) {
            $objectId = intval($object[$this->m_tables['objects'] . '__id']);
            $queryObjects[] = $contactList . '__' . $this->m_tables['objects'] . '__id = ' . $this->convert_sql_id($objectId);
        }

        $queryRoles = [];
        foreach ($roles as $l_role) {
            $queryRoles[] = $contactList . '__' . $this->m_tables['roles'] . '__id = ' . $this->convert_sql_id($l_role[$this->m_tables['roles'] . '__id']);
        }

        $query = 'SELECT ' . $this->m_tables['objects'] . '__id, ' . $this->m_tables['objects'] . '__' . $this->m_tables['object_types'] . '__id, ' .
            $this->m_tables['object_types'] . '__isysgui_cats__id, GROUP_CONCAT(' . $contactList . '__isys_obj__id) AS assigned_objects
            FROM ' . $contactList . '
                INNER JOIN isys_connection ON isys_connection__id = ' . $contactList . '__isys_connection__id
                INNER JOIN ' . $this->m_tables['objects'] . ' ON ' . $this->m_tables['objects'] . '__id = isys_connection__' . $this->m_tables['objects'] . '__id
                INNER JOIN ' . $this->m_tables['object_types'] . ' ON ' . $this->m_tables['object_types'] . '__id = ' . $this->m_tables['objects'] . '__' .
            $this->m_tables['object_types'] . '__id
            WHERE ' . $contactList . '__status = ' . $this->convert_sql_id(C__RECORD_STATUS__NORMAL) . '
                AND (' . implode(' OR ', $queryObjects) . ')
                AND (' . implode(' OR ', $queryRoles) . ')
            GROUP BY ' . $this->m_tables['objects'] . '__id';
        $contacts = $this->retrieve($query)
            ->__as_array();

        return $this->resolve_receivers_from_contacts($contacts);
    }

    /**
     * Resolves receivers from contacts.
     *
     * @param array $p_contacts Contacts
     *
     * @return array Object identifiers
     * @throws isys_exception_database
     */
    protected function resolve_receivers_from_contacts($p_contacts)
    {
        $l_receivers = [];

        $l_num = count($p_contacts);
        if ($l_num == 0) {
            $this->m_log->debug('There are no contacts.');

            return $l_receivers;
        } elseif ($l_num == 1) {
            $this->m_log->debug('There is 1 contact.');
        } else {
            $this->m_log->debug(sprintf('There are %s contacts', $l_num));
        }

        $l_organizations = [];
        $l_groups = [];

        foreach ($p_contacts as $l_contact) {
            // Switch between by specific category organizations, persons, and person groups:
            $catsId = $l_contact[$this->m_tables['object_types'] . '__isysgui_cats__id'];
            if (is_value_in_constants($catsId, ['C__CATS__ORGANIZATION', 'C__CATS__ORGANIZATION_MASTER_DATA'])) {
                $l_organizations[] = $l_contact[$this->m_tables['objects'] . '__id'];

                // Set assigned objects
                if (isset($l_contact['assigned_objects'])) {
                    self::$m_receivers_roles_object_mapping[$l_contact[$this->m_tables['objects'] . '__id']] = explode(',', $l_contact['assigned_objects']);
                }
            } elseif (is_value_in_constants($catsId, ['C__CATS__PERSON', 'C__CATS__PERSON_MASTER'])) {
                // Add person directly to receivers:
                $l_receivers[] = $l_contact[$this->m_tables['objects'] . '__id'];
                if (isset($l_contact['assigned_objects'])) {
                    self::$m_receivers_roles_object_mapping[$l_contact[$this->m_tables['objects'] . '__id']] =
                        array_unique(
                            array_merge(
                                self::$m_receivers_roles_object_mapping[$l_contact[$this->m_tables['objects'] . '__id']] ?:[],
                                explode(',', $l_contact['assigned_objects'])
                            )
                        );
                }
            } elseif (is_value_in_constants($catsId, ['C__CATS__PERSON_GROUP', 'C__CATS__PERSON_GROUP_MASTER'])) {
                $l_groups[] = $l_contact[$this->m_tables['objects'] . '__id'];

                // Set assigned objects
                if (isset($l_contact['assigned_objects'])) {
                    self::$m_receivers_roles_object_mapping[$l_contact[$this->m_tables['objects'] . '__id']] = explode(',', $l_contact['assigned_objects']);
                }
            }
        }

        // Fetch organization assigned persons:
        if (count($l_organizations) > 0) {
            $l_assignments = [];

            foreach ($l_organizations as $l_organization) {
                $l_assignments[] = 'isys_cats_organization_list__isys_obj__id = ' . $this->convert_sql_id($l_organization);
            }

            $l_query = 'SELECT isys_cats_person_list__isys_obj__id, isys_cats_organization_list__isys_obj__id FROM isys_cats_person_list ' .
                'INNER JOIN isys_connection ON isys_connection__id = isys_cats_person_list__isys_connection__id ' .
                'INNER JOIN isys_cats_organization_list ON isys_connection__isys_obj__id = isys_cats_organization_list__isys_obj__id ' . 'WHERE ' .
                implode(' OR ', $l_assignments) . ';';

            $l_persons = $this->retrieve($l_query)
                ->__as_array();

            foreach ($l_persons as $l_person) {
                // Transfer assigned objects of organization to person
                self::$m_receivers_roles_object_mapping[$l_person['isys_cats_person_list__isys_obj__id']] = array_unique(
                    array_merge(
                        self::$m_receivers_roles_object_mapping[$l_person['isys_cats_person_list__isys_obj__id']] ?: [],
                        self::$m_receivers_roles_object_mapping[$l_person['isys_cats_organization_list__isys_obj__id']]
                    )
                );

                $l_receivers[] = $l_person['isys_cats_person_list__isys_obj__id'];
            }
        }

        // Fetch group assigned persons:
        if (count($l_groups) > 0) {
            $l_assignments = [];

            foreach ($l_groups as $l_group) {
                if (!in_array($l_group, $l_receivers) && $this->hasEmail($l_group)) {
                    $l_receivers[] = $l_group;
                }

                $l_assignments[] = 'isys_person_2_group__isys_obj__id__group = ' . $this->convert_sql_id($l_group);
            }

            if (count($l_assignments) > 0) {
                $l_query = 'SELECT isys_person_2_group__isys_obj__id__person, isys_person_2_group__isys_obj__id__group FROM
                        isys_person_2_group WHERE ' . implode(' OR ', $l_assignments) . ';';

                $l_persons = $this->retrieve($l_query)
                    ->__as_array();

                foreach ($l_persons as $l_person) {
                    // Transfer assigned objects of group to person
                    self::$m_receivers_roles_object_mapping[$l_person['isys_person_2_group__isys_obj__id__person']] = array_unique(array_merge(
                        self::$m_receivers_roles_object_mapping[$l_person['isys_person_2_group__isys_obj__id__person']] ?: [],
                        self::$m_receivers_roles_object_mapping[$l_person['isys_person_2_group__isys_obj__id__group']] ?: []
                    ));

                    if (!in_array($l_person['isys_person_2_group__isys_obj__id__person'], $l_receivers)) {
                        $l_receivers[] = $l_person['isys_person_2_group__isys_obj__id__person'];
                    }
                }
            }
        }

        return array_unique($l_receivers);
    }

    /**
     * Check if object has an E-Mail
     *
     * @param int $objId
     *
     * @return bool
     */
    private function hasEmail($objId)
    {
        $query = 'SELECT 1 FROM isys_catg_mail_addresses_list WHERE isys_catg_mail_addresses_list__isys_obj__id = ' . $this->convert_sql_id($objId);
        $result = $this->retrieve($query);
        return ($result instanceof isys_component_dao_result && count($result));
    }

    /**
     * Get user`s language by id
     *
     * @param int $userId
     * @return string|null
     * @throws isys_exception_database
     */
    public function getUserLanguage(int $userId): ?string
    {
        // SQL for retrieving user`s language
        $sql = "SELECT isys_user_locale__language AS `language`
            FROM isys_user_locale
            INNER JOIN isys_user_setting on isys_user_locale__isys_user_setting__id = isys_user_setting__id
            WHERE isys_user_setting__isys_obj__id = {$userId}
            LIMIT 1;";

        // Get user`s language as id
        $languageId = $this->retrieve($sql)->get_row_value('language');

        // Validate language ID.
        if (!empty($languageId) && is_numeric($languageId)) {
            try {
                // Convert ID to language abbreviation.
                return isys_application::instance()->container->get('locales')->resolve_language_by_constant($languageId);
            } catch (Throwable $e) {
            }
        }

        return null;
    }

    /**
     * Get templates for notification type by id
     *
     * @param int       $notificationTypeId
     * @param string    $languageAbbreviation
     *
     * @return isys_component_dao_result
     * @throws isys_exception_database
     */
    public function getTemplatesForNotificationTypeId($notificationTypeId, $languageAbbreviation)
    {
        $sql = '
                SELECT * from isys_notification_template inot
                WHERE inot.isys_notification_template__isys_notification_type__id = ' . $this->convert_sql_id($notificationTypeId) . '
                  AND inot.isys_notification_template__locale = ' . $this->convert_sql_text($languageAbbreviation) . '
        ';

        return $this->retrieve($sql);
    }

    /**
     * @param isys_component_database $p_db
     * @param Logger $p_log
     */
    public function __construct(isys_component_database $p_db, Logger $p_log)
    {
        parent::__construct($p_db);

        $language = isys_application::instance()->container->get('language');

        $this->m_log = $p_log;
        $this->m_types = [
            self::NOTIFICATIONS          => [
                'title' => $language->get('LC__NOTIFICATIONS__NOTIFICATIONS')
            ],
            self::NOTIFICATION_DOMAINS   => [
                'title' => $language->get('LC__NOTIFICATIONS__DOMAINS')
            ],
            self::NOTIFICATION_ROLES     => [
                'title' => $language->get('LC__NOTIFICATIONS__NOTIFICATION_ROLES')
            ],
            self::NOTIFICATION_TYPES     => [
                'title' => $language->get('LC__NOTIFICATIONS__NOTIFICATION_TYPES')
            ],
            self::NOTIFICATION_TEMPLATES => [
                'title' => $language->get('LC__NOTIFICATIONS__NOTIFICATION_TEMPLATES')
            ]
        ];
    }
}
