first commit

This commit is contained in:
2024-07-31 13:12:38 +07:00
commit b4e8cbe182
10213 changed files with 3125839 additions and 0 deletions

View File

@@ -0,0 +1,52 @@
<?php
namespace Elementor\Modules\SiteNavigation\Data;
use Elementor\Plugin;
use Elementor\Data\V2\Base\Controller as Base_Controller;
use Elementor\Modules\SiteNavigation\Data\Endpoints\Add_New_Post;
use Elementor\Modules\SiteNavigation\Data\Endpoints\Duplicate_Post;
use Elementor\Modules\SiteNavigation\Data\Endpoints\Homepage;
use Elementor\Modules\SiteNavigation\Data\Endpoints\Recent_Posts;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Controller extends Base_Controller {
public function get_name() {
return 'site-navigation';
}
public function get_items_permissions_check( $request ) {
return current_user_can( 'edit_posts' );
}
public function create_items_permissions_check( $request ): bool {
// Permissions check is located in the endpoint
return true;
}
public function get_item_permissions_check( $request ) {
return $this->get_items_permissions_check( $request );
}
public function create_item_permissions_check( $request ): bool {
return $this->create_items_permissions_check( $request );
}
public function register_endpoints() {
$this->register_endpoint( new Recent_Posts( $this ) );
$this->register_endpoint( new Add_New_Post( $this ) );
if ( Plugin::$instance->experiments->is_feature_active( 'pages_panel' ) ) {
$this->register_endpoint( new Duplicate_Post( $this ) );
$this->register_endpoint( new Homepage( $this ) );
}
}
protected function register_index_endpoint() {
// Bypass, currently does not required.
}
}

View File

@@ -0,0 +1,87 @@
<?php
namespace Elementor\Modules\SiteNavigation\Data\Endpoints;
use Elementor\Data\V2\Base\Endpoint;
use Elementor\Plugin;
use Elementor\User;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Add_New_Post extends Endpoint {
protected function register() {
$args = [
'post_type' => [
'description' => 'Post type to create',
'type' => 'string',
'required' => false,
'default' => 'post',
'sanitize_callback' => function ( $value ) {
return sanitize_text_field( $value );
},
'validate_callback' => 'rest_validate_request_arg',
],
];
$this->register_items_route( \WP_REST_Server::CREATABLE, $args );
}
public function get_name() {
return 'add-new-post';
}
public function get_format() {
return 'site-navigation/add-new-post';
}
public function create_items( $request ) {
$post_type = $request->get_param( 'post_type' );
if ( ! $this->validate_post_type( $post_type ) ) {
return new \WP_Error( 400, sprintf( 'Post type %s does not exist.', $post_type ), [ 'status' => 400 ] );
}
if ( ! User::is_current_user_can_edit_post_type( $post_type ) ) {
return new \WP_Error( 401, sprintf( 'User dont have capability to create page of type - %s.', $post_type ), [ 'status' => 401 ] );
}
// Temporary solution for the fact that documents creation not using the actual registered post types.
$post_type = $this->map_post_type( $post_type );
$document = Plugin::$instance->documents->create( $post_type );
if ( is_wp_error( $document ) ) {
return new \WP_Error( 500, sprintf( 'Error while creating %s.', $post_type ) );
}
return [
'id' => $document->get_main_id(),
'edit_url' => $document->get_edit_url(),
];
}
private function validate_post_type( $post_type ): bool {
$post_types = get_post_types();
return in_array( $post_type, $post_types );
}
/**
* Map post type to Elementor document type.
*
* @param $post_type
*
* @return string
*/
private function map_post_type( $post_type ): string {
$post_type_map = [
'page' => 'wp-page',
'post' => 'wp-post',
];
return $post_type_map[ $post_type ] ?? $post_type;
}
}

View File

@@ -0,0 +1,154 @@
<?php
namespace Elementor\Modules\SiteNavigation\Data\Endpoints;
use Elementor\Data\V2\Base\Endpoint;
use Elementor\Plugin;
use Elementor\User;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Duplicate_Post extends Endpoint {
protected function register() {
$args = [
'post_id' => [
'description' => 'Post id to duplicate',
'type' => 'integer',
'required' => true,
'sanitize_callback' => 'absint',
'validate_callback' => 'rest_validate_request_arg',
],
'title' => [
'description' => 'Post title',
'type' => 'string',
'required' => false,
'sanitize_callback' => function ( $value ) {
return sanitize_text_field( $value );
},
'validate_callback' => 'rest_validate_request_arg',
],
];
$this->register_items_route( \WP_REST_Server::CREATABLE, $args );
}
public function get_name() {
return 'duplicate-post';
}
public function get_format() {
return 'site-navigation/duplicate-post';
}
public function create_items( $request ) {
$post_id = $request->get_param( 'post_id' );
$post_title = $request->get_param( 'title' );
$post = get_post( $post_id );
if ( ! User::is_current_user_can_edit_post_type( $post->post_type ) ) {
return new \WP_Error( 401, sprintf( 'User dont have capability to create page of type - %s.', $post->post_type ), [ 'status' => 401 ] );
}
if ( ! $post ) {
return new \WP_Error( 500, 'Post not found' );
}
$new_post_id = $this->duplicate_post( $post, $post_title );
if ( is_wp_error( $new_post_id ) ) {
return new \WP_Error( 500, 'Error while duplicating post.' );
}
//Duplicate all post meta
$this->duplicate_post_meta( $post_id, $new_post_id );
//Duplicate all taxonomies
$this->duplicate_post_taxonomies( $post_id, $new_post_id );
return [
'post_id' => $new_post_id,
];
}
/**
* Duplicate post
*
* @param $post
*
* @return int|\WP_Error
*/
private function duplicate_post( $post, $post_title ) {
$post_status = 'draft';
$current_user = wp_get_current_user();
$new_post_author = $current_user->ID;
$args = [
'comment_status' => $post->comment_status,
'ping_status' => $post->ping_status,
'post_author' => $new_post_author,
'post_content' => $post->post_content,
'post_excerpt' => $post->post_excerpt,
'post_parent' => $post->post_parent,
'post_password' => $post->post_password,
'post_status' => $post_status,
'post_title' => $post_title,
'post_type' => $post->post_type,
'to_ping' => $post->to_ping,
'menu_order' => $post->menu_order,
];
return wp_insert_post( $args );
}
/**
* Duplicate the associated post meta to the new post ID.
*
* @param int $post_id
* @param int $new_post_id
*/
private function duplicate_post_meta( int $post_id, int $new_post_id ) {
$post_meta = get_post_meta( $post_id );
if ( empty( $post_meta ) || ! is_array( $post_meta ) ) {
return;
}
foreach ( $post_meta as $key => $values ) {
if ( '_wp_old_slug' === $key ) { // Ignore this meta key
continue;
}
foreach ( $values as $value ) {
$value = maybe_unserialize( $value );
add_post_meta( $new_post_id, $key, wp_slash( $value ) );
}
}
}
/**
* duplicate_post_taxonomies
*
* @param int $post_id
* @param int $new_post_id
*/
private function duplicate_post_taxonomies( $post_id, $new_post_id ) {
$taxonomies = array_map( 'sanitize_text_field', get_object_taxonomies( get_post_type( $post_id ) ) );
if ( empty( $taxonomies ) || ! is_array( $taxonomies ) ) {
return;
}
foreach ( $taxonomies as $taxonomy ) {
$post_terms = wp_get_object_terms( $post_id, $taxonomy, [ 'fields' => 'slugs' ] );
if ( ! is_wp_error( $post_terms ) ) {
wp_set_object_terms( $new_post_id, $post_terms, $taxonomy, false );
}
}
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace Elementor\Modules\SiteNavigation\Data\Endpoints;
use Elementor\Data\V2\Base\Endpoint;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Homepage extends Endpoint {
public function get_permission_callback( $request ) {
return current_user_can( 'edit_posts' );
}
public function get_name() {
return 'homepage';
}
public function get_format() {
return 'site-navigation/homepage';
}
public function get_items( $request ) {
$homepage_id = get_option( 'page_on_front' );
$show_on_front = get_option( 'show_on_front' );
return 'page' === $show_on_front ? intval( $homepage_id ) : 0;
}
}

View File

@@ -0,0 +1,101 @@
<?php
namespace Elementor\Modules\SiteNavigation\Data\Endpoints;
use Elementor\Core\Base\Document;
use Elementor\Core\Kits\Documents\Kit;
use Elementor\Data\V2\Base\Endpoint;
use Elementor\Plugin;
use Elementor\TemplateLibrary\Source_Local;
use Elementor\Utils;
use WP_REST_Server;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Recent_Posts extends Endpoint {
public function register_items_route( $methods = WP_REST_Server::READABLE, $args = [] ) {
$args = [
'posts_per_page' => [
'description' => 'Number of posts to return',
'type' => 'integer',
'required' => true,
'sanitize_callback' => 'absint',
'validate_callback' => 'rest_validate_request_arg',
],
'post_type' => [
'description' => 'Post types to retrieve',
'type' => 'array',
'required' => false,
'default' => [ 'page', 'post', Source_Local::CPT ],
'sanitize_callback' => 'rest_sanitize_array',
'validate_callback' => 'rest_validate_request_arg',
],
'post__not_in' => [
'description' => 'Post id`s to exclude',
'type' => 'array',
'required' => [],
'sanitize_callback' => 'wp_parse_id_list',
'validate_callback' => 'rest_validate_request_arg',
],
];
parent::register_items_route( $methods, $args );
}
public function get_name() {
return 'recent-posts';
}
public function get_format() {
return 'site-navigation/recent-posts';
}
public function get_items( $request ) {
$args = [
'posts_per_page' => $request->get_param( 'posts_per_page' ),
'post_type' => $request->get_param( 'post_type' ),
'fields' => 'ids',
'meta_query' => [
[
'key' => Document::TYPE_META_KEY,
'value' => Kit::get_type(), // Exclude kits.
'compare' => '!=',
],
],
];
$exclude = $request->get_param( 'post__not_in' );
if ( ! empty( $exclude ) ) {
$args['post__not_in'] = $exclude;
}
$recently_edited_query = Utils::get_recently_edited_posts_query( $args );
$recent = [];
foreach ( $recently_edited_query->posts as $id ) {
$document = Plugin::$instance->documents->get( $id );
$recent[] = [
'id' => $id,
'title' => get_the_title( $id ),
'edit_url' => $document->get_edit_url(),
'date_modified' => get_post_timestamp( $id, 'modified' ),
'type' => [
'post_type' => get_post_type( $id ),
'doc_type' => $document->get_name(),
'label' => $document->get_title(),
],
'user_can' => [
'edit' => current_user_can( 'edit_post', $id ),
],
];
}
return $recent;
}
}

View File

@@ -0,0 +1,84 @@
<?php
namespace Elementor\Modules\SiteNavigation;
use Elementor\Core\Base\Module as Module_Base;
use Elementor\Core\Experiments\Manager as Experiments_Manager;
use Elementor\Modules\SiteNavigation\Data\Controller;
use Elementor\Modules\SiteNavigation\Rest_Fields\Page_User_Can;
use Elementor\Plugin;
use Elementor\Utils;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class Module extends Module_Base {
const PAGES_PANEL_EXPERIMENT_NAME = 'pages_panel';
/**
* Initialize the Site navigation module.
* @return void
* @throws \Exception
*/
public function __construct() {
Plugin::$instance->data_manager_v2->register_controller( new Controller() );
$is_tests = Utils::is_elementor_tests();
$is_v2_experiment_on = Plugin::$instance->experiments->is_feature_active( 'editor_v2' );
if ( ! $is_v2_experiment_on && ! $is_tests ) {
return;
}
$this->register_pages_panel_experiment();
if ( Plugin::$instance->experiments->is_feature_active( self::PAGES_PANEL_EXPERIMENT_NAME ) ) {
add_filter( 'elementor/editor/v2/scripts/env', function( $env ) {
$env['@elementor/editor-site-navigation'] = [
'is_pages_panel_active' => true,
];
return $env;
} );
$this->register_rest_fields();
}
}
/**
* Retrieve the module name.
*
* @return string
*/
public function get_name() {
return 'site-navigation';
}
/**
* Register Experiment
*
* @since 3.16.0
*
* @return void
* @throws \Exception
*/
private function register_pages_panel_experiment() {
Plugin::$instance->experiments->add_feature( [
'name' => self::PAGES_PANEL_EXPERIMENT_NAME,
'title' => esc_html__( 'Pages Panel', 'elementor' ),
'release_status' => Experiments_Manager::RELEASE_STATUS_ALPHA,
'default' => Experiments_Manager::STATE_INACTIVE,
'hidden' => true,
'dependencies' => [
'editor_v2',
],
] );
}
private function register_rest_fields() {
add_action( 'rest_api_init', function() {
( new Page_User_Can() )->register_rest_field();
} );
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace Elementor\Modules\SiteNavigation\Rest_Fields;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Page_User_Can {
public function register_rest_field() {
if ( ! isset( $_GET['_fields'] ) ) {
return;
}
$fields = sanitize_text_field( wp_unslash( $_GET['_fields'] ) );
$array_fields = explode( ',', $fields );
if ( ! in_array( 'user_can', $array_fields ) ) {
return;
}
register_rest_field(
'page',
'user_can',
[
'get_callback' => [ $this, 'get_callback' ],
'schema' => [
'description' => __( 'Whether the current user can edit or delete this post', 'elementor' ),
'type' => 'array',
],
]
);
}
public function get_callback( $post ) {
$can_edit = current_user_can( 'edit_post', $post['id'] );
$can_delete = current_user_can( 'delete_post', $post['id'] );
return [
'edit' => $can_edit,
'delete' => $can_delete,
];
}
}