HEX
Server: LiteSpeed
System: Linux eko108.isimtescil.net 4.18.0-477.21.1.lve.1.el8.x86_64 #1 SMP Tue Sep 5 23:08:35 UTC 2023 x86_64
User: uyarreklamcomtr (11202)
PHP: 7.4.33
Disabled: opcache_get_status
Upload Files
File: /var/www/vhosts/uyarreklam.com.tr/httpdocs/cloud.tar
class-cloud-api.php000064400000031716151540014520010242 0ustar00<?php

namespace Code_Snippets\Cloud;

use Code_Snippets\Snippet;
use WP_Error;
use function Code_Snippets\get_snippet_by_cloud_id;
use function Code_Snippets\get_snippets;
use function Code_Snippets\save_snippet;
use function Code_Snippets\update_snippet_fields;

/**
 * Functions used to manage cloud synchronisation.
 *
 * @package Code_Snippets
 */
class Cloud_API {

	/**
	 * Key used to access the local-to-cloud map transient data.
	 */
	private const CLOUD_MAP_TRANSIENT_KEY = 'cs_local_to_cloud_map';

	/**
	 * Days to cache data retrieved from API.
	 */
	private const DAYS_TO_STORE_CS = 1;

	/**
	 * Name of key used to cache cloud settings.
	 *
	 * @var string
	 */
	private const CLOUD_SETTINGS_CACHE_KEY = 'code_snippets_cloud_settings';

	/**
	 * Token used for public API access.
	 *
	 * @var string
	 */
	private const CLOUD_SEARCH_API_TOKEN = 'csc-1a2b3c4d5e6f7g8h9i0j';

	/**
	 * Cached list of cloud links.
	 *
	 * @var Cloud_Link[]|null
	 */
	private ?array $cached_cloud_links = null;

	/**
	 * Retrieve the Cloud URL from wp-config or fallback to default.
	 *
	 * @return string
	 */
	public static function get_cloud_url(): string {
		return defined( 'CS_CLOUD_URL' )
			? CS_CLOUD_URL
			: 'https://codesnippets.cloud/';
	}

	/**
	 * Retrieve the Cloud API URL from wp-config or fallback to default.
	 *
	 * @return string
	 */
	public static function get_cloud_api_url(): string {
		return defined( 'CS_CLOUD_API_URL' )
			? CS_CLOUD_API_URL
			: self::get_cloud_url() . 'api/v1/';
	}

	/**
	 * Retrieve the cloud local token.
	 *
	 * @return string
	 */
	public static function get_local_token(): string {
		return self::CLOUD_SEARCH_API_TOKEN;
	}

	/**
	 * Check that the cloud key is valid and verified.
	 *
	 * @return boolean
	 */
	public static function is_cloud_key_verified(): bool {
		return false;
	}

	/**
	 * Check if the API key is set and verified.
	 *
	 * @return boolean
	 */
	public static function is_cloud_connection_available(): bool {
		return false;
	}

	/**
	 * Create local-to-cloud map to keep track of local snippets that have been synced to the cloud.
	 *
	 * @return Cloud_Link[]
	 */
	private function get_cloud_links(): ?array {
		// Return the cached data if available.
		if ( is_array( $this->cached_cloud_links ) ) {
			return $this->cached_cloud_links;
		}

		// Fetch data from the stored transient, if available.
		$transient_data = get_transient( self::CLOUD_MAP_TRANSIENT_KEY );
		if ( is_array( $transient_data ) ) {
			$this->cached_cloud_links = $transient_data;
			return $this->cached_cloud_links;
		}

		// Otherwise, regenerate the local-to-cloud-map.
		$this->cached_cloud_links = [];

		// Fetch and iterate through all local snippets to create the map.
		foreach ( get_snippets() as $local_snippet ) {
			// Skip snippets that are only stored locally.
			if ( ! $local_snippet->cloud_id ) {
				continue;
			}

			$link = new Cloud_Link();
			$cloud_id_owner = $this->get_cloud_id_and_ownership( $local_snippet->cloud_id );
			$cloud_id_int = intval( $cloud_id_owner['cloud_id'] );
			$link->local_id = $local_snippet->id;
			$link->cloud_id = $cloud_id_int;
			$link->is_owner = $cloud_id_owner['is_owner'];
			// Check if cloud id exists in cloud_id_rev array - this shows if the snippet is in the codevault.
			$link->in_codevault = $cloud_id_rev[ $cloud_id_int ] ?? false;

			// Get the cloud snippet revision if in codevault get from cloud_id_rev array otherwise get from cloud.
			if ( $link->in_codevault ) {
				$cloud_snippet_revision = $cloud_id_rev[ $cloud_id_int ] ?? $this->get_cloud_snippet_revision( $local_snippet->cloud_id );
				$link->update_available = $local_snippet->revision < $cloud_snippet_revision;
			}

			$this->cached_cloud_links[] = $link;
		}

		set_transient(
			self::CLOUD_MAP_TRANSIENT_KEY,
			$this->cached_cloud_links,
			DAY_IN_SECONDS * self::DAYS_TO_STORE_CS
		);

		return $this->cached_cloud_links;
	}

	/**
	 * Get ownership and Cloud ID of a snippet.
	 *
	 * @param string $cloud_id Cloud ID.
	 *
	 * @return array<string, mixed>
	 */
	public function get_cloud_id_and_ownership( string $cloud_id ): array {
		$cloud_id_owner = explode( '_', $cloud_id );

		return [
			'cloud_id'        => (int) $cloud_id_owner[0] ?? '',
			'is_owner'        => isset( $cloud_id_owner[1] ) && $cloud_id_owner[1],
			'is_owner_string' => isset( $cloud_id_owner[1] ) && $cloud_id_owner[1] ? '1' : '0',
		];
	}

	/**
	 * Unpack JSON data from a request response.
	 *
	 * @param array|WP_Error $response Response from wp_request_*.
	 *
	 * @return array<string, mixed>|null Associative array of JSON data on success, null on failure.
	 */
	private static function unpack_request_json( $response ): ?array {
		$body = wp_remote_retrieve_body( $response );
		return $body ? json_decode( $body, true ) : null;
	}

	/**
	 * Search Code Snippets Cloud -> Static Function
	 *
	 * @param string  $search_method Search by name of codevault or keyword(s).
	 * @param string  $search        Search query.
	 * @param integer $page          Search result page to retrieve. Defaults to '0'.
	 *
	 * @return Cloud_Snippets Result of search query.
	 */
	public static function fetch_search_results( string $search_method, string $search, int $page = 0 ): Cloud_Snippets {
		$api_url = add_query_arg(
			[
				's_method'   => $search_method,
				's'          => $search,
				'page'       => $page,
				'site_token' => self::get_local_token(),
				'site_host'  => wp_parse_url( get_site_url(), PHP_URL_HOST ),
			],
			self::get_cloud_api_url() . 'public/search'
		);

		$results = self::unpack_request_json( wp_remote_get( $api_url ) );

		$results = new Cloud_Snippets( $results );
		$results->page = $page;

		return $results;
	}

	/**
	 * Add a new link item to the local-to-cloud map.
	 *
	 * @param Cloud_Link $link Link to add.
	 *
	 * @return void
	 */
	public function add_cloud_link( Cloud_Link $link ) {
		$local_to_cloud_map = get_transient( self::CLOUD_MAP_TRANSIENT_KEY );
		$local_to_cloud_map[] = $link;

		set_transient(
			self::CLOUD_MAP_TRANSIENT_KEY,
			$local_to_cloud_map,
			DAY_IN_SECONDS * self::DAYS_TO_STORE_CS
		);
	}

	/**
	 * Delete a snippet from local-to-cloud map.
	 *
	 * @param int $snippet_id Local snippet ID.
	 *
	 * @return void
	 */
	public function delete_snippet_from_transient_data( int $snippet_id ) {
		if ( ! $this->cached_cloud_links ) {
			$this->get_cloud_links();
		}

		foreach ( $this->cached_cloud_links as $link ) {
			if ( $link->local_id === $snippet_id ) {
				// Remove the link from the local_to_cloud_map.
				$index = array_search( $link, $this->cached_cloud_links, true );
				unset( $this->cached_cloud_links[ $index ] );

				// Update the transient data.
				set_transient(
					self::CLOUD_MAP_TRANSIENT_KEY,
					$this->cached_cloud_links,
					DAY_IN_SECONDS * self::DAYS_TO_STORE_CS
				);
			}
		}
	}

	/**
	 * Retrieve a single cloud snippet from the API.
	 *
	 * @param int $cloud_id Remote cloud snippet ID.
	 *
	 * @return Cloud_Snippet Retrieved snippet.
	 */
	public static function get_single_snippet_from_cloud( int $cloud_id ): Cloud_Snippet {
		$url = self::get_cloud_api_url() . sprintf( 'public/getsnippet/%s', $cloud_id );
		$response = wp_remote_get( $url );
		$cloud_snippet = self::unpack_request_json( $response );

		return new Cloud_Snippet( $cloud_snippet['snippet'] );
	}

	/**
	 * Get the current revision of a single cloud snippet.
	 *
	 * @param string $cloud_id Cloud snippet ID.
	 *
	 * @return string|null Revision number on success, null otherwise.
	 */
	public static function get_cloud_snippet_revision( string $cloud_id ): ?string {
		$api_url = self::get_cloud_api_url() . sprintf( 'public/getsnippetrevision/%s', $cloud_id );
		$body = wp_remote_retrieve_body( wp_remote_get( $api_url ) );

		if ( ! $body ) {
			return null;
		}

		$cloud_snippet_revision = json_decode( $body, true );
		return $cloud_snippet_revision['snippet_revision'] ?? null;
	}

	/**
	 * Download a snippet from the cloud.
	 *
	 * @param int|string $cloud_id The cloud ID of the snippet as string from query args.
	 * @param string     $source   Unused in Core.
	 * @param string     $action   The action to be performed: 'download' or 'update'.
	 *
	 * @return array<string, string|bool> Result of operation: an array with `success` and `error_message` keys.
	 *
	 * @noinspection PhpUnusedParameterInspection
	 */
	public function download_or_update_snippet( int $cloud_id, string $source, string $action ): array {
		$cloud_id = intval( $cloud_id );

		$snippet_to_store = $this->get_single_snippet_from_cloud( $cloud_id );

		switch ( $action ) {
			case 'download':
				return $this->download_snippet_from_cloud( $snippet_to_store );
			case 'update':
				return $this->update_snippet_from_cloud( $snippet_to_store );
			default:
				return [
					'success' => false,
					'error'   => __( 'Invalid action.', 'code-snippets' ),
				];
		}
	}

	/**
	 * Download a snippet from the cloud.
	 *
	 * @param Cloud_Snippet $snippet_to_store The snippet to be downloaded.
	 *
	 * @return array The result of the download.
	 */
	public function download_snippet_from_cloud( Cloud_Snippet $snippet_to_store ): array {
		$snippet = new Snippet( $snippet_to_store );

		// Set the snippet id to 0 to ensure that the snippet is saved as a new snippet.
		$ownership = $snippet_to_store->is_owner ? '1' : '0';
		$snippet->id = 0;
		$snippet->active = 0;
		$snippet->cloud_id = $snippet_to_store->id . '_' . $ownership;
		$snippet->desc = $snippet_to_store->description ? $snippet_to_store->description : '';

		// Save the snippet to the database.
		$new_snippet = save_snippet( $snippet );

		$link = new Cloud_Link();
		$link->local_id = $new_snippet->id;
		$link->cloud_id = $snippet_to_store->id;
		$link->is_owner = $snippet_to_store->is_owner;
		$link->in_codevault = false;
		$link->update_available = false;

		$this->add_cloud_link( $link );

		return [
			'success'    => true,
			'action'     => 'Single Downloaded',
			'snippet_id' => $new_snippet->id,
			'link_id'    => $link->cloud_id,
		];
	}

	/**
	 * Update a snippet from the cloud.
	 *
	 * @param Cloud_Snippet $snippet_to_store Snippet to be updated.
	 *
	 * @return array The result of the update.
	 */
	public function update_snippet_from_cloud( Cloud_Snippet $snippet_to_store ): array {
		$cloud_id = $snippet_to_store->id . '_' . ( $snippet_to_store->is_owner ? '1' : '0' );

		$local_snippet = get_snippet_by_cloud_id( sanitize_key( $cloud_id ) );

		// Only update the code, active and revision fields.
		$fields = [
			'code'     => $snippet_to_store->code,
			'active'   => false,
			'revision' => $snippet_to_store->revision,
		];

		update_snippet_fields( $local_snippet->id, $fields );
		$this->clear_caches();

		return [
			'success' => true,
			'action'  => __( 'Updated', 'code-snippets' ),
		];
	}

	/**
	 * Find the cloud link for a given cloud snippet identifier.
	 *
	 * @param int $cloud_id Cloud ID.
	 *
	 * @return Cloud_Link|null
	 */
	public function get_link_for_cloud_id( int $cloud_id ): ?Cloud_Link {
		$cloud_links = $this->get_cloud_links();

		if ( $cloud_links ) {
			foreach ( $cloud_links as $cloud_link ) {
				if ( $cloud_link->cloud_id === $cloud_id ) {
					return $cloud_link;
				}
			}
		}

		return null;
	}


	/**
	 * Find the cloud link for a given cloud snippet.
	 *
	 * @param Cloud_Snippet $cloud_snippet Cloud snippet.
	 *
	 * @return Cloud_Link|null
	 */
	public function get_link_for_cloud_snippet( Cloud_Snippet $cloud_snippet ): ?Cloud_Link {
		return $this->get_link_for_cloud_id( $cloud_snippet->id );
	}

	/**
	 * Translate a snippet scope to a type.
	 *
	 * @param string $scope The scope of the snippet.
	 *
	 * @return string The type of the snippet.
	 */
	public static function get_type_from_scope( string $scope ): string {
		switch ( $scope ) {
			case 'global':
				return 'php';
			case 'site-css':
				return 'css';
			case 'site-footer-js':
				return 'js';
			case 'content':
				return 'html';
			default:
				return '';
		}
	}

	/**
	 * Translate a snippet status to a status-name.
	 *
	 * @param int $status The scope of the snippet.
	 *
	 * @return string The style to be used for the stats badge.
	 */
	public static function get_status_name_from_status( int $status ): string {
		switch ( $status ) {
			case 3:
				return __( 'Private', 'code-snippets' );
			case 4:
				return __( 'Public', 'code-snippets' );
			case 5:
				return __( 'Unverified', 'code-snippets' );
			case 6:
				return __( 'AI Verified', 'code-snippets' );
			case 8:
				return __( 'Pro Verified', 'code-snippets' );
			default:
				return '';
		}
	}

	/**
	 * Renders the html for the preview thickbox popup.
	 *
	 * @return void
	 */
	public static function render_cloud_snippet_thickbox() {
		add_thickbox();
		?>
		<div id="show-code-preview" style="display: none;">
			<h3 id="snippet-name-thickbox"></h3>
			<h4><?php esc_html_e( 'Snippet Code:', 'code-snippets' ); ?></h4>
			<pre class="thickbox-code-viewer">
				<code id="snippet-code-thickbox"></code>
			</pre>
		</div>
		<?php
	}

	/**
	 * Refresh the cached synced data.
	 *
	 * @return void
	 */
	public function clear_caches() {
		$this->cached_cloud_links = null;

		delete_transient( self::CLOUD_MAP_TRANSIENT_KEY );
	}
}
class-cloud-link.php000064400000003071151540014520010417 0ustar00<?php

namespace Code_Snippets\Cloud;

use Code_Snippets\Data_Item;

/**
 * A connection between a local snippet and remote cloud snippet.
 *
 * @package Code_Snippets
 *
 * @property integer $local_id         ID of local snippet as stored in WordPress database, if applicable.
 * @property integer $cloud_id         ID of remote snippet on cloud platform, if applicable.
 * @property boolean $is_owner         Ownership status of remote snippet on cloud platform.
 * @property boolean $in_codevault     Whether the remote snippet is stored in the users' codevault.
 * @property boolean $update_available If synchronised, whether there is an update available on the cloud platform.
 */
class Cloud_Link extends Data_Item {

	/**
	 * Constructor function
	 *
	 * @param array<string, mixed>|object $data Initial data fields.
	 */
	public function __construct( $data = null ) {
		parent::__construct(
			[
				'local_id'         => 0,
				'cloud_id'         => 0,
				'is_owner'         => false,
				'in_codevault'     => false,
				'update_available' => false,
			],
			$data
		);
	}

	/**
	 * Prepare a value before it is stored.
	 *
	 * @param mixed  $value Value to prepare.
	 * @param string $field Field name.
	 *
	 * @return mixed Value in the correct format.
	 */
	protected function prepare_field( $value, string $field ) {
		switch ( $field ) {
			case 'local_id':
			case 'remote_id':
				return absint( $value );

			case 'is_owner':
			case 'in_codevault':
			case 'update_available':
				return is_bool( $value ) ? $value : (bool) $value;

			default:
				return $value;
		}
	}
}
class-cloud-search-list-table.php000064400000030257151540014520012773 0ustar00<?php
/**
 * Contains the class for handling the cloud search results table
 *
 * @package Code_Snippets
 */

namespace Code_Snippets\Cloud;

use WP_Plugin_Install_List_Table;
use function Code_Snippets\code_snippets;

if ( ! class_exists( 'WP_Plugin_Install_List_Table' ) ) {
	require_once ABSPATH . 'wp-admin/includes/class-wp-plugin-install-list-table.php';
}

/**
 * Class for handling the cloud search results table.
 *
 * @property string $_pagination Pagination HTML.
 *
 * @package Code_Snippets
 */
class Cloud_Search_List_Table extends WP_Plugin_Install_List_Table {

	/**
	 * Instance of Cloud API class.
	 *
	 * @var Cloud_API
	 */
	protected Cloud_API $cloud_api;

	/**
	 * Items for the cloud list table.
	 *
	 * @var Cloud_Snippets
	 */
	protected Cloud_Snippets $cloud_snippets;

	/**
	 * Class constructor.
	 */
	public function __construct() {
		/**
		 * Declare global variable due to undeclared warning.
		 *
		 * @noinspection PhpUnusedLocalVariableInspection
		 */
		global $tab;

		parent::__construct(
			[
				'singular' => 'cloud-snippet',
				'plural'   => 'cloud-snippets',
				'ajax'     => false,
			]
		);

		// Strip the result query arg from the URL.
		$_SERVER['REQUEST_URI'] = remove_query_arg( [ 'result' ] );

		$this->cloud_api = code_snippets()->cloud_api;
	}

	/**
	 * Prepare items for the table.
	 *
	 * @return void
	 */
	public function prepare_items() {
		$this->cloud_snippets = $this->fetch_snippets();
		$this->items = $this->cloud_snippets->snippets;

		$this->process_actions();

		$this->set_pagination_args(
			[
				'per_page'    => count( $this->cloud_snippets->snippets ),
				'total_items' => $this->cloud_snippets->total_snippets,
				'total_pages' => $this->cloud_snippets->total_pages,
			]
		);
	}

	/**
	 * Process any actions that have been submitted, such as downloading cloud snippets to the local database.
	 *
	 * @return void
	 */
	public function process_actions() {
		$_SERVER['REQUEST_URI'] = remove_query_arg(
			[ 'action', 'snippet', '_wpnonce', 'source', 'cloud-bundle-run', 'cloud-bundle-show', 'bundle_share_name', 'cloud_bundles' ]
		);

		// Check request is coming form the cloud search page.
		if ( isset( $_REQUEST['type'] ) && 'cloud_search' === $_REQUEST['type'] ) {
			if ( isset( $_REQUEST['action'], $_REQUEST['snippet'], $_REQUEST['source'] ) ) {
				cloud_lts_process_download_action(
					sanitize_key( wp_unslash( $_REQUEST['action'] ) ),
					sanitize_key( wp_unslash( $_REQUEST['source'] ) ),
					sanitize_key( wp_unslash( $_REQUEST['snippet'] ) )
				);
			}
		}
	}

	/**
	 * Output table rows.
	 *
	 * @return void
	 */
	public function display_rows() {
		/**
		 * The current table item.
		 *
		 * @var $item Cloud_Snippet
		 */
		foreach ( $this->items as $item ) {
			?>
			<div class="plugin-card cloud-search-card plugin-card-<?php echo sanitize_html_class( $item->id ); ?>">
				<?php
				cloud_lts_display_column_hidden_input( 'code', $item );
				cloud_lts_display_column_hidden_input( 'name', $item );
				?>
				<div class="plugin-card-top">
					<div class="name column-name">
						<h3>
							<?php
							$link = code_snippets()->cloud_api->get_link_for_cloud_snippet( $item );

							if ( $link ) {
								printf( '<a href="%s">', esc_url( code_snippets()->get_snippet_edit_url( $link->local_id ) ) );
							} else {
								printf(
									'<a href="%s" title="%s" class="cloud-snippet-preview thickbox" data-snippet="%s" data-lang="%s">',
									'#TB_inline?&width=700&height=500&inlineId=show-code-preview',
									esc_attr__( 'Preview this snippet', 'code-snippets' ),
									esc_attr( $item->id ),
									esc_attr( Cloud_API::get_type_from_scope( $item->scope ) )
								);
							}

							echo esc_html( $item->name );

							// Grab first tag in array of tags.
							$category = count( $item->tags ) > 0 ? strtolower( esc_attr( $item->tags[0] ) ) : '';

							printf(
								'<img src="%s" class="plugin-icon" alt="%s">',
								esc_url( "https://codesnippets.cloud/images/plugin-icons/$category-logo.png" ),
								esc_attr( $category )
							);

							echo '</a>';
							?>
						</h3>
					</div>
					<div class="action-links">
						<?php echo wp_kses_post( cloud_lts_build_action_links( $item, 'search' ) ); ?>
					</div>
					<div class="desc column-description">
						<p><?php echo wp_kses_post( $this->process_description( $item->description ) ); ?></p>
						<p class="authors">
							<cite>
								<?php
								esc_html_e( 'Codevault: ', 'code-snippets' );

								printf(
									'<a target="_blank" href="%s">%s</a>',
									esc_url( sprintf( 'https://codesnippets.cloud/codevault/%s', $item->codevault ) ),
									esc_html( $item->codevault )
								);

								?>
							</cite>
						</p>
					</div>
				</div>
				<div class="plugin-card-bottom cloud-search-card-bottom">
					<div class="vers column-rating voted-info">
						<svg xmlns="http://www.w3.org/2000/svg"
						     fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="thumbs-up">
							<path stroke-linecap="round" stroke-linejoin="round"
							      d="M6.633 10.5c.806 0 1.533-.446 2.031-1.08a9.041 9.041 0 012.861-2.4c.723-.384 1.35-.956 1.653-1.715a4.498 4.498 0 00.322-1.672V3a.75.75 0 01.75-.75A2.25 2.25 0 0116.5 4.5c0 1.152-.26 2.243-.723 3.218-.266.558.107 1.282.725 1.282h3.126c1.026 0 1.945.694 2.054 1.715.045.422.068.85.068 1.285a11.95 11.95 0 01-2.649 7.521c-.388.482-.987.729-1.605.729H13.48c-.483 0-.964-.078-1.423-.23l-3.114-1.04a4.501 4.501 0 00-1.423-.23H5.904M14.25 9h2.25M5.904 18.75c.083.205.173.405.27.602.197.4-.078.898-.523.898h-.908c-.889 0-1.713-.518-1.972-1.368a12 12 0 01-.521-3.507c0-1.553.295-3.036.831-4.398C3.387 10.203 4.167 9.75 5 9.75h1.053c.472 0 .745.556.5.96a8.958 8.958 0 00-1.302 4.665c0 1.194.232 2.333.654 3.375z"></path>
						</svg>
						<span class="num-ratings" aria-hidden="true">
							<?php
							// translators: 1: number of votes.
							$votes_text = _nx( '%d time', '%d times', $item->vote_count, 'vote count', 'code-snippets' );
							$votes_text = sprintf( $votes_text, number_format_i18n( $item->vote_count ) );

							// translators: 1: number of users.
							$users_text = _n( '%d user', '%d users', $item->total_votes, 'code-snippets' );
							$users_text = sprintf( $users_text, number_format_i18n( $item->total_votes ) );

							// translators: 1: number of votes with label, 2: number of users with label.
							echo esc_html( sprintf( _x( '%1$s by %2$s', 'votes', 'code-snippets' ), $votes_text, $users_text ) );
							?>
						</span>
					</div>
					<div class="column-updated">
						<strong><?php esc_html_e( 'Last Updated:', 'code-snippets' ); ?></strong>
						<?php
						// translators: %s: Human-readable time difference.
						echo esc_html( sprintf( __( '%s ago', 'code-snippets' ), human_time_diff( strtotime( $item->updated ) ) ) );
						?>
					</div>
					<div class="column-downloaded">
						<?php
						$status_name = $this->cloud_api->get_status_name_from_status( $item->status );

						printf(
							'<a class="snippet-type-badge snippet-status" data-type="%s">%s</a>',
							esc_attr( sanitize_title_with_dashes( strtolower( $status_name ) ) ),
							esc_html( $status_name )
						);
						?>
						<div class="tooltip-box">
							<span class="dashicons dashicons-editor-help"></span>
							<div class="tooltip-text">
								<p class="tooltip-text-title">
									<?php esc_html_e( 'Snippet Statuses', 'code-snippets' ); ?>
								</p>

								<p class="tooltip-text-item">
									<a class="snippet-type-badge snippet-status" data-type="public">
										<?php esc_html_e( 'Public', 'code-snippets' ); ?></a>
									<?php esc_html_e( 'Snippet has passed basic review.', 'code-snippets' ); ?>
								</p>

								<p class="tooltip-text-item">
									<a class="snippet-type-badge snippet-status" data-type="ai-verified">
										<?php esc_html_e( 'AI Verified', 'code-snippets' ); ?></a>
									<?php esc_html_e( ' Snippet has been tested by our AI bot.', 'code-snippets' ); ?>
								</p>

								<p class="tooltip-text-item">
									<a class="snippet-type-badge snippet-status" data-type="unverified">
										<?php esc_html_e( 'Unverified', 'code-snippets' ); ?></a>
									<?php esc_html_e( ' Snippet has not undergone any review yet.', 'code-snippets' ); ?>
								</p>

								<p class="tooltip-text-link">
									<a class="tooltip-text-link"
									   href="https://codesnippets.cloud/getstarted"
									   target="_blankx">
										<?php esc_html_e( 'View the full list.', 'code-snippets' ); ?></a>
								</p>
							</div>
						</div>
					</div>
					<div class="column-compatibility">
						<strong><?php esc_html_e( 'WP Compatability:', 'code-snippets' ); ?></strong>
						<?php
						if ( empty( $wp_tested ) ) {
							echo '<span class="compatibility-untested">', esc_html__( 'Not indicated by author', 'code-snippets' ), '</span>';
						} else {
							// translators: tested status.
							$text = sprintf( __( 'Author states %s', 'code-snippets' ), $wp_tested );
							echo '<span class="compatibility-compatible">', esc_html( $text ), '</span>';
						}
						?>
					</div>
				</div>
			</div>
			<?php
		}
	}

	/**
	 * Process the description text - limit to 150 characters.
	 *
	 * @param string|null $description Description as provided by the API.
	 *
	 * @return string formatted description string max 150 chars.
	 */
	protected function process_description( ?string $description ): string {
		$description = wp_strip_all_tags( $description );
		return strlen( $description ) > 150 ? substr( $description, 0, 150 ) . '…' : $description;
	}

	/**
	 * Text displayed when no snippet data is available.
	 *
	 * @return void
	 */
	public function no_items() {
		if ( ! empty( $_REQUEST['cloud_search'] ) && count( $this->cloud_snippets->snippets ) < 1 ) {
			echo '<p class="no-results">',
			esc_html__( 'No snippets or codevault could be found with that search term. Please try again.', 'code-snippets' ),
			'</p>';
		} else {
			echo '<p>', esc_html__( 'Please enter a term to start searching code snippets in the cloud.', 'code-snippets' ), '</p>';
		}
	}

	/**
	 * Fetch the snippets used to populate the table.
	 *
	 * @return Cloud_Snippets
	 */
	public function fetch_snippets(): Cloud_Snippets {
		// Check if search term has been entered.
		if ( isset( $_REQUEST['type'], $_REQUEST['cloud_search'], $_REQUEST['cloud_select'] ) &&
		     'cloud_search' === sanitize_key( wp_unslash( $_REQUEST['type'] ) )
		) {
			// If we have a search query, then send a search request to cloud server API search endpoint.
			$search_query = sanitize_text_field( wp_unslash( $_REQUEST['cloud_search'] ) );
			$search_by = sanitize_text_field( wp_unslash( $_REQUEST['cloud_select'] ) );

			return Cloud_API::fetch_search_results( $search_by, $search_query, $this->get_pagenum() - 1 );
		}

		// If no search results, then return empty object.
		return new Cloud_Snippets();
	}

	/**
	 * Gets the current search result page number.
	 *
	 * @return integer
	 */
	public function get_pagenum(): int {
		$page = isset( $_REQUEST['search_page'] ) ? absint( $_REQUEST['search_page'] ) : 0;

		if ( isset( $this->_pagination_args['total_pages'] ) && $page > $this->_pagination_args['total_pages'] ) {
			$page = $this->_pagination_args['total_pages'];
		}

		return max( 1, $page );
	}

	/**
	 * Display the table.
	 *
	 * @return void
	 */
	public function display() {
		Cloud_API::render_cloud_snippet_thickbox();
		parent::display();
	}

	/**
	 * Displays the pagination.
	 *
	 * @param string $which Context where the pagination will be displayed.
	 *
	 * @return void
	 */
	protected function pagination( $which ) {
		$total_items = $this->_pagination_args['total_items'];
		$total_pages = $this->_pagination_args['total_pages'];
		$pagenum = $this->get_pagenum();

		if ( 'top' === $which && $total_pages > 1 ) {
			$this->screen->render_screen_reader_content( 'heading_pagination' );
		}

		$paginate = cloud_lts_pagination( $which, 'search', $total_items, $total_pages, $pagenum );
		$page_class = $paginate['page_class'];
		$output = $paginate['output'];

		$this->_pagination = "<div class='tablenav-pages$page_class'>$output</div>";

		// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		echo $this->_pagination;

		// echo wp_kses_post( $this->_pagination ); TODO: This removes the top input box for page number.
	}
}
class-cloud-snippet.php000064400000005146151540014520011151 0ustar00<?php

namespace Code_Snippets\Cloud;

use Code_Snippets\Data_Item;
use function Code_Snippets\code_snippets_build_tags_array;

/**
 * A snippet object as retrieved from the cloud API.
 *
 * @since   3.4.0
 * @package Code_Snippets
 *
 * @property int           $id               The remote ID.
 * @property string        $name             The snippet title.
 * @property string        $description      The formatted description.
 * @property string        $code             The executable code.
 * @property array<string> $tags             An array of the tags.
 * @property string        $scope            The scope name.
 * @property string        $codevault        Name of user codevault.
 * @property string        $total_votes      The total number of votes.
 * @property string        $vote_count       The number of actual votes.
 * @property string        $wp_tested        Tested with WP version.
 * @property string        $status           Snippet Status ID.
 * @property string        $created          The date and time when the snippet data was first created, in ISO format.
 * @property string        $updated          When the snippet was last updated, in ISO format.
 * @property integer       $revision         The update revision number.
 * @property bool          $is_owner         If user is owner or author of snippet.
 */
class Cloud_Snippet extends Data_Item {

	/**
	 * Constructor function.
	 *
	 * @param array<string, mixed>|null $initial_data Initial snippet data.
	 */
	public function __construct( ?array $initial_data = null ) {
		parent::__construct(
			[
				'id'             => '',
				'cloud_id'       => '',
				'name'           => '',
				'description'    => '',
				'code'           => '',
				'tags'           => [],
				'scope'          => '',
				'status'         => '',
				'codevault'      => '',
				'total_votes'    => '',
				'vote_count'     => '',
				'wp_tested'      => '',
				'created'        => '',
				'updated'        => '',
				'revision'       => 0,
				'is_owner'       => false,
				'shared_network' => false,
			],
			$initial_data
		);
	}

	/**
	 * Prepare a value before it is stored.
	 *
	 * @param mixed  $value Value to prepare.
	 * @param string $field Field name.
	 *
	 * @return mixed Value in the correct format.
	 */
	protected function prepare_field( $value, string $field ) {
		switch ( $field ) {
			case 'id':
			case 'revision':
				return absint( $value );

			case 'is_owner':
				return (bool) $value;
			case 'description':
				return ( null === $value ) ? '' : $value;
			case 'tags':
				return code_snippets_build_tags_array( $value );

			default:
				return $value;
		}
	}
}
class-cloud-snippets.php000064400000004227151540014520011333 0ustar00<?php

namespace Code_Snippets\Cloud;

use Code_Snippets\Data_Item;

/**
 * A list of snippets as retrieved from the cloud API.
 *
 * @since   3.4.0
 * @package Code_Snippets
 *
 * @property Cloud_Snippet[] $snippets        List of snippet items for the current page.
 * @property integer         $page            Page of data that this data belongs to.
 * @property integer         $total_pages     Total number of available pages of items.
 * @property integer         $total_snippets  Total number of available snippet items.
 * @property array           $cloud_id_rev    An array of all cloud snippet IDs and their revision numbers.
 * @property bool            $success         If the request has any results.
 */
class Cloud_Snippets extends Data_Item {

	/**
	 * Class constructor.
	 *
	 * @param array<string, Cloud_Snippet[]|integer> $initial_data Initial data.
	 */
	public function __construct( $initial_data = null ) {
		parent::__construct(
			[
				'snippets'       => [],
				'total_snippets' => 0,
				'total_pages'    => 0,
				'page'           => 0,
				'cloud_id_rev'   => [],
			],
			$initial_data,
			[
				'items'        => 'snippets',
				'total_items'  => 'total_snippets',
				'page'         => 'page',
				'cloud_id_rev' => 'cloud_id_rev',
			]
		);
	}

	/**
	 * Prepare a value before it is stored.
	 *
	 * @param mixed  $value Value to prepare.
	 * @param string $field Field name.
	 *
	 * @return mixed Value in the correct format.
	 */
	protected function prepare_field( $value, string $field ) {
		switch ( $field ) {
			case 'page':
			case 'total_pages':
			case 'total_snippets':
				return absint( $value );

			default:
				return $value;
		}
	}

	/**
	 * Prepare the `snippets` field by ensuring it is a list of Cloud_Snippets objects.
	 *
	 * @param mixed $snippets The field as provided.
	 *
	 * @return Cloud_Snippets[] The field in the correct format.
	 */
	protected function prepare_snippets( $snippets ): array {
		$result = [];
		$snippets = is_array( $snippets ) ? $snippets : [ $snippets ];

		foreach ( $snippets as $snippet ) {
			$result[] = $snippet instanceof Cloud_Snippet ? $snippet : new Cloud_Snippet( $snippet );
		}

		return $result;
	}
}
list-table-shared-ops.php000064400000020561151540014520011361 0ustar00<?php
/**
 * Functions to perform snippet operations
 *
 * @package Code_Snippets
 */

namespace Code_Snippets\Cloud;

use function Code_Snippets\code_snippets;

/**
 * Display a hidden input field for a certain column and snippet value.
 *
 * @param string        $column_name Column name.
 * @param Cloud_Snippet $snippet     Column item.
 *
 * @return void
 */
function cloud_lts_display_column_hidden_input( string $column_name, Cloud_Snippet $snippet ) {
	printf(
		'<input id="cloud-snippet-%s-%s" class="cloud-snippet-item" type="hidden" name="%s" value="%s" />',
		esc_attr( $column_name ),
		esc_attr( $snippet->id ),
		esc_attr( $column_name ),
		esc_attr( $snippet->$column_name )
	);
}

/**
 * Process the download snippet action
 *
 * @param string $action         Action - 'download' or 'update'.
 * @param string $source         Source - 'search' or 'cloud'.
 * @param string $snippet        Snippet ID.
 * @param int    $codevault_page The current page of the codevault.
 *
 * @return void
 */
function cloud_lts_process_download_action( string $action, string $source, string $snippet, int $codevault_page = 0 ) {
	if ( 'download' === $action || 'update' === $action ) {
		$result = code_snippets()->cloud_api->download_or_update_snippet( $snippet, $source, $action, $codevault_page );

		if ( $result['success'] ) {
			$redirect_uri = $result['snippet_id'] ?
				code_snippets()->get_snippet_edit_url( (int) $result['snippet_id'] ) :
				add_query_arg( 'result', $result['action'] );

			wp_safe_redirect( esc_url_raw( $redirect_uri ) );
			exit;
		}
	}
}

/**
 * Build action links for snippet.
 *
 * @param Cloud_Snippet $cloud_snippet Snippet/Column item.
 * @param string        $source        Source - 'search' or 'codevault'.
 *
 * @return string
 */
function cloud_lts_build_action_links( Cloud_Snippet $cloud_snippet, string $source ): string {
	$lang = Cloud_API::get_type_from_scope( $cloud_snippet->scope );
	$link = code_snippets()->cloud_api->get_link_for_cloud_snippet( $cloud_snippet );
	$additional_classes = 'search' === $source ? 'action-button-link' : '';
	$is_licensed = code_snippets()->licensing->is_licensed();
	$download = true;
	$action_link = '';

	if ( ! $is_licensed && in_array( $lang, [ 'css', 'js' ], true ) ) {
		$download = false;
	}

	if ( $link ) {
		if ( $is_licensed && $link->update_available ) {
			$download = false;
			$update_url = add_query_arg(
				[
					'action'  => 'update',
					'snippet' => $cloud_snippet->id,
					'source'  => $source,
				]
			);
			$action_link = sprintf(
				'<a class="cloud-snippet-update %s" href="%s">%s</a>',
				$additional_classes,
				esc_url( $update_url ),
				esc_html__( 'Update Available', 'code-snippets' )
			);
		} else {
			return sprintf(
				'<a href="%s" class="cloud-snippet-downloaded %s">%s</a>',
				esc_url( code_snippets()->get_snippet_edit_url( $link->local_id ) ),
				$additional_classes,
				esc_html__( 'View', 'code-snippets' )
			);
		}
	}

	if ( $download ) {
		$download_url = add_query_arg(
			[
				'action'  => 'download',
				'snippet' => $cloud_snippet->id,
				'source'  => $source,
			]
		);

		$action_link = $is_licensed ?
			sprintf(
				'<a class="cloud-snippet-download %s" href="%s">%s</a>',
				$additional_classes,
				esc_url( $download_url ),
				esc_html__( 'Download', 'code-snippets' )
			) :
			sprintf(
				'<a class="cloud-snippet-download %s" href="%s" target="_blank"><span class="go-pro-badge">%s</span>%s</a>',
				$additional_classes,
				'https://codesnippets.pro/pricing/',
				esc_html_x( 'Pro', 'pro only', 'code-snippets' ),
				esc_html_x( ' Only', 'pro only', 'code-snippets' )
			);
	}

	$thickbox_url = '#TB_inline?&width=700&height=500&inlineId=show-code-preview';

	$thickbox_link = sprintf(
		'<a href="%s" title="%s" class="cloud-snippet-preview cloud-snippet-preview-style thickbox %s" data-snippet="%s" data-lang="%s">%s</a>',
		esc_url( $thickbox_url ),
		esc_attr( $cloud_snippet->name ),
		$additional_classes,
		esc_attr( $cloud_snippet->id ),
		esc_attr( $lang ),
		esc_html__( 'Preview', 'code-snippets' )
	);

	return $action_link . $thickbox_link;
}

/**
 * Build the pagination functionality
 *
 * @param string $which       Context where the pagination will be displayed.
 * @param string $source      Source - 'search' or 'cloud'.
 * @param int    $total_items Total number of items.
 * @param int    $total_pages Total number of pages.
 * @param int    $pagenum     Current page number.
 *
 * @return array
 */
function cloud_lts_pagination( string $which, string $source, int $total_items, int $total_pages, int $pagenum ): array {
	/* translators: %s: Number of items. */
	$num = sprintf( _n( '%s item', '%s items', $total_items, 'code-snippets' ), number_format_i18n( $total_items ) );
	$output = '<span class="displaying-num">' . $num . '</span>';

	$current = isset( $_REQUEST['cloud_page'] ) ? (int) $_REQUEST['cloud_page'] : $pagenum;
	$current_url = remove_query_arg( wp_removable_query_args() ) . '#' . $source;

	$page_links = array();

	$html_current_page = '';
	$total_pages_before = '<span class="paging-input">';
	$total_pages_after = '</span></span>';

	$disable_first = false;
	$disable_last = false;
	$disable_prev = false;
	$disable_next = false;

	if ( 1 === $current ) {
		$disable_first = true;
		$disable_prev = true;
	}

	if ( $total_pages === $current ) {
		$disable_last = true;
		$disable_next = true;
	}

	if ( $disable_first ) {
		$page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&laquo;</span>';
	} else {
		$page_links[] = sprintf(
			'<a class="first-page button" href="%s"><span class="screen-reader-text">%s</span><span aria-hidden="true">&lsaquo;</span></a>',
			esc_url( remove_query_arg( $source . '_page', $current_url ) ),
			esc_html__( 'First page', 'code-snippets' )
		);
	}

	if ( $disable_prev ) {
		$page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&lsaquo;</span>';
	} else {
		$page_links[] = sprintf(
			'<a class="prev-page button" href="%s"><span class="screen-reader-text">%s</span><span aria-hidden="true">&laquo;</span></a>',
			esc_url( add_query_arg( $source . '_page', max( 1, $current - 1 ), $current_url ) ),
			esc_html__( 'Previous page', 'code-snippets' )
		);
	}

	if ( 'bottom' === $which ) {
		$html_current_page = $current;
		$total_pages_before = sprintf( '<span class="screen-reader-text">%s</span><span id="table-paging" class="paging-input"><span class="tablenav-paging-text">', __( 'Current page', 'code-snippets' ) );
	}

	if ( 'top' === $which ) {
		$html_current_page = sprintf(
			'<label for="current-page-selector" class="screen-reader-text">%s</label><input class="current-page-selector" id="current-page-selector" type="text" name="%s_page" value="%s" size="%d" aria-describedby="table-paging" /><span class="tablenav-paging-text">',
			__( 'Current page', 'code-snippets' ),
			$source,
			$current,
			strlen( $total_pages )
		);
	}

	$html_total_pages = sprintf( '<span class="total-pages">%s</span>', number_format_i18n( $total_pages ) );

	/* translators: 1: Current page, 2: Total pages. */
	$current_html = _x( '%1$s of %2$s', 'paging', 'code-snippets' );
	$page_links[] = $total_pages_before . sprintf( $current_html, $html_current_page, $html_total_pages ) . $total_pages_after;

	if ( $disable_next ) {
		$page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&rsaquo;</span>';
	} else {
		$page_links[] = sprintf(
			'<a class="next-page button" href="%s"><span class="screen-reader-text">%s</span><span aria-hidden="true">%s</span></a>',
			esc_url( add_query_arg( $source . '_page', min( $total_pages, $current + 1 ), $current_url ) ),
			__( 'Next page' ),
			'&rsaquo;'
		);
	}

	if ( $disable_last ) {
		$page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&raquo;</span>';
	} else {
		$page_links[] = sprintf(
			'<a class="last-page button" href="%s"><span class="screen-reader-text">%s</span><span aria-hidden="true">%s</span></a>',
			esc_url( add_query_arg( $source . '_page', $total_pages, $current_url ) ),
			__( 'Last page', 'code-snippets' ),
			'&raquo;'
		);
	}

	$pagination_links_class = 'pagination-links';
	if ( ! empty( $infinite_scroll ) ) {
		$pagination_links_class .= ' hide-if-js';
	}

	$output .= "\n<span class='$pagination_links_class'>" . implode( "\n", $page_links ) . '</span>';

	return [
		'output'     => $output,
		'page_class' => $total_pages ? ( $total_pages < 2 ? ' one-page' : '' ) : ' no-pages',
	];
}