table_name = $wpdb->prefix . 'advausro_temp_roles'; // Register custom schedule early add_filter('cron_schedules', function ($advausro_schedules) { $advausro_schedules['every_five_minutes'] = [ 'interval' => 300, 'display' => __('Every 5 Minutes', 'advanced-use-role-management') ]; return $advausro_schedules; }); // Register your actions add_action('wp_ajax_advausro_assign_temp_role', [$this, 'advausro_assign_temp_role']); add_action('wp_ajax_advausro_remove_temp_role', [$this, 'advausro_remove_temp_role']); add_action('wp_ajax_advausro_check_and_remove_expired_roles', [$this, 'advausro_check_and_remove_expired_roles']); add_action('advausro_check_expired_roles', [$this, 'advausro_remove_expired_roles']); // Schedule cron *after init* add_action('init', function () { if (!wp_next_scheduled('advausro_check_expired_roles')) { wp_schedule_event(time(), 'every_five_minutes', 'advausro_check_expired_roles'); } }); } public static function advausro_get_instance() { if (self::$advausro_instance === null) { self::$advausro_instance = new self(); } return self::$advausro_instance; } public function advausro_assign_temp_role() { check_ajax_referer('advausro_ajax_nonce', 'security'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => esc_html__('Unauthorized access', 'advanced-use-role-management')]); wp_die(); } $advausro_user_id = isset($_POST['user_id']) ? absint(wp_unslash($_POST['user_id'])) : 0; $advausro_role_slug = isset($_POST['role_slug']) ? sanitize_text_field(wp_unslash($_POST['role_slug'])) : ''; $advausro_expires_at = isset($_POST['expires_at']) ? sanitize_text_field(wp_unslash($_POST['expires_at'])) : ''; if (!$advausro_user_id || !$advausro_role_slug || !$advausro_expires_at) { wp_send_json_error(['message' => 'Missing required fields.']); } try { $advausro_expires_dt = new \DateTime($advausro_expires_at, new \DateTimeZone(wp_timezone_string())); } catch (\Exception $advausro_e) { wp_send_json_error(['message' => 'Invalid expiration format.']); } if ($advausro_expires_dt->getTimestamp() <= time()) { wp_send_json_error(['message' => 'Expiration must be in the future.']); } $advausro_user = get_user_by('id', $advausro_user_id); if (!$advausro_user) { wp_send_json_error(['message' => 'User not found.']); } if (!get_role($advausro_role_slug)) { wp_send_json_error(['message' => 'Selected role does not exist.']); } global $wpdb; // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name is built from the local WordPress prefix. $advausro_existing = $wpdb->get_var($wpdb->prepare( "SELECT id FROM {$this->table_name} WHERE user_id = %d AND role_slug = %s", $advausro_user_id, $advausro_role_slug )); if ($advausro_existing) { wp_send_json_error(['message' => 'Role already temporarily assigned.']); } $role_snapshot = $this->advausro_get_role_snapshot($advausro_user); if (empty($role_snapshot['roles'])) { wp_send_json_error(['message' => 'User has no roles available to snapshot.']); } if (!get_user_meta($advausro_user_id, '_advausro_original_roles', true)) { update_user_meta($advausro_user_id, '_advausro_original_roles', $role_snapshot); } if (!in_array($advausro_role_slug, $advausro_user->roles, true)) { $advausro_user->add_role($advausro_role_slug); } $advausro_expires_at_formatted = $advausro_expires_dt->format('Y-m-d H:i:s'); $insert_result = $wpdb->insert($this->table_name, [ 'user_id' => $advausro_user_id, 'role_slug' => $advausro_role_slug, 'expires_at' => $advausro_expires_at_formatted ]); if ($insert_result === false) { $this->advausro_restore_roles($advausro_user, $role_snapshot); delete_user_meta($advausro_user_id, '_advausro_original_roles'); error_log('DEBUG AURM: temporary role assignment insert failed for user ' . (int) $advausro_user_id); wp_send_json_error(['message' => 'Failed to assign temporary role: ' . $wpdb->last_error]); } wp_send_json_success(['message' => 'Temporary role assigned.']); } public function advausro_remove_temp_role() { check_ajax_referer('advausro_ajax_nonce', 'security'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => esc_html__('Unauthorized access', 'advanced-use-role-management')]); wp_die(); } $advausro_role_id = isset($_POST['role_id']) ? absint(wp_unslash($_POST['role_id'])) : 0; global $wpdb; // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name is built from the local WordPress prefix. $advausro_entry = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$this->table_name} WHERE id = %d", $advausro_role_id)); if (!$advausro_entry) { wp_send_json_error(['message' => 'Entry not found.']); } $advausro_user = get_user_by('id', $advausro_entry->user_id); if ($advausro_user) { $this->advausro_restore_user_from_entry($advausro_user, $advausro_entry); } $wpdb->delete($this->table_name, ['id' => $advausro_entry->id]); wp_send_json_success(['message' => 'Temporary role removed.']); } public function advausro_remove_expired_roles() { $this->advausro_process_expired_roles(); } public function advausro_check_and_remove_expired_roles() { check_ajax_referer('advausro_ajax_nonce', 'security'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => esc_html__('Unauthorized access', 'advanced-use-role-management')]); wp_die(); } global $wpdb; $advausro_now = current_time('mysql'); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name is built from the local WordPress prefix. $advausro_expired = $wpdb->get_results($wpdb->prepare( "SELECT id FROM {$this->table_name} WHERE expires_at <= %s", $advausro_now )); $advausro_expired_ids = array_column($advausro_expired, 'id'); $this->advausro_process_expired_roles(); wp_send_json_success(['message' => 'Checked and cleaned expired roles.', 'removed_ids' => $advausro_expired_ids]); } private function advausro_process_expired_roles(): void { global $wpdb; $advausro_now = current_time('mysql'); $advausro_expired = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$this->table_name} WHERE expires_at <= %s", $advausro_now ) // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name is built from the local WordPress prefix. ); foreach ($advausro_expired as $advausro_entry) { $advausro_user = get_user_by('id', $advausro_entry->user_id); if ($advausro_user) { $this->advausro_restore_user_from_entry($advausro_user, $advausro_entry); } $wpdb->delete($this->table_name, ['id' => $advausro_entry->id]); } } private function advausro_restore_user_from_entry(\WP_User $advausro_user, $advausro_entry): void { $snapshot = get_user_meta($advausro_entry->user_id, '_advausro_original_roles', true); $advausro_user->remove_role($advausro_entry->role_slug); if (!is_array($snapshot) || empty($snapshot['roles']) || !is_array($snapshot['roles'])) { return; } $this->advausro_restore_roles($advausro_user, $snapshot); if (!$this->advausro_user_has_active_temp_roles((int) $advausro_entry->user_id, (int) $advausro_entry->id)) { delete_user_meta($advausro_entry->user_id, '_advausro_original_roles'); } } private function advausro_get_role_snapshot(\WP_User $advausro_user): array { $roles = array_values(array_filter(array_map('sanitize_key', (array) $advausro_user->roles))); return [ 'roles' => $roles, 'captured_at' => current_time('mysql'), ]; } private function advausro_restore_roles(\WP_User $advausro_user, array $snapshot): void { $snapshot_roles = array_values(array_filter(array_map('sanitize_key', (array) ($snapshot['roles'] ?? [])))); if (empty($snapshot_roles)) { return; } $current_roles = array_values(array_filter(array_map('sanitize_key', (array) $advausro_user->roles))); foreach ($snapshot_roles as $role_slug) { if (get_role($role_slug) && !in_array($role_slug, $current_roles, true)) { $advausro_user->add_role($role_slug); } } $current_roles = array_values(array_filter(array_map('sanitize_key', (array) $advausro_user->roles))); $extra_roles = array_diff($current_roles, $snapshot_roles); foreach ($extra_roles as $role_slug) { $advausro_user->remove_role($role_slug); } if (empty($advausro_user->roles) && !empty($snapshot_roles[0]) && get_role($snapshot_roles[0])) { $advausro_user->set_role($snapshot_roles[0]); } } private function advausro_user_has_active_temp_roles(int $user_id, int $exclude_entry_id = 0): bool { global $wpdb; $query = "SELECT COUNT(*) FROM {$this->table_name} WHERE user_id = %d"; $args = [$user_id]; if ($exclude_entry_id > 0) { $query .= " AND id != %d"; $args[] = $exclude_entry_id; } $active_count = $wpdb->get_var( $wpdb->prepare($query, $args) // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name is built from the local WordPress prefix. ); return (int) $active_count > 0; } } add_action('init', function () { ADVAUSRO_TemporaryRoleManager::advausro_get_instance(); });