Current File : /home/digitaw/www/wp-content/updraft/plugins-old/simple-history/dropins/class-export-dropin.php
<?php
namespace Simple_History\Dropins;

use Simple_History\Log_Query;
use Simple_History\Helpers;

/**
 * Dropin Name: Export
 * Dropin Description: Adds a tab with export options
 * Dropin URI: https://simple-history.com/
 * Author: Pär Thernström
 */
class Export_Dropin extends Dropin {
	/**
	 * @inheritdoc
	 */
	public function loaded() {
		$this->simple_history->register_settings_tab(
			array(
				'slug' => 'export',
				'name' => _x( 'Export', 'Export dropin: Tab name on settings page', 'simple-history' ),
				'icon' => 'download',
				'order' => 50,
				'function' => array( $this, 'output' ),
			)
		);

		add_action( 'init', array( $this, 'downloadExport' ) );
	}

	/**
	 * Download export file.
	 */
	public function downloadExport() {
		if ( isset( $_POST['simple-history-action'] ) && $_POST['simple-history-action'] === 'export-history' ) {
			// Will die if nonce not valid.
			check_admin_referer( self::class . '-action-export' );

			$export_format = sanitize_text_field( wp_unslash( $_POST['format'] ?? 'json' ) );

			// Disable relative time output in header.
			add_filter( 'simple_history/header_time_ago_max_time', '__return_zero' );
			add_filter( 'simple_history/header_just_now_max_time', '__return_zero' );

			// Don't use "You" if event is initiated by the same user that does the export.
			add_filter( 'simple_history/header_initiator_use_you', '__return_false' );

			$query = new Log_Query();

			$query_args = array(
				'paged' => 1,
				'posts_per_page' => 3000,
			);

			$events = $query->query( $query_args );

			// $events->total_row_count;
			$pages_count = $events['pages_count'];
			$page_current = $events['page_current'];

			$fp = fopen( 'php://output', 'w' );

			$attachment_header_template = 'Content-Disposition: attachment; filename="%1$s"';

			if ( 'csv' == $export_format ) {
				$filename = 'simple-history-export-' . time() . '.csv';
				header( 'Content-Type: text/plain' );
				header( sprintf( $attachment_header_template, $filename ) );
			} elseif ( 'json' == $export_format ) {
				$filename = 'simple-history-export-' . time() . '.json';
				header( 'Content-Type: application/json' );
				header( sprintf( $attachment_header_template, $filename ) );
			} elseif ( 'html' == $export_format ) {
				$filename = 'simple-history-export-' . time() . '.html';
				header( 'Content-Type: text/html' );
			}

			// Some formats need to output some stuff before the actual loops.
			if ( 'json' == $export_format ) {
				$json_row = '[';
				fwrite( $fp, $json_row );
			} elseif ( 'html' == $export_format ) {
				$html = sprintf(
					'
				<!doctype html>
				<meta charset="utf-8">
				<title>Simple History export</title>
				<ul>
				'
				);
				fwrite( $fp, $html );
			}

			// Paginate through all pages and all their rows.
			$row_loop = 0;
			while ( $page_current <= $pages_count + 1 ) {

				foreach ( $events['log_rows'] as $one_row ) {

					set_time_limit( 30 );

					if ( 'csv' == $export_format ) {
						$header_output = strip_tags( html_entity_decode( $this->simple_history->get_log_row_header_output( $one_row ), ENT_QUOTES, 'UTF-8' ) );
						$header_output = trim( preg_replace( '/\s\s+/', ' ', $header_output ) );

						$message_output = strip_tags( html_entity_decode( $this->simple_history->get_log_row_plain_text_output( $one_row ), ENT_QUOTES, 'UTF-8' ) );

						$user_email = empty( $one_row->context['_user_email'] ) ? null : $one_row->context['_user_email'];
						$user_login = empty( $one_row->context['_user_login'] ) ? null : $one_row->context['_user_login'];

						// User roles, at time of export.
						$user = get_user_by( 'email', $user_email );
						$user_roles = $user->roles ?? array();
						$user_roles_comma_separated = implode( ', ', $user_roles );

						// Date local time.
						$date_local = wp_date( 'Y-m-d H:i:s', strtotime( $one_row->date ) );

						fputcsv(
							$fp,
							array(
								$this->esc_csv_field( $one_row->date ),
								$this->esc_csv_field( $date_local ),
								$this->esc_csv_field( $one_row->logger ),
								$this->esc_csv_field( $one_row->level ),
								$this->esc_csv_field( $one_row->initiator ),
								$this->esc_csv_field( $one_row->context_message_key ),
								$this->esc_csv_field( $user_email ),
								$this->esc_csv_field( $user_login ),
								$this->esc_csv_field( $user_roles_comma_separated ),
								$this->esc_csv_field( $header_output ),
								$this->esc_csv_field( $message_output ),
								// phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
								$this->esc_csv_field( $one_row->subsequentOccasions ),
							)
						);
					} elseif ( 'json' == $export_format ) {
						// If not first loop then add a comma between all json objects.
						if ( $row_loop == 0 ) {
							$comma = "\n";
						} else {
							$comma = ",\n";
						}

						$json_row = $comma . Helpers::json_encode( $one_row );
						fwrite( $fp, $json_row );
					} elseif ( 'html' == $export_format ) {
						$html = sprintf(
							'
							<li>
								<div>%1$s</div>
								<div>%2$s</div>
								<div>%3$s</div>
							</li>
							',
							$this->simple_history->get_log_row_header_output( $one_row ),
							$this->simple_history->get_log_row_plain_text_output( $one_row ),
							$this->simple_history->get_log_row_details_output( $one_row )
						);

						fwrite( $fp, $html );
					}// End if().

					$row_loop++;
				}// End foreach().

				flush();

				// Fetch next page
				// @TODO: must take into consideration that new items can be added while we do the fetch.
				$page_current++;
				$query_args['paged'] = $page_current;
				$events = $query->query( $query_args );
			}// End while().

			if ( 'json' == $export_format ) {
				$json_row = ']';
				fwrite( $fp, $json_row );
			} elseif ( 'html' == $export_format ) {
				$html = sprintf( '</ul>' );
				fwrite( $fp, $html );
			}

			fclose( $fp );
			flush();

			exit;
		}// End if().
	}

	/**
	 * Output for the export tab on the settings page.
	 */
	public function output() {
		?>

		<div class="wrap">
			<?php
			echo wp_kses(
				Helpers::get_settings_section_title_output(
					__( 'Export history', 'simple-history' ),
					'download'
				),
				array(
					'span' => array(
						'class' => array(),
					),
				)
			);
			?>
			<p><?php echo esc_html_x( 'The export function will export the full history.', 'Export dropin: introtext', 'simple-history' ); ?></p>

			<form method="post">

				<h3><?php echo esc_html_x( 'Choose format to export to', 'Export dropin: format', 'simple-history' ); ?></h3>

				<p>
					<label>
						<input type="radio" name="format" value="json" checked>
						<?php echo esc_html_x( 'JSON', 'Export dropin: export format', 'simple-history' ); ?>
					</label>
				</p>

				<p>
					<label>
						<input type="radio" name="format" value="csv">
						<?php echo esc_html_x( 'CSV', 'Export dropin: export format', 'simple-history' ); ?>
					</label>
				</p>

				<p>
					<button type="submit" class="button button-primary">
						<?php echo esc_html_x( 'Download Export File', 'Export dropin: submit button', 'simple-history' ); ?>
					</button>
					<input type="hidden" name="simple-history-action" value="export-history">
				</p>

				<?php
				wp_nonce_field( self::class . '-action-export' );
				?>

			</form>
		
		</div>

		<?php
	}

	/**
	 * Escape a string to be used in a CSV context, by adding an apostrophe if field
	 * begins with '=', '+', '-', or '@'.
	 *
	 * Function taken from the Jetpack Plugin, Copyright Automattic.
	 *
	 * @see https://www.drupal.org/project/webform/issues/3157877#:~:text=At%20present%2C%20the%20best%20defence%20strategy%20we%20are%20aware%20of%20is%20prefixing%20cells%20that%20start%20with%20%E2%80%98%3D%E2%80%99%20%2C%20%27%2B%27%20or%20%27%2D%27%20with%20an%20apostrophe.
	 * @see https://github.com/Automattic/jetpack/blob/d4068d52c35a30edc01b9356a4764132aeb532fd/projects/packages/forms/src/contact-form/class-contact-form-plugin.php#L1854
	 * @param string $field - the CSV field.
	 * @return string
	 */
	public function esc_csv_field( $field ) {
		// Bail if not string.
		if ( ! is_string( $field ) ) {
			return '';
		}

		$active_content_triggers = array( '=', '+', '-', '@' );

		if ( in_array( substr( $field, 0, 1 ), $active_content_triggers, true ) ) {
			$field = "'" . $field;
		}

		return $field;
	}
}