EVENT_SOURCE_TRIGGERS, 'object' => EVENT_OBJECT_TRIGGER], ['source' => EVENT_SOURCE_DISCOVERY, 'object' => EVENT_OBJECT_DHOST], ['source' => EVENT_SOURCE_DISCOVERY, 'object' => EVENT_OBJECT_DSERVICE], ['source' => EVENT_SOURCE_AUTOREGISTRATION, 'object' => EVENT_OBJECT_AUTOREGHOST], ['source' => EVENT_SOURCE_INTERNAL, 'object' => EVENT_OBJECT_TRIGGER], ['source' => EVENT_SOURCE_INTERNAL, 'object' => EVENT_OBJECT_ITEM], ['source' => EVENT_SOURCE_INTERNAL, 'object' => EVENT_OBJECT_LLDRULE], ['source' => EVENT_SOURCE_SERVICE, 'object' => EVENT_OBJECT_SERVICE] ]; } function get_events_unacknowledged($db_element, $value_trigger = null, $value_event = null, $ack = false) { $elements = ['hosts' => [], 'hosts_groups' => [], 'triggers' => []]; get_map_elements($db_element, $elements); if (empty($elements['hosts_groups']) && empty($elements['hosts']) && empty($elements['triggers'])) { return 0; } $options = [ 'output' => ['triggerid'], 'monitored' => 1, 'skipDependent' => 1, 'limit' => CSettingsHelper::get(CSettingsHelper::SEARCH_LIMIT) + 1 ]; if (!is_null($value_trigger)) { $options['filter'] = ['value' => $value_trigger]; } if (!empty($elements['hosts_groups'])) { $options['groupids'] = array_unique($elements['hosts_groups']); } if (!empty($elements['hosts'])) { $options['hostids'] = array_unique($elements['hosts']); } if (!empty($elements['triggers'])) { $options['triggerids'] = array_unique($elements['triggers']); } $triggerids = API::Trigger()->get($options); return API::Event()->get([ 'source' => EVENT_SOURCE_TRIGGERS, 'object' => EVENT_OBJECT_TRIGGER, 'countOutput' => true, 'objectids' => zbx_objectValues($triggerids, 'triggerid'), 'filter' => [ 'value' => $value_event, 'acknowledged' => $ack ? 1 : 0 ] ]); } /** * * @param array $event An array of event data. * @param string $event['eventid'] Event ID. * @param string $event['r_eventid'] OK event ID. * @param string $event['cause_eventid'] Cause event ID. * @param string $event['correlationid'] OK Event correlation ID. * @param string $event['userid'] User ID who generated the OK event. * @param string $event['name'] Event name. * @param string $event['acknowledged'] State of acknowledgement. * @param CCOl $event['opdata'] Operational data with expanded macros. * @param string $event['comments'] Trigger description with expanded macros. * @param array $allowed An array of user role rules. * @param bool $allowed['ui_correlation'] Whether user is allowed to visit event correlation page. * * @return CTableInfo */ function make_event_details(array $event, array $allowed) { $is_acknowledged = ($event['acknowledged'] == EVENT_ACKNOWLEDGED); $table = (new CTableInfo()) ->addRow([ _('Event'), (new CCol($event['name']))->addClass(ZBX_STYLE_WORDWRAP) ]) ->addRow([ _('Operational data'), $event['opdata']->addClass(ZBX_STYLE_WORDBREAK) ]) ->addRow([ _('Severity'), CSeverityHelper::makeSeverityCell((int) $event['severity']) ]) ->addRow([ _('Time'), zbx_date2str(DATE_TIME_FORMAT_SECONDS, $event['clock']) ]) ->addRow([ _('Acknowledged'), (new CSpan($is_acknowledged ? _('Yes') : _('No')))->addClass( $is_acknowledged ? ZBX_STYLE_GREEN : ZBX_STYLE_RED ) ]); if ($event['r_eventid'] != 0) { if ($event['correlationid'] != 0) { $correlations = API::Correlation()->get([ 'output' => ['correlationid', 'name'], 'correlationids' => [$event['correlationid']] ]); if ($correlations) { if ($allowed['ui_correlation']) { $correlation_name = (new CLink($correlations[0]['name'], (new CUrl('zabbix.php')) ->setArgument('action', 'correlation.edit') ->setArgument('correlationid', $correlations[0]['correlationid']) ->getUrl() ))->addClass(ZBX_STYLE_LINK_ALT); } else { $correlation_name = $correlations[0]['name']; } } else { $correlation_name = _('Event correlation rule'); } $table->addRow([_('Resolved by'), $correlation_name]); } elseif ($event['userid'] != 0) { if ($event['userid'] == CWebUser::$data['userid']) { $table->addRow([_('Resolved by'), getUserFullname([ 'username' => CWebUser::$data['username'], 'name' => CWebUser::$data['name'], 'surname' => CWebUser::$data['surname'] ])]); } else { $user = API::User()->get([ 'output' => ['username', 'name', 'surname'], 'userids' => [$event['userid']] ]); if ($user) { $table->addRow([_('Resolved by'), getUserFullname($user[0])]); } else { $table->addRow([_('Resolved by'), _('Inaccessible user')]); } } } else { $table->addRow([_('Resolved by'), _('Trigger')]); } } $tags = makeTags([$event]); $table ->addRow([_('Tags'), $tags[$event['eventid']]]) ->addRow([_('Description'), (new CDiv(zbx_str2links($event['comments'])))->addClass(ZBX_STYLE_WORDBREAK)]); if ($event['cause_eventid'] == 0) { $table->addRow([_('Rank'), _('Cause')]); } else { $cause_event = API::Event()->get([ 'output' => ['name', 'objectid'], 'eventids' => $event['cause_eventid'] ]); if ($cause_event) { $cause_event = reset($cause_event); $has_trigger = (bool) API::Trigger()->get([ 'output' => [], 'triggerids' => [$cause_event['objectid']] ]); $table->addRow([_('Rank'), [ _('Symptom'), ' (', $has_trigger ? new CLink( $cause_event['name'], (new CUrl('tr_events.php')) ->setArgument('triggerid', $cause_event['objectid']) ->setArgument('eventid', $event['cause_eventid']) ) : $cause_event['name'], ')' ] ]); } } return $table; } /** * Check if event status is UPDATING depending on acknowledges task ID. * * @param bool $in_closing True if problem is in CLOSING state. * @param array $event Event data. * @param array $event['acknowledges'] List of event acknowledges. * @param int $event['acknowledges'][]['action'] Event action type. * @param string $event['acknowledges'][]['taskid'] Task ID. * * @return bool */ function isEventUpdating(bool $in_closing, array $event): bool { $in_updating = false; if (!$in_closing) { foreach ($event['acknowledges'] as $acknowledge) { if (($acknowledge['action'] & ZBX_PROBLEM_UPDATE_RANK_TO_CAUSE) == ZBX_PROBLEM_UPDATE_RANK_TO_CAUSE || ($acknowledge['action'] & ZBX_PROBLEM_UPDATE_RANK_TO_SYMPTOM) == ZBX_PROBLEM_UPDATE_RANK_TO_SYMPTOM) { // If currently is cause or symptom and there is an active task. if ($acknowledge['taskid'] != 0) { $in_updating = true; break; } } } } return $in_updating; } /** * Calculate and return a rank change icon depending on current event rank change. If event is currently a cause event * and it is undergoing a rank change, return cause event icon. If event is currently a symptom event and it is * undergoing a rank change, return symptom event icon. Icon can be displayed regarless if current status is in closing. * * @param array $event Event data. * @param array $event['acknowledges'] List of event acknowledges. * @param int $event['acknowledges'][]['action'] Event action type. * @param string $event['acknowledges'][]['taskid'] Task ID. * * @return Ctag|null */ function getEventStatusUpdateIcon(array $event): ?Ctag { $icon = null; $icon_class = ''; foreach ($event['acknowledges'] as $acknowledge) { // If currently is symptom and there is an active task to convert to cause, set icon style to cause. if (($acknowledge['action'] & ZBX_PROBLEM_UPDATE_RANK_TO_CAUSE) == ZBX_PROBLEM_UPDATE_RANK_TO_CAUSE && $acknowledge['taskid'] != 0) { $icon_class = ZBX_ICON_ARROW_RIGHT_TOP; break; } // If currently is cause and there is an active task to convert to symptom, set icon style to symptom. if (($acknowledge['action'] & ZBX_PROBLEM_UPDATE_RANK_TO_SYMPTOM) == ZBX_PROBLEM_UPDATE_RANK_TO_SYMPTOM && $acknowledge['taskid'] != 0) { $icon_class = ZBX_ICON_ARROW_TOP_RIGHT; break; } } if ($icon_class !== '') { $icon = (new CSpan()) ->addClass($icon_class) ->addClass('js-blink') ->setTitle(_('Updating')); } return $icon; } /** * Calculate and return event status string: PROBLEM, RESOLVED, CLOSING or UPDATING depending on acknowledges task ID. * * @param bool $in_closing True if problem is in CLOSING state. * @param array $event Event data. * * @return string */ function getEventStatusString(bool $in_closing, array $event): string { if ($event['r_eventid'] != 0) { $value_str = isEventUpdating($in_closing, $event) ? _('UPDATING') : _('RESOLVED'); } else { if ($in_closing) { $value_str = _('CLOSING'); } else { $value_str = isEventUpdating($in_closing, $event) ? _('UPDATING') : _('PROBLEM'); } } return $value_str; } /** * * @param array $startEvent An array of event data. * @param string $startEvent['eventid'] Event ID. * @param string $startEvent['objectid'] Object ID. * @param array $allowed An array of user role rules. * @param bool $allowed['add_comments'] Whether user is allowed to add problems comments. * @param bool $allowed['change_severity'] Whether user is allowed to change problems severity. * @param bool $allowed['acknowledge'] Whether user is allowed to acknowledge problems. * @param bool $allowed['close'] Whether user is allowed to close problems. * @param bool $allowed['suppress_problems'] Whether user is allowed to suppress/unsuppress problems. * @param bool $allowed['rank_change'] Whether user is allowed to change problem ranking. * * @return CTableInfo */ function make_small_eventlist(array $startEvent, array $allowed) { $table = (new CTableInfo()) ->setHeader([ _('Time'), _('Recovery time'), _('Status'), _('Age'), _('Duration'), _('Update'), _('Actions') ]); $events = API::Event()->get([ 'output' => ['eventid', 'source', 'object', 'objectid', 'acknowledged', 'clock', 'ns', 'severity', 'r_eventid', 'cause_eventid' ], 'selectAcknowledges' => ['userid', 'clock', 'message', 'action', 'old_severity', 'new_severity', 'suppress_until', 'taskid' ], 'source' => EVENT_SOURCE_TRIGGERS, 'object' => EVENT_OBJECT_TRIGGER, 'value' => TRIGGER_VALUE_TRUE, 'objectids' => $startEvent['objectid'], 'eventid_till' => $startEvent['eventid'], 'sortfield' => ['clock', 'eventid'], 'sortorder' => ZBX_SORT_DOWN, 'limit' => 20, 'preservekeys' => true ]); $r_eventids = []; foreach ($events as $event) { $r_eventids[$event['r_eventid']] = true; } unset($r_eventids[0]); $r_events = $r_eventids ? API::Event()->get([ 'output' => ['clock'], 'source' => EVENT_SOURCE_TRIGGERS, 'object' => EVENT_OBJECT_TRIGGER, 'eventids' => array_keys($r_eventids), 'preservekeys' => true ]) : []; $triggerids = []; foreach ($events as &$event) { $triggerids[] = $event['objectid']; $event['r_clock'] = array_key_exists($event['r_eventid'], $r_events) ? $r_events[$event['r_eventid']]['clock'] : 0; } unset($event); // Get trigger severities. $triggers = $triggerids ? API::Trigger()->get([ 'output' => ['priority', 'manual_close'], 'triggerids' => $triggerids, 'preservekeys' => true ]) : []; $actions = getEventsActionsIconsData($events, $triggers); $users = API::User()->get([ 'output' => ['username', 'name', 'surname'], 'userids' => array_keys($actions['userids']), 'preservekeys' => true ]); foreach ($events as $event) { $duration = ($event['r_eventid'] != 0) ? zbx_date2age($event['clock'], $event['r_clock']) : zbx_date2age($event['clock']); $can_be_closed = $allowed['close']; $in_closing = false; if ($event['r_eventid'] != 0) { $value = TRIGGER_VALUE_FALSE; $value_clock = $event['r_clock']; $can_be_closed = false; } else { if (hasEventCloseAction($event['acknowledges'])) { $in_closing = true; $can_be_closed = false; } $value = $in_closing ? TRIGGER_VALUE_FALSE : TRIGGER_VALUE_TRUE; $value_clock = $in_closing ? time() : $event['clock']; } $value_str = getEventStatusString($in_closing, $event); $is_acknowledged = ($event['acknowledged'] == EVENT_ACKNOWLEDGED); $cell_status = new CSpan($value_str); if (isEventUpdating($in_closing, $event)) { $cell_status->addClass('js-blink'); } /* * Add colors to span depending on configuration and trigger parameters. No blinking added to status, * since the page is not on autorefresh. */ addTriggerValueStyle($cell_status, $value, $value_clock, $is_acknowledged); // Create acknowledge link. $problem_update_link = ($allowed['add_comments'] || $allowed['change_severity'] || $allowed['acknowledge'] || $can_be_closed || $allowed['suppress_problems'] || $allowed['rank_change']) ? (new CLink(_('Update'))) ->addClass(ZBX_STYLE_LINK_ALT) ->setAttribute('data-eventid', $event['eventid']) ->onClick('acknowledgePopUp({eventids: [this.dataset.eventid]}, this);') : new CSpan(_('Update')); $table->addRow([ (new CLink(zbx_date2str(DATE_TIME_FORMAT_SECONDS, $event['clock']), 'tr_events.php?triggerid='.$event['objectid'].'&eventid='.$event['eventid'] ))->addClass('action'), ($event['r_eventid'] == 0) ? '' : (new CLink(zbx_date2str(DATE_TIME_FORMAT_SECONDS, $event['r_clock']), 'tr_events.php?triggerid='.$event['objectid'].'&eventid='.$event['eventid'] ))->addClass('action'), $cell_status, zbx_date2age($event['clock']), $duration, $problem_update_link, makeEventActionsIcons($event['eventid'], $actions['data'], $users, $is_acknowledged) ]); } return $table; } /** * Checks if event is closed. * * @param array $event Event object. * @param array $event['r_eventid'] OK event id. 0 if not resolved. * @param array $event['acknowledges'] List of problem updates. * @param int $event['acknowledges'][]['action'] Action performed in update. * * @return bool */ function isEventClosed(array $event): bool { if ($event['r_eventid'] != 0) { return true; } else { return hasEventCloseAction($event['acknowledges']); } } /** * Checks if event has manual close action. * * @param array $event Event object. * @param array $event['acknowledges'] List of problem updates. * @param int $event['acknowledges'][]['action'] Action performed in update. * * @return bool */ function hasEventCloseAction(array $acknowledges): bool { foreach ($acknowledges as $acknowledge) { if (($acknowledge['action'] & ZBX_PROBLEM_UPDATE_CLOSE) == ZBX_PROBLEM_UPDATE_CLOSE) { // If at least one manual close update was found, event is closing. return true; } } return false; } /** * Returns true if event is suppressed and not unsuppressed after that. * * @param array $acknowledges * @param int $acknowledges['action'] * @param ?array $unsuppression_action [OUT] Variable to store suppression action data. * * @return bool */ function isEventRecentlySuppressed(array $acknowledges, &$suppression_action = null): bool { CArrayHelper::sort($acknowledges, [['field' => 'clock', 'order' => ZBX_SORT_DOWN]]); foreach ($acknowledges as $ack) { if (!array_key_exists('suppress_until', $ack)) { continue; } if (($ack['action'] & ZBX_PROBLEM_UPDATE_UNSUPPRESS) == ZBX_PROBLEM_UPDATE_UNSUPPRESS) { return false; } elseif (($ack['action'] & ZBX_PROBLEM_UPDATE_SUPPRESS) == ZBX_PROBLEM_UPDATE_SUPPRESS) { if ($ack['suppress_until'] == ZBX_PROBLEM_SUPPRESS_TIME_INDEFINITE || $ack['suppress_until'] > time()) { $suppression_action = $ack; return true; } break; } } return false; } /** * Returns true if event is unsuppressed and not suppressed after that. * * @param array $acknowledges * @param int $acknowledges['action'] * @param ?array $unsuppression_action [OUT] Variable to store unsuppression action data. * * @return bool */ function isEventRecentlyUnsuppressed(array $acknowledges, &$unsuppression_action = null): bool { CArrayHelper::sort($acknowledges, [['field' => 'clock', 'order' => ZBX_SORT_DOWN]]); foreach ($acknowledges as $ack) { if (($ack['action'] & ZBX_PROBLEM_UPDATE_SUPPRESS) == ZBX_PROBLEM_UPDATE_SUPPRESS) { return false; } elseif (($ack['action'] & ZBX_PROBLEM_UPDATE_UNSUPPRESS) == ZBX_PROBLEM_UPDATE_UNSUPPRESS) { $unsuppression_action = $ack; return true; } } return false; } /** * Place filter tags at the beginning of tags array. * * @param array $event_tags * @param string $event_tags[]['tag'] * @param string $event_tags[]['value'] * @param array $f_tags * @param int $f_tags[][]['operator'] * @param string $f_tags[][]['value'] * * @return array */ function orderEventTags(array $event_tags, array $f_tags) { $first_tags = []; foreach ($event_tags as $i => $tag) { if (array_key_exists($tag['tag'], $f_tags)) { foreach ($f_tags[$tag['tag']] as $f_tag) { if (($f_tag['operator'] == TAG_OPERATOR_EQUAL && $tag['value'] === $f_tag['value']) || ($f_tag['operator'] == TAG_OPERATOR_LIKE && ($f_tag['value'] === '' || stripos($tag['value'], $f_tag['value']) !== false))) { $first_tags[] = $tag; unset($event_tags[$i]); break; } } } } return array_merge($first_tags, $event_tags); } /** * Place priority tags at the beginning of tags array. * * @param array $event_tags An array of event tags. * @param string $event_tags[]['tag'] Tag name. * @param string $event_tags[]['value'] Tag value. * @param array $priorities An array of priority tag names. * * @return array */ function orderEventTagsByPriority(array $event_tags, array $priorities) { $first_tags = []; foreach ($priorities as $priority) { foreach ($event_tags as $i => $tag) { if ($tag['tag'] === $priority) { $first_tags[] = $tag; unset($event_tags[$i]); } } } return array_merge($first_tags, $event_tags); } /** * Create element with tags. * * @param array $list * @param string $list[][$key] * @param array $list[]['tags'] * @param string $list[]['tags'][]['tag'] * @param string $list[]['tags'][]['value'] * @param bool $html * @param string $key Name of tag source ID. Possible values: * - 'eventid' - for events and problems (default); * - 'hostid' - for hosts and host prototypes; * - 'templateid' - for templates; * - 'triggerid' - for triggers; * - 'httptestid' - for web scenarios. * @param int $list_tag_count Maximum number of tags to display. * @param array $filter_tags An array of tag filtering data. * @param ?array $subfilter_tags Array of selected sub-filter tags. Null when tags are not clickable. * @param array $subfilter_tags[] * @param array $subfilter_tags[][] * @param array $subfilter_tags[][] * @param array $subfilter_tags[][] * @param string $filter_tags[]['tag'] * @param int $filter_tags[]['operator'] * @param string $filter_tags[]['value'] * @param int $tag_name_format Tag name format. Possible values: * - TAG_NAME_FULL (default); * - TAG_NAME_SHORTENED; * - TAG_NAME_NONE. * @param string $tag_priority A list of comma-separated tag names. * * @return array */ function makeTags(array $list, bool $html = true, string $key = 'eventid', int $list_tag_count = ZBX_TAG_COUNT_DEFAULT, array $filter_tags = [], ?array $subfilter_tags = null, int $tag_name_format = TAG_NAME_FULL, string $tag_priority = ''): array { $tags = []; if ($html) { // Convert $filter_tags to a more usable format. $f_tags = []; foreach ($filter_tags as $tag) { $f_tags[$tag['tag']][] = [ 'operator' => $tag['operator'], 'value' => $tag['value'] ]; } } if ($tag_priority !== '') { $p_tags = explode(',', $tag_priority); $p_tags = array_map('trim', $p_tags); } foreach ($list as $element) { $tags[$element[$key]] = []; if (!$element['tags']) { continue; } CArrayHelper::sort($element['tags'], ['tag', 'value']); if ($html) { // Show first n tags and "..." with hint box if there are more. $e_tags = $f_tags ? orderEventTags($element['tags'], $f_tags) : $element['tags']; if ($tag_priority !== '') { $e_tags = orderEventTagsByPriority($e_tags, $p_tags); } $tags_shown = 0; foreach ($e_tags as $tag) { $value = getTagString($tag, $tag_name_format); if ($value !== '') { if ($subfilter_tags !== null && !(array_key_exists($tag['tag'], $subfilter_tags) && array_key_exists($tag['value'], $subfilter_tags[$tag['tag']]))) { $tags[$element[$key]][] = (new CSimpleButton($value)) ->setAttribute('data-key', $tag['tag']) ->setAttribute('data-value', $tag['value']) ->onClick( 'view.setSubfilter([`subfilter_tags[${encodeURIComponent(this.dataset.key)}][]`,'. 'this.dataset.value'. ']);' ) ->addClass(ZBX_STYLE_BTN_TAG) ->setHint(getTagString($tag), '', false); } else { $tags[$element[$key]][] = (new CSpan($value)) ->addClass(ZBX_STYLE_TAG) ->setHint(getTagString($tag)); } $tags_shown++; if ($tags_shown >= $list_tag_count) { break; } } } if (count($element['tags']) > $tags_shown) { // Display all tags in hint box. $hint_content = []; foreach ($element['tags'] as $tag) { $value = getTagString($tag); if ($subfilter_tags !== null && !(array_key_exists($tag['tag'], $subfilter_tags) && array_key_exists($tag['value'], $subfilter_tags[$tag['tag']]))) { $hint_content[$element[$key]][] = (new CSimpleButton($value)) ->setAttribute('data-key', $tag['tag']) ->setAttribute('data-value', $tag['value']) ->onClick( 'view.setSubfilter([`subfilter_tags[${encodeURIComponent(this.dataset.key)}][]`,'. 'this.dataset.value'. ']);' ) ->addClass(ZBX_STYLE_BTN_TAG) ->setHint(getTagString($tag), '', false); } else { $hint_content[$element[$key]][] = (new CSpan($value)) ->addClass(ZBX_STYLE_TAG) ->setHint($value); } } $tags[$element[$key]][] = (new CButtonIcon(ZBX_ICON_MORE)) ->setHint($hint_content, ZBX_STYLE_HINTBOX_WRAP); } } else { // Show all and uncut for CSV. foreach ($element['tags'] as $tag) { $tags[$element[$key]][] = getTagString($tag); } } } return $tags; } /** * Returns tag name in selected format. * * @param array $tag * @param string $tag['tag'] * @param string $tag['value'] * @param int $tag_name_format TAG_NAME_* * * @return string */ function getTagString(array $tag, $tag_name_format = TAG_NAME_FULL) { switch ($tag_name_format) { case TAG_NAME_NONE: return $tag['value']; case TAG_NAME_SHORTENED: return mb_substr($tag['tag'], 0, 3).(($tag['value'] === '') ? '' : ': '.$tag['value']); default: return $tag['tag'].(($tag['value'] === '') ? '' : ': '.$tag['value']); } } /** * Validate if the given events can change the rank by moving to a new cause. Linking a cause event with its symptoms * (or only cause or only symptoms) to another different cause or symptom is allowed and will switch to the new cause as * a result. Linking a cause to one of its own symptoms is also allowed and will simply switch the cause and symptom as * a result. Linking a symptom to same cause is not allowed and that event ID is skipped. Linking a symptom to symptom * of same cause is also not allowed and is skipped. * * @param array $eventids Array of event IDs that should be converted to symptom events. * @param string $cause_eventid Event ID that will be the new cause ID for given $eventids. * * @return array Returns event IDs that are allowed to change rank. */ function validateEventRankChangeToSymptom(array $eventids, string $cause_eventid): array { $eventids = array_fill_keys($eventids, true); $all_eventids = $eventids; $all_eventids[$cause_eventid] = true; // Get all the events that were given in the request to check permissions. $events = API::Event()->get([ 'output' => ['eventid', 'cause_eventid'], 'eventids' => array_keys($all_eventids), 'preservekeys' => true ]); // Early return. In case one of the events are missing, no rank change can occur. if (count($events) != count($all_eventids)) { return []; } // Early return. No matter if cause or symptom, source and destination cannot be the same. if (count($eventids) == 1 && bccomp(key($eventids), $cause_eventid) == 0) { return []; } $dst_event = $events[$cause_eventid]; foreach (array_keys($eventids) as $eventid) { $event = $events[$eventid]; // Given cause is being moved. if ($event['cause_eventid'] == 0) { // Destination is cause. Cause is moved to same cause. Skip this event ID. if ($dst_event['cause_eventid'] == 0 && bccomp($eventid, $dst_event['eventid']) == 0) { unset($eventids[$eventid]); } } // Given symptom is moved. else { // Destination is cause. if ($dst_event['cause_eventid'] == 0) { // Symptom current cause is the same as new cause. Skip this event ID. if (bccomp($event['cause_eventid'], $dst_event['eventid']) == 0) { unset($eventids[$eventid]); } } // Destination is symptom. else { // Symptom destination is self. Skip this Event ID. if (bccomp($eventid, $dst_event['eventid']) == 0) { unset($eventids[$eventid]); } // If given symptom cause is not also in the list, skip this Event ID. if (!array_key_exists($event['cause_eventid'], $eventids)) { unset($eventids[$eventid]); } } } } return array_keys($eventids); }