$options */ $options = Options::get( 'events', array() ); if ( ! is_array( $options ) ) { $options = array(); } $categories = self::get_categories_id(); if ( ! is_array( $categories ) || ! array_key_exists( $category, $categories ) ) { return; } $category = $categories[$category]; $event_key = md5( $title ); $event_found = false; foreach ( $options as $index => $option_event ) { if ( isset( $option_event['key'] ) && $option_event['key'] === $event_key ) { $options[$index]['new_state'] = $new_state; $options[$index]['message'] = $message; $event_found = true; break; } } if ( ! $event_found ) { $dates = self::get_dates(); $options[] = array( 'key' => $event_key, 'title' => 'WP - ' . $title, 'message' => $message, 'category' => $category, 'startDate' => $dates['date_now'], 'endDate' => $direct ? $dates['date_now'] : $dates['date_five'], 'timezone' => $dates['timezone'], 'initial_state' => $initial_state, 'new_state' => $new_state, ); } Options::update( 'events', $options ); if ( $direct ) { self::send_events(); } } /** * Processes and sends events. * * This method can be called either by the WordPress cron system or directly * to check pending events and send them to the API. * It first performs an event check then attempts to send them. * * @return void * * @since 3.2.0 */ public static function send_events() { self::check_events(); self::send_to_api(); } /** * Updates event annotation scheduling via cron. * * This method creates a cron instance and checks for scheduled tasks * using transients to upload event annotations. It ensures that the * upload_event_annotation task is properly scheduled. * * @return void * * @since 3.2.0 */ public static function update_event_annotation() { $cron = new Cron(); $cron->check_scheduled_task_with_transient( 'upload_event_annotation', array( $cron, 'upload_event_annotation', ) ); } /** * Checks and cleans the list of pending events. * * This method goes through the event list and removes those * where the initial state and new state are identical, considering * that no real change has occurred. * * @return void * * @since 3.2.0 */ private static function check_events() { /** @var array $events */ $events = Options::get( 'events', array() ); if ( ! is_array( $events ) ) { $events = array(); } if ( count( $events ) === 0 ) { return; } foreach ( $events as $index => $event ) { if ( array_key_exists( 'initial_state', $event ) && array_key_exists( 'new_state', $event ) && $event['initial_state'] !== null && $event['new_state'] !== null && $event['initial_state'] === $event['new_state'] ) { unset( $events[$index] ); } } Options::update( 'events', $events ); } /** * Sends pending events to the Actirise API. * * This method retrieves all pending events and sends them * one by one to the API. After a successful send, the event is removed * from the queue. In case of error, the event remains in the queue * and an error log is generated. * * @return void * * @since 3.2.0 */ private static function send_to_api() { /** @var string $token */ $token = Options::get( 'settings-analytics-token', '' ); if ( ! is_string( $token ) || $token === '' ) { return; } /** @var array $events */ $events = Options::get( 'events', array() ); if ( ! is_array( $events ) ) { $events = array(); } if ( count( $events ) === 0 ) { return; } $website = Helpers::get_website_info(); if ( ! is_array( $website ) || empty( $website ) ) { return; } $dates = self::get_dates(); foreach ( $events as $index => $event ) { if ( ! isset( $event['startDate'], $event['endDate'], $event['title'], $event['message'], $event['category'] ) ) { continue; } $body = array( 'startDate' => $event['startDate'], 'endDate' => $event['endDate'], 'websites' => array( $website['@id'] ), 'title' => $event['title'], 'description' => $event['message'], 'category' => $event['category'], 'scope' => 'public', 'timezone' => $dates['timezone'], 'allAllowedWebsites' => false, ); $api = new Api(); $response = $api->post( 'api', 'event_annotations', $body, $token ); if ( is_wp_error( $response ) ) { Logger::add_log( 'event annotation error: ' . $response->get_error_message(), 'include/eventannotation', 'error' ); continue; } unset( $events[$index] ); } Options::update( 'events', $events ); } /** * Retrieves the event annotation categories from the API. * * This method fetches the available event categories from the Actirise API * and maps them to the appropriate category identifiers used by the plugin. * It specifically looks for 'Website update' and 'Configuration Changes' categories. * * @return array|null Array of category mappings or null on error. * * @since 3.2.0 */ private static function get_categories_id() { $token = Options::get( 'settings-analytics-token', '' ); if ( ! is_string( $token ) || $token === '' ) { return null; } $api = new Api(); $response = $api->get( 'api', 'event_annotation_categories', array(), $token, true ); if ( is_wp_error( $response ) ) { Logger::add_log( 'get categories id error: ' . $response->get_error_message(), 'include/eventannotation', 'error' ); return null; } if ( ! is_array( $response ) || ! array_key_exists( 'hydra:member', $response ) ) { Logger::add_log( 'get categories id error: invalid response', 'include/eventannotation', 'error' ); return null; } $categories = $response['hydra:member']; if ( ! is_array( $categories ) || empty( $categories ) ) { return null; } /** @var array $cat_id */ $cat_id = array(); foreach ( $categories as $category ) { if ( isset( $category['name'], $category['@id'] ) && is_string( $category['@id'] ) ) { if ( $category['name'] === 'Website update' ) { $cat_id['website_update'] = $category['@id']; } elseif ( $category['name'] === 'Configuration Changes' ) { $cat_id['config_changes'] = $category['@id']; } } } if ( count( $cat_id ) === 0 ) { Logger::add_log( 'get categories id error: no categories found', 'include/eventannotation', 'error' ); return null; } return $cat_id; } /** * Retrieves current date information with timezone. * * This method generates date information including the current date, * a date 5 minutes in the future, and the timezone. It uses WordPress's * configured timezone if available, otherwise defaults to UTC. * * @return array{date_now: string, date_five: string, timezone: string} Array containing date information. * * @since 3.2.0 */ private static function get_dates() { $timezone = 'UTC'; $timezone_string = get_option( 'timezone_string' ); if ( is_string( $timezone_string ) && $timezone_string !== '' ) { $timezone = $timezone_string; } else { $gmt_offset = get_option( 'gmt_offset', 0 ); $offset = is_numeric( $gmt_offset ) ? (float) $gmt_offset : 0.0; $hours = (int) $offset; $minutes = ( $offset - $hours ); $sign = ( $offset < 0 ) ? '-' : '+'; $abs_hour = abs( $hours ); $abs_mins = abs( $minutes * 60 ); $timezone = sprintf( '%s%02d:%02d', $sign, $abs_hour, $abs_mins ); } $current_date = new \DateTime( 'now' ); $current_date->setTimezone( new \DateTimeZone( $timezone ) ); $dates = array( 'date_now' => $current_date->format( 'Y-m-d\TH:i:s' ), 'date_five' => $current_date->modify( '+5 minutes' )->format( 'Y-m-d\TH:i:s' ), 'timezone' => $timezone, ); return $dates; } }