add_from_file( $file );
}
}
/**
* Add new logger from file inside autoloader path.
*
* @param string $file Path to file.
*/
public function add_from_file( $file ) {
$this->add_from_class( $this->get_class_name( $file ) );
}
/**
* Add new logger given class name.
*
* @param string $class Class name.
*/
public function add_from_class( $class ) {
$this->add_instance( new $class() );
}
/**
* Add newly created logger to list.
*
* @param AbstractLogger $logger - The new logger.
*/
public function add_instance( AbstractLogger $logger ) {
$this->loggers[] = $logger;
}
/**
* Get class name for logger classes.
*
* @param string $file – File name.
* @return string
*/
private function get_class_name( $file ) {
if ( empty( $file ) ) {
return false;
}
// Replace file path, `class-`, and `.php` in the file string.
$file = str_replace( array( MWPAL_BASE_DIR . 'includes/loggers/', 'class-', '.php' ), '', $file );
$file = str_replace( 'logger', 'Logger', $file );
$file = ucfirst( $file );
$file = '\WSAL\MainWPExtension\Loggers\\' . $file;
return $file;
}
/**
* Register a whole group of items.
*
* @param array $groups - An array with group name as the index and an array of group items as the value.
* Item values is an array of [type, code, description, message] respectively.
*/
public function RegisterGroup( $groups ) {
foreach ( $groups as $name => $group ) {
foreach ( $group as $subname => $subgroup ) {
foreach ( $subgroup as $item ) {
/*
* For legacy events keys 4 & 5 are not set. Make them empty
* strings so that `list` doesn't throw warnings/fail.
*/
$item[4] = ( isset( $item[4] ) ) ? $item[4] : '';
$item[5] = ( isset( $item[5] ) ) ? $item[5] : '';
list( $type, $code, $desc, $mesg, $object, $event_type ) = $item;
$this->Register( array( $type, $code, $name, $subname, $desc, $mesg, $object, $event_type ) );
}
}
}
}
/**
* Register an alert type.
*
* @param array $info - Array of [type, code, category, description, message] respectively.
* @throws \Exception - Error if alert is already registered.
*/
public function Register( $info ) {
if ( 1 === func_num_args() ) {
// Handle single item.
list($type, $code, $catg, $subcatg, $desc, $mesg, $object, $event_type ) = $info;
if ( isset( $this->alerts[ $type ] ) ) {
add_action( 'admin_notices', array( $this, 'duplicate_event_notice' ) );
/* Translators: Event ID */
throw new \Exception( sprintf( esc_html__( 'Event %s already registered with Activity Log MainWP Extension.', 'mwp-al-ext' ), $type ) );
}
$this->alerts[ $type ] = new Alert( $type, $code, $catg, $subcatg, $desc, $mesg, $object, $event_type );
} else {
// Handle multiple items.
foreach ( func_get_args() as $arg ) {
$this->Register( $arg );
}
}
}
/**
* Duplicate Event Notice
*
* WP admin notice for duplicate event.
*/
public function duplicate_event_notice() {
$class = 'notice notice-error';
$message = __( 'You have custom events that are using the same ID or IDs which are already registered in the plugin, so they have been disabled.', 'mwp-al-ext' );
printf(
/* Translators: 1.CSS classes, 2. Notice, 3. Contact us link */
'
%2$s %3$s ' . esc_html__( '%4$s to help you solve this issue.', 'mwp-al-ext' ) . '
',
esc_attr( $class ),
'' . esc_html__( 'ERROR:', 'mwp-al-ext' ) . '',
esc_html( $message ),
'' . esc_html__( 'Contact us', 'mwp-al-ext' ) . ''
);
}
/**
* Method: Returns an array of loaded loggers.
*
* @return AbstractLogger[]
*/
public function get_loggers() {
return $this->loggers;
}
/**
* Return alert given alert type.
*
* @param integer $type - Alert type.
* @param mixed $default - Returned if alert is not found.
* @return \WSAL\MainWPExtension\Alert
*/
public function GetAlert( $type, $default = null ) {
foreach ( $this->alerts as $alert ) {
if ( $alert->type == $type ) {
return $alert;
}
}
return $default;
}
/**
* Returns all supported alerts.
*
* @return \WSAL\MainWPExtension\Alert[]
*/
public function GetAlerts() {
return $this->alerts;
}
/**
* Method: Returns array of alerts by category.
*
* @param string $category - Alerts category.
* @return \WSAL\MainWPExtension\Alert[]
*/
public function get_alerts_by_category( $category ) {
// Categorized alerts array.
$alerts = array();
foreach ( $this->alerts as $alert ) {
if ( $category === $alert->catg ) {
$alerts[ $alert->type ] = $alert;
}
}
return $alerts;
}
/**
* Method: Returns array of alerts by sub-category.
*
* @param string $sub_category - Alerts sub-category.
* @return \WSAL\MainWPExtension\Alert[]
*/
public function get_alerts_by_sub_category( $sub_category ) {
// Sub-categorized alerts array.
$alerts = array();
foreach ( $this->alerts as $alert ) {
if ( $sub_category === $alert->subcatg ) {
$alerts[ $alert->type ] = $alert;
}
}
return $alerts;
}
/**
* Returns all supported alerts.
*
* @param bool $sorted – Sort the alerts array or not.
* @return array
*/
public function get_categorized_alerts( $sorted = true ) {
$result = array();
foreach ( $this->alerts as $alert ) {
if ( ! isset( $result[ $alert->catg ] ) ) {
$result[ $alert->catg ] = array();
}
if ( ! isset( $result[ $alert->catg ][ $alert->subcatg ] ) ) {
$result[ $alert->catg ][ $alert->subcatg ] = array();
}
$result[ $alert->catg ][ $alert->subcatg ][] = $alert;
}
if ( $sorted ) {
ksort( $result );
}
return $result;
}
/**
* Log events in the database.
*
* @param array $events – Activity Log Events.
* @param integer $site_id – Site ID according to MainWP.
* @return void
*/
public function log_events( $events, $site_id ) {
if ( empty( $events ) ) {
return;
}
if ( is_array( $events ) ) {
foreach ( $events as $event_id => $event ) {
// Get loggers.
$loggers = $this->get_loggers();
// Get meta data of event.
$meta_data = $event['meta_data'];
$user_data = isset( $meta_data['UserData'] ) ? $meta_data['UserData'] : false;
// Username.
if ( isset( $user_data['username'] ) ) {
$meta_data['Username'] = $user_data['username'];
}
// First name.
if ( isset( $user_data['first_name'] ) ) {
$meta_data['FirstName'] = $user_data['first_name'];
}
// Last name.
if ( isset( $user_data['last_name'] ) ) {
$meta_data['LastName'] = $user_data['last_name'];
}
// Log the events in DB.
foreach ( $loggers as $logger ) {
$logger->log( $event['alert_id'], $meta_data, $event['created_on'], $site_id );
}
}
}
}
/**
* Trigger an event.
*
* @param integer $type - Event type.
* @param array $data - Event data.
*/
public function trigger( $type, $data = array() ) {
// Get username.
if ( ! isset( $data['Username'] ) || empty( $data['Username'] ) ) {
$data['Username'] = wp_get_current_user()->user_login;
}
// Trigger event.
$this->commit_event( $type, $data, null );
}
/**
* Method: Commit an alert now.
*
* @param int $type - Alert type.
* @param array $data - Data of the alert.
* @param array $cond - Condition for the alert.
* @param bool $retry - Retry.
* @internal
*
* @throws Exception - Error if alert is not registered.
*/
protected function commit_event( $type, $data, $cond, $retry = true ) {
if ( ! $cond || call_user_func( $cond, $this ) ) {
$mainwp_alerts = array_keys( $this->alerts );
if ( $this->is_enabled( $type ) && in_array( $type, $mainwp_alerts, true ) ) {
if ( isset( $this->alerts[ $type ] ) ) {
// Ok, convert alert to a log entry.
$this->triggered_types[] = $type;
$this->log( $type, $data );
} elseif ( $retry ) {
// This is the last attempt at loading alerts from default file.
MWPAL_Extension\mwpal_extension()->load_events();
return $this->commit_event( $type, $data, $cond, false );
} else {
// In general this shouldn't happen, but it could, so we handle it here.
/* translators: Event ID */
throw new \Exception( sprintf( esc_html__( 'Event with code %d has not be registered.', 'mwp-al-ext' ), $type ) );
}
}
}
}
/**
* Returns whether alert of type $type is enabled or not.
*
* @since 1.0.4
*
* @param integer $type - Alert type.
* @return boolean - True if enabled, false otherwise.
*/
public function is_enabled( $type ) {
$disabled_events = MWPAL_Extension\mwpal_extension()->settings->get_disabled_events();
return ! in_array( $type, $disabled_events, true );
}
/**
* Log Alert
*
* Converts an Alert into a Log entry (by invoking loggers).
* You should not call this method directly.
*
* @param integer $type - Alert type.
* @param array $data - Misc alert data.
*/
protected function log( $type, $data = array() ) {
// Client IP.
if ( ! isset( $data['ClientIP'] ) ) {
$client_ip = MWPAL_Extension\mwpal_extension()->settings->get_main_client_ip();
if ( ! empty( $client_ip ) ) {
$data['ClientIP'] = $client_ip;
}
}
// User agent.
if ( ! isset( $data['UserAgent'] ) ) {
if ( isset( $_SERVER['HTTP_USER_AGENT'] ) ) {
$data['UserAgent'] = sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) );
}
}
// Username.
if ( ! isset( $data['Username'] ) && ! isset( $data['CurrentUserID'] ) ) {
if ( function_exists( 'get_current_user_id' ) ) {
$data['CurrentUserID'] = get_current_user_id();
}
}
// Current user roles.
if ( ! isset( $data['CurrentUserRoles'] ) && function_exists( 'is_user_logged_in' ) && is_user_logged_in() ) {
$current_user_roles = MWPAL_Extension\mwpal_extension()->settings->get_current_user_roles();
if ( ! empty( $current_user_roles ) ) {
$data['CurrentUserRoles'] = $current_user_roles;
}
}
// Get event severity.
$alert_obj = $this->GetAlert( $type );
$alert_code = $alert_obj ? $alert_obj->code : 0;
$severity = MWPAL_Extension\mwpal_extension()->constants->GetConstantBy( 'value', $alert_code );
/**
* Events Severity.
*
* Add event severity to the metadata of the event.
* The lower the number, the higher is the severity.
*
* Based on monolog log levels:
*
* Formerly based on Syslog severity levels (https://en.wikipedia.org/wiki/Syslog#Severity_level).
*
* @see https://github.com/Seldaek/monolog/blob/main/doc/01-usage.md#log-levels
* @since 2.0.0
*/
if ( is_object( $severity ) && property_exists( $severity, 'name' ) ) {
if ( 'E_CRITICAL' === $severity->name ) {
// CRITICAL (500): Critical conditions.
$data['Severity'] = 500;
} elseif ( 'E_WARNING' === $severity->name ) {
// WARNING (300): Exceptional occurrences that are not errors.
$data['Severity'] = 300;
} elseif ( 'E_NOTICE' === $severity->name ) {
// DEBUG (100): Detailed debug information.
$data['Severity'] = 100;
} elseif ( property_exists( $severity, 'value' ) ) {
$data['Severity'] = $severity->value;
}
}
/*
* In cases where we were not able to figure out a severity already
* use a default of 200: info.
*
* @since 2.0.0
*/
if ( ! isset( $data['Severity'] ) ) {
// Assuming this is a misclassified item and using info code.
// INFO (200): Interesting events.
$data['Severity'] = 200;
}
// Alert `object type`.
if ( ! isset( $data['Object'] ) && isset( $this->alerts[ $type ]->object ) ) {
$data['Object'] = $this->alerts[ $type ]->object;
}
// Alert `event type`.
if ( ! isset( $data['EventType'] ) && isset( $this->alerts[ $type ]->event_type ) ) {
$data['EventType'] = $this->alerts[ $type ]->event_type;
}
// Log event.
foreach ( $this->loggers as $logger ) {
$logger->log( $type, $data, null, 0 );
}
}
/**
* Delete Events from Child Sites.
*
* @since 1.0.1
*
* @param integer $site_id - Child site ID.
*/
public function delete_site_events( $site_id = 0 ) {
if ( $site_id ) {
// Delete events by site id.
$delete_query = new \WSAL\MainWPExtension\Models\OccurrenceQuery();
$delete_query->addCondition( 'site_id = %s ', $site_id );
$delete_query->getAdapter()->Delete( $delete_query );
}
}
/**
* Retrieve Events Manually.
*
* @since 1.1
*/
public function retrieve_events_manually() {
// Get MainWP sites.
$mwp_sites = MWPAL_Extension\mwpal_extension()->settings->get_wsal_child_sites();
if ( ! empty( $mwp_sites ) ) {
$trigger_retrieving = true; // Event 7711.
$trigger_ready = true; // Event 7712.
$server_ip = MWPAL_Extension\mwpal_extension()->settings->get_server_ip(); // Get server IP.
foreach ( $mwp_sites as $site_id => $site ) {
// Delete events by site id.
$this->delete_site_events( $site_id );
// Fetch events by site id.
$sites_data[ $site_id ] = $this->fetch_site_events( $site_id, $trigger_retrieving );
// Set $trigger_retrieving to false to avoid logging 7711 multiple times.
$trigger_retrieving = false;
if ( $trigger_ready && ( isset( $sites_data[ $site_id ]['events'] ) || isset( $sites_data[ $site_id ]['incompatible__skipped'] ) ) ) {
// Extension is ready after retrieving.
$this->trigger(
7712,
array(
'mainwp_dash' => true,
'Username' => 'System',
'ClientIP' => ! empty( $server_ip ) ? $server_ip : false,
)
);
$trigger_ready = false;
}
}
// Set child site events.
$this->set_site_events( $sites_data );
}
}
/**
* Fetch Events from Child Sites.
*
* @since 1.0.1
*
* @param integer $site_id - Child site id.
* @param bool $trigger_retrieving - True if trigger retrieve events alert.
* @param array $post_data - MainWP post data.
* @return array
*/
public function fetch_site_events( $site_id = 0, $trigger_retrieving = true, $post_data = array() ) {
$sites_data = array();
if ( $site_id ) {
$plugin = MWPAL_Extension\mwpal_extension();
// Get server IP.
$server_ip = $plugin->settings->get_server_ip();
if ( $trigger_retrieving ) {
// Extension has started retrieving.
$this->trigger(
7711,
array(
'mainwp_dash' => true,
'Username' => 'System',
'ClientIP' => ! empty( $server_ip ) ? $server_ip : false,
)
);
}
// Post data for child sites.
if ( empty( $post_data ) ) {
$post_data = array(
'events_count' => $plugin->settings->get_child_site_events(),
);
}
// Call to child sites to fetch WSAL events.
$sites_data = $plugin->make_api_call( $site_id, 'get_events', $post_data );
// Check if we recognize all the received events.
if ( ! empty( $sites_data ) && array_key_exists( 'events', $sites_data ) ) {
$codes = array_unique( wp_list_pluck( $sites_data['events'], 'alert_id' ) );
$event_definitions_missing = false;
if ( ! empty( $codes ) ) {
foreach ( $codes as $code ) {
$alert = $this->GetAlert( $code );
if ( is_null( $alert ) ) {
$event_definitions_missing = true;
break;
}
}
}
if ( $event_definitions_missing ) {
// Some event definitions are missing. Try to update them from the child site.
$plugin->update_dynamic_event_definitions( $site_id );
}
}
}
return $sites_data;
}
/**
* Save Events from Child Sites.
*
* @since 1.0.1
*
* @param array $sites_data - Sites data.
*/
public function set_site_events( $sites_data = array() ) {
if ( ! empty( $sites_data ) && is_array( $sites_data ) ) {
// Get MainWP child sites.
$mwp_sites = MWPAL_Extension\mwpal_extension()->settings->get_mwp_child_sites();
// Get server IP.
$server_ip = MWPAL_Extension\mwpal_extension()->settings->get_server_ip();
foreach ( $sites_data as $site_id => $site_data ) {
// If $site_data doesn't have the keys we expected then it failed to retrieve logs.
if ( ! empty( $site_data ) && ! ( isset( $site_data['events'] ) && isset( $site_data['users'] ) ) ) {
// Search for the site data.
$key = array_search( $site_id, array_column( $mwp_sites, 'id' ), false );
if ( false !== $key && isset( $mwp_sites[ $key ] ) ) {
// Extension is unable to retrieve events.
$this->trigger(
7710,
array(
'friendly_name' => $mwp_sites[ $key ]['name'],
'site_url' => $mwp_sites[ $key ]['url'],
'site_id' => $mwp_sites[ $key ]['id'],
'mainwp_dash' => true,
'Username' => 'System',
'ClientIP' => ! empty( $server_ip ) ? $server_ip : false,
)
);
}
} elseif ( empty( $site_data ) || ! isset( $site_data['events'] ) ) {
continue;
}
if ( isset( $site_data['events'] ) ) {
$this->log_events( $site_data['events'], $site_id );
}
if ( isset( $site_data['users'] ) ) {
save_child_site_users( $site_id, $site_data['users'] );
}
}
}
}
/**
* Get event objects.
*
* @return array
*/
public function get_event_objects_data() {
if ( isset( self::$objects ) ) {
return self::$objects;
}
$objects = array(
'activity-logs' => __( 'Activity Logs', 'mwp-al-ext' ),
'child-site' => __( 'Child Site', 'mwp-al-ext' ),
'extension' => __( 'Extension', 'mwp-al-ext' ),
'mainwp' => __( 'MainWP', 'mwp-al-ext' ),
'uptime-monitor' => __( 'Uptime Monitor', 'mwp-al-ext' ),
);
$result = apply_filters(
'wsal_event_objects',
$objects
);
asort( $result );
self::$objects = $result;
return $result;
}
/**
* Returns the text to display for object.
*
* @param string $object - Object.
* @return string
*/
public function get_display_object_text( $object ) {
$data = $this->get_event_objects_data();
if ( array_key_exists( $object, $data ) ) {
return $data[ $object ];
}
return '';
}
/**
* Get event type data.
*
* @return array
*/
public function get_event_type_data() {
if ( isset( self::$event_types ) ) {
return self::$event_types;
}
$event_types = array(
'activated' => __( 'Activated', 'mwp-al-ext' ),
'added' => __( 'Added', 'mwp-al-ext' ),
'created' => __( 'Created', 'mwp-al-ext' ),
'deactivated' => __( 'Deactivated', 'mwp-al-ext' ),
'deleted' => __( 'Deleted', 'mwp-al-ext' ),
'failed' => __( 'Failed', 'mwp-al-ext' ),
'finished' => __( 'Finished', 'mwp-al-ext' ),
'installed' => __( 'Installed', 'mwp-al-ext' ),
'modified' => __( 'Modified', 'mwp-al-ext' ),
'removed' => __( 'Removed', 'mwp-al-ext' ),
'started' => __( 'Started', 'mwp-al-ext' ),
'stopped' => __( 'Stopped', 'mwp-al-ext' ),
'synced' => __( 'Synced', 'mwp-al-ext' ),
'uninstalled' => __( 'Uninstalled', 'mwp-al-ext' ),
'updated' => __( 'Updated', 'mwp-al-ext' ),
);
$result = apply_filters(
'wsal_event_type_data',
$event_types
);
asort( $result );
self::$event_types = $result;
return $result;
}
/**
* Returns the text to display for event type.
*
* @param string $event_type - Event type.
* @return string
*/
public function get_display_event_type_text( $event_type ) {
$data = $this->get_event_type_data();
if ( array_key_exists( $event_type, $data ) ) {
return $data[ $event_type ];
}
return '';
}
}