Initial commit: WordPress wp-content (themes, plugins, languages)
- Theme: momentry (custom theme with REST API routes) - Plugins: code-snippets (contains all API proxies) - Languages: zh_TW translations - Excludes: cache, backups, uploads, logs
This commit is contained in:
527
plugins/code-snippets/php/cloud/class-cloud-api.php
Normal file
527
plugins/code-snippets/php/cloud/class-cloud-api.php
Normal file
@@ -0,0 +1,527 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 'Private' status code.
|
||||
*/
|
||||
public const STATUS_PRIVATE = 3;
|
||||
|
||||
/**
|
||||
* 'Public' status code.
|
||||
*/
|
||||
public const STATUS_PUBLIC = 4;
|
||||
|
||||
/**
|
||||
* 'Public' status code.
|
||||
*/
|
||||
public const STATUS_UNVERIFIED = 5;
|
||||
|
||||
/**
|
||||
* 'AI Verified' status code.
|
||||
*/
|
||||
public const STATUS_AI_VERIFIED = 6;
|
||||
|
||||
/**
|
||||
* 'Pro Verified' status code.
|
||||
*/
|
||||
public const STATUS_PRO_VERIFIED = 8;
|
||||
|
||||
/**
|
||||
* Retrieve the Cloud URL from wp-config or fallback to default.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpUndefinedConstantInspection
|
||||
*/
|
||||
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
|
||||
*
|
||||
* @noinspection PhpUndefinedConstantInspection
|
||||
*/
|
||||
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'
|
||||
);
|
||||
|
||||
$raw = self::unpack_request_json( wp_remote_get( $api_url ) );
|
||||
|
||||
$results = new Cloud_Snippets( $raw );
|
||||
$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 '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the label for a given cloud status.
|
||||
*
|
||||
* @param int $status Cloud status code.
|
||||
*
|
||||
* @return string The label for the status.
|
||||
*/
|
||||
public static function get_status_label( int $status ): string {
|
||||
$labels = [
|
||||
self::STATUS_PRIVATE => __( 'Private', 'code-snippets' ),
|
||||
self::STATUS_PUBLIC => __( 'Public', 'code-snippets' ),
|
||||
self::STATUS_UNVERIFIED => __( 'Unverified', 'code-snippets' ),
|
||||
self::STATUS_AI_VERIFIED => __( 'AI Verified', 'code-snippets' ),
|
||||
self::STATUS_PRO_VERIFIED => __( 'Pro Verified', 'code-snippets' ),
|
||||
];
|
||||
|
||||
return $labels[ $status ] ?? __( 'Unknown', 'code-snippets' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the badge class for a given cloud status.
|
||||
*
|
||||
* @param int $status Cloud status code.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_status_badge( int $status ): string {
|
||||
$badge_names = [
|
||||
self::STATUS_PRIVATE => 'private',
|
||||
self::STATUS_PUBLIC => 'public',
|
||||
self::STATUS_UNVERIFIED => 'failure',
|
||||
self::STATUS_AI_VERIFIED => 'success',
|
||||
self::STATUS_PRO_VERIFIED => 'info',
|
||||
];
|
||||
|
||||
return $badge_names[ $status ] ?? 'neutral';
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 );
|
||||
}
|
||||
}
|
||||
61
plugins/code-snippets/php/cloud/class-cloud-link.php
Normal file
61
plugins/code-snippets/php/cloud/class-cloud-link.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,361 @@
|
||||
<?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() {
|
||||
$per_page = $this->get_items_per_page( 'snippets_per_page', 10 );
|
||||
$user_per_page = (int) get_user_option( 'snippets_per_page', get_current_user_id() );
|
||||
if ( $user_per_page > 0 ) {
|
||||
$per_page = $user_per_page;
|
||||
}
|
||||
|
||||
// Fetch snippets, passing a 0-based page index to the Cloud API (WP list tables are 1-based).
|
||||
$page_index = max( 0, $this->get_pagenum() - 1 );
|
||||
$this->cloud_snippets = $this->fetch_snippets( $per_page, $page_index );
|
||||
$this->items = $this->cloud_snippets->snippets;
|
||||
|
||||
$this->process_actions();
|
||||
|
||||
$this->set_pagination_args(
|
||||
[
|
||||
'per_page' => $per_page,
|
||||
'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 from the cloud search page.
|
||||
if ( ! isset( $_REQUEST['type'] ) || 'cloud_search' !== sanitize_key( wp_unslash( $_REQUEST['type'] ) ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! isset( $_REQUEST['action'], $_REQUEST['snippet'], $_REQUEST['source'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$action = sanitize_key( wp_unslash( $_REQUEST['action'] ) );
|
||||
$source = sanitize_key( wp_unslash( $_REQUEST['source'] ) );
|
||||
$snippet_id = absint( wp_unslash( $_REQUEST['snippet'] ) );
|
||||
|
||||
if ( ! in_array( $action, [ 'download', 'update' ], true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! $snippet_id ) {
|
||||
return;
|
||||
}
|
||||
|
||||
check_admin_referer( cloud_lts_get_snippet_action_nonce_action( $action, $snippet_id, $source ) );
|
||||
|
||||
cloud_lts_process_download_action(
|
||||
$action,
|
||||
$source,
|
||||
(string) $snippet_id,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Output table rows.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function display_rows() {
|
||||
$status_descriptions = [
|
||||
Cloud_API::STATUS_PUBLIC =>
|
||||
__( 'Snippet has passed basic review.', 'code-snippets' ),
|
||||
Cloud_API::STATUS_AI_VERIFIED =>
|
||||
__( 'Snippet has been tested by our AI bot.', 'code-snippets' ),
|
||||
Cloud_API::STATUS_UNVERIFIED =>
|
||||
__( 'Snippet has not undergone any review yet.', 'code-snippets' ),
|
||||
];
|
||||
|
||||
/**
|
||||
* The current table item.
|
||||
*
|
||||
* @var $item Cloud_Snippet
|
||||
*/
|
||||
foreach ( $this->items as $item ) {
|
||||
?>
|
||||
<div class="plugin-card cloud-snippet-card plugin-card-<?php echo esc_attr( $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="column-name">
|
||||
<h3>
|
||||
<?php
|
||||
|
||||
// Grab first tag in array of tags.
|
||||
$category = count( $item->tags ) > 0 ? strtolower( esc_attr( $item->tags[0] ) ) : 'general';
|
||||
|
||||
printf(
|
||||
'<img src="%s" class="title-icon" alt="%s">',
|
||||
esc_url( "https://codesnippets.cloud/images/plugin-icons/$category-logo.png" ),
|
||||
esc_attr( $category )
|
||||
);
|
||||
|
||||
$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 );
|
||||
|
||||
|
||||
|
||||
echo '</a>';
|
||||
?>
|
||||
</h3>
|
||||
<ul class="action-buttons">
|
||||
<?php echo cloud_lts_build_action_links( $item, 'search' ); ?>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="column-description">
|
||||
<p><?php echo wp_kses_post( $this->process_description( $item->description ) ); ?></p>
|
||||
<p class="authors">
|
||||
<cite>
|
||||
<?php
|
||||
printf(
|
||||
'%s <a target="_blank" href="%s">%s</a>',
|
||||
esc_html__( 'Codevault:', 'code-snippets' ),
|
||||
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="cloud-meta-row">
|
||||
<div class="column-downloaded">
|
||||
<div class="badge <?php echo esc_attr( $this->cloud_api->get_status_badge( $item->status ) ); ?>-badge tooltip tooltip-block tooltip-end">
|
||||
<?php
|
||||
|
||||
echo esc_html( $this->cloud_api->get_status_label( $item->status ) );
|
||||
|
||||
if ( isset( $status_descriptions[ $item->status ] ) ) {
|
||||
echo '<span class="dashicons dashicons-info-outline"></span>';
|
||||
printf( '<div class="tooltip-content">%s</div>', esc_html( $status_descriptions[ $item->status ] ) );
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column-votes">
|
||||
<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-votes" aria-hidden="true">
|
||||
<?php
|
||||
echo esc_html( number_format_i18n( $item->vote_count ) );
|
||||
?>
|
||||
</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>
|
||||
</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( int $per_page = 10, int $page_index = 0 ): 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'] ) );
|
||||
|
||||
// Pass the provided 0-based page index to the API.
|
||||
return Cloud_API::fetch_search_results( $search_by, $search_query, $page_index );
|
||||
}
|
||||
|
||||
// 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 ) {
|
||||
if ( empty( $this->_pagination_args ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$total_items = $this->_pagination_args['total_items'] ?? 0;
|
||||
$total_pages = $this->_pagination_args['total_pages'] ?? 0;
|
||||
// get_pagenum already returns a 1-based page number used for display.
|
||||
$pagenum_display = $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_display );
|
||||
$page_class = $paginate['page_class'];
|
||||
$output = $paginate['output'];
|
||||
|
||||
$this->_pagination = "<div class='tablenav-pages$page_class'>$output</div>";
|
||||
|
||||
echo wp_kses_post( $this->_pagination );
|
||||
}
|
||||
}
|
||||
87
plugins/code-snippets/php/cloud/class-cloud-snippet.php
Normal file
87
plugins/code-snippets/php/cloud/class-cloud-snippet.php
Normal file
@@ -0,0 +1,87 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
}
|
||||
107
plugins/code-snippets/php/cloud/class-cloud-snippets.php
Normal file
107
plugins/code-snippets/php/cloud/class-cloud-snippets.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?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 ) {
|
||||
$initial_data = $this->normalize_cloud_api( $initial_data );
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize payloads returned by the cloud API into the shape expected by this class.
|
||||
*
|
||||
* @param mixed $initial_data Raw data passed into the constructor.
|
||||
*
|
||||
* @return mixed Normalized data array or original value when no normalization is required.
|
||||
*/
|
||||
private function normalize_cloud_api( $initial_data ) {
|
||||
// pagination metadata is nested under a 'meta' key.
|
||||
if ( is_array( $initial_data ) && isset( $initial_data['meta'] ) ) {
|
||||
$meta = $initial_data['meta'];
|
||||
$normalized = [];
|
||||
$normalized['snippets'] = $initial_data['snippets'] ?? $initial_data['data'] ?? [];
|
||||
$normalized['total_snippets'] = isset( $meta['total'] ) ? (int) $meta['total'] : 0;
|
||||
$normalized['total_pages'] = isset( $meta['total_pages'] ) ? (int) $meta['total_pages'] : 0;
|
||||
$normalized['page'] = isset( $meta['page'] ) ? max( 0, (int) $meta['page'] - 1 ) : 0;
|
||||
$normalized['cloud_id_rev'] = $initial_data['cloud_id_rev'] ?? [];
|
||||
$initial_data = $normalized;
|
||||
}
|
||||
|
||||
return $initial_data;
|
||||
}
|
||||
}
|
||||
287
plugins/code-snippets/php/cloud/list-table-shared-ops.php
Normal file
287
plugins/code-snippets/php/cloud/list-table-shared-ops.php
Normal file
@@ -0,0 +1,287 @@
|
||||
<?php
|
||||
/**
|
||||
* Functions to perform snippet operations
|
||||
*
|
||||
* @package Code_Snippets
|
||||
*/
|
||||
|
||||
namespace Code_Snippets\Cloud;
|
||||
|
||||
use function Code_Snippets\code_snippets;
|
||||
|
||||
/**
|
||||
* Build the nonce action string for cloud snippet state-changing operations.
|
||||
*
|
||||
* @param string $action Action - 'download' or 'update'.
|
||||
* @param int $snippet_id Cloud snippet ID.
|
||||
* @param string $source Source - 'search' or 'cloud'.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function cloud_lts_get_snippet_action_nonce_action( string $action, int $snippet_id, string $source ): string {
|
||||
return sprintf( 'cloud-snippet-action|%s|%s|%d', $action, $source, $snippet_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 string HTML
|
||||
*/
|
||||
function cloud_lts_build_column_hidden_input( string $column_name, Cloud_Snippet $snippet ): string {
|
||||
return sprintf(
|
||||
'<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.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function cloud_lts_process_download_action( string $action, string $source, string $snippet ) {
|
||||
if ( 'download' === $action || 'update' === $action ) {
|
||||
$result = code_snippets()->cloud_api->download_or_update_snippet( $snippet, $source, $action );
|
||||
|
||||
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 Action link HTML.
|
||||
*/
|
||||
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 );
|
||||
$is_licensed = code_snippets()->licensing->is_licensed();
|
||||
$download = $is_licensed || ! in_array( $lang, [ 'css', 'js' ], true );
|
||||
$snippet_id = (int) $cloud_snippet->id;
|
||||
|
||||
if ( $link ) {
|
||||
if ( $is_licensed && $link->update_available ) {
|
||||
$update_url = wp_nonce_url(
|
||||
add_query_arg(
|
||||
[
|
||||
'action' => 'update',
|
||||
'snippet' => $snippet_id,
|
||||
'source' => $source,
|
||||
]
|
||||
),
|
||||
cloud_lts_get_snippet_action_nonce_action( 'update', $snippet_id, $source )
|
||||
);
|
||||
return sprintf(
|
||||
'<li><a class="button button-primary" href="%s">%s</a></li>',
|
||||
esc_url( $update_url ),
|
||||
esc_html__( 'Update Available', 'code-snippets' )
|
||||
);
|
||||
} else {
|
||||
return sprintf(
|
||||
'<li><a class="button" href="%s">%s</a></li>',
|
||||
esc_url( code_snippets()->get_snippet_edit_url( $link->local_id ) ),
|
||||
esc_html__( 'View', 'code-snippets' )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ( $download ) {
|
||||
$download_query = [
|
||||
'action' => 'download',
|
||||
'snippet' => $snippet_id,
|
||||
'source' => $source,
|
||||
];
|
||||
|
||||
// Preserve current cloud page if present so downstream handlers receive pagination context.
|
||||
if ( isset( $_REQUEST['cloud_page'] ) ) {
|
||||
$download_query['cloud_page'] = (int) wp_unslash( $_REQUEST['cloud_page'] );
|
||||
}
|
||||
|
||||
$download_url = wp_nonce_url(
|
||||
add_query_arg( $download_query ),
|
||||
cloud_lts_get_snippet_action_nonce_action( 'download', $snippet_id, $source )
|
||||
);
|
||||
|
||||
$download_button = sprintf(
|
||||
'<li><a class="button button-primary" href="%s">%s</a></li>',
|
||||
esc_url( $download_url ),
|
||||
esc_html__( 'Download', 'code-snippets' )
|
||||
);
|
||||
} else {
|
||||
$download_button = sprintf(
|
||||
'<li><span class="%s">%s <span class="tooltip-content">%s</span></span></li>',
|
||||
'button button-primary button-disabled tooltip tooltip-block tooltip-end',
|
||||
esc_html__( 'Download', 'code-snippets' ),
|
||||
esc_html__( 'This snippet type is only available in Code Snippets Pro', 'code-snippets' )
|
||||
);
|
||||
}
|
||||
|
||||
$preview_button = sprintf(
|
||||
'<li><a href="%s" aria-label="%s" class="%s" data-snippet="%s" data-lang="%s">%s</a></li>',
|
||||
'#TB_inline?&width=700&height=500&inlineId=show-code-preview',
|
||||
esc_attr( $cloud_snippet->name ),
|
||||
'cloud-snippet-preview thickbox button',
|
||||
esc_attr( $cloud_snippet->id ),
|
||||
esc_attr( $lang ),
|
||||
esc_html__( 'Preview', 'code-snippets' )
|
||||
);
|
||||
|
||||
return $download_button . $preview_button;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>';
|
||||
|
||||
$param_key = $source . '_page';
|
||||
$current = isset( $_REQUEST[ $param_key ] ) ? (int) $_REQUEST[ $param_key ] : $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">«</span>';
|
||||
} else {
|
||||
$page_links[] = sprintf(
|
||||
'<a class="first-page button" href="%s"><span class="screen-reader-text">%s</span><span aria-hidden="true">«</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">‹</span>';
|
||||
} else {
|
||||
$page_links[] = sprintf(
|
||||
'<a class="prev-page button" href="%s"><span class="screen-reader-text">%s</span><span aria-hidden="true">‹</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">›</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 ) ),
|
||||
esc_html__( 'Next page', 'code-snippets' ),
|
||||
'›'
|
||||
);
|
||||
}
|
||||
|
||||
if ( $disable_last ) {
|
||||
$page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">»</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 ) ),
|
||||
esc_html__( 'Last page', 'code-snippets' ),
|
||||
'»'
|
||||
);
|
||||
}
|
||||
|
||||
$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>';
|
||||
|
||||
$page_class = $total_pages ? '' : ' no-pages';
|
||||
|
||||
return [
|
||||
'output' => $output,
|
||||
'page_class' => $page_class,
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user