Commit realizado el 12:13:52 08-04-2024

This commit is contained in:
Pagina Web Monito
2024-04-08 12:13:55 -04:00
commit 0c33094de9
7815 changed files with 1365694 additions and 0 deletions

File diff suppressed because one or more lines are too long

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 @@
<svg width="38" height="38" viewBox="0 0 38 38" xmlns="http://www.w3.org/2000/svg" stroke="#6c7781"><g fill="none" fill-rule="evenodd"><g transform="translate(1 1)" stroke-width="2"><circle stroke-opacity=".5" cx="18" cy="18" r="18"/><path d="M36 18c0-9.94-8.06-18-18-18"><animateTransform attributeName="transform" type="rotate" from="0 18 18" to="360 18 18" dur="1s" repeatCount="indefinite"/></path></g></g></svg>

After

Width:  |  Height:  |  Size: 417 B

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,16 @@
<?php
/**
* Block script dependencies.
*
* @package RankMath
* @subpackage RankMath\ContentAI
* @author Rank Math <support@rankmath.com>
*/
return [
'dependencies' => [
'wp-element',
'wp-block-editor',
],
'version' => rank_math()->version,
];

View File

@@ -0,0 +1 @@
!function(){"use strict";var e=wp.blocks,t=wp.blockEditor,n=wp.element,r=wp.i18n,a=function(){return!rankMath.isUserRegistered||!rankMath.contentAIPlan||!rankMath.contentAICredits||rankMath.contentAiMigrating},o=JSON.parse('{"$schema":"https://schemas.wp.org/trunk/block.json","apiVersion":2,"name":"rank-math/command","title":"AI Assistant [Content AI]","icon":"star","category":"rank-math-blocks","description":"Generate content without any hassle, powered by Rank Math\'s Content AI.","keywords":["ai","content","rank math","rankmath","content ai","seo"],"textdomain":"rank-math-pro","editorScript":"file:../js/index.js","attributes":{"content":{"type":"string","source":"html","selector":"p","default":"<span>//</span>","__experimentalRole":"content"}}}');function c(e){return c="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},c(e)}function l(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e,t,n){return(t=function(e){var t=function(e,t){if("object"!==c(e)||null===e)return e;var n=e[Symbol.toPrimitive];if(void 0!==n){var r=n.call(e,t||"default");if("object"!==c(r))return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"===c(t)?t:String(t)}(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var m=o.name,u={icon:wp.element.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 17.08 18.02"},wp.element.createElement("g",{id:"Layer_2","data-name":"Layer 2"},wp.element.createElement("g",{id:"Layer_1-2","data-name":"Layer 1"},wp.element.createElement("path",{d:"M16.65,12.08a.83.83,0,0,0-1.11.35A7.38,7.38,0,1,1,9,1.63a7.11,7.11,0,0,1,.92.06A2.52,2.52,0,0,1,11,.23,8.87,8.87,0,0,0,9,0a9,9,0,1,0,8,13.19A.83.83,0,0,0,16.65,12.08Z"}),wp.element.createElement("path",{d:"M7.68,7.29A1.58,1.58,0,0,0,6.2,8.94a1.57,1.57,0,0,0,1.48,1.64A1.56,1.56,0,0,0,9.16,8.94,1.57,1.57,0,0,0,7.68,7.29Z"}),wp.element.createElement("path",{d:"M13.34,4.71a2.45,2.45,0,0,1-1,.2A2.53,2.53,0,0,1,9.93,3,7.18,7.18,0,0,0,9.12,3a6,6,0,1,0,4.22,1.73ZM10.53,11.3a.75.75,0,1,1-1.5,0v-.06a2.4,2.4,0,0,1-1.66.69,2.81,2.81,0,0,1-2.58-3,2.82,2.82,0,0,1,2.58-3A2.39,2.39,0,0,1,9,6.64a.75.75,0,0,1,1.5.07Zm2.56,0a.75.75,0,1,1-1.5,0V6.71a.75.75,0,1,1,1.5,0Z"}),wp.element.createElement("circle",{cx:"12.42",cy:"2.37",r:"1.45"})))),edit:function(e){var o=e.attributes,c=e.onReplace,l=e.setAttributes,i=o.content,m=(0,t.useBlockProps)({className:"rank-math-content-ai-command"}),u=(0,n.useRef)(null);return(0,n.useEffect)((function(){var e=u.current;e.focus();var t=document.createRange(),n=window.getSelection();t.selectNodeContents(e),t.collapse(!1),n.removeAllRanges(),n.addRange(t)}),[]),wp.element.createElement("div",m,wp.element.createElement(t.BlockControls,null),a()&&wp.element.createElement("div",{className:"rich-text",ref:u},rankMath.isUserRegistered?rankMath.contentAIPlan?wp.element.createElement(React.Fragment,null,(0,r.__)("You have exhausted your Content AI Credits.","rank-math"),wp.element.createElement("a",{href:"https://rankmath.com/kb/how-to-use-content-ai/?play-video=ioPeVIntJWw&utm_source=Plugin&utm_medium=Buy+Credits+Button&utm_campaign=WP",target:"_blank",rel:"noreferrer"},(0,r.__)("Get more","rank-math"))):wp.element.createElement(React.Fragment,null,(0,r.__)("You do not have a Content AI plan.","rank-math"),wp.element.createElement("a",{href:"https://rankmath.com/kb/how-to-use-content-ai/?play-video=ioPeVIntJWw&utm_source=Plugin&utm_medium=Buy+Plan+Button&utm_campaign=WP"},(0,r.__)("Choose your plan","rank-math"))):wp.element.createElement(React.Fragment,null,(0,r.__)("Start using Content AI by connecting your RankMath account.","rank-math"),wp.element.createElement("a",{href:rankMath.connectSiteUrl},(0,r.__)("Connect Now","rank-math")))),!a()&&wp.element.createElement(t.RichText,{tagName:"div",allowedFormats:[],value:i,onChange:function(e){return l({content:e})},onSplit:function(){return!1},onReplace:c,"data-empty":!i,isSelected:!0,ref:u}))}};(0,e.registerBlockType)(function(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?l(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):l(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}({name:m},o),u)}();

View File

@@ -0,0 +1,21 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"name": "rank-math/command",
"title": "AI Assistant [Content AI]",
"icon": "star",
"category": "rank-math-blocks",
"description": "Generate content without any hassle, powered by Rank Math's Content AI.",
"keywords": [ "ai", "content", "rank math", "rankmath", "content ai", "seo" ],
"textdomain": "rank-math-pro",
"editorScript": "file:../js/index.js",
"attributes": {
"content": {
"type": "string",
"source": "html",
"selector": "p",
"default": "<span>//</span>",
"__experimentalRole": "content"
}
}
}

View File

@@ -0,0 +1,101 @@
/**
* WordPress dependencies
*/
import {
RichText,
useBlockProps,
BlockControls,
} from '@wordpress/block-editor'
import { useRef, useEffect } from '@wordpress/element'
import { __ } from '@wordpress/i18n'
/**
* Internal dependencies
*/
import hasError from '../../../../assets/src/page/helpers/hasError'
const getErrorMessage = () => {
if ( ! rankMath.isUserRegistered ) {
return (
<>
{
__( 'Start using Content AI by connecting your RankMath account.', 'rank-math' )
}
<a href={ rankMath.connectSiteUrl }>{ __( 'Connect Now', 'rank-math' ) }</a>
</>
)
}
if ( ! rankMath.contentAIPlan ) {
return (
<>
{
__( 'You do not have a Content AI plan.', 'rank-math' )
}
<a href="https://rankmath.com/kb/how-to-use-content-ai/?play-video=ioPeVIntJWw&utm_source=Plugin&utm_medium=Buy+Plan+Button&utm_campaign=WP">{ __( 'Choose your plan', 'rank-math' ) }</a>
</>
)
}
return (
<>
{
__( 'You have exhausted your Content AI Credits.', 'rank-math' )
}
<a href="https://rankmath.com/kb/how-to-use-content-ai/?play-video=ioPeVIntJWw&utm_source=Plugin&utm_medium=Buy+Credits+Button&utm_campaign=WP" target="_blank" rel="noreferrer">{ __( 'Get more', 'rank-math' ) }</a>
</>
)
}
export default ( {
attributes,
onReplace,
setAttributes,
} ) => {
const { content } = attributes
const blockProps = useBlockProps( {
className: 'rank-math-content-ai-command',
} )
const contentEditableRef = useRef( null )
useEffect( () => {
const { current: contentEditable } = contentEditableRef
contentEditable.focus()
const range = document.createRange()
const selection = window.getSelection()
range.selectNodeContents( contentEditable )
range.collapse( false )
selection.removeAllRanges()
selection.addRange( range )
}, [] )
return (
<div { ...blockProps }>
<BlockControls />
{
hasError() &&
<div className="rich-text" ref={ contentEditableRef }>
{ getErrorMessage() }
</div>
}
{
! hasError() &&
<RichText
tagName="div"
allowedFormats={ [] }
value={ content }
onChange={ ( newContent ) => {
return setAttributes( { content: newContent } )
} }
onSplit={ () => {
return false
} }
onReplace={ onReplace }
data-empty={ content ? false : true }
isSelected={ true }
ref={ contentEditableRef }
/>
}
</div>
)
}

View File

@@ -0,0 +1,21 @@
/**
* WordPress dependencies
*/
import { registerBlockType } from '@wordpress/blocks'
/**
* Internal dependencies
*/
import edit from './edit'
import metadata from './block.json'
const { name } = metadata
export { metadata, name }
export const settings = {
icon: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 17.08 18.02"><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path d="M16.65,12.08a.83.83,0,0,0-1.11.35A7.38,7.38,0,1,1,9,1.63a7.11,7.11,0,0,1,.92.06A2.52,2.52,0,0,1,11,.23,8.87,8.87,0,0,0,9,0a9,9,0,1,0,8,13.19A.83.83,0,0,0,16.65,12.08Z"/><path d="M7.68,7.29A1.58,1.58,0,0,0,6.2,8.94a1.57,1.57,0,0,0,1.48,1.64A1.56,1.56,0,0,0,9.16,8.94,1.57,1.57,0,0,0,7.68,7.29Z" /><path d="M13.34,4.71a2.45,2.45,0,0,1-1,.2A2.53,2.53,0,0,1,9.93,3,7.18,7.18,0,0,0,9.12,3a6,6,0,1,0,4.22,1.73ZM10.53,11.3a.75.75,0,1,1-1.5,0v-.06a2.4,2.4,0,0,1-1.66.69,2.81,2.81,0,0,1-2.58-3,2.82,2.82,0,0,1,2.58-3A2.39,2.39,0,0,1,9,6.64a.75.75,0,0,1,1.5.07Zm2.56,0a.75.75,0,1,1-1.5,0V6.71a.75.75,0,1,1,1.5,0Z" /><circle cx="12.42" cy="2.37" r="1.45" /></g></g></svg>,
edit,
}
registerBlockType( { name, ...metadata }, settings )

View File

@@ -0,0 +1,65 @@
<?php
/**
* The TOC Block
*
* @since 1.0.104
* @package RankMath
* @subpackage RankMath\ContentAI
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\ContentAI;
use WP_Block_Type_Registry;
use RankMath\Helper;
use RankMath\Traits\Hooker;
defined( 'ABSPATH' ) || exit;
/**
* Content AI Command Block class.
*/
class Block_Command {
use Hooker;
/**
* Block type name.
*
* @var string
*/
private $block_type = 'rank-math/command';
/**
* The single instance of the class.
*
* @var Block_Command
*/
protected static $instance = null;
/**
* Retrieve main Block_Command instance.
*
* Ensure only one instance is loaded or can be loaded.
*
* @return Block_Command
*/
public static function get() {
if ( is_null( self::$instance ) && ! ( self::$instance instanceof Block_Command ) ) {
self::$instance = new Block_Command();
}
return self::$instance;
}
/**
* The Constructor.
*/
public function __construct() {
if ( WP_Block_Type_Registry::get_instance()->is_registered( $this->block_type ) ) {
return;
}
register_block_type( RANK_MATH_PATH . 'includes/modules/content-ai/blocks/command/assets/src/block.json' );
}
}

View File

@@ -0,0 +1,209 @@
<?php
/**
* Add Content AI Bulk Action options.
*
* @since 1.0.212
* @package RankMath
* @subpackage RankMath\Content_AI_Page
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\ContentAI;
use RankMath\Traits\Hooker;
use RankMath\Helper;
use RankMath\Helpers\Str;
use RankMath\Paper\Paper;
use RankMath\Admin\Admin_Helper;
use RankMath\Post;
defined( 'ABSPATH' ) || exit;
/**
* Bulk_Actions class.
*/
class Bulk_Actions {
use Hooker;
/**
* The Constructor.
*/
public function __construct() {
$this->action( 'admin_init', 'init_admin', 15 );
}
/**
* Init.
*/
public function init_admin() {
$post_types = Helper::get_settings( 'general.content_ai_post_types', [] );
foreach ( $post_types as $post_type ) {
$this->filter( "bulk_actions-edit-{$post_type}", 'bulk_actions', 9 );
$this->filter( "handle_bulk_actions-edit-{$post_type}", 'handle_bulk_actions', 10, 3 );
}
$taxonomies = Helper::get_accessible_taxonomies();
unset( $taxonomies['post_format'] );
$taxonomies = wp_list_pluck( $taxonomies, 'label', 'name' );
foreach ( $taxonomies as $taxonomy => $label ) {
$this->filter( "bulk_actions-edit-{$taxonomy}", 'bulk_actions' );
$this->filter( "handle_bulk_actions-edit-{$taxonomy}", 'handle_bulk_actions', 10, 3 );
}
$this->filter( 'wp_bulk_edit_seo_meta_post_args', 'update_background_process_args' );
}
/**
* Add bulk actions for applicable posts, pages, CPTs.
*
* @param array $actions Actions.
* @return array New actions.
*/
public function bulk_actions( $actions ) {
if ( ! Helper::has_cap( 'content_ai' ) ) {
return $actions;
}
$actions['rank_math_ai_options'] = __( '&#8595; Rank Math Content AI', 'rank-math' );
$actions['rank_math_content_ai_fetch_seo_title'] = esc_html__( 'Write SEO Title with AI', 'rank-math' );
$actions['rank_math_content_ai_fetch_seo_description'] = esc_html__( 'Write SEO Description with AI', 'rank-math' );
$actions['rank_math_content_ai_fetch_seo_title_description'] = esc_html__( 'Write SEO Title & Description with AI', 'rank-math' );
return $actions;
}
/**
* Handle bulk actions for applicable posts, pages, CPTs.
*
* @param string $redirect Redirect URL.
* @param string $doaction Performed action.
* @param array $object_ids Post IDs.
*
* @return string New redirect URL.
*/
public function handle_bulk_actions( $redirect, $doaction, $object_ids ) {
if ( empty( $object_ids ) || ! in_array( $doaction, [ 'rank_math_content_ai_fetch_seo_title', 'rank_math_content_ai_fetch_seo_description', 'rank_math_content_ai_fetch_seo_title_description' ], true ) ) {
return $redirect;
}
if ( ! empty( get_option( 'rank_math_content_ai_posts' ) ) ) {
Helper::add_notification(
esc_html__( 'Another bulk editing process is already running. Please try again later after the existing process is complete.', 'rank-math' ),
[
'type' => 'warning',
'id' => 'rank_math_content_ai_posts_error',
'classes' => 'rank-math-notice',
]
);
return $redirect;
}
$action = 'both';
if ( 'rank_math_content_ai_fetch_seo_title' === $doaction ) {
$action = 'title';
}
if ( 'rank_math_content_ai_fetch_seo_description' === $doaction ) {
$action = 'description';
}
$is_post_list = Admin_Helper::is_post_list();
$data = [
'action' => $action,
'language' => Helper::get_settings( 'general.content_ai_language', Helper::content_ai_default_language() ),
'posts' => [],
'is_taxonomy' => ! $is_post_list,
];
$method = $is_post_list ? 'get_post_data' : 'get_term_data';
foreach ( $object_ids as $object_id ) {
$data['posts'][] = $this->$method( $object_id );
}
Bulk_Edit_SEO_Meta::get()->start( $data );
return $redirect;
}
/**
* Change the timeout value in Background_Process to resolve the issue with notifications not appearing after completion in v1.2.
*
* @param array $args Process args.
*
* @return array
*/
public function update_background_process_args( $args ) {
$args['timeout'] = 0.01;
return $args;
}
/**
* Get Post data.
*
* @param integer $object_id Post ID.
*
* @return array Post data.
*/
private function get_post_data( $object_id ) {
$object = get_post( $object_id );
return [
'post_id' => $object_id,
'post_type' => 'download' === $object->post_type ? 'Product' : ucfirst( $object->post_type ),
'title' => get_the_title( $object_id ),
'focus_keyword' => Post::get_meta( 'focus_keyword', $object_id ),
'summary' => Helper::replace_vars( $this->get_post_description( $object ), $object ),
];
}
/**
* Get Term data.
*
* @param integer $object_id Term ID.
*
* @return array Term data.
*/
private function get_term_data( $object_id ) {
$object = get_term( $object_id );
return [
'post_id' => $object_id,
'post_type' => $object->taxonomy,
'title' => $object->name,
'focus_keyword' => get_term_meta( $object_id, 'rank_math_focus_keyword', true ),
'summary' => Helper::replace_vars( $this->get_term_description( $object ), $object ),
];
}
/**
* Get post description.
*
* @param WP_Post $object Post Instance.
*
* @return string Post description.
*/
private function get_post_description( $object ) {
$description = Post::get_meta( 'description', $object->ID );
if ( '' !== $description ) {
return $description;
}
return ! empty( $object->post_excerpt ) ? $object->post_excerpt : Str::truncate( Paper::get_from_options( "pt_{$object->post_type}_description", $object ), 160 );
}
/**
* Get post description.
*
* @param WP_Post $object Post Instance.
*
* @return string Post description.
*/
private function get_term_description( $object ) {
$description = get_term_meta( $object->term_id, 'rank_math_description', true );
if ( '' !== $description ) {
return $description;
}
return ! empty( $object->description ) ? $object->description : Str::truncate( Paper::get_from_options( "tax_{$object->taxonomy}_description", $object ), 160 );
}
}

View File

@@ -0,0 +1,226 @@
<?php
/**
* Bulk Edit SEO Meta data from Content AI API.
*
* @since 1.0.108
* @package RankMath
* @subpackage RankMath\Status
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\ContentAI;
use RankMath\Helper;
use RankMath\Admin\Admin_Helper;
defined( 'ABSPATH' ) || exit;
/**
* Bulk_Edit_SEO_Meta class.
*/
class Bulk_Edit_SEO_Meta extends \WP_Background_Process {
/**
* Action.
*
* @var string
*/
protected $action = 'bulk_edit_seo_meta';
/**
* Main instance.
*
* Ensure only one instance is loaded or can be loaded.
*
* @return Bulk_Edit_SEO_Meta
*/
public static function get() {
static $instance;
if ( is_null( $instance ) && ! ( $instance instanceof Bulk_Edit_SEO_Meta ) ) {
$instance = new Bulk_Edit_SEO_Meta();
}
return $instance;
}
/**
* Start creating batches.
*
* @param array $data Posts data.
*/
public function start( $data ) {
Helper::add_notification(
esc_html__( 'Bulk editing SEO meta started. It might take few minutes to complete the process.', 'rank-math' ),
[
'type' => 'success',
'id' => 'rank_math_content_ai_posts_started',
'classes' => 'rank-math-notice',
]
);
$action = $data['action'];
$posts = $data['posts'];
$language = $data['language'];
update_option( 'rank_math_content_ai_posts', $posts );
$chunks = array_chunk( $posts, 10, true );
foreach ( $chunks as $chunk ) {
$this->push_to_queue(
[
'posts' => $chunk,
'action' => $action,
'language' => $language,
'is_taxonomy' => ! empty( $data['is_taxonomy'] ),
]
);
}
$this->save()->dispatch();
}
/**
* Task to perform.
*
* @param string $data Posts to process.
*/
public function wizard( $data ) {
$this->task( $data );
}
/**
* Cancel the Bulk edit process.
*/
public function cancel() {
delete_option( 'rank_math_content_ai_posts' );
delete_option( 'rank_math_content_ai_posts_processed' );
parent::clear_scheduled_event();
}
/**
* Complete.
*
* Override if applicable, but ensure that the below actions are
* performed, or, call parent::complete().
*/
protected function complete() {
$posts = get_option( 'rank_math_content_ai_posts' );
delete_option( 'rank_math_content_ai_posts' );
delete_option( 'rank_math_content_ai_posts_processed' );
Helper::add_notification(
// Translators: placeholder is the number of modified items.
sprintf( _n( 'SEO meta successfully updated in %d item.', 'SEO meta successfully updated in %d items.', count( $posts ), 'rank-math' ), count( $posts ) ),
[
'type' => 'success',
'id' => 'rank_math_content_ai_posts',
'classes' => 'rank-math-notice',
]
);
parent::complete();
}
/**
* Task to perform.
*
* @param array $data Posts to process.
*
* @return bool
*/
protected function task( $data ) {
try {
$posts = json_decode( wp_remote_retrieve_body( $this->get_posts( $data ) ), true );
if ( empty( $posts['meta'] ) ) {
return false;
}
foreach ( $posts['meta'] as $post_id => $data ) {
$method = ! empty( $data['object_type'] ) && 'term' === $data['object_type'] ? 'update_term_meta' : 'update_post_meta';
if ( ! empty( $data['title'] ) ) {
$method( $post_id, 'rank_math_title', sanitize_text_field( $data['title'] ) );
}
if ( ! empty( $data['description'] ) ) {
$method( $post_id, 'rank_math_description', sanitize_textarea_field( $data['description'] ) );
}
}
$this->update_content_ai_posts_count( count( $posts['meta'] ) );
$credits = ! empty( $posts['credits'] ) ? $posts['credits'] : [];
if ( ! empty( $credits['available'] ) ) {
$credits = $credits['available'] - $credits['taken'];
Helper::update_credits( $credits );
if ( $credits <= 0 ) {
$posts_processed = get_option( 'rank_math_content_ai_posts_processed' );
delete_option( 'rank_math_content_ai_posts' );
delete_option( 'rank_math_content_ai_posts_processed' );
Helper::add_notification(
// Translators: placeholder is the number of modified posts.
sprintf( esc_html__( 'SEO meta successfully updated in %d posts. The process was stopped as you have used all the credits on your site.', 'rank-math' ), $posts_processed ),
[
'type' => 'success',
'id' => 'rank_math_content_ai_posts',
'classes' => 'rank-math-notice',
]
);
wp_clear_scheduled_hook( 'wp_bulk_edit_seo_meta_cron' );
}
}
return false;
} catch ( Exception $error ) {
return true;
}
}
/**
* Get Posts to bulk update the data.
*
* @param array $data Data to process.
*
* @return array
*/
private function get_posts( $data ) {
$connect_data = Admin_Helper::get_registration_data();
$posts = array_values( $data['posts'] );
$action = $data['action'];
$language = $data['language'];
$data = [
'posts' => $posts,
'output' => $action,
'language' => $language,
'choices' => 1,
'username' => $connect_data['username'],
'api_key' => $connect_data['api_key'],
'site_url' => $connect_data['site_url'],
'is_taxonomy' => ! empty( $data['is_taxonomy'] ),
'plugin_version' => rank_math()->version,
];
return wp_remote_post(
CONTENT_AI_URL . '/ai/bulk_seo_meta',
[
'headers' => [
'content-type' => 'application/json',
],
'timeout' => 60000,
'body' => wp_json_encode( $data ),
]
);
}
/**
* Keep count of the Content AI posts that were processed.
*
* @param int $count Number of posts processed.
*
* @return void
*/
private function update_content_ai_posts_count( $count ) {
$content_ai_posts_count = get_option( 'rank_math_content_ai_posts_processed', 0 ) + $count;
update_option( 'rank_math_content_ai_posts_processed', $content_ai_posts_count, false );
}
}

View File

@@ -0,0 +1,374 @@
<?php
/**
* The Role Manager Module.
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Content_AI_Page
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\ContentAI;
use RankMath\ContentAI\Content_AI;
use RankMath\Helper;
use RankMath\Traits\Hooker;
use RankMath\Admin\Page;
use RankMath\Helpers\Param;
use WP_Block_Editor_Context;
defined( 'ABSPATH' ) || exit;
/**
* Content_AI_Page class.
*/
class Content_AI_Page {
use Hooker;
/**
* The Constructor.
*/
public function __construct() {
$this->action( 'rank_math/admin_bar/items', 'admin_bar_items', 11 );
$this->action( 'rank_math/admin/editor_scripts', 'enqueue' );
$this->action( 'init', 'init' );
$this->filter( 'wp_insert_post_data', 'remove_unused_generated_content' );
$this->filter( 'rank_math/database/tools', 'add_tools' );
$this->filter( 'rank_math/tools/content_ai_cancel_bulk_edit_process', 'cancel_bulk_edit_process' );
if ( Param::get( 'page' ) !== 'rank-math-content-ai-page' ) {
return;
}
$this->action( 'rank_math/before_help_link', 'add_credits_remaining' );
$this->action( 'admin_footer', 'content_editor_settings' );
add_filter( 'should_load_block_editor_scripts_and_styles', '__return_true' );
}
/**
* Init function.
*/
public function init() {
$this->register_post_type();
$this->register_admin_page();
Block_Command::get();
Bulk_Edit_SEO_Meta::get();
Event_Scheduler::get();
}
/**
* Add Credits remaining text before the help link in the admin header.
*/
public function add_credits_remaining() {
// Early bail if site is not connected or doesn't have a Content AI Plan.
if ( ! Helper::is_site_connected() || ! Helper::get_content_ai_plan() ) {
return;
}
$credits = Helper::get_credits();
?>
<div class="credits-remaining">
<?php echo esc_html__( 'Credits Remaining: ', 'rank-math' ); ?>
<strong><?php echo esc_html( $credits ); ?></strong>
</div>
<?php
}
/**
* Enqueue Content AI Page CSS.
*/
public function enqueue() {
wp_enqueue_style(
'rank-math-content-ai-page',
rank_math()->plugin_url() . 'includes/modules/content-ai/assets/css/content-ai-page.css',
[ 'rank-math-common' ],
rank_math()->version
);
wp_enqueue_script(
'rank-math-content-ai-page',
rank_math()->plugin_url() . 'includes/modules/content-ai/assets/js/content-ai-page.js',
[
'lodash',
'wp-dom-ready',
'rank-math-content-ai',
],
rank_math()->version,
true
);
wp_set_script_translations( 'rank-math-content-ai-page', 'rank-math' );
}
/**
* Register admin page.
*/
public function register_admin_page() {
$uri = untrailingslashit( plugin_dir_url( __FILE__ ) );
$new_label = '<span class="rank-math-new-label" style="color:#ed5e5e;font-size:10px;font-weight:normal;">' . esc_html__( 'New!', 'rank-math' ) . '</span>';
if ( 'rank-math-content-ai-page' === Param::get( 'page' ) ) {
Helper::add_json( 'isContentAIPage', true );
Content_AI::localized_data();
}
new Page(
'rank-math-content-ai-page',
esc_html__( 'Content AI', 'rank-math' ),
[
'position' => 4,
'parent' => 'rank-math',
// Translators: placeholder is the new label.
'menu_title' => sprintf( esc_html__( 'Content AI %s', 'rank-math' ), $new_label ),
'capability' => 'rank_math_content_ai',
'render' => dirname( __FILE__ ) . '/views/main.php',
'classes' => [ 'rank-math-page' ],
'assets' => [
'styles' => [
'wp-edit-post' => '',
'rank-math-common' => '',
'rank-math-cmb2' => '',
'wp-block-library' => '',
'rank-math-content-ai-page' => $uri . '/assets/css/content-ai-page.css',
],
'scripts' => [
'lodash' => '',
'wp-components' => '',
'wp-block-library' => '',
'wp-format-library' => '',
'wp-edit-post' => '',
'wp-blocks' => '',
'wp-element' => '',
'wp-editor' => '',
'rank-math-block-faq' => rank_math()->plugin_url() . 'assets/admin/js/blocks.js',
'rank-math-analyzer' => rank_math()->plugin_url() . 'assets/admin/js/analyzer.js',
'rank-math-content-ai' => rank_math()->plugin_url() . 'includes/modules/content-ai/assets/js/content-ai.js',
'rank-math-content-ai-page' => $uri . '/assets/js/content-ai-page.js',
],
],
]
);
}
/**
* Add admin bar item.
*
* @param Admin_Bar_Menu $menu Menu class instance.
*/
public function admin_bar_items( $menu ) {
$url = Helper::get_admin_url( 'content-ai-page' );
$menu->add_sub_menu(
'content-ai-page',
[
'title' => esc_html__( 'Content AI', 'rank-math' ),
'href' => $url,
'priority' => 50,
]
);
$items = [
'content-ai-tools' => [
'title' => esc_html__( 'AI Tools', 'rank-math' ),
'href' => $url . '#ai-tools',
'meta' => [ 'title' => esc_html__( 'Content AI Tools', 'rank-math' ) ],
],
'content-ai-editor' => [
'title' => esc_html__( 'Content Editor', 'rank-math' ),
'href' => $url . '#content-editor',
'meta' => [ 'title' => esc_html__( 'Content AI Editor', 'rank-math' ) ],
],
'content-ai-chat' => [
'title' => esc_html__( 'Chat', 'rank-math' ),
'href' => $url . '#chat',
'meta' => [ 'title' => esc_html__( 'Content AI Chat', 'rank-math' ) ],
],
'content-ai-history' => [
'title' => esc_html__( 'History', 'rank-math' ),
'href' => $url . '#history',
'meta' => [ 'title' => esc_html__( 'Content AI History', 'rank-math' ) ],
],
];
foreach ( $items as $id => $args ) {
$menu->add_sub_menu( $id, $args, 'content-ai-page' );
}
}
/**
* Add Content Editor Settings.
*/
public function content_editor_settings() {
$post = $this->get_content_editor_post();
$block_editor_context = new WP_Block_Editor_Context( [ 'post' => [] ] );
// Flag that we're loading the block editor.
$current_screen = get_current_screen();
$current_screen->is_block_editor( true );
$editor_settings = [
'availableTemplates' => [],
'disablePostFormats' => true,
'autosaveInterval' => 0,
'richEditingEnabled' => user_can_richedit(),
'supportsLayout' => function_exists( 'wp_theme_has_theme_json' ) ? wp_theme_has_theme_json() : false,
'supportsTemplateMode' => false,
'enableCustomFields' => false,
];
$editor_settings = get_block_editor_settings( $editor_settings, $block_editor_context );
/**
* Scripts
*/
wp_enqueue_media( [ 'post' => $post->ID ] );
?>
<div id="editor2" data-settings='<?php echo esc_attr( wp_json_encode( $editor_settings ) ); ?>' data-post-id="<?php echo esc_attr( $post->ID ); ?>"></div>
<?php
wp_set_script_translations( 'rank-math-content-ai', 'rank-math' );
wp_set_script_translations( 'rank-math-content-ai-page', 'rank-math' );
}
/**
* Remove unsed content generated from the Toolbar option of the Content AI.
*
* @param array $data An array of slashed, sanitized, and processed post data.
*/
public function remove_unused_generated_content( $data ) {
$blocks = parse_blocks( $data['post_content'] );
if ( empty( $blocks ) ) {
return $data;
}
$update = false;
foreach ( $blocks as $key => $block ) {
if ( 'rank-math/command' === $block['blockName'] ) {
unset( $blocks[ $key ] );
$update = true;
}
}
if ( $update ) {
$data['post_content'] = serialize_blocks( $blocks );
}
return $data;
}
/**
* Add database tools.
*
* @param array $tools Array of tools.
*
* @return array
*/
public function add_tools( $tools ) {
$posts = get_option( 'rank_math_content_ai_posts' );
// Early Bail if process is not running.
if ( empty( $posts ) ) {
return $tools;
}
$processed = get_option( 'rank_math_content_ai_posts_processed' );
$tools['content_ai_cancel_bulk_edit_process'] = [
'title' => esc_html__( 'Cancel Content AI Bulk Editing Process', 'rank-math' ),
'description' => sprintf(
// Translators: placeholders are the number of posts that were processed.
esc_html__( 'Terminate the ongoing Content AI Bulk Editing Process to halt any pending modifications and revert to the previous state. The bulk metadata has been generated for %1$d out of %1$d posts so far.', 'rank-math' ),
$processed,
count( $posts )
),
'button_text' => esc_html__( 'Terminate', 'rank-math' ),
];
return $tools;
}
/**
* Function to cancel the Bulk Edit process.
*/
public function cancel_bulk_edit_process() {
Bulk_Edit_SEO_Meta::get()->cancel();
Helper::remove_notification( 'rank_math_content_ai_posts_started' );
return __( 'Bulk Editing Process Successfully Cancelled', 'rank-math' );
}
/**
* Register Content AI post type to use the post in Content Editor.
*/
private function register_post_type() {
register_post_type(
'rm_content_editor',
[
'label' => 'RM Content Editor',
'public' => false,
'publicly_queryable' => false,
'show_ui' => false,
'show_in_rest' => true,
'has_archive' => false,
'show_in_menu' => false,
'show_in_nav_menus' => false,
'delete_with_user' => false,
'exclude_from_search' => false,
'capability_type' => 'page',
'map_meta_cap' => true,
'hierarchical' => false,
'rewrite' => false,
'query_var' => false,
'supports' => [ 'editor' ],
]
);
}
/**
* Get Content Editor post.
*
* @return int Post ID.
*/
private function get_content_editor_post() {
$posts = get_posts(
[
'post_type' => 'rm_content_editor',
'numberposts' => 1,
'post_status' => 'any',
]
);
if ( empty( $posts ) ) {
$post_id = wp_insert_post(
[
'post_type' => 'rm_content_editor',
'post_content' => '<!-- wp:rank-math/command /-->',
]
);
return get_post( $post_id );
}
$ai_post = current( $posts );
$content = $ai_post->post_content;
$blocks = parse_blocks( $content );
if ( ! empty( $blocks ) && count( $blocks ) < 2 && 'core/paragraph' === $blocks[0]['blockName'] ) {
$content = do_blocks( $ai_post->post_content );
$content = trim( preg_replace( '/<p[^>]*><\\/p[^>]*>/', '', $content ) );
}
if ( ! $content ) {
wp_update_post(
[
'ID' => $ai_post->ID,
'post_content' => '<!-- wp:rank-math/command /-->',
]
);
}
return $ai_post;
}
}

View File

@@ -0,0 +1,360 @@
<?php
/**
* The Content AI module.
*
* @since 1.0.71
* @package RankMath
* @subpackage RankMath
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\ContentAI;
use RankMath\KB;
use RankMath\Helper;
use RankMath\Helpers\Sitepress;
use RankMath\Helpers\Url;
use RankMath\Helpers\Arr;
use RankMath\Admin\Admin_Helper as AdminHelper;
use RankMath\CMB2;
use RankMath\Traits\Hooker;
use RankMath\Traits\Ajax;
defined( 'ABSPATH' ) || exit;
/**
* Content_AI class.
*/
class Content_AI {
use Hooker, Ajax;
/**
* Class constructor.
*/
public function __construct() {
$this->action( 'rest_api_init', 'init_rest_api' );
new Content_AI_Page();
new Bulk_Actions();
if ( ! Helper::has_cap( 'content_ai' ) ) {
return;
}
$this->filter( 'rank_math/analytics/post_data', 'add_contentai_data', 10, 2 );
$this->filter( 'rank_math/settings/general', 'add_settings' );
$this->action( 'rank_math/admin/editor_scripts', 'editor_scripts', 20 );
$this->filter( 'rank_math/metabox/post/values', 'add_metadata', 10, 2 );
$this->action( 'cmb2_admin_init', 'add_content_ai_metabox', 11 );
$this->action( 'rank_math/deregister_site', 'remove_credits_data' );
$this->filter( 'rank_math/elementor/dark_styles', 'add_dark_style' );
$this->filter( 'rank_math/status/rank_math_info', 'content_ai_info' );
$this->action( 'rank_math/connect/account_connected', 'refresh_content_ai_credits' );
}
/**
* Add dark style
*
* @param array $styles The dark mode styles.
*/
public function add_dark_style( $styles = [] ) {
$styles['rank-math-content-ai-dark'] = rank_math()->plugin_url() . 'includes/modules/content-ai/assets/css/content-ai-dark.css';
return $styles;
}
/**
* Add Content AI score in Single Page Site Analytics.
*
* @param array $data array.
* @param \WP_REST_Request $request post object.
* @return array $data sorted array.
*/
public function add_contentai_data( $data, \WP_REST_Request $request ) {
$post_id = $data['object_id'];
$content_ai_data = Helper::get_post_meta( 'contentai_score', $post_id );
$content_ai_score = ! empty( $content_ai_data ) ? round( array_sum( array_values( $content_ai_data ) ) / count( $content_ai_data ) ) : 0;
$data['contentAiScore'] = absint( $content_ai_score );
return $data;
}
/**
* Load the REST API endpoints.
*/
public function init_rest_api() {
$rest = new Rest();
$rest->register_routes();
}
/**
* Remove credits data when site is disconnected.
*/
public function remove_credits_data() {
delete_option( 'rank_math_ca_credits' );
}
/**
* Add module settings in the General Settings panel.
*
* @param array $tabs Array of option panel tabs.
* @return array
*/
public function add_settings( $tabs ) {
Arr::insert(
$tabs,
[
'content-ai' => [
'icon' => 'rm-icon rm-icon-content-ai',
'title' => esc_html__( 'Content AI', 'rank-math' ),
/* translators: Link to kb article */
'desc' => sprintf( esc_html__( 'Get sophisticated AI suggestions for related Keywords, Questions & Links to include in the SEO meta & Content Area. %s.', 'rank-math' ), '<a href="' . KB::get( 'content-ai-settings', 'Options Panel Content AI Tab' ) . '" target="_blank">' . esc_html__( 'Learn more', 'rank-math' ) . '</a>' ),
'file' => dirname( __FILE__ ) . '/views/options.php',
],
],
8
);
return $tabs;
}
/**
* Add link suggestion metabox.
*/
public function add_content_ai_metabox() {
if ( ! self::can_add_tab() || 'classic' !== Helper::get_current_editor() ) {
return;
}
$id = 'rank_math_metabox_content_ai';
$cmb = new_cmb2_box(
[
'id' => $id,
'title' => esc_html__( 'Content AI', 'rank-math' ),
'object_types' => array_keys( Helper::get_accessible_post_types() ),
'context' => 'side',
'priority' => 'high',
'mb_callback_args' => [ '__block_editor_compatible_meta_box' => false ],
]
);
CMB2::pre_init( $cmb );
// Move content AI metabox below the Publish box.
$this->reorder_content_ai_metabox( $id );
}
/**
* Enqueue assets for post/term/user editors.
*
* @return void
*/
public function editor_scripts() {
if ( ! self::can_add_tab() ) {
return;
}
$editor = Helper::get_current_editor();
if ( ! $editor ) {
return;
}
wp_register_style( 'rank-math-common', rank_math()->plugin_url() . 'assets/admin/css/common.css', null, rank_math()->version );
wp_enqueue_style(
'rank-math-content-ai',
rank_math()->plugin_url() . 'includes/modules/content-ai/assets/css/content-ai.css',
[ 'rank-math-common' ],
rank_math()->version
);
wp_enqueue_script(
'rank-math-content-ai',
rank_math()->plugin_url() . 'includes/modules/content-ai/assets/js/content-ai.js',
[ 'rank-math-editor' ],
rank_math()->version,
true
);
wp_set_script_translations( 'rank-math-content-ai', 'rank-math' );
$this->localized_data();
}
/**
* Add meta data to use in gutenberg.
*
* @param array $values Aray of tabs.
* @param Screen $screen Sceen object.
*
* @return array
*/
public function add_metadata( $values, $screen ) {
$countries = [];
foreach ( Helper::choices_contentai_countries() as $value => $label ) {
$countries[] = [
'value' => $value,
'label' => $label,
];
}
$values['contentAiCountry'] = Helper::get_settings( 'general.content_ai_country', 'all' );
$values['countries'] = $countries;
$values['ca_credits'] = Helper::get_credits();
$values['ca_keyword'] = '';
$values['ca_viewed'] = true;
$content_ai_viewed = get_option( 'rank_math_content_ai_viewed', false );
if ( ! $content_ai_viewed ) {
$values['ca_viewed'] = false;
update_option( 'rank_math_content_ai_viewed', true );
}
$keyword = $screen->get_meta( $screen->get_object_type(), $screen->get_object_id(), 'rank_math_ca_keyword' );
if ( empty( $keyword ) ) {
return $values;
}
$data = get_option( 'rank_math_ca_data' );
$country = empty( $keyword['country'] ) ? '' : $keyword['country'];
if (
! empty( $data[ $country ] ) &&
! empty( $data[ $country ][ mb_strtolower( $keyword['keyword'] ) ] )
) {
$values['ca_data'] = $data[ $country ][ mb_strtolower( $keyword['keyword'] ) ];
}
$values['ca_keyword'] = $keyword;
$content_ai_data = $screen->get_meta( $screen->get_object_type(), $screen->get_object_id(), 'rank_math_contentai_score' );
$content_ai_score = ! empty( $content_ai_data ) && is_array( $content_ai_data ) ? round( array_sum( array_values( $content_ai_data ) ) / count( $content_ai_data ) ) : 0;
$values['contentAiScore'] = absint( $content_ai_score );
return $values;
}
/**
* Whether to load Content AI data.
*/
public static function can_add_tab() {
return in_array( Helper::get_post_type(), (array) Helper::get_settings( 'general.content_ai_post_types' ), true );
}
/**
* Localized data to use on the Content AI page.
*/
public static function localized_data() {
Helper::add_json( 'ca_audience', (array) Helper::get_settings( 'general.content_ai_audience', 'General Audience' ) );
Helper::add_json( 'ca_tone', (array) Helper::get_settings( 'general.content_ai_tone', 'Formal' ) );
Helper::add_json( 'ca_language', Helper::get_settings( 'general.content_ai_language', Helper::content_ai_default_language() ) );
Helper::add_json( 'contentAIHistory', Helper::get_outputs() );
Helper::add_json( 'contentAIChats', Helper::get_chats() );
Helper::add_json( 'contentAIRecentPrompts', Helper::get_recent_prompts() );
Helper::add_json( 'contentAIPrompts', Helper::get_prompts() );
Helper::add_json( 'isUserRegistered', Helper::is_site_connected() );
Helper::add_json( 'connectSiteUrl', AdminHelper::get_activate_url( Url::get_current_url() ) );
Helper::add_json( 'contentAICredits', Helper::get_content_ai_credits() );
Helper::add_json( 'contentAIPlan', Helper::get_content_ai_plan() );
Helper::add_json( 'contentAIErrors', Helper::get_content_ai_errors() );
Helper::add_json( 'connectData', AdminHelper::get_registration_data() );
Helper::add_json( 'registerWriteShortcut', version_compare( get_bloginfo( 'version' ), '6.2', '>=' ) );
Helper::add_json( 'contentAiMigrating', get_site_transient( 'rank_math_content_ai_migrating_user' ) );
Helper::add_json( 'contentAiUrl', CONTENT_AI_URL . '/ai/' );
$refresh_date = Helper::get_content_ai_refresh_date();
Helper::add_json( 'contentAIRefreshDate', $refresh_date ? wp_date( 'Y-m-d g:ia', $refresh_date ) : '' );
}
/**
* Add Content AI details in System Info
*
* @param array $rankmath Array of rankmath.
*/
public function content_ai_info( $rankmath ) {
$refresh_date = Helper::get_content_ai_refresh_date();
$content_ai = [
'ca_plan' => [
'label' => esc_html__( 'Content AI Plan', 'rank-math' ),
'value' => \ucwords( Helper::get_content_ai_plan() ),
],
'ca_credits' => [
'label' => esc_html__( 'Content AI Credits', 'rank-math' ),
'value' => Helper::get_content_ai_credits(),
],
'ca_refresh_date' => [
'label' => esc_html__( 'Content AI Refresh Date', 'rank-math' ),
'value' => $refresh_date ? wp_date( 'Y-m-d g:i a', $refresh_date ) : '',
],
];
array_splice( $rankmath['fields'], 3, 0, $content_ai );
return $rankmath;
}
/**
* Refresh Content AI credits when account is connected.
*
* @param array $data Authentication data.
*
* @return void
*/
public function refresh_content_ai_credits( $data ) {
Helper::get_content_ai_credits( true );
}
/**
* Reorder the Content AI metabox in Classic editor.
*
* @param string $id Metabox ID.
* @return void
*/
private function reorder_content_ai_metabox( $id ) {
$post_type = Helper::get_post_type();
if ( ! $post_type ) {
return;
}
$user = wp_get_current_user();
$order = (array) get_user_option( 'meta-box-order_' . $post_type, $user->ID );
if ( ! empty( $order['normal'] ) && false !== strpos( $order['normal'], $id ) ) {
return;
}
$order['side'] = ! isset( $order['side'] ) ? '' : $order['side'];
if ( false !== strpos( $order['side'], $id ) ) {
return;
}
if ( false === strpos( $order['side'], 'submitdiv' ) ) {
$order['side'] = 'submitdiv,' . $order['side'];
}
if ( ',' === substr( $order['side'], -1 ) ) {
$order['side'] = substr( $order['side'], 0, -1 );
}
$current_order = [];
$current_order = explode( ',', $order['side'] );
$key = array_search( 'submitdiv', $current_order, true );
if ( false === $key ) {
return;
}
$new_order = array_merge(
array_slice( $current_order, 0, $key + 1 ),
[ $id ]
);
if ( count( $current_order ) > $key ) {
$new_order = array_merge(
$new_order,
array_slice( $current_order, $key + 1 )
);
}
$order['side'] = implode( ',', array_unique( $new_order ) );
update_user_option( $user->ID, 'meta-box-order_' . $post_type, $order, true );
}
}

View File

@@ -0,0 +1,117 @@
<?php
/**
* The Event Scheduler for Content AI to update the prompts and credits data.
*
* @since 1.0.123
* @package RankMath
* @subpackage RankMath\ContentAI
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\ContentAI;
use WP_Block_Type_Registry;
use RankMath\Helper;
use RankMath\Admin\Admin_Helper;
use RankMath\Traits\Hooker;
defined( 'ABSPATH' ) || exit;
/**
* Event_Scheduler class.
*/
class Event_Scheduler {
use Hooker;
/**
* The single instance of the class.
*
* @var Event_Scheduler
*/
protected static $instance = null;
/**
* Retrieve main Block_Command instance.
*
* Ensure only one instance is loaded or can be loaded.
*
* @return Event_Scheduler
*/
public static function get() {
if ( is_null( self::$instance ) && ! ( self::$instance instanceof Event_Scheduler ) ) {
self::$instance = new Event_Scheduler();
}
return self::$instance;
}
/**
* The Constructor.
*/
public function __construct() {
if ( ! Helper::is_site_connected() ) {
return;
}
$credits = get_option( 'rank_math_ca_credits' );
if ( ! empty( $credits['refresh_date'] ) ) {
wp_schedule_single_event( absint( $credits['refresh_date'] ) + 60, 'rank_math/content-ai/update_plan' );
}
$this->action( 'rank_math/content-ai/update_prompts', 'update_prompts_data' );
$this->action( 'rank_math/content-ai/update_plan', 'update_content_ai_plan' );
$this->action( 'admin_footer', 'update_prompts_on_new_site' );
}
/**
* Fetch and update the prompts data daily.
*
* @return void
*/
public function update_prompts_data() {
if ( ! Helper::get_credits() || ! Helper::get_content_ai_plan() ) {
return;
}
$prompt_data = [];
$data = wp_remote_get( CONTENT_AI_URL . '/ai/default_prompts' );
$response_code = wp_remote_retrieve_response_code( $data );
if ( is_wp_error( $data ) || 200 !== $response_code ) {
return;
}
update_option( 'rank_math_prompts_updated', true );
$data = wp_remote_retrieve_body( $data );
$data = json_decode( $data, true );
if ( empty( $data ) ) {
return;
}
Helper::save_default_prompts( $data );
}
/**
* Run the credits endpoint to update the plan on reset date.
*
* @return void
*/
public function update_content_ai_plan() {
Helper::get_content_ai_credits( true );
wp_clear_scheduled_hook( 'rank_math/content-ai/update_plan' );
}
/**
* Function to update Prompts data on new sites.
*
* @return void
*/
public function update_prompts_on_new_site() {
$prompts = Helper::get_prompts();
if ( get_option( 'rank_math_prompts_updated' ) || ! empty( $prompts ) ) {
return;
}
do_action( 'rank_math/content-ai/update_prompts' );
}
}

View File

@@ -0,0 +1,545 @@
<?php
/**
* The Global functionality of the plugin.
*
* Defines the functionality loaded on admin.
*
* @since 1.0.71
* @package RankMath
* @subpackage RankMath\Rest
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\ContentAI;
use WP_Error;
use WP_REST_Server;
use WP_REST_Request;
use WP_REST_Controller;
use RankMath\Helper;
use RankMath\Admin\Admin_Helper;
defined( 'ABSPATH' ) || exit;
/**
* Rest class.
*/
class Rest extends WP_REST_Controller {
/**
* Registered data.
*
* @var array|false
*/
private $registered;
/**
* Constructor.
*/
public function __construct() {
$this->namespace = \RankMath\Rest\Rest_Helper::BASE . '/ca';
$this->registered = Admin_Helper::get_registration_data();
}
/**
* Registers the routes for the objects of the controller.
*/
public function register_routes() {
register_rest_route(
$this->namespace,
'/researchKeyword',
[
'methods' => WP_REST_Server::CREATABLE,
'callback' => [ $this, 'research_keyword' ],
'permission_callback' => [ $this, 'has_permission' ],
]
);
register_rest_route(
$this->namespace,
'/getCredits',
[
'methods' => WP_REST_Server::CREATABLE,
'callback' => [ $this, 'get_credits' ],
'permission_callback' => [ $this, 'has_permission' ],
]
);
register_rest_route(
$this->namespace,
'/createPost',
[
'methods' => WP_REST_Server::CREATABLE,
'callback' => [ $this, 'create_post' ],
'permission_callback' => [ $this, 'has_permission' ],
]
);
register_rest_route(
$this->namespace,
'/saveOutput',
[
'methods' => WP_REST_Server::CREATABLE,
'callback' => [ $this, 'save_output' ],
'permission_callback' => [ $this, 'has_permission' ],
]
);
register_rest_route(
$this->namespace,
'/deleteOutput',
[
'methods' => WP_REST_Server::CREATABLE,
'callback' => [ $this, 'delete_output' ],
'permission_callback' => [ $this, 'has_permission' ],
]
);
register_rest_route(
$this->namespace,
'/updateRecentPrompt',
[
'methods' => WP_REST_Server::CREATABLE,
'callback' => [ $this, 'update_recent_prompt' ],
'permission_callback' => [ $this, 'has_permission' ],
]
);
register_rest_route(
$this->namespace,
'/updatePrompt',
[
'methods' => WP_REST_Server::CREATABLE,
'callback' => [ $this, 'update_prompt' ],
'permission_callback' => [ $this, 'has_permission' ],
]
);
register_rest_route(
$this->namespace,
'/savePrompts',
[
'methods' => WP_REST_Server::CREATABLE,
'callback' => [ $this, 'save_prompts' ],
'permission_callback' => [ $this, 'has_permission' ],
]
);
register_rest_route(
$this->namespace,
'/pingContentAI',
[
'methods' => WP_REST_Server::READABLE,
'callback' => [ $this, 'ping_content_ai' ],
'permission_callback' => [ $this, 'has_ping_permission' ],
]
);
register_rest_route(
$this->namespace,
'/migrateuser',
[
'methods' => WP_REST_Server::READABLE,
'callback' => [ $this, 'migrate_user' ],
'permission_callback' => [ $this, 'has_permission' ],
]
);
}
/**
* Check API key in request.
*
* @param WP_REST_Request $request Full details about the request.
* @return bool Whether the API key matches or not.
*/
public function has_ping_permission( WP_REST_Request $request ) {
if ( empty( $this->registered ) ) {
return false;
}
return $request->get_param( 'apiKey' ) === $this->registered['api_key'] &&
$request->get_param( 'username' ) === $this->registered['username'];
}
/**
* Determines if the current user can manage analytics.
*
* @return true
*/
public function has_permission() {
if ( ! Helper::has_cap( 'content_ai' ) || empty( $this->registered ) ) {
return new WP_Error(
'rest_cannot_access',
__( 'Sorry, only authenticated users can research the keyword.', 'rank-math' ),
[ 'status' => rest_authorization_required_code() ]
);
}
return true;
}
/**
* Get Content AI Credits.
*
* @param WP_REST_Request $request Full details about the request.
*
* @return int Credits.
*/
public function get_credits( WP_REST_Request $request ) {
$credits = Helper::get_content_ai_credits( true, true );
if ( ! empty( $credits['error'] ) ) {
$error = $credits['error'];
$error_texts = Helper::get_content_ai_errors();
return [
'error' => ! empty( $error_texts[ $error ] ) ? wp_specialchars_decode( $error_texts[ $error ], ENT_QUOTES ) : $error,
'credits' => isset( $credits['credits'] ) ? $credits['credits'] : '',
];
}
return $credits;
}
/**
* Research a keyword.
*
* @param WP_REST_Request $request Full details about the request.
*
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
*/
public function research_keyword( WP_REST_Request $request ) {
$object_id = $request->get_param( 'objectID' );
$country = $request->get_param( 'country' );
$keyword = mb_strtolower( $request->get_param( 'keyword' ) );
$force_update = $request->get_param( 'force_update' );
$keyword_data = get_option( 'rank_math_ca_data' );
if ( ! in_array( get_post_type( $object_id ), (array) Helper::get_settings( 'general.content_ai_post_types' ), true ) ) {
return [
'data' => esc_html__( 'Content AI is not enabled on this Post type.', 'rank-math' ),
];
}
if ( ! apply_filters( 'rank_math/content_ai/call_api', true ) ) {
return [
'data' => 'show_dummy_data',
];
}
if (
! $force_update &&
! empty( $keyword_data ) &&
! empty( $keyword_data[ $country ] ) &&
! empty( $keyword_data[ $country ][ $keyword ] )
) {
update_post_meta(
$object_id,
'rank_math_ca_keyword',
[
'keyword' => $keyword,
'country' => $country,
]
);
return [
'data' => $keyword_data[ $country ][ $keyword ],
'keyword' => $keyword,
];
}
$data = $this->get_researched_data( $keyword, $country, $force_update );
if ( ! empty( $data['error'] ) ) {
return $this->get_errored_data( $data['error'] );
}
$credits = ! empty( $data['credits'] ) ? $data['credits'] : 0;
if ( ! empty( $credits ) ) {
$credits = $credits['available'] - $credits['taken'];
}
$data = $data['data']['details'];
$this->get_recommendations( $data );
update_post_meta(
$object_id,
'rank_math_ca_keyword',
[
'keyword' => $keyword,
'country' => $country,
]
);
$keyword_data[ $country ][ $keyword ] = $data;
update_option( 'rank_math_ca_data', $keyword_data, false );
Helper::update_credits( $credits );
return [
'data' => $keyword_data[ $country ][ $keyword ],
'credits' => $credits,
'keyword' => $keyword,
];
}
/**
* Create a new Post from Content AI Page.
*
* @param WP_REST_Request $request Full details about the request.
*
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
*/
public function create_post( WP_REST_Request $request ) {
$content = $request->get_param( 'content' );
$title = 'Content AI Post';
$blocks = parse_blocks( $content );
$current_block = ! empty( $blocks ) ? current( $blocks ) : '';
if (
! empty( $current_block ) &&
$current_block['blockName'] === 'core/heading' &&
$current_block['attrs']['level'] === 1
) {
$title = wp_strip_all_tags( $current_block['innerHTML'] );
}
$post_id = wp_insert_post(
[
'post_title' => $title,
'post_content' => $content,
]
);
return wp_specialchars_decode( add_query_arg( 'tab', 'content-ai', get_edit_post_link( $post_id ) ) );
}
/**
* Save the API output.
*
* @param WP_REST_Request $request Full details about the request.
*
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
*/
public function save_output( WP_REST_Request $request ) {
$outputs = $request->get_param( 'outputs' );
$endpoint = $request->get_param( 'endpoint' );
$is_chat = $request->get_param( 'isChat' );
$attributes = $request->get_param( 'attributes' );
$credits_data = $request->get_param( 'credits' );
if ( ! empty( $credits_data ) ) {
$credits = ! empty( $credits_data['credits'] ) ? $credits_data['credits'] : [];
$data = [
'credits' => ! empty( $credits['available'] ) ? $credits['available'] - $credits['taken'] : 0,
'plan' => ! empty( $credits_data['plan'] ) ? $credits_data['plan'] : '',
'refresh_date' => ! empty( $credits_data['refreshDate'] ) ? $credits_data['refreshDate'] : '',
];
Helper::update_credits( $data );
}
if ( $is_chat ) {
Helper::update_chats( current( $outputs ), end( $attributes['messages'] ), $attributes['session'], $attributes['isNew'], $attributes['regenerate'] );
return true;
}
return Helper::update_outputs( $endpoint, $outputs );
}
/**
* Delete the API output.
*
* @param WP_REST_Request $request Full details about the request.
*
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
*/
public function delete_output( WP_REST_Request $request ) {
$is_chat = $request->get_param( 'isChat' );
if ( $is_chat ) {
return Helper::delete_chats( $request->get_param( 'index' ) );
}
return Helper::delete_outputs();
}
/**
* Update the Prompts.
*
* @param WP_REST_Request $request Full details about the request.
*
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
*/
public function update_prompt( WP_REST_Request $request ) {
$prompt = $request->get_param( 'prompt' );
if ( is_string( $prompt ) ) {
return Helper::delete_prompt( $prompt );
}
return Helper::update_prompts( $prompt );
}
/**
* Save the Prompts.
*
* @param WP_REST_Request $request Full details about the request.
*
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
*/
public function save_prompts( WP_REST_Request $request ) {
$prompts = $request->get_param( 'prompts' );
if ( empty( $prompts ) ) {
return false;
}
return Helper::save_default_prompts( $prompts );
}
/**
* Update the Recent Prompts.
*
* @param WP_REST_Request $request Full details about the request.
*
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
*/
public function update_recent_prompt( WP_REST_Request $request ) {
$prompt = $request->get_param( 'prompt' );
return Helper::update_recent_prompts( $prompt );
}
/**
* Endpoing to update the AI plan and credits.
*
* @param WP_REST_Request $request Full details about the request.
*
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
*/
public function ping_content_ai( WP_REST_Request $request ) {
$credits = ! empty( $data['credits'] ) ? json_decode( $data['credits'], true ) : [];
$data = [
'credits' => ! empty( $credits['available'] ) ? $credits['available'] - $credits['taken'] : 0,
'plan' => $request->get_param( 'plan' ),
'refresh_date' => $request->get_param( 'refreshDate' ),
];
Helper::update_credits( $data );
return true;
}
/**
* Migrate user to nest js server.
*
* @param WP_REST_Request $request Full details about the request.
*
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
*/
public function migrate_user( WP_REST_Request $request ) {
return Helper::migrate_user_to_nest_js();
}
/**
* Get data from the API.
*
* @param string $keyword Researched keyword.
* @param string $country Researched country.
* @param bool $force_update Whether to force update the researched data.
*
* @return array
*/
private function get_researched_data( $keyword, $country, $force_update = false ) {
$args = [
'username' => rawurlencode( $this->registered['username'] ),
'api_key' => rawurlencode( $this->registered['api_key'] ),
'keyword' => rawurlencode( $keyword ),
'site_url' => rawurlencode( Helper::get_home_url() ),
'new_api' => 1,
];
if ( 'all' !== $country ) {
$args['locale'] = rawurlencode( $country );
}
if ( $force_update ) {
$args['force_refresh'] = 1;
}
$url = add_query_arg(
$args,
CONTENT_AI_URL . '/ai/research'
);
$data = wp_remote_get(
$url,
[
'timeout' => 60,
]
);
$response_code = wp_remote_retrieve_response_code( $data );
if ( 200 !== $response_code ) {
return [
'error' => 410 !== $response_code ? $data['response']['message'] : wp_kses_post(
sprintf(
// Translators: link to the update page.
__( 'There is a new version of Content AI available! %s the Rank Math SEO plugin to use this feature.', 'rank-math' ),
'<a href="' . esc_url( self_admin_url( 'update-core.php' ) ) . '">' . __( 'Please update', 'rank-math' ) . '</a>'
)
),
];
}
$data = wp_remote_retrieve_body( $data );
$data = json_decode( $data, true );
if ( empty( $data['error'] ) && empty( $data['data']['details'] ) ) {
return [
'error' => esc_html__( 'No data found for the researched keyword.', 'rank-math' ),
];
}
return $data;
}
/**
* Get errored data.
*
* @param array $error Error data received from the API.
*
* @return array
*/
private function get_errored_data( $error ) {
if ( empty( $error['code'] ) ) {
return [
'data' => $error,
];
}
if ( 'invalid_domain' === $error['code'] ) {
return [
'data' => esc_html__( 'This feature is not available on the localhost.', 'rank-math' ),
];
}
if ( 'domain_limit_reached' === $error['code'] ) {
return [
'data' => esc_html__( 'You have used all the free credits which are allowed to this domain.', 'rank-math' ),
];
}
return [
'data' => '',
'credits' => $error['code'],
];
}
/**
* Get the Recommendations data.
*
* @param array $data Researched data.
*/
private function get_recommendations( &$data ) {
foreach ( $data['recommendations'] as $key => $value ) {
if ( ! is_array( $value ) ) {
continue;
}
$data['recommendations'][ $key ]['total'] = array_sum( $value );
}
}
}

View File

@@ -0,0 +1,17 @@
<?php
/**
* Content AI Page main view.
*
* @package RankMath
* @subpackage RankMath\Role_Manager
*/
defined( 'ABSPATH' ) || exit;
// Header.
rank_math()->admin->display_admin_header();
?>
<div class="wrap rank-math-wrap" id="rank-math-content-ai-page">
<div class="rank-math-box container">
</div>
</div>

View File

@@ -0,0 +1,224 @@
<?php
/**
* Content AI general settings.
*
* @package RankMath
* @subpackage RankMath\ContentAI
*/
use RankMath\Helper;
use RankMath\KB;
use RankMath\Admin\Admin_Helper;
defined( 'ABSPATH' ) || exit;
if ( ! Helper::is_site_connected() ) {
$cmb->add_field(
[
'id' => 'rank_math_content_ai_settings',
'type' => 'raw',
'content' => '<div id="setting-panel-content-ai" class="rank-math-tab rank-math-options-panel-content exclude">
<div class="wp-core-ui rank-math-ui connect-wrap">
<a href="' . Admin_Helper::get_activate_url( admin_url( 'admin.php??page=rank-math-options-general#setting-panel-content-ai' ) ) . '" class="button button-primary button-connect button-animated" name="rank_math_activate">'
. esc_html__( 'Connect Your Rank Math Account', 'rank-math' )
. '</a>
</div>
<div id="rank-math-pro-cta" class="content-ai-settings">
<div class="rank-math-cta-box width-100 no-shadow no-padding no-border">
<h3>' . esc_html__( 'Benefits of Connecting Rank Math Account', 'rank-math' ) . '</h3>
<ul>
<li>' . esc_html__( 'Gain Access to 40+ Advanced AI Tools.', 'rank-math' ) . '</li>
<li>' . esc_html__( 'Experience the Revolutionary AI-Powered Content Editor.', 'rank-math' ) . '</li>
<li>' . esc_html__( 'Engage with RankBot, Our AI Chatbot, For SEO Advice.', 'rank-math' ) . '</li>
<li>' . esc_html__( 'Escape the Writer\'s Block Using AI to Write Inside WordPress.', 'rank-math' ) . '</li>
</ul>
</div>
</div>
</div>',
]
);
return;
}
$cmb->add_field(
[
'id' => 'content_ai_country',
'type' => 'select',
'name' => esc_html__( 'Default Country', 'rank-math' ),
'desc' => esc_html__( 'Content AI tailors keyword research to the target country for highly relevant suggestions. You can override this in individual posts/pages/CPTs.', 'rank-math' ),
'options' => Helper::choices_contentai_countries(),
'default' => 'all',
]
);
$cmb->add_field(
[
'id' => 'content_ai_tone',
'type' => 'select',
'name' => esc_html__( 'Default Tone', 'rank-math' ),
'desc' => esc_html__( 'This feature enables the default primary tone or writing style that characterizes your content. You can override this in individual tools.', 'rank-math' ),
'default' => 'Formal',
'attributes' => ( 'data-s2' ),
'options' => [
'Analytical' => esc_html__( 'Analytical', 'rank-math' ),
'Argumentative' => esc_html__( 'Argumentative', 'rank-math' ),
'Casual' => esc_html__( 'Casual', 'rank-math' ),
'Conversational' => esc_html__( 'Conversational', 'rank-math' ),
'Creative' => esc_html__( 'Creative', 'rank-math' ),
'Descriptive' => esc_html__( 'Descriptive', 'rank-math' ),
'Emotional' => esc_html__( 'Emotional', 'rank-math' ),
'Empathetic' => esc_html__( 'Empathetic', 'rank-math' ),
'Expository' => esc_html__( 'Expository', 'rank-math' ),
'Factual' => esc_html__( 'Factual', 'rank-math' ),
'Formal' => esc_html__( 'Formal', 'rank-math' ),
'Friendly' => esc_html__( 'Friendly', 'rank-math' ),
'Humorous' => esc_html__( 'Humorous', 'rank-math' ),
'Informal' => esc_html__( 'Informal', 'rank-math' ),
'Journalese' => esc_html__( 'Journalese', 'rank-math' ),
'Narrative' => esc_html__( 'Narrative', 'rank-math' ),
'Objective' => esc_html__( 'Objective', 'rank-math' ),
'Opinionated' => esc_html__( 'Opinionated', 'rank-math' ),
'Persuasive' => esc_html__( 'Persuasive', 'rank-math' ),
'Poetic' => esc_html__( 'Poetic', 'rank-math' ),
'Satirical' => esc_html__( 'Satirical', 'rank-math' ),
'Story-telling' => esc_html__( 'Story-telling', 'rank-math' ),
'Subjective' => esc_html__( 'Subjective', 'rank-math' ),
'Technical' => esc_html__( 'Technical', 'rank-math' ),
],
],
);
$cmb->add_field(
[
'id' => 'content_ai_audience',
'type' => 'select',
'name' => esc_html__( 'Default Audience', 'rank-math' ),
'desc' => esc_html__( 'This option lets you set the default audience that usually reads your content. You can override this in individual tools.', 'rank-math' ),
'default' => 'General Audience',
'attributes' => ( 'data-s2' ),
'options' => [
'Activists' => esc_html__( 'Activists', 'rank-math' ),
'Artists' => esc_html__( 'Artists', 'rank-math' ),
'Authors' => esc_html__( 'Authors', 'rank-math' ),
'Bargain Hunters' => esc_html__( 'Bargain Hunters', 'rank-math' ),
'Bloggers' => esc_html__( 'Bloggers', 'rank-math' ),
'Business Owners' => esc_html__( 'Business Owners', 'rank-math' ),
'Collectors' => esc_html__( 'Collectors', 'rank-math' ),
'Cooks' => esc_html__( 'Cooks', 'rank-math' ),
'Crafters' => esc_html__( 'Crafters', 'rank-math' ),
'Dancers' => esc_html__( 'Dancers', 'rank-math' ),
'DIYers' => esc_html__( 'DIYers', 'rank-math' ),
'Designers' => esc_html__( 'Designers', 'rank-math' ),
'Educators' => esc_html__( 'Educators', 'rank-math' ),
'Engineers' => esc_html__( 'Engineers', 'rank-math' ),
'Entrepreneurs' => esc_html__( 'Entrepreneurs', 'rank-math' ),
'Environmentalists' => esc_html__( 'Environmentalists', 'rank-math' ),
'Fashionistas' => esc_html__( 'Fashionistas', 'rank-math' ),
'Fitness Enthusiasts' => esc_html__( 'Fitness Enthusiasts', 'rank-math' ),
'Foodies' => esc_html__( 'Foodies', 'rank-math' ),
'Gaming Enthusiasts' => esc_html__( 'Gaming Enthusiasts', 'rank-math' ),
'Gardeners' => esc_html__( 'Gardeners', 'rank-math' ),
'General Audience' => esc_html__( 'General Audience', 'rank-math' ),
'Health Enthusiasts' => esc_html__( 'Health Enthusiasts', 'rank-math' ),
'Healthcare Professionals' => esc_html__( 'Healthcare Professionals', 'rank-math' ),
'Indoor Hobbyists' => esc_html__( 'Indoor Hobbyists', 'rank-math' ),
'Investors' => esc_html__( 'Investors', 'rank-math' ),
'Job Seekers' => esc_html__( 'Job Seekers', 'rank-math' ),
'Movie Buffs' => esc_html__( 'Movie Buffs', 'rank-math' ),
'Musicians' => esc_html__( 'Musicians', 'rank-math' ),
'Outdoor Enthusiasts' => esc_html__( 'Outdoor Enthusiasts', 'rank-math' ),
'Parents' => esc_html__( 'Parents', 'rank-math' ),
'Pet Owners' => esc_html__( 'Pet Owners', 'rank-math' ),
'Photographers' => esc_html__( 'Photographers', 'rank-math' ),
'Podcast Listeners' => esc_html__( 'Podcast Listeners', 'rank-math' ),
'Professionals' => esc_html__( 'Professionals', 'rank-math' ),
'Retirees' => esc_html__( 'Retirees', 'rank-math' ),
'Russian' => esc_html__( 'Russian', 'rank-math' ),
'Seniors' => esc_html__( 'Seniors', 'rank-math' ),
'Social Media Users' => esc_html__( 'Social Media Users', 'rank-math' ),
'Sports Fans' => esc_html__( 'Sports Fans', 'rank-math' ),
'Students' => esc_html__( 'Students', 'rank-math' ),
'Tech Enthusiasts' => esc_html__( 'Tech Enthusiasts', 'rank-math' ),
'Travelers' => esc_html__( 'Travelers', 'rank-math' ),
'TV Enthusiasts' => esc_html__( 'TV Enthusiasts', 'rank-math' ),
'Video Creators' => esc_html__( 'Video Creators', 'rank-math' ),
'Writers' => esc_html__( 'Writers', 'rank-math' ),
],
],
);
$cmb->add_field(
[
'id' => 'content_ai_language',
'type' => 'select',
'name' => esc_html__( 'Default Language', 'rank-math' ),
'desc' => esc_html__( 'This option lets you set the default language for content generated using Content AI. You can override this in individual tools.', 'rank-math' ),
'default' => Helper::content_ai_default_language(),
'attributes' => ( 'data-s2' ),
'options' => [
'US English' => esc_html__( 'US English', 'rank-math' ),
'UK English' => esc_html__( 'UK English', 'rank-math' ),
'Bulgarian' => esc_html__( 'Bulgarian', 'rank-math' ),
'Chinese' => esc_html__( 'Chinese', 'rank-math' ),
'Czech' => esc_html__( 'Czech', 'rank-math' ),
'Danish' => esc_html__( 'Danish', 'rank-math' ),
'Dutch' => esc_html__( 'Dutch', 'rank-math' ),
'Estonian' => esc_html__( 'Estonian', 'rank-math' ),
'Finnish' => esc_html__( 'Finnish', 'rank-math' ),
'French' => esc_html__( 'French', 'rank-math' ),
'German' => esc_html__( 'German', 'rank-math' ),
'Greek' => esc_html__( 'Greek', 'rank-math' ),
'Hebrew' => esc_html__( 'Hebrew', 'rank-math' ),
'Hungarian' => esc_html__( 'Hungarian', 'rank-math' ),
'Indonesian' => esc_html__( 'Indonesian', 'rank-math' ),
'Italian' => esc_html__( 'Italian', 'rank-math' ),
'Japanese' => esc_html__( 'Japanese', 'rank-math' ),
'Korean' => esc_html__( 'Korean', 'rank-math' ),
'Latvian' => esc_html__( 'Latvian', 'rank-math' ),
'Lithuanian' => esc_html__( 'Lithuanian', 'rank-math' ),
'Norwegian' => esc_html__( 'Norwegian', 'rank-math' ),
'Polish' => esc_html__( 'Polish', 'rank-math' ),
'Portuguese' => esc_html__( 'Portuguese', 'rank-math' ),
'Romanian' => esc_html__( 'Romanian', 'rank-math' ),
'Russian' => esc_html__( 'Russian', 'rank-math' ),
'Slovak' => esc_html__( 'Slovak', 'rank-math' ),
'Slovenian' => esc_html__( 'Slovenian', 'rank-math' ),
'Spanish' => esc_html__( 'Spanish', 'rank-math' ),
'Swedish' => esc_html__( 'Swedish', 'rank-math' ),
],
],
);
$post_types = Helper::choices_post_types();
if ( isset( $post_types['attachment'] ) ) {
unset( $post_types['attachment'] );
}
$cmb->add_field(
[
'id' => 'content_ai_post_types',
'type' => 'multicheck_inline',
'name' => esc_html__( 'Select Post Type', 'rank-math' ),
'desc' => esc_html__( 'Choose the type of posts/pages/CPTs where you want to use Content AI.', 'rank-math' ),
'options' => $post_types,
'default' => array_keys( $post_types ),
]
);
$credits = Helper::get_credits();
if ( Helper::is_site_connected() && false !== $credits ) {
$update_credits = '<a href="#" class="rank-math-tooltip update-credit">
<i class="dashicons dashicons-image-rotate"></i>
<span>' . esc_html__( 'Click to refresh the available credits.', 'rank-math' ) . '</span>
</a>';
$refresh_date = Helper::get_content_ai_refresh_date();
$cmb->add_field(
[
'id' => 'content_ai_credits',
'type' => 'raw',
/* translators: 1. Credits left 2. Buy more credits link */
'content' => '<div class="cmb-row buy-more-credits rank-math-exclude-from-search">' . $update_credits . sprintf( esc_html__( '%1$s credits left this month. Credits will renew on %2$s or you can upgrade to get more credits %3$s.', 'rank-math' ), '<strong>' . $credits . '</strong>', wp_date( 'Y-m-d g:i a', $refresh_date ), '<a href="' . KB::get( 'content-ai-pricing-tables', 'Buy CAI Credits Options Panel' ) . '" target="_blank">' . esc_html__( 'here', 'rank-math' ) . '</a>' ) . '</div>',
]
);
}