first commit

This commit is contained in:
User A0264400
2026-04-01 23:20:16 +03:00
commit a766acdc90
23071 changed files with 4933189 additions and 0 deletions

View File

@@ -0,0 +1 @@
<?php return array('dependencies' => array('react', 'react-dom', 'wp-components', 'wp-element', 'wp-hooks', 'wp-i18n'), 'version' => '8dfacdbbef66c52ec305');

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,112 @@
2.0.6 - 16th June 2025
- Improvement: Offloaded custom fonts to Google Fonts to reduce bundle size and improve performance.
- Improvement: Cleaned up unnecessary bloat code to optimize performance and maintainability.
- Fix: Resolved an duplicate content rendering issue.
2.0.5 - 30th April 2025
- Improvement: Added a caching mechanism for credit response to optimize API calls.
2.0.4 - 14th April 2025
- Improvement: Enhanced credit exceeded error in the chat interface.
- Improvement: Now showing credits usage directly on chat window instead of open popup.
2.0.3 - 9th April 2025
- Improvement: Clearer error messages when AI credits are exhausted or user is invalid.
- Fix: Resolved an toolbar icons size issue for Ask AI in WordPress 6.8.
2.0.2 - 31st January 2025
- Reverted: Caching mechanism for credit response to optimize API calls due to unforeseen issues.
2.0.1 - 31st January 2025
- Improvement: Added a caching mechanism for credit response to optimize API calls.
- Fix: Resolved an issue wherein the AI Assistant side bar would not be clickable for some teams.
2.0.0 - 9th December 2024
- New: The AI Assistant is now Location Aware.
- Improvement: Revamped the entire UI of the Assistant Sidebar.
1.2.4 - 1st November 2024
- Fix: Added a function check for 'get_current_screen'.
1.2.3 - 23rd October 2024
- Improvement: Added support for the 'source' parameter in the middleware URL.
1.2.2 - 8th October 2024
- Excluded assets from loading via Spectra for SureForms CPTs.
1.2.1 - 27th September 2024
- Improvement: Hid the chat textbox interface when the user is not authorized.
- Improvement: Removed the GPT model parameter for real-time model updates from the middleware.
1.2.0 - 26th July 2024
- Improvement: Switched from the GPT 3.5 Turbo modal to the 4o Mini modal.
1.1.10 - 15th July 2024
- Fix: Resolved an issue with the AI Assistant admin-bar trigger loading order in WordPress 6.6.
v1.1.9 - 2nd July 2024
- Improvement: The library's text-domain support has been removed & translation is now managed from the project level.
v1.1.8 - 27th June 2024
- Improved security.
v1.1.7 - 24th June 2024
- Feature: Added tooltips for icons in the Chat sidebar for improved user experience.
- Improvement: Added disconnection status to the settings option to avoid deletion after disconnecting.
- Fix: Resolved the empty text glitch that would occur when editing the first message in the chat.
- Fix: Resolved PHP warnings in the widget editor for PluginSidebar and PluginSidebarMoreMenuItem.
v1.1.6 - 15th May 2024
- Improvement: Added dropdown menu for common Zip AI Assistant actions in the toolbar.
- Improvement: Upgraded the `get_credit_server_response` helper function to support data.
v1.1.5 - 25th April 2024
- Fix: Copy and Regenerate buttons are now visible by default instead of being displayed on hover.
v1.1.4 - 23rd April 2024
- Replaced "AI Assistant" text with a Sparkles icon for clearer visual representation.
v1.1.3 - 14th March 2024
- Removed the highlight color for Richtexts.
- Replaced ZipWP logo with the AI Sparkle icon for all AI-Assistant-related buttons.
- Improved the AI Assistant sidebar UI on the front end.
v1.1.2 - 27th February 2024
- Improvement: Added ZipWP API server helper functions to get the current plan.
v1.1.1 - 16th February 2024
- Improvement: Added middleware parameter handling for the affiliate ID.
- Improvement: Locally loaded Google Fonts for the AI Assistant sidebar.
- Fix: Restricted front-end assets for non-admin users.
v1.1.0 - 17th January 2024
- Feature: Made the AI Assistant sidebar accessible outside the editor.
- Improvement: Added the 'Force Enabled' function to ensure that modules are enabled.
v1.0.9 - 11th January 2024
- Improvement: Added middleware parameter handling for the plugin slug.
v1.0.8 - 18th December 2023
- Fix: Added compatibility with PHP version 7.0 and above.
v1.0.7 - 2nd December 2023
- Improvement: Updated the middleware request and the Zip AI settings that are saved.
- Fix: Implemented proper multi-setting updation.
v1.0.6 - 30th November 2023
- Improvement: Added a filter to disable the entire library from loading.
v1.0.5 - 30th November 2023
- Improvement: Added a new module structure and removed the admin page from Tools.
v1.0.4 - 30th November 2023
- Updating `distignore` for composer update compatibility.
v1.0.3 - 11th November 2023
- Improvement: Added an option to disable Zip Ai Assistant from the sidebar.
v1.0.2 - 9th November 2023
- Improvement: Better one-click command.
v1.0.1 - 9th November 2023
- Initial Commit

View File

@@ -0,0 +1,518 @@
<?php
/**
* Zip AI - Admin Configurations.
*
* @package zip-ai
*/
namespace ZipAI\Classes;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// Classes to be used, in alphabetical order.
use ZipAI\Classes\Helper;
use ZipAI\Classes\Module;
use ZipAI\Classes\Utils;
/**
* The Admin_Configurations Class.
*/
class Admin_Configurations {
/**
* The menu slug.
*
* @since 1.0.0
* @var string Menu slug.
*/
private $menu_slug = ZIP_AI_MENU_SLUG;
/**
* Instance of this class.
*
* @since 1.0.0
* @var object Class object.
*/
private static $instance;
/**
* Custom Capability
*
* @since 1.1.8
* @access public
* @var string capabilities.
*/
public static $custom_capability = 'manage_zip_ai_assistant';
/**
* Initiator of this class.
*
* @since 1.0.0
* @return object initialized object of this class.
*/
public static function get_instance() {
if ( ! isset( self::$instance ) ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor of this class.
*
* @since 1.0.0
* @return void
*/
public function __construct() {
// Setup the Admin Scripts.
// add_action( 'admin_init', array( $this, 'settings_admin_scripts' ) ); - This can be added later if required.
// Verify Zip AI Authorization.
add_action( 'admin_init', array( $this, 'verify_authorization' ) );
/**
* This action hook is used to add custom capabilities to the administrator role.
* The custom capability is 'manage_zip_ai_assistant'.
* This action hook is triggered when the admin initiates, meaning when the admin
* goes to the admin dashboard, this action hook is triggered.
* The priority of this action hook is set to 10, meaning it will be executed before
* other action hooks with lower priorities.
* The callback function for this action hook is the 'add_custom_capabilities' method
* of the same class.
*/
add_action( 'admin_init', array( $this, 'add_custom_capabilities' ), 10 );
// Setup the Admin Menu Page.
// add_action( 'admin_menu', array( $this, 'setup_menu_page' ) ); - This can be added later if required.
// Setup the Admin Ajax Actions.
add_action( 'wp_ajax_zip_ai_toggle_assistant_status_ajax', array( $this, 'toggle_assistant_status_ajax' ) );
add_action( 'wp_ajax_zip_ai_disabler_ajax', array( $this, 'disabler_ajax' ) );
}
/**
* Add custom capabilities.
*
* @since 1.1.8
* @return void
*/
public function add_custom_capabilities() {
// Remove the custom capability from all roles except the roles specified in the filter.
$this->remove_custom_capability_from_other_roles();
/**
* Get the additional roles to add the custom capability.
*
* The additional roles are filtered by the `zip_ai_assistant_capability_additional_roles` filter.
* If this filter is not used, then only the 'administrator' role will get the custom capability.
* The default additional roles are 'administrator' and 'editor'.
*
* @since 1.1.8
*
* @return array The additional roles to add the custom capability.
*/
$roles = apply_filters(
'zip_ai_assistant_capability_additional_roles',
array( 'administrator', 'editor' )
);
// Loop through each role and add the custom capability to the role object.
foreach ( $roles as $role_slug ) {
// Get the role object by the role slug.
$role_object = get_role( $role_slug );
// Check if the role object exists.
if ( $role_object ) {
// Add the custom capability to the role object.
$role_object->add_cap( self::$custom_capability );
}
}
}
/**
* Remove custom capabilities.
*
* @since 1.1.8
* @return void
*/
public function remove_custom_capability_from_other_roles() {
// Set the default role to retain the custom capability.
$default_role = 'administrator';
// Get the default role object.
$default_role_object = get_role( $default_role );
// Remove the custom capability from all roles except the default role.
// Here, we are iterating through all the roles and removing the custom capability
// from each role except the default role.
if ( $default_role_object ) {
// Get all the role names.
$roles = wp_roles()->role_names;
// Exclude the default role from the list of roles.
unset( $roles[ $default_role ] );
// Loop through each role.
foreach ( $roles as $role_slug => $role_name ) {
// Get the role object by the role slug.
$role_object = get_role( $role_slug );
// Check if the role object exists and if the role object has the custom capability.
if ( $role_object && $role_object->has_cap( self::$custom_capability ) ) {
// Remove the custom capability from the role object.
$role_object->remove_cap( self::$custom_capability );
}
}
}
}
/**
* Add the Zip AI menu page.
*
* @since 1.0.0
* @return void
*/
public function setup_menu_page() {
// If the current user does not have the required capability, then abandon ship.
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
$capability = 'manage_options';
// Add the Zip AI Submenu.
add_submenu_page(
apply_filters( 'zip_ai_parent_page', 'none' ), // The parent page of this menu.
apply_filters( 'zip_ai_page_title', 'Zip - AI Assistant' ), // The page title.
apply_filters( 'zip_ai_menu_title', 'Zip - AI Assistant' ), // The menu title.
apply_filters( 'zip_ai_menu_capability', $capability ), // The capability required for access to this page.
$this->menu_slug, // The menu slug.
array( $this, 'render_dashboard' ), // The rendered output function.
apply_filters( 'zip_ai_menu_position', 1 ) // The position of this menu item in the menu.
);
}
/**
* Verify if the user was given authorization to use Zip AI.
*
* @since 1.0.0
* @return void
*/
public function verify_authorization() {
// If the current user does not have the required capability or the referrer is empty, then abandon ship.
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
// Get the nonce.
$nonce = ( isset( $_GET['nonce'] ) ) ? sanitize_key( $_GET['nonce'] ) : '';
// If the nonce is not valid, or if there's no token, then abandon ship.
if ( false === wp_verify_nonce( $nonce, 'zip_ai_auth_nonce' ) ) {
return;
}
// Redirect to the settings page if the user is trying to revoke the token.
if ( isset( $_GET['revoke_zip_ai_authorization_token'] ) && 'definitely' === sanitize_text_field( $_GET['revoke_zip_ai_authorization_token'] ) ) {
// Clear out the Zip AI settings and disconnect the user.
Helper::update_admin_settings_option( 'zip_ai_settings', [ 'status' => 'disconnected' ] );
// Redirect to the settings page.
$redirection_url = apply_filters( 'zip_ai_revoke_redirection_url', admin_url() );
wp_safe_redirect( $redirection_url );
exit;
}//end if
// If none of the required data is received, abandon ship.
if ( ! isset( $_GET['credit_token'] ) && ! isset( $_GET['token'] ) && ! isset( $_GET['email'] ) ) {
return;
}
// Get the existing options, and update the auth token before updating the option.
$db_settings_options = Helper::get_setting();
// At this point, the user is connected with Zip AI.
$db_settings_options['status'] = 'connected';
// Update the auth token if needed.
if ( isset( $_GET['credit_token'] ) && is_string( $_GET['credit_token'] ) ) {
$db_settings_options['auth_token'] = Utils::encrypt( sanitize_text_field( $_GET['credit_token'] ) );
}
// Update the Zip token if needed.
if ( isset( $_GET['token'] ) && is_string( $_GET['token'] ) ) {
$db_settings_options['zip_token'] = Utils::encrypt( sanitize_text_field( $_GET['token'] ) );
}
// Update the email if needed.
if ( isset( $_GET['email'] ) && is_string( $_GET['email'] ) ) {
$db_settings_options['email'] = sanitize_email( $_GET['email'] );
}
// Update the Zip AI settings.
Helper::update_admin_settings_option( 'zip_ai_settings', $db_settings_options );
// Redirect to the settings page.
if ( apply_filters( 'zip_ai_auth_redirection_flag', true ) ) {
$redirection_url = apply_filters( 'zip_ai_auth_redirection_url', admin_url( 'admin.php?page=zip-ai' ) );
wp_safe_redirect( $redirection_url );
exit;
}
}
/**
* Setup the Ajax Event to Entirely Disable the Zip AI Library from loading.
*
* @since 1.0.5
* @return void
*/
public function disabler_ajax() {
// If the current user does not have the required capability, then abandon ship.
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error();
}
// Verify the nonce.
check_ajax_referer( 'zip_ai_admin_nonce', 'nonce' );
// Have a variable to check if the module was disabled.
$is_module_disabled = false;
// Check if the Zip AI Assistant was requested to be disabled.
if ( ! empty( $_POST['disable_zip_ai_assistant'] ) ) {
$is_module_disabled = Module::disable( 'ai_assistant' );
}
// Send the status based on whether the Zip AI Library is enabled or not.
if ( $is_module_disabled ) {
wp_send_json_success();
} else {
wp_send_json_error();
}
}
/**
* Setup the AI Assistant Toggle Ajax.
*
* @since 1.0.0
* @return void
*/
public function toggle_assistant_status_ajax() {
// If the current user does not have the required capability, then abandon ship.
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error();
}
// Verify the nonce.
check_ajax_referer( 'zip_ai_admin_nonce', 'nonce' );
// If the Zip options is not an array, then send an error.
if ( empty( $_POST['enable_zip_chat'] ) || ! is_string( $_POST['enable_zip_chat'] ) ) {
wp_send_json_error();
}
// Create a variable to check if the assistant module was toggled.
$is_module_toggled = false;
// Update the enabled status.
if ( 'enabled' === sanitize_text_field( $_POST['enable_zip_chat'] ) ) {
$is_module_toggled = Module::enable( 'ai_assistant' );
} else {
$is_module_toggled = Module::disable( 'ai_assistant' );
}
// Send the status based on whether the module was toggled.
if ( $is_module_toggled ) {
wp_send_json_success();
} else {
wp_send_json_error();
}
}
/**
* Render the Zip AI Admin Settings Page.
*
* @since 1.0.0
* @return void
*/
public function render_dashboard() {
// Render the dashboard app div.
?>
<div id="zip-ai__dashboard-app--wrapper">
<div id="zip-ai-dashboard-app" class="zip-ai-dashboard-app"></div>
</div>
<?php
}
/**
* Load the Admin Settings and Scripts on initialization.
*
* @since 1.0.0
* @return void
*/
public function settings_admin_scripts() {
// If the current page is not the Zip AI Settings page, then abandon ship.
if ( empty( $_GET['page'] ) || ( $this->menu_slug !== $_GET['page'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended
return;
}
// Enqueue the Admin Styles and Scripts for the React App.
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_styles_and_scripts' ) );
// Add the footer link if needed.
if ( Helper::is_authorized() ) {
// Add the footer link.
add_filter( 'admin_footer_text', array( $this, 'add_footer_link' ), 99 );
}
}
/**
* Enqueues the needed CSS/JS for Zip AI's admin settings page.
*
* @since 1.0.0
* @return void
*/
public function enqueue_styles_and_scripts() {
if ( class_exists( '\UAGB_Admin_Helper' ) && method_exists( '\UAGB_Admin_Helper', 'should_exclude_assets_for_cpt' ) ) {
if ( \UAGB_Admin_Helper::should_exclude_assets_for_cpt() ) {
return; // Early return to prevent loading assets.
}
}
// Enqueue the admin Google Fonts and WP Components.
$admin_slug = 'zip-ai-admin';
wp_enqueue_style(
$admin_slug . '-font',
'https://fonts.googleapis.com/css2?family=Inter:wght@400;500&display=swap',
array(),
ZIP_AI_VERSION
);
wp_enqueue_style( 'wp-components' );
// Set the default credit details.
$credit_details = array(
'used' => 0,
'total' => 0,
'threshold' => array(
'medium' => ZIP_AI_CREDIT_THRESHOLD_MEDIUM,
'high' => ZIP_AI_CREDIT_THRESHOLD_HIGH,
),
'percentage' => 0,
);
// Get the response from the endpoint.
$response = Helper::get_credit_server_response( 'usage' );
// If the response is not an error, then update the credit details.
if (
empty( $response['error'] )
&& ! empty( $response['total_credits'] )
) {
$credit_details['used'] = ! empty( $response['total_used_credits'] ) ? $response['total_used_credits'] : 0;
$credit_details['total'] = $response['total_credits'];
$credit_details['percentage'] = intval( ( $credit_details['used'] / $credit_details['total'] ) * 100 );
}
// Add the data to localize.
$localize = apply_filters(
'zip_ai_admin_localize',
array(
'admin_url' => admin_url(),
'ajax_url' => admin_url( 'admin-ajax.php' ),
'auth_middleware' => Helper::get_auth_middleware_url(),
'auth_revoke_url' => Helper::get_auth_revoke_url(),
'credit_topup_url' => ZIP_AI_CREDIT_TOPUP_URL,
'is_authorized' => Helper::is_authorized(),
'is_ai_assistant_enabled' => Module::is_enabled( 'ai_assistant' ),
'admin_nonce' => wp_create_nonce( 'zip_ai_admin_nonce' ),
'page_slug' => $this->menu_slug,
'credit_details' => Helper::get_credit_details(),
)
);
// Enqueue the admin scripts.
$this->localize_and_enqueue_admin_scripts( $localize );
}
/**
* Localize and Enqueue the Admin Scripts.
*
* @param array $localize The data to localize.
* @since 1.0.0
* @return void
*/
public function localize_and_enqueue_admin_scripts( $localize ) {
// Set the required variables.
$handle = 'zip-ai-admin-settings';
$build_path = ZIP_AI_DIR . 'admin/dashboard-app/build/';
$build_url = ZIP_AI_URL . 'admin/dashboard-app/build/';
$script_asset_path = $build_path . 'dashboard-app.asset.php';
$script_info = file_exists( $script_asset_path )
? include $script_asset_path
: array(
'dependencies' => array(),
'version' => ZIP_AI_VERSION,
);
$script_dep = array_merge( $script_info['dependencies'], array( 'updates' ) );
// Register the admin scripts.
wp_register_script(
$handle,
$build_url . 'dashboard-app.js',
$script_dep,
$script_info['version'],
true
);
// Register the admin styles.
wp_register_style(
$handle,
$build_url . 'dashboard-app.css',
array(),
ZIP_AI_VERSION
);
// Register the admin Google Fonts.
wp_register_style(
'zip-ai-admin-google-fonts',
'https://fonts.googleapis.com/css2?family=Inter:wght@200&display=swap',
array(),
ZIP_AI_VERSION
);
// Enqueue the admin scripts.
wp_enqueue_script( $handle );
// Set the script translations.
wp_set_script_translations( $handle, apply_filters( 'zip_ai_library_textdomain', 'zip-ai' ) );
// Enqueue the Google Fonts.
wp_enqueue_style( 'zip-ai-admin-google-fonts' );
// Enqueue the admin styles.
wp_enqueue_style( $handle );
// Set the RTL styles.
wp_style_add_data( $handle, 'rtl', 'replace' );
// Localize the script.
wp_localize_script( $handle, 'zip_ai_react', $localize );
}
/**
* Add the footer link.
*
* @since 1.0.0
* @return string The footer link.
*/
public function add_footer_link() {
return '<span id="footer-thankyou">' . sprintf(
/* translators: %1$s: HTML link start tag, %2$s: HTML link end tag. */
__( 'Thank you for using %1$sZip AI.%2$s', 'ultimate-addons-for-gutenberg' ),
'<a href="https://wpspectra.com/zip-ai/" class="focus:text-spec-hover active:text-spec-hover hover:text-spec-hover" target="_blank" rel="noopener noreferrer">',
'</a>'
) . '</span>';
}
}

View File

@@ -0,0 +1,608 @@
<?php
/**
* Zip AI - Helper.
*
* This file contains the helper functions of Zip AI.
* Helpers are functions that are used throughout the library.
*
* @package zip-ai
*/
namespace ZipAI\Classes;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// Classes to be used, in alphabetical order.
use ZipAI\Classes\Utils;
/**
* The Helper Class.
*/
class Helper {
/**
* Get an option from the database.
*
* @param string $key The option key.
* @param mixed $default The option default value if option is not available.
* @param boolean $network_override Whether to allow the network admin setting to be overridden on subsites.
* @since 1.0.0
* @return mixed The option value.
*/
public static function get_admin_settings_option( $key, $default = false, $network_override = false ) {
// Get the site-wide option if we're in the network admin.
return $network_override && is_multisite() ? get_site_option( $key, $default ) : get_option( $key, $default );
}
/**
* Update an option from the database.
*
* @param string $key The option key.
* @param mixed $value The value to update.
* @param bool $network_override Whether to allow the network_override admin setting to be overridden on subsites.
* @since 1.0.0
* @return bool True if the option was updated, false otherwise.
*/
public static function update_admin_settings_option( $key, $value, $network_override = false ) {
// Update the site-wide option if we're in the network admin, and return the updated status.
return $network_override && is_multisite() ? update_site_option( $key, $value ) : update_option( $key, $value );
}
/**
* Delete an option from the database for.
*
* @param string $key The option key.
* @param boolean $network_override Whether to allow the network admin setting to be overridden on subsites.
* @since 1.0.0
* @return void
*/
public static function delete_admin_settings_option( $key, $network_override = false ) {
// Delete the site-wide option if we're in the network admin.
if ( $network_override && is_multisite() ) {
delete_site_option( $key );
} else {
delete_option( $key );
}
}
/**
* Check if Zip AI is authorized.
*
* @since 1.0.0
* @return boolean True if Zip AI is authorized, false otherwise.
*/
public static function is_authorized() {
// Get the Zip AI settings.
$existing_settings = self::get_admin_settings_option( 'zip_ai_settings' );
// If the Zip AI settings are empty, return false.
if ( empty( $existing_settings ) || ! is_array( $existing_settings ) ) {
return false;
}
// Return true if the auth token is set and is a string.
return (
! empty( $existing_settings['auth_token'] )
&& is_string( $existing_settings['auth_token'] )
&& ! empty( trim( $existing_settings['auth_token'] ) )
);
}
/**
* Get the Zip AI Settings.
*
* If used with a key, it will return that specific setting.
* If used without a key, it will return the entire settings array.
*
* @param string $key The setting key.
* @param mixed $default The default value to return if the setting is not found.
* @since 1.0.0
* @return mixed|array The setting value, or the default.
*/
public static function get_setting( $key = '', $default = array() ) {
// Get the Zip AI settings.
$existing_settings = self::get_admin_settings_option( 'zip_ai_settings' );
// If the Zip AI settings are empty, return the fallback.
if ( empty( $existing_settings ) || ! is_array( $existing_settings ) ) {
return $default;
}
// If the key is empty, return the entire settings array - otherwise return the specific setting or the fallback.
if ( empty( $key ) ) {
return $existing_settings;
} else {
return isset( $existing_settings[ $key ] ) ? $existing_settings[ $key ] : $default;
}
}
/**
* Get the Zip AI Response from the Zip Credit Server.
*
* @param string $endpoint The endpoint to get the response from.
* @param array $body The data to be passed as the request body, if any.
* @param array $extra_args Extra arguments to be passed to the request, if any.
* @since 1.0.0
* @return array The Zip AI Response.
*/
public static function get_credit_server_response( $endpoint, $body = [], $extra_args = [] ) {
// If the endpoint is not a string, then abandon ship.
if ( ! is_string( $endpoint ) ) {
return array(
'error' => __( 'The Zip AI Endpoint was not declared', 'ultimate-addons-for-gutenberg' ),
);
}
// Get the Auth Token from the Zip AI Settings.
$auth_token = self::get_decrypted_auth_token();
// If the Zip Auth Token is not set, then abandon ship.
if ( empty( $auth_token ) || ! is_string( $auth_token ) ) {
return array(
'error' => __( 'The Zip AI Auth Token is not set.', 'ultimate-addons-for-gutenberg' ),
);
}
// Set the API URL.
$api_url = ZIP_AI_CREDIT_SERVER_API . $endpoint;
$api_args = array(
'headers' => array(
'Authorization' => 'Bearer ' . $auth_token,
),
'timeout' => 30, // phpcs:ignore WordPressVIPMinimum.Performance.RemoteRequestTimeout.timeout_timeout -- 30 seconds is required sometime for open ai responses
);
// If the data array was passed, add it to the args.
if ( ! empty( $body ) && is_array( $body ) ) {
$api_args['body'] = $body;
}
// If there are any extra arguments, then we can overwrite the required arguments.
if ( ! empty( $extra_args ) && is_array( $extra_args ) ) {
$api_args = array_merge(
$api_args,
$extra_args
);
}
// Get the response from the endpoint.
$response = wp_remote_post(
$api_url,
$api_args
);
// If the response was an error.
if ( is_wp_error( $response ) || empty( $response ) ) {
$error_message = __( 'Empty response from API.', 'ultimate-addons-for-gutenberg' );
$error_code = __( 'empty_response', 'ultimate-addons-for-gutenberg' );
if ( is_wp_error( $response ) ) {
$error_message = $response->get_error_message();
$error_code = $response->get_error_code();
}
return array(
'error' => $error_message,
'code' => $error_code,
);
}
// Get the response body.
$response_body = wp_remote_retrieve_body( $response );
$response_body = json_decode( $response_body, true );
$status_code = wp_remote_retrieve_response_code( $response );
// Check if the status code is 403 or 401 error.
if ( 401 === $status_code || 403 === $status_code ) {
$error_message = isset( $response_body['error'] ) ? $response_body['error'] : __( 'You do not have permission to perform this action.', 'ultimate-addons-for-gutenberg' );
$error_code = isset( $response_body['code'] ) ? $response_body['code'] : 'forbidden';
return array(
'error' => $error_message,
'code' => $error_code,
);
}
// If the response body is not a JSON, then abandon ship.
if ( 200 !== $status_code || empty( $response_body ) ) {
$error_message = __( 'Encountered an error while processing your request. Please try again.', 'ultimate-addons-for-gutenberg' );
$error_code = 'unknown_error';
return array(
'error' => $error_message,
'code' => $error_code,
);
}
// Return the response body.
return $response_body;
}
/**
* Get a response from the ZipWP API server.
*
* @param string $endpoint The endpoint to get the response from.
* @since 1.1.2
* @return array The ZipWP API Response.
*/
public static function get_zipwp_api_response( $endpoint ) {
// If the endpoint is not a string, then abandon ship.
if ( ! is_string( $endpoint ) ) {
return array(
'error' => __( 'The ZipWP Endpoint was not declared', 'ultimate-addons-for-gutenberg' ),
);
}
// Get the ZipWP Token from the Zip AI Settings.
$zipwp_token = self::get_decrypted_zipwp_token();
// If the ZipWP Token is not set, then abandon ship.
if ( empty( $zipwp_token ) || ! is_string( $zipwp_token ) ) {
return array(
'error' => __( 'The ZipWP Token is not set.', 'ultimate-addons-for-gutenberg' ),
);
}
// Set the API URL.
$api_url = ZIP_AI_ZIPWP_API . $endpoint;
// Get the response from the endpoint.
$response = wp_remote_get(
$api_url,
array(
'headers' => array(
'Content-Type' => 'application/json',
'Accept' => 'application/json',
'Authorization' => 'Bearer ' . $zipwp_token,
),
'sslverify' => false,
'timeout' => 30, // phpcs:ignore WordPressVIPMinimum.Performance.RemoteRequestTimeout.timeout_timeout -- 30 seconds is required sometime for the ZipWP API response
)
);
// If the response was an error, or not a 200 status code, then abandon ship.
if ( is_wp_error( $response ) || empty( $response['response'] ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
return array(
'error' => __( 'The ZipWP API server is not responding.', 'ultimate-addons-for-gutenberg' ),
);
}
// Get the response body.
$response_body = wp_remote_retrieve_body( $response );
// If the response body is not a JSON, then abandon ship.
if ( empty( $response_body ) || ! json_decode( $response_body ) ) {
return array(
'error' => __( 'The ZipWP API server encountered an error.', 'ultimate-addons-for-gutenberg' ),
);
}
// Return the response body.
return json_decode( $response_body, true );
}
/**
* Get the decrypted token from the Zip AI Settings.
*
* @param string $token_name The name of the token.
* @since 1.1.2
* @return string The decrypted token.
*/
private static function get_decrypted_token( $token_name ) {
// Get the Zip AI Settings.
$zip_ai_token = self::get_setting( $token_name );
// Return early if the ZipWP token is not set.
if ( empty( $zip_ai_token ) || ! is_string( $zip_ai_token ) ) {
return '';
}
// Return the decrypted ZipWP token.
return ! empty( trim( $zip_ai_token ) ) ? Utils::decrypt( $zip_ai_token ) : '';
}
/**
* Get the decrypted auth token.
*
* @since 1.0.0
* @return string The decrypted auth token.
*/
public static function get_decrypted_auth_token() {
return self::get_decrypted_token( 'auth_token' );
}
/**
* Get the decrypted ZipWP token.
*
* @since 1.1.2
* @return string The decrypted ZipWP token.
*/
public static function get_decrypted_zipwp_token() {
return self::get_decrypted_token( 'zip_token' );
}
/**
* Get cached credit details.
*
* @since 2.0.5
*
* @return array|false
*/
public static function get_cached_credit_details() {
$cached = get_transient( 'zip_ai_credit_details' );
return $cached ? json_decode( $cached, true ) : false;
}
/**
* Cache credit details.
*
* @since 2.0.5
*
* @param array $details The credit details to cache.
* @param int $duration The cache duration in seconds.
* @return void
*/
public static function cache_credit_details( $details, $duration = 300 ) {
// 5 minutes default
set_transient( 'zip_ai_credit_details', wp_json_encode( $details ), $duration );
}
/**
* This helper function returns credit details.
*
* @since 2.0.5
* @return array
*/
public static function get_credit_details() {
$cached = self::get_cached_credit_details();
if ( false !== $cached ) {
return $cached;
}
// Set the default credit details.
$credit_details = array(
'used' => 0,
'total' => 0,
'threshold' => array(
'medium' => ZIP_AI_CREDIT_THRESHOLD_MEDIUM,
'high' => ZIP_AI_CREDIT_THRESHOLD_HIGH,
),
'percentage' => 0,
'status' => 'success',
);
// Get the response from the endpoint.
$response = self::get_credit_server_response( 'usage' );
// If the response is not an error, then update the credit details.
if (
empty( $response['error'] )
&& ! empty( $response['total_credits'] )
) {
$credit_details['used'] = ! empty( $response['total_used_credits'] ) ? $response['total_used_credits'] : 0;
$credit_details['total'] = $response['total_credits'];
$credit_details['percentage'] = intval( ( $credit_details['used'] / $credit_details['total'] ) * 100 );
self::cache_credit_details( $credit_details );
} else {
$credit_details['status'] = 'error';
}
return $credit_details;
}
/**
* Get fresh credit details without cache.
*
* @since 2.0.5
* @return array
*/
public static function get_fresh_credit_details() {
// Set the default credit details.
$credit_details = array(
'used' => 0,
'total' => 0,
'threshold' => array(
'medium' => ZIP_AI_CREDIT_THRESHOLD_MEDIUM,
'high' => ZIP_AI_CREDIT_THRESHOLD_HIGH,
),
'percentage' => 0,
'status' => 'success',
);
// Get the response from the endpoint.
$response = self::get_credit_server_response( 'usage' );
// If the response is not an error, then update the credit details.
if (
empty( $response['error'] )
&& ! empty( $response['total_credits'] )
) {
$credit_details['used'] = ! empty( $response['total_used_credits'] ) ? $response['total_used_credits'] : 0;
$credit_details['total'] = $response['total_credits'];
$credit_details['percentage'] = intval( ( $credit_details['used'] / $credit_details['total'] ) * 100 );
// Update cache with fresh data.
self::cache_credit_details( $credit_details );
} else {
$credit_details['status'] = 'error';
}
return $credit_details;
}
/**
* This helper function returns the current plan details.
*
* @since 1.1.2
* @return array
*/
public static function get_current_plan_details() {
// Get current auth token.
$current_auth_token = self::get_decrypted_auth_token();
// Get cached auth token.
$cached_auth_token = get_transient( 'zip_ai_auth_token' );
// If auth token has changed or doesn't exist, clear all caches.
if ( $current_auth_token !== $cached_auth_token ) {
delete_transient( 'zip_ai_current_plan_details' );
delete_transient( 'zip_ai_credit_details' );
// Cache the new auth token.
set_transient( 'zip_ai_auth_token', $current_auth_token, DAY_IN_SECONDS );
}
// Check for cached response first.
$cached_response = get_transient( 'zip_ai_current_plan_details' );
if ( false !== $cached_response ) {
return json_decode( $cached_response, true );
}
$current_plan_details = [];
// Get the response from the endpoint.
$response = self::get_zipwp_api_response( 'plan/current-plan' );
// If the response is not an error, then use it - else create an error response array.
if ( empty( $response['error'] ) && is_array( $response ) ) {
$current_plan_details = $response;
if ( empty( $current_plan_details['status'] ) ) {
$current_plan_details['status'] = 'ok';
}
} else {
$current_plan_details['status'] = 'error';
if ( ! empty( $response['error'] ) ) {
$current_plan_details['error'] = $response['error'];
}
return $current_plan_details; // Return error immediately without caching.
}
// Filter for developers to modify cache duration, default 24 hours.
$cache_duration = apply_filters( 'zip_ai_plan_details_cache_duration', DAY_IN_SECONDS );
// Validate cache duration.
if ( ! is_numeric( $cache_duration ) ) {
$cache_duration = DAY_IN_SECONDS;
}
// Ensure cache duration is between 1 minute and 1 week for optimal performance and freshness.
$cache_duration = absint( $cache_duration );
if ( $cache_duration < MINUTE_IN_SECONDS || $cache_duration > WEEK_IN_SECONDS ) {
$cache_duration = DAY_IN_SECONDS;
}
// Cache the response with JSON encoding for security.
set_transient( 'zip_ai_current_plan_details', wp_json_encode( $current_plan_details ), $cache_duration );
return $current_plan_details;
}
/**
* Get the authorization middleware url.
*
* @param array $params An array of parameters to add to the middleware URL.
* @since 1.0.0
* @return string The authorization middleware url.
*/
public static function get_auth_middleware_url( $params = [] ) {
// Create the Redirect URL.
$redirect_url = add_query_arg(
array(
'nonce' => wp_create_nonce( 'zip_ai_auth_nonce' ),
'scs-authorize' => 'true',
),
admin_url()
);
// Create the Authentication URL.
$auth_url = add_query_arg(
apply_filters(
'zip_ai_auth_middleware_args',
array(
'type' => 'token',
'redirect_url' => rawurlencode( $redirect_url ),
)
),
ZIP_AI_MIDDLEWARE
);
// Add the plugin param if passed.
if ( ! empty( $params['plugin'] ) && is_string( $params['plugin'] ) ) {
$auth_url = add_query_arg(
'plugin',
sanitize_text_field( $params['plugin'] ),
$auth_url
);
}
// Add the source param if passed.
if ( ! empty( $params['source'] ) && is_string( $params['source'] ) ) {
$auth_url = add_query_arg(
'source',
sanitize_text_field( $params['source'] ),
$auth_url
);
}
// Add the affiliate param if passed.
$affiliate = get_option( 'zipwp_partner_url_param', '' );
$affiliate = is_string( $affiliate ) ? sanitize_text_field( $affiliate ) : '';
if ( ! empty( $affiliate ) ) {
$auth_url = add_query_arg(
'aff',
$affiliate,
$auth_url
);
}
return $auth_url;
}
/**
* Clear the credit details cache.
*
* @since 2.0.5
* @return void
*/
public static function clear_credit_details_cache() {
delete_transient( 'zip_ai_credit_details' );
}
/**
* Clear the current plan cache.
*
* @since 2.0.5
* @return void
*/
public static function clear_current_plan_cache() {
delete_transient( 'zip_ai_current_plan_details' );
}
/**
* Get the revoke url for the auth token and clear all caches.
*
* @since 1.0.0
* @return string The authorization revoke url.
*/
public static function get_auth_revoke_url() {
$revoke_url = add_query_arg(
apply_filters(
'zip_ai_auth_revoke_args',
array(
array(
'nonce' => wp_create_nonce( 'zip_ai_auth_nonce' ),
'revoke_zip_ai_authorization_token' => 'definitely',
),
admin_url(),
)
)
);
return $revoke_url;
}
}

View File

@@ -0,0 +1,211 @@
<?php
/**
* Zip AI - Module.
*
* This file is used to register and manage the Zip AI Modules.
*
* @package zip-ai
*/
namespace ZipAI\Classes;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// Classes to be used, in alphabetical order.
use ZipAI\Classes\Helper;
/**
* The Module Class.
*/
class Module {
/**
* Private Variable of all the valid Zip AI Modules.
*
* @since 1.0.5
* @var array $valid_modules Array of all the available Zip AI Modules.
*/
private static $valid_modules = [
'ai_assistant',
'ai_design_copilot',
];
/**
* Update the status of Zip AI Module(s).
*
* @param string|array $module_name Name of the module or an array of module names.
* @param string $status Status of the module(s) to be updated.
* @since 1.0.5
* @return boolean True if Zip AI Module(s) status has been updated, false otherwise.
*/
private static function update_status( $module_name, $status ) {
// If the status is not a valid status, return.
if ( ! in_array( $status, [ 'enabled', 'disabled' ], true ) ) {
return false;
}
// If the module name is a string, format it into an array.
if ( is_string( $module_name ) && ! empty( trim( $module_name ) ) ) {
$module_name = [ $module_name ];
} elseif ( ! is_array( $module_name ) ) {
return false;
}
// Get all the modules.
$all_modules = self::get_all_modules();
// Ensure that the modules that are to be updated are valid.
$module_name = array_intersect( array_keys( $all_modules ), $module_name );
// Modules from DB is an array of arrays, where the keys are the module names, and the values are an array of module data.
// We need to update all the modules that are passed in the $module_name array, making their status as $status.
array_walk(
$all_modules,
function( &$module, $module_key ) use ( $module_name, $status ) {
// If the module is not in the module name array, return it as it is.
if ( ! is_array( $module ) || ! in_array( $module_key, $module_name, true ) ) {
return $module;
}
// If the module is in the module name array, update the status.
$module['status'] = $status;
// Return the updated module.
return $module;
}
);
// Update the modules array.
return Helper::update_admin_settings_option( 'zip_ai_modules', $all_modules );
}
/**
* Function to migrate older Zip AI settings into the new modular format.
*
* @since 1.0.5
* @return void
*/
public static function migrate_options() {
// Get the existing Zip AI settings option.
$existing_settings = Helper::get_admin_settings_option( 'zip_ai_settings', [] );
// If the chat enabled option is set, migrate it.
if ( isset( $existing_settings['chat_enabled'] ) ) {
// Set the new option value based on the chat enabled value.
$ai_assistant_status = false === $existing_settings['chat_enabled'] ? 'disabled' : 'enabled';
// Update the AI assistant module status.
$ai_assistant_migrated = self::update_status( 'ai_assistant', $ai_assistant_status );
// If the migration was successful, unset the chat enabled value and update the settings.
if ( $ai_assistant_migrated ) {
unset( $existing_settings['chat_enabled'] );
Helper::update_admin_settings_option( 'zip_ai_settings', $existing_settings );
}
}
}
/**
* Function to get all the availabe Zip AI Modules, after applying the filter.
*
* First all the filtered modules and the modules from the database will be fetched.
* Then the database modules will be cross-checked against the valid filtered modules.
* This is done so that even if a value exists in the database, if the product that is adding the filter is disabled, the feature will be considered as non-existent.
* Finally the required data from the database will overwrite the filtered defaults, and only the valid modules will be returned for use.
*
* @since 1.0.5
* @return array Array of all the available Zip AI Modules and their details.
*/
public static function get_all_modules() {
$filtered_modules = apply_filters( 'zip_ai_modules', [] );
// Ensure that the modules are in the correct format.
$filtered_modules = is_array( $filtered_modules ) ? $filtered_modules : [];
// Get the existing Zip AI modules from the DB.
$modules_from_db = Helper::get_admin_settings_option( 'zip_ai_modules', [] );
// Ensure that the modules are in the correct format.
$modules_from_db = is_array( $modules_from_db ) ? $modules_from_db : [];
// Only load the modules from the database that have the same keys as the filtered modules.
$modules_from_db = array_intersect_key( $modules_from_db, $filtered_modules );
// Set the final modules array, where the database values override the filtered values.
$filtered_modules = array_merge( $filtered_modules, $modules_from_db );
// Ensure that only the valid modules are returned.
return array_intersect_key( $filtered_modules, array_flip( self::$valid_modules ) );
}
/**
* Enable Zip AI Module(s).
*
* If a string is passed, that module will be enabled if valid.
* If an array is passed, all valid modules will be enabled.
*
* @param string|array $module_name Name of the module or an array of module names.
* @since 1.0.5
* @return boolean True if Zip AI module(s) has been enabled, false otherwise.
*/
public static function enable( $module_name ) {
return self::update_status( $module_name, 'enabled' );
}
/**
* Function to disable Zip AI Module(s).
*
* If a string is passed, that module will be disabled if valid.
* If an array is passed, all valid modules will be disabled.
*
* @param string|array $module_name Name of the module or an array of module names.
* @since 1.0.5
* @return boolean True if Zip AI module(s) has been enabled, false otherwise.
*/
public static function disable( $module_name ) {
return self::update_status( $module_name, 'disabled' );
}
/**
* Function to check if Zip AI Module is enabled.
*
* @param string $module_name Name of the module.
* @since 1.0.5
* @return boolean True if Zip AI is enabled, false otherwise.
*/
public static function is_enabled( $module_name ) {
// If the module name is not a string, abandon ship.
if ( ! is_string( $module_name ) ) {
return false;
}
// Get all the modules.
$all_modules = self::get_all_modules();
// If the given module name is not a valid module or if the module does not have a status, abandon ship.
if ( ! array_key_exists( $module_name, $all_modules ) || empty( $all_modules[ $module_name ]['status'] ) ) {
return false;
}
// Return based on whether Zip AI is enabled or not.
return 'enabled' === $all_modules[ $module_name ]['status'];
}
/**
* Enable the given Zip AI module if it exists, else create and enable it.
*
* @param array $modules The reference to the modules array that will be modified.
* @param string $module_name The module name.
* @since 1.1.0
* @return void
*/
public static function force_enabled( &$modules, $module_name ) {
if ( empty( $modules[ $module_name ] ) || ! is_array( $modules[ $module_name ] ) ) {
$modules[ $module_name ] = array( 'status' => 'enabled' );
} else {
$modules[ $module_name ]['status'] = 'enabled';
}
}
}

View File

@@ -0,0 +1,810 @@
<?php
/**
* Zip AI - Admin Configurations.
*
* @package zip-ai
*/
namespace ZipAI\Classes;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// Classes to be used, in alphabetical order.
use ZipAI\Classes\Helper;
use ZipAI\Classes\Module;
/**
* The Sidebar_Configurations Class.
*/
class Sidebar_Configurations {
/**
* The namespace for the Rest Routes.
*
* @since 1.0.0
* @var string
*/
private $namespace = 'zip_ai';
/**
* Instance of this class.
*
* @since 1.0.0
* @var object Class object.
*/
private static $instance;
/**
* Initiator of this class.
*
* @since 1.0.0
* @return object initialized object of this class.
*/
public static function get_instance() {
if ( ! isset( self::$instance ) ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor of this class.
*
* @since 1.0.0
* @return void
*/
public function __construct() {
if ( ! current_user_can( 'manage_zip_ai_assistant' ) ) {
return;
}
global $wp_version;
// Set the priority for loading ZIP AI Adminbar trigger.
$admin_trigger_priority = version_compare( $wp_version, '6.6', '<' ) ? 999 : 6;
// Setup the Sidebar Rest Routes.
add_action( 'rest_api_init', array( $this, 'register_route' ) );
add_action( 'admin_bar_menu', array( $this, 'add_admin_trigger' ), $admin_trigger_priority );
// Setup the Sidebar Auth Ajax.
add_action( 'wp_ajax_verify_zip_ai_authenticity', array( $this, 'verify_authenticity' ) );
// Setup the Sidebar Credit Details Ajax.
add_action( 'wp_ajax_get_latest_credit_details', array( $this, 'get_latest_credit_details' ) );
add_action( 'wp_ajax_get_fresh_credit_details', array( $this, 'get_fresh_credit_details' ) );
// Render the Sidebar React App in the Footer in the Gutenberg Editor, Admin, and the Front-end.
add_action( 'admin_footer', array( $this, 'render_sidebar_markup' ) );
add_action( 'wp_footer', array( $this, 'render_sidebar_markup' ) );
// Add the Sidebar to the Gutenberg Editor, Admin, and the Front-end.
add_action( 'admin_enqueue_scripts', array( $this, 'load_sidebar_assets' ) );
add_action( 'wp_enqueue_scripts', array( $this, 'load_sidebar_assets' ) );
}
/**
* Register All Routes.
*
* @hooked - rest_api_init
* @since 1.0.0
* @return void
*/
public function register_route() {
register_rest_route(
$this->namespace,
'/generate',
array(
array(
'methods' => \WP_REST_Server::CREATABLE,
'callback' => array( $this, 'generate_ai_content' ),
'permission_callback' => function () {
return current_user_can( 'manage_zip_ai_assistant' );
},
'args' => array(
'use_system_message' => array(
'sanitize_callback' => array( $this, 'sanitize_boolean_field' ),
),
),
),
)
);
}
/**
* Checks whether the value is boolean or not.
*
* @param mixed $value value to be checked.
* @since 1.0.0
* @return boolean
*/
public function sanitize_boolean_field( $value ) {
return filter_var( $value, FILTER_VALIDATE_BOOLEAN );
}
/**
* Update ZIP AI Assistant options.
*
* @param array $params Parameters for updating options.
* @since 1.1.6
* @return void
*/
public function update_zip_ai_assistant_options( $params ) {
$last_message_tone = '';
$last_index = count( $params['message_array'] ) - 1;
// Find the last match if it exist.
for ( $i = $last_index; $i >= 0; $i-- ) {
$content = $params['message_array'][ $i ]['content'];
preg_match( '/Rewrite in a (\w+) tone/', $content, $matches_tone );
if ( ! empty( $matches_tone ) && empty( $last_message_tone ) ) {
$last_message_tone = $matches_tone[1];
}
// If both language and message tone are found, break the loop.
if ( ! empty( $last_message_tone ) ) {
break;
}
}
$option_name = 'zip_ai_assistant_option';
$current_options = array();
// If options exist, fetch them.
if ( get_option( $option_name ) ) {
$current_options = get_option( $option_name );
}
if ( ! empty( $last_message_tone ) ) {
$current_options['last_used']['changeTone'] = [
'value' => $last_message_tone,
'label' => __( ucfirst( $last_message_tone ), 'ultimate-addons-for-gutenberg' ), //phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText
];
}
// Update options in the database.
Helper::update_admin_settings_option( $option_name, $current_options );
}
/**
* Fetches ai data from the middleware server.
*
* @param \WP_REST_Request $request request object.
* @since 1.0.0
* @return void
*/
public function generate_ai_content( $request ) {
// Get the params.
$params = $request->get_params();
// Update the ZIP AI Assistant options for last used language and tone.
$this->update_zip_ai_assistant_options( $params );
// If the nessage array doesn't exist, abandon ship.
if ( empty( $params['message_array'] ) || ! is_array( $params['message_array'] ) ) {
wp_send_json_error( array( 'message' => __( 'The message array was not supplied', 'ultimate-addons-for-gutenberg' ) ) );
}
// Set the character count to 0, and create messages array.
$character_count = 0;
$messages = array();
// Start with the last message - going upwards until the character count hits 2500.
foreach ( array_reverse( $params['message_array'] ) as $current_message ) {
// If the message content doesn't exist, skip it.
if ( empty( $current_message['content'] ) ) {
continue;
}
$message_length = strlen( $current_message['content'] );
// If adding this message exceeds 2500 characters, break the loop.
$character_count += $message_length;
if ( $character_count > 2500 ) {
break;
}
// Add the message to the start of the messages to send to the SCS Middleware.
array_unshift( $messages, $current_message );
}
// Finally add the system message to the start of the array.
if ( ! empty( $params['use_system_message'] ) ) {
// Get the AI training message according to the location of the current page.
$initial_messages = self::assign_ai_assistant_purpose( $params );
foreach ( array_reverse( $initial_messages ) as $initial_message ) {
array_unshift(
$messages,
$initial_message
);
}
}
// Set the required values to send to the middleware server.
$endpoint = 'chat/completions';
$data = array(
'temperature' => 0.7,
'top_p' => 1,
'frequency_penalty' => 0.8,
'presence_penalty' => 1,
'messages' => $messages,
);
// Get the response from the endpoint.
$response = Helper::get_credit_server_response( $endpoint, $data );
if ( ! empty( $response['error'] ) ) {
// If the response has an error, handle it and report it back.
$message = '';
if ( ! empty( $response['error']['message'] ) ) { // If any error message received from OpenAI.
$message = $response['error']['message'];
} elseif ( is_string( $response['error'] ) ) { // If any error message received from server.
if ( ! empty( $response['code'] && is_string( $response['code'] ) ) ) {
$message = $this->custom_message( $response['code'] );
}
$message = ! empty( $message ) ? $message : $response['error'];
}
wp_send_json_error(
array(
'message' => $message,
'code' => $response['code'],
)
);
} elseif ( is_array( $response['choices'] ) && ! empty( $response['choices'][0]['message']['content'] ) ) {
// If the message was sent successfully, send it successfully.
wp_send_json_success(
array(
'message' => $response['choices'][0]['message']['content'],
'code' => $response['code'],
)
);
} else {
// If you've reached here, then something has definitely gone amuck. Abandon ship.
wp_send_json_error(
array(
'message' => __( 'Something went wrong', 'ultimate-addons-for-gutenberg' ),
'code' => $response['code'],
)
);
}//end if
}
/**
* This function converts the code recieved from scs to a readable error message.
* Useful to provide better language for error codes.
*
* @param string $code error code received from SCS ( Credits server ).
* @since 1.0.0
* @return string
*/
private function custom_message( $code ) {
$message_array = array(
'no_auth' => __( 'Authentication failed. Invalid or missing bearer token.', 'ultimate-addons-for-gutenberg' ),
'insufficient_credits' => array(
'title' => __( 'You\'ve run out of credits.', 'ultimate-addons-for-gutenberg' ),
'type' => 'assemble-error',
'content' => __( 'To continue using the assistant and access its full features, please purchase more credits.', 'ultimate-addons-for-gutenberg' ),
'button_content' => array(
'text' => __( 'Buy more credits', 'ultimate-addons-for-gutenberg' ),
'url' => 'https://app.zipwp.com/credits-pricing?source=spectra',
),
),
);
return isset( $message_array[ $code ] ) ? $message_array[ $code ] : '';
}
/**
* Ajax handeler to verify the Zip AI authorization.
*
* @since 1.0.0
* @return void
*/
public function verify_authenticity() {
// Check the nonce.
check_ajax_referer( 'zip_ai_ajax_nonce', 'nonce' );
// Set an array of data to be sent.
$required_details = [
'is_authorized' => Helper::is_authorized(),
];
// If the user is authorized, get the credit details.
if ( $required_details['is_authorized'] ) {
$required_details['credit_details'] = Helper::get_credit_details();
}
// Get the current plan details that need to be localized.
$response_zipwp_plan = Helper::get_current_plan_details();
// If the response is not an error, then proceed to localize the required details.
if ( is_array( $response_zipwp_plan ) && 'error' !== $response_zipwp_plan['status'] ) {
// Add the team name if it exists.
if ( ! empty( $response_zipwp_plan['team']['name'] ) ) {
$required_details['team_name'] = $response_zipwp_plan['team']['name'];
}
}
// Send a boolean based on whether the auth token has been added.
wp_send_json_success( $required_details );
}
/**
* Enqueue the AI Asssitant Sidebar assets.
*
* @return void
* @since 1.0.0
*/
public function load_sidebar_assets() {
if ( class_exists( '\UAGB_Admin_Helper' ) && method_exists( '\UAGB_Admin_Helper', 'should_exclude_assets_for_cpt' ) ) {
if ( \UAGB_Admin_Helper::should_exclude_assets_for_cpt() ) {
return; // Early return to prevent loading assets.
}
}
// If the admin bar is not visible, we don't want to load the sidebar assets.
if ( ! is_admin_bar_showing() ) {
return;
}
// Set the required variables.
$handle = 'zip-ai-sidebar';
$build_path = ZIP_AI_DIR . 'sidebar/build/';
$build_url = ZIP_AI_URL . 'sidebar/build/';
$script_asset_path = $build_path . 'sidebar-app.asset.php';
$script_info = file_exists( $script_asset_path )
? include $script_asset_path
: array(
'dependencies' => array(),
'version' => ZIP_AI_VERSION,
);
// If this is in the front-end, remove any editor-specific dependencies.
// This will work as intended because the React components for the editor have checks to render the same, leaving no errors.
$script_dep = ! is_admin() ? array_diff(
$script_info['dependencies'],
[
'wp-block-editor',
'wp-edit-post',
'wp-rich-text',
]
) : $script_info['dependencies'];
// Resolving conflict with wigdget page query monitor warning.
global $pagenow;
if ( 'widgets.php' === $pagenow ) {
$script_dep = array_diff( $script_info['dependencies'], [ 'wp-edit-post' ] );
}
// Note that the current screen function is loaded after admin_init, so if it doesn't exist set screen to null.
$screen = ( is_admin() && function_exists( 'get_current_screen' ) ) ? get_current_screen() : null;
// Register the sidebar scripts.
wp_register_script(
$handle,
$build_url . 'sidebar-app.js',
$script_dep,
$script_info['version'],
true
);
// Register the sidebar styles.
wp_register_style(
$handle,
$build_url . 'sidebar-app.css',
array(),
ZIP_AI_VERSION
);
// Enqueue the sidebar scripts.
wp_enqueue_script( $handle );
// Set the script translations.
wp_set_script_translations( $handle, apply_filters( 'zip_ai_library_textdomain', 'zip-ai' ) );
// Enqueue the sidebar styles.
wp_enqueue_style( $handle );
// Create the middleware parameters array and the credit topup URL.
$middleware_params = [];
$credit_topup_url = esc_url( ZIP_AI_CREDIT_TOPUP_URL );
// Get the collab product details, and extract the slug from there if it exists.
$collab_product_details = apply_filters( 'zip_ai_collab_product_details', null );
// If the collab details is an array and has the plugin slug, add it to the middleware params.
if ( is_array( $collab_product_details )
&& ! empty( $collab_product_details['product_slug'] )
&& is_string( $collab_product_details['product_slug'] )
) {
$middleware_params['plugin'] = sanitize_text_field( $collab_product_details['product_slug'] );
// Also update the plugin as the source param for the Get Credits URL.
$credit_topup_url = esc_url( add_query_arg( 'source', $collab_product_details['product_slug'], ZIP_AI_CREDIT_TOPUP_URL ) );
}
// Get the current plan details that need to be localized.
$response_zipwp_plan = Helper::get_current_plan_details();
$current_zipwp_plan = array();
// If the response is not an error, then proceed to localize the required details.
if ( is_array( $response_zipwp_plan ) && 'error' !== $response_zipwp_plan['status'] ) {
// Add the team name if it exists.
if ( ! empty( $response_zipwp_plan['team']['name'] ) ) {
$current_zipwp_plan['team_name'] = $response_zipwp_plan['team']['name'];
}
}
// Get the ID based on the current URL - this will avoid incorrectly getting popups as the page.
$current_url = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
$post_id = url_to_postid( set_url_scheme( $current_url ) );
// If this is an editor page, this won't work - so if it doesn't, try getting the ID.
if ( empty( $post_id ) ) {
$post_id = get_the_ID();
}
// Identify the special page if required.
$special_page;
switch ( true ) {
case ( function_exists( 'is_shop' ) && is_shop() ):
$special_page = 'shop';
break;
case ( function_exists( 'is_cart' ) && is_cart() ):
$special_page = 'cart';
break;
case ( function_exists( 'is_checkout' ) && is_checkout() ):
$special_page = 'checkout';
break;
default:
$special_page = null;
}
// Set the current view - this will determine what the initial prompts should be.
$current_view = 'default';
// If you can get the current screen ( alluding to the fact that you're in the admin pages ), then proceed.
if ( function_exists( 'get_current_screen' ) ) {
$current_screen = get_current_screen();
// Check if this a WooCommerce product edit page.
if (
isset( $current_screen->base )
&& isset( $current_screen->id )
&& 'post' === $current_screen->base
&& 'product' === $current_screen->id
) {
$current_view = 'editing_product';
}
}
// Localize the script required for the Zip AI Sidebar.
wp_localize_script(
$handle,
'zip_ai_react',
array(
'ajax_url' => admin_url( 'admin-ajax.php' ),
'ajax_nonce' => wp_create_nonce( 'zip_ai_ajax_nonce' ),
'admin_nonce' => wp_create_nonce( 'zip_ai_admin_nonce' ),
'site_url' => get_site_url(),
'current_post_id' => $post_id,
'special_page' => $special_page,
'is_admin' => is_admin(),
'auth_middleware' => Helper::get_auth_middleware_url( $middleware_params ),
'is_authorized' => Helper::is_authorized(),
'is_ai_assistant_enabled' => Module::is_enabled( 'ai_assistant' ),
'is_customize_preview' => is_customize_preview(),
'collab_product_details' => $collab_product_details,
'zip_ai_assistant_options' => get_option( 'zip_ai_assistant_option' ),
'is_widgets_page' => $screen->id ?? null,
'current_status' => Helper::get_setting( 'status' ),
'current_plan_details' => $current_zipwp_plan,
'current_view' => $current_view,
'credit_details' => Helper::get_credit_details(),
'credit_topup_url' => $credit_topup_url,
)
);
wp_enqueue_style(
'zip-ai-sidebar-fonts',
'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Courier+Prime:wght@400&display=swap',
array(),
ZIP_AI_VERSION
);
}
/**
* Add the Zip AI Assistant Sidebar to the admin bar.
*
* @param object $admin_bar The admin bar object.
* @since 1.1.0
* @return void
*/
public function add_admin_trigger( $admin_bar ) {
if ( class_exists( '\UAGB_Admin_Helper' ) && method_exists( '\UAGB_Admin_Helper', 'should_exclude_assets_for_cpt' ) ) {
if ( \UAGB_Admin_Helper::should_exclude_assets_for_cpt() ) {
return; // Early return to prevent loading assets.
}
}
$args = array(
'id' => 'zip-ai-assistant',
'title' => '<span class="ab-icon" aria-hidden="true" style="margin: 0">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" aria-hidden="true" focusable="false"><path d="M9.8132 15.9038L9 18.75L8.1868 15.9038C7.75968 14.4089 6.59112 13.2403 5.09619 12.8132L2.25 12L5.09619 11.1868C6.59113 10.7597 7.75968 9.59112 8.1868 8.09619L9 5.25L9.8132 8.09619C10.2403 9.59113 11.4089 10.7597 12.9038 11.1868L15.75 12L12.9038 12.8132C11.4089 13.2403 10.2403 14.4089 9.8132 15.9038Z" stroke="currentColor" stroke-width="1.4" fill="none" stroke-linecap="round" stroke-linejoin="round"></path><path d="M16.8942 20.5673L16.5 21.75L16.1058 20.5673C15.8818 19.8954 15.3546 19.3682 14.6827 19.1442L13.5 18.75L14.6827 18.3558C15.3546 18.1318 15.8818 17.6046 16.1058 16.9327L16.5 15.75L16.8942 16.9327C17.1182 17.6046 17.6454 18.1318 18.3173 18.3558L19.5 18.75L18.3173 19.1442C17.6454 19.3682 17.1182 19.8954 16.8942 20.5673Z" stroke="currentColor" stroke-width="1.4" fill="none" stroke-linecap="round" stroke-linejoin="round"></path><path d="M18.2589 8.71454L18 9.75L17.7411 8.71454C17.4388 7.50533 16.4947 6.56117 15.2855 6.25887L14.25 6L15.2855 5.74113C16.4947 5.43883 17.4388 4.49467 17.7411 3.28546L18 2.25L18.2589 3.28546C18.5612 4.49467 19.5053 5.43883 20.7145 5.74113L21.75 6L20.7145 6.25887C19.5053 6.56117 18.5612 7.50532 18.2589 8.71454Z" stroke="currentColor" stroke-width="1.4" fill="none" stroke-linecap="round" stroke-linejoin="round"></path></svg>
</span>',
'href' => 'javascript:void(0);',
'parent' => 'top-secondary',
);
$admin_bar->add_node( $args );
}
/**
* Render the AI Assistant Sidebar markup.
*
* @since 1.1.0
* @return void
*/
public static function render_sidebar_markup() {
if ( class_exists( '\UAGB_Admin_Helper' ) && method_exists( '\UAGB_Admin_Helper', 'should_exclude_assets_for_cpt' ) ) {
if ( \UAGB_Admin_Helper::should_exclude_assets_for_cpt() ) {
return; // Early return to prevent loading assets.
}
}
// If the adminbar is visible on this screen, render the admin trigger.
if ( is_admin_bar_showing() ) {
?>
<div id="zip-ai-sidebar-admin-trigger"></div>
<?php
}
// Render the sidebar div.
?>
<div id="zip-ai-sidebar"></div>
<?php
}
/**
* Assign the purpose of the AI Assistant given the current page.
*
* @param array<mixed> $params An array of all the parameters.
* @since 2.0.0
* @return array<array<string,string>> An array all the required system messages.
*/
public static function assign_ai_assistant_purpose( $params ) {
// Get the site details.
$site_details = [];
if ( ! empty( trim( get_bloginfo( 'name' ) ) ) ) {
$site_details['name'] = esc_html( get_bloginfo( 'name' ) );
}
if ( ! empty( trim( get_bloginfo( 'description' ) ) ) ) {
$site_details['description'] = esc_html( get_bloginfo( 'description' ) );
}
// If there are ZipWP details, overwrite the default details with the improved ones.
$zipwp_details = get_option( 'zipwp_user_business_details', '' );
if ( is_array( $zipwp_details ) ) {
if ( ! empty( $zipwp_details['business_name'] ) && is_string( $zipwp_details['business_name'] ) && ! empty( trim( $zipwp_details['business_name'] ) ) ) {
$site_details['name'] = esc_html( $zipwp_details['business_name'] );
}
if ( ! empty( $zipwp_details['business_description'] ) && is_string( $zipwp_details['business_description'] ) && ! empty( trim( $zipwp_details['business_description'] ) ) ) {
$site_details['description'] = esc_html( $zipwp_details['business_description'] );
}
}
// Create the site detail message based on whether the name, description, both, or none are set.
$site_detail_message = '';
if ( ! empty( $site_details['name'] ) && ! empty( $site_details['description'] ) ) {
$site_detail_message = 'The name of my site is "' . $site_details['name'] . '" and the tagline/description of my site is "' . $site_details['description'] . '".';
} elseif ( ! empty( $site_details['name'] ) ) {
$site_detail_message = 'The name of my site is "' . $site_details['name'] . '".';
} elseif ( ! empty( $site_details['description'] ) ) {
$site_detail_message = 'The tagline/description of my site is "' . $site_details['description'] . '".';
}
// Create the default website-containing message so that links can be created.
$website_detail_message = 'When helping me with something that needs me to log in to my WordPress dashboard, generate the exact URL of the page I need to go to at the end of the message. My website is \'example.com\'. Make sure all URLs contain the link text in square brackets, and the URL in round brackets.';
// Set the common content that will be used for all cases.
$appended_common_rule = '\n\n\nYou can help me with everything I need even if it is not related to my site. You will only generate content for what you are asked.';
// All the role setting messages.
$role_settings_content = [
'default' => 'You are my AI Assistant. You are a content writer that writes content for my website.' . $appended_common_rule,
'wordpress_assistant' => 'You are my WordPress Assistant. You know everything about improving and optimizing my WordPress website for my visitors.' . $appended_common_rule,
'e_commerce_expert' => 'You are my WordPress E-commerce Expert. You know everything about improving and optimizing my E-Commerce website for my customers.' . $appended_common_rule,
];
// Create an array for all the system messages.
$page_based_system_messages = [];
// First determine if you're on any post.
if ( ! empty( $params['current_post_id'] ) && is_numeric( $params['current_post_id'] ) ) {
// Get the required details based on the ID.
$page_details = self::get_page_details( $params['current_post_id'] );
$page_post_type = get_post_type( $params['current_post_id'] );
// Check if the current page is a WooCommerce product.
$this_is_a_product_page = 'product' === $page_post_type;
// Set the role based on the page type.
if ( $this_is_a_product_page ) {
// Set the role of an E-commerce expert.
array_push( $page_based_system_messages, self::get_formatted_system_role( $role_settings_content['e_commerce_expert'] ) );
if ( ! empty( $site_detail_message ) ) {
array_push( $page_based_system_messages, self::get_formatted_system_role( $site_detail_message ) );
}
array_push( $page_based_system_messages, self::get_formatted_system_role( 'This is a product page.' ) );
} else {
// Set the role of a WordPress expert.
array_push( $page_based_system_messages, self::get_formatted_system_role( $role_settings_content['wordpress_assistant'] ) );
if ( ! empty( $site_detail_message ) ) {
array_push( $page_based_system_messages, self::get_formatted_system_role( $site_detail_message ) );
}
}
// Add the page details.
$page_detail_message = 'These are the details of the current page that you and I are on, in case you are asked something about it\n\n\nPage ID: `' . $params['current_post_id'] . '`\nTitle: `' . $page_details['title'] . '`\nContent:`' . $page_details['content'] . '`';
array_push( $page_based_system_messages, self::get_formatted_system_role( $page_detail_message ) );
// Add the website based message.
array_push( $page_based_system_messages, self::get_formatted_system_role( $website_detail_message ) );
return $page_based_system_messages;
} elseif ( ! empty( $params['special_page'] ) && is_string( $params['special_page'] ) ) {
// Set the role of an E-commerce expert.
array_push( $page_based_system_messages, self::get_formatted_system_role( $role_settings_content['e_commerce_expert'] ) );
if ( ! empty( $site_detail_message ) ) {
array_push( $page_based_system_messages, self::get_formatted_system_role( $site_detail_message ) );
}
$special_page_id;
switch ( $params['special_page'] ) {
case 'shop':
array_push( $page_based_system_messages, self::get_formatted_system_role( 'This is my shop page.' ) );
$special_page_id = get_option( 'woocommerce_shop_page_id' );
break;
case 'cart':
array_push( $page_based_system_messages, self::get_formatted_system_role( 'This is the cart page.' ) );
$special_page_id = get_option( 'woocommerce_cart_page_id' );
break;
case 'checkout':
array_push( $page_based_system_messages, self::get_formatted_system_role( 'This is the checkout page.' ) );
$special_page_id = get_option( 'woocommerce_checkout_page_id' );
break;
}
if ( ! empty( $special_page_id ) && is_numeric( $special_page_id ) ) {
$page_details = self::get_page_details( $special_page_id );
$page_detail_message = 'These are the details of the current page that you and I are on, in case you are asked something about it\n\n\nPage ID: `' . $special_page_id . '`\nTitle: `' . $page_details['title'] . '`\nContent:`' . $page_details['content'] . '`';
array_push( $page_based_system_messages, self::get_formatted_system_role( $page_detail_message ) );
}
// Add the website based message.
array_push( $page_based_system_messages, self::get_formatted_system_role( $website_detail_message ) );
return $page_based_system_messages;
}
// If you're not on a post, then the assistant is a WordPress based expert.
array_push( $page_based_system_messages, self::get_formatted_system_role( $role_settings_content['wordpress_assistant'] ) );
// Add the site details message if required.
if ( ! empty( $site_detail_message ) ) {
array_push( $page_based_system_messages, self::get_formatted_system_role( $site_detail_message ) );
}
// Add the website based message.
array_push( $page_based_system_messages, self::get_formatted_system_role( $website_detail_message ) );
return $page_based_system_messages;
}
/**
* Get the required page details for AI from the given post ID.
*
* @param int $current_post_id The current post ID.
* @since 2.0.0
* @return array<string,mixed> An array of all the required Post details.
*/
public static function get_page_details( $current_post_id ) {
// Regular expression to match opening or closing tags.
$tag_regex = '/(<\/?[a-zA-Z]+[^>]*>|<\/?[a-zA-Z]+[^>]*>)/';
// Get all the required details of the current post.
$page_title = get_post_field( 'post_title', $current_post_id );
$page_content = get_post_field( 'post_content', $current_post_id );
$page_url = get_permalink( $current_post_id );
// Replace the Page URL with the dummy.
$page_url = str_replace( preg_replace( '#^https?://#', '', get_site_url() ), 'example.com', $page_url );
// Split the post content based to put all tags and content on new lines.
$content_parts = preg_split( $tag_regex, $page_content, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE );
// If any part is a tag, delete it.
$content_parts = array_filter(
$content_parts,
function ( $content_part ) {
// Only return true for elements that are not tags, or elements that aren't just neewline characters.
return (
( false === str_starts_with( $content_part, '<' ) )
&& ( false === str_starts_with( $content_part, "\n" ) )
);
}
);
// Combine all the parts into a single string with line breaks.
$page_content = implode( "\n", $content_parts );
$page_content = preg_replace( '/\n{2,}/', "\n", $page_content );
// Return the required details.
return [
'title' => $page_title,
'content' => $page_content,
'url' => $page_url,
];
}
/**
* A small private function to take in any given content, and return a formatted array for OpenAI as a system message.
*
* @param string $content The content to be put as the message.
* @param string $role The role of the message, as per OpenAI standards.
* @since 2.0.0
* @return array<string,mixed> An array containing the role and content of the message.
*/
private static function get_formatted_system_role( $content, $role = 'system' ) {
return [
'role' => $role,
'content' => $content,
];
}
/**
* Ajax handeler to get the latest Zip AI credit details.
*
* @since 2.0.0
* @return void
*/
public function get_latest_credit_details() {
// Check the nonce.
check_ajax_referer( 'zip_ai_ajax_nonce', 'nonce' );
// Set an array of data to be sent.
$latest_credit_details = Helper::get_credit_details();
// If an error was encountered, send the error details.
if ( isset( $latest_credit_details['status'] ) && 'error' === $latest_credit_details['status'] ) {
wp_send_json_error( $latest_credit_details );
}
// Send the latest credit details.
wp_send_json_success( $latest_credit_details );
}
/**
* Ajax handeler to get fresh Zip AI credit details.
*
* @since 2.0.5
* @return void
*/
public function get_fresh_credit_details() {
// Check the nonce.
check_ajax_referer( 'zip_ai_ajax_nonce', 'nonce' );
// Set an array of data to be sent.
$latest_credit_details = Helper::get_fresh_credit_details();
// If an error was encountered, send the error details.
if ( isset( $latest_credit_details['status'] ) && 'error' === $latest_credit_details['status'] ) {
wp_send_json_error( $latest_credit_details );
}
// Send the latest credit details.
wp_send_json_success( $latest_credit_details );
}
}

View File

@@ -0,0 +1,59 @@
<?php
/**
* Zip AI - Utils.
*
* This file contains all the utility functions of Zip AI.
* Utilities manipulate data and perform actions that are not directly related to the library.
*
* @package zip-ai
*/
namespace ZipAI\Classes;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* The Utils Class.
*/
class Utils {
/**
* Encrypt data using base64.
*
* @param string $input The input string which needs to be encrypted.
* @since 1.0.0
* @return string The encrypted string.
*/
public static function encrypt( $input ) {
// If the input is empty or not a string, then abandon ship.
if ( empty( $input ) || ! is_string( $input ) ) {
return '';
}
// Encrypt the input and return it.
$base_64 = base64_encode( $input ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
$encode = rtrim( $base_64, '=' );
return $encode;
}
/**
* Decrypt data using base64.
*
* @param string $input The input string which needs to be decrypted.
* @since 1.0.0
* @return string The decrypted string.
*/
public static function decrypt( $input ) {
// If the input is empty or not a string, then abandon ship.
if ( empty( $input ) || ! is_string( $input ) ) {
return '';
}
// Decrypt the input and return it.
$base_64 = $input . str_repeat( '=', strlen( $input ) % 4 );
$decode = base64_decode( $base_64 ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
return $decode;
}
}

View File

@@ -0,0 +1,138 @@
<?php
/**
* Plugin Loader.
*
* @package zip-ai
* @since 1.0.0
*/
namespace ZipAI;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// Classes to be used, in alphabetical order.
use ZipAI\Classes\Admin_Configurations;
use ZipAI\Classes\Module;
use ZipAI\Classes\Sidebar_Configurations;
if ( ! class_exists( '\ZipAI\Loader' ) ) {
/**
* Plugin_Loader
*
* @since 1.0.0
*/
class Loader {
/**
* Instance
*
* @access private
* @var object Class Instance.
* @since 1.0.0
*/
private static $instance;
/**
* Initiator
*
* @since 1.0.0
* @return object initialized object of class.
*/
public static function get_instance() {
if ( ! isset( self::$instance ) ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Autoload classes.
*
* @param string $class class name.
*/
public function autoload( $class ) {
if ( 0 !== strpos( $class, __NAMESPACE__ ) ) {
return;
}
$class_to_load = $class;
$filename = strtolower(
preg_replace(
[ '/^' . __NAMESPACE__ . '\\\/', '/([a-z])([A-Z])/', '/_/', '/\\\/' ],
[ '', '$1-$2', '-', DIRECTORY_SEPARATOR ],
$class_to_load
)
);
$file = ZIP_AI_DIR . $filename . '.php';
// if the file redable, include it.
if ( is_readable( $file ) ) {
require_once $file;
}
}
/**
* Constructor
*
* @since 1.0.0
*/
public function __construct() {
spl_autoload_register( [ $this, 'autoload' ] );
add_action( 'plugins_loaded', [ $this, 'setup_classes' ], 20 );
$this->define_constants();
}
/**
* Define the required constants.
*
* @since 1.0.0
* @return void
*/
public function define_constants() {
define( 'ZIP_AI_FILE', __FILE__ );
define( 'ZIP_AI_DIR', plugin_dir_path( ZIP_AI_FILE ) );
define( 'ZIP_AI_URL', plugins_url( '/', ZIP_AI_FILE ) );
define( 'ZIP_AI_VERSION', '2.0.6' );
define( 'ZIP_AI_MENU_SLUG', 'zip-ai' );
define( 'ZIP_AI_MIDDLEWARE', 'https://app.zipwp.com/auth/' );
define( 'ZIP_AI_ZIPWP_API', 'https://api.zipwp.com/api/' );
define( 'ZIP_AI_CREDIT_SERVER_API', 'https://credits.startertemplates.com/api/' );
define( 'ZIP_AI_CREDIT_TOPUP_URL', 'https://app.zipwp.com/credits-pricing' );
define( 'ZIP_AI_CREDIT_THRESHOLD_MEDIUM', 65 );
define( 'ZIP_AI_CREDIT_THRESHOLD_HIGH', 85 );
}
/**
* Setup the required classes.
*
* @since 1.0.0
* @return void
*/
public function setup_classes() {
// Migrate any older modules to the new format.
Module::migrate_options();
// Enable the Zip AI Chat Sidebar if required - filter is for old users.
if ( apply_filters( 'zip_ai_enable_chat_sidebar', true ) && Module::is_enabled( 'ai_assistant' ) ) {
Sidebar_Configurations::get_instance();
}
// Enable the Zip AI Admin Configurations if required.
if ( is_admin() ) {
Admin_Configurations::get_instance();
}
}
}
/**
* Kicking this off by calling 'get_instance()' method
*/
Loader::get_instance();
}

View File

@@ -0,0 +1 @@
<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-plugins', 'wp-rich-text'), 'version' => 'aceddf37ee8a94bd3c13');

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
{
"zip-ai": "2.0.6"
}

View File

@@ -0,0 +1,21 @@
<?php
/**
* Plugin Name: Zip AI Assistant
* Description: Library which interacts with SCS and provide multiple useful modules.
* Author: Brainstorm Force
* Version: 2.0.6
* License: GPL v2
* Text Domain: zip-ai
*
* @package zip-ai
*/
// Exit if Zip AI is already loaded.
if ( defined( 'ZIP_AI_DIR' ) ) {
return;
}
// Load the Zip AI Loader.
if ( apply_filters( 'zip_ai_load_library', true ) ) {
require_once 'loader.php';
}