*/ public static function header(): array { return [ __('Entry', 'aipcf-ai-provider-for-cloudflare'), __('Time (UTC)', 'aipcf-ai-provider-for-cloudflare'), __('Who', 'aipcf-ai-provider-for-cloudflare'), __('Tool', 'aipcf-ai-provider-for-cloudflare'), __('Action', 'aipcf-ai-provider-for-cloudflare'), __('Status', 'aipcf-ai-provider-for-cloudflare'), __('Message', 'aipcf-ai-provider-for-cloudflare'), __('Neurons', 'aipcf-ai-provider-for-cloudflare'), __('Type', 'aipcf-ai-provider-for-cloudflare'), __('ID', 'aipcf-ai-provider-for-cloudflare'), __('Title', 'aipcf-ai-provider-for-cloudflare'), __('URL', 'aipcf-ai-provider-for-cloudflare'), __('Field', 'aipcf-ai-provider-for-cloudflare'), __('Old value', 'aipcf-ai-provider-for-cloudflare'), __('New value', 'aipcf-ai-provider-for-cloudflare'), __('Result', 'aipcf-ai-provider-for-cloudflare'), __('Reason', 'aipcf-ai-provider-for-cloudflare'), ]; } /** * Writes the entries matching the filters, newest first. Returns the number of rows written. * * @param resource $out * @param array $filters */ public static function write($out, array $filters): int { fwrite($out, "\xEF\xBB\xBF"); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fwrite -- streaming to the download or WP-CLI output self::put($out, self::header()); $rows = 0; $before = PHP_INT_MAX; do { $entries = ActivityLog::entriesBefore($filters, $before, self::PAGE); if ($entries === []) { break; } $ids = array_column($entries, 'id'); $items = ActivityLog::items($ids); $posts = []; foreach ($items as $list) { foreach ($list as $i) { if ($i['object_id'] > 0 && in_array($i['object_type'], ['post', 'attachment'], true)) { $posts[] = $i['object_id']; } } } if ($posts !== []) { _prime_post_caches(array_values(array_unique($posts)), false, false); } foreach ($entries as $e) { $base = [ (string) $e['id'], gmdate('Y-m-d\TH:i:s\Z', (int) strtotime($e['ts'] . ' UTC')), Labels::who($e), Labels::tool((string) $e['tool']), Labels::action((string) $e['action']), Labels::status((string) $e['status']), (string) $e['message'], $e['neurons'] > 0 ? (string) round($e['neurons'], 2) : '', ]; $list = $items[$e['id']] ?? []; if ($list === []) { self::put($out, array_merge($base, array_fill(0, 9, ''))); $rows++; continue; } foreach ($list as $i) { self::put($out, array_merge($base, self::itemCells($i))); $rows++; } } $before = (int) end($entries)['id']; if (function_exists('wp_cache_supports') && wp_cache_supports('flush_runtime')) { wp_cache_flush_runtime(); // keep memory flat on a long export } } while (count($entries) === self::PAGE); return $rows; } /** * A cell a spreadsheet could take for a formula gets a leading apostrophe, so generated * text, a post title or a setting value can never run as one. Leading spaces and line * breaks are looked past (some importers trim them), and the full-width signs count too. */ public static function cell(string $value): string { $start = ltrim($value, " \t\r\n\0\x0B"); if ($start === '') { return $value; } $first = mb_substr($start, 0, 1); return in_array($first, ['=', '+', '-', '@', "\u{FF1D}", "\u{FF0B}", "\u{FF0D}", "\u{FF20}"], true) || $value[0] === "\t" || $value[0] === "\r" ? "'" . $value : $value; } /** * @param array $i * @return list */ private static function itemCells(array $i): array { $type = (string) $i['object_type']; $id = (int) $i['object_id']; $title = ''; $url = ''; if ($id > 0 && in_array($type, ['post', 'attachment'], true)) { $post = get_post($id); if ($post instanceof \WP_Post) { $title = get_the_title($post); $url = $type === 'attachment' ? (string) wp_get_attachment_url($id) : (string) get_permalink($post); } } return [ $type, $id > 0 ? (string) $id : '', $title, $url, self::field((string) $i['field']), Labels::value((string) $i['old_value']), Labels::value((string) $i['new_value']), Labels::result((string) $i['result']), (string) $i['reason'], ]; } /** * The field's name, with the stored key when they differ: "Meta description (rank_math_description)". */ private static function field(string $key): string { $label = Labels::field($key); return $label === $key || $key === '' ? $key : $label . ' (' . $key . ')'; } /** * @param resource $out * @param list $row */ private static function put($out, array $row): void { fputcsv($out, array_map([self::class, 'cell'], $row), ',', '"', ''); } }