first commit
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
namespace ElementorPro\Modules\Forms\Submissions\Database\Entities;
|
||||
|
||||
use Elementor\Core\Base\Base_Object;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* The Form_Snapshot is a snapshot of the form as it saved in the document data, on each submission creation it updates the snapshot to the current state of the form,
|
||||
* As a consequence the queries are quicker (filters, export, etc.) and in case the form itself removed from the document, the Form_Snapshot
|
||||
* remains and allows the user export and filter submissions as before.
|
||||
*/
|
||||
class Form_Snapshot extends Base_Object implements \JsonSerializable {
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $post_id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* @var array {
|
||||
* @type string $id
|
||||
* @type string $type
|
||||
* @type string $label
|
||||
* }
|
||||
*/
|
||||
public $fields = [];
|
||||
|
||||
/**
|
||||
* @param $post_id
|
||||
* @param $form_id
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function generate_key( $post_id, $form_id ) {
|
||||
return "{$post_id}_{$form_id}";
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_key() {
|
||||
return static::generate_key( $this->post_id, $this->id );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_label() {
|
||||
return "{$this->name} ($this->id)";
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement for the JsonSerializable method, will trigger when trying to json_encode this object.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function jsonSerialize() {
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => $this->name,
|
||||
'fields' => $this->fields,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Form constructor.
|
||||
*
|
||||
* @param $post_id
|
||||
* @param $data
|
||||
*/
|
||||
public function __construct( $post_id, $data ) {
|
||||
$this->post_id = (int) $post_id;
|
||||
$this->id = $data['id'];
|
||||
$this->name = $data['name'];
|
||||
$this->fields = $data['fields'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
namespace ElementorPro\Modules\Forms\Submissions\Database;
|
||||
|
||||
use Elementor\Core\Base\Base_Object;
|
||||
use Elementor\Core\Utils\Collection;
|
||||
|
||||
class Migration extends Base_Object {
|
||||
const OPTION_DB_VERSION = 'elementor_submissions_db_version';
|
||||
|
||||
// This version must be updated when new migration created.
|
||||
const CURRENT_DB_VERSION = 5;
|
||||
|
||||
private static $migrations = [
|
||||
1 => Migrations\Initial::class,
|
||||
// It jumps from version 1 to 4 because some users already migrated the DB when the migrations system worked with the Elementor Pro version
|
||||
// when the int value of the version "3.2.0" was 3.
|
||||
4 => Migrations\Referer_Extra::class,
|
||||
5 => Migrations\Fix_Indexes::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Checks if there is a need to run migrations.
|
||||
*/
|
||||
public static function install() {
|
||||
$installed_version = intval( get_option( self::OPTION_DB_VERSION ) );
|
||||
|
||||
// Up to date. Nothing to do.
|
||||
if ( static::CURRENT_DB_VERSION <= $installed_version ) {
|
||||
return;
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
|
||||
( new Collection( static::$migrations ) )
|
||||
->filter( function ( $_, $version ) use ( $installed_version ) {
|
||||
// Filter all the migrations that already done.
|
||||
return $version > $installed_version;
|
||||
} )
|
||||
->map( function ( $migration_class_name, $version ) use ( $wpdb ) {
|
||||
/** @var Migrations\Base_Migration $migration */
|
||||
$migration = new $migration_class_name( $wpdb );
|
||||
$migration->run();
|
||||
|
||||
// In case some migration failed it updates version every migration.
|
||||
update_option( static::OPTION_DB_VERSION, $version );
|
||||
} );
|
||||
|
||||
update_option( static::OPTION_DB_VERSION, self::CURRENT_DB_VERSION );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
namespace ElementorPro\Modules\Forms\Submissions\Database\Migrations;
|
||||
|
||||
use Elementor\Core\Base\Base_Object;
|
||||
use ElementorPro\Modules\Forms\Submissions\Database\Query;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
abstract class Base_Migration extends Base_Object {
|
||||
/*
|
||||
* Ref: wp-admin/includes/schema.php::wp_get_db_schema
|
||||
*
|
||||
* Indexes have a maximum size of 767 bytes. Historically, we haven't need to be concerned about that.
|
||||
* As of 4.2, however, we moved to utf8mb4, which uses 4 bytes per character. This means that an index which
|
||||
* used to have room for floor(767/3) = 255 characters, now only has room for floor(767/4) = 191 characters.
|
||||
*/
|
||||
const MAX_INDEX_LENGTH = 191;
|
||||
|
||||
/**
|
||||
* @var \wpdb
|
||||
*/
|
||||
protected $wpdb;
|
||||
|
||||
/**
|
||||
* @var Query
|
||||
*/
|
||||
protected $query;
|
||||
|
||||
/**
|
||||
* Base_Migration constructor.
|
||||
*
|
||||
* @param \wpdb $wpdb
|
||||
*/
|
||||
public function __construct( \wpdb $wpdb ) {
|
||||
$this->wpdb = $wpdb;
|
||||
$this->query = Query::get_instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run migration.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
abstract public function run();
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
namespace ElementorPro\Modules\Forms\Submissions\Database\Migrations;
|
||||
|
||||
use Elementor\Core\Utils\Collection;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Fix_Indexes extends Base_Migration {
|
||||
/**
|
||||
* In the previous migrations some databases had problems with the indexes.
|
||||
* this migration checks if user's tables are filled with required indexes, if not it creates them.
|
||||
*/
|
||||
public function run() {
|
||||
$indexes = $this->get_indexes();
|
||||
|
||||
foreach ( $indexes as $table => $table_indexes ) {
|
||||
$existing_indexes = $this->get_existed_indexes_of( $table );
|
||||
|
||||
// Protect from database errors (for example: table do not exists somehow).
|
||||
if ( null === $existing_indexes ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$indexes_query = $table_indexes->except( $existing_indexes )->implode( ',' );
|
||||
|
||||
$this->wpdb->query( "ALTER TABLE `{$table}` {$indexes_query};" ); // phpcs:ignore
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user exited indexes
|
||||
*
|
||||
* @param $table_name
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
private function get_existed_indexes_of( $table_name ) {
|
||||
$result = $this->wpdb->get_results( "SHOW INDEX FROM {$table_name};", ARRAY_A ); // phpcs:ignore
|
||||
|
||||
if ( false === $result ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return ( new Collection( $result ) )
|
||||
->map( function ( $row ) {
|
||||
if ( ! isset( $row['Key_name'] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $row['Key_name'];
|
||||
} )
|
||||
->filter()
|
||||
->values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the database indexes.
|
||||
*
|
||||
* @return Collection[]
|
||||
*/
|
||||
private function get_indexes() {
|
||||
$max_index_length = static::MAX_INDEX_LENGTH;
|
||||
|
||||
return [
|
||||
$this->query->get_table_submissions() => new Collection( [
|
||||
'main_meta_id_index' => 'ADD INDEX `main_meta_id_index` (`main_meta_id`)',
|
||||
'hash_id_unique_index' => "ADD UNIQUE INDEX `hash_id_unique_index` (`hash_id` ({$max_index_length}))",
|
||||
'hash_id_index' => "ADD INDEX `hash_id_index` (`hash_id` ({$max_index_length}))",
|
||||
'type_index' => "ADD INDEX `type_index` (`type` ({$max_index_length}))",
|
||||
'post_id_index' => 'ADD INDEX `post_id_index` (`post_id`)',
|
||||
'element_id_index' => "ADD INDEX `element_id_index` (`element_id` ({$max_index_length}))",
|
||||
'campaign_id_index' => 'ADD INDEX `campaign_id_index` (`campaign_id`)',
|
||||
'user_id_index' => 'ADD INDEX `user_id_index` (`user_id`)',
|
||||
'user_ip_index' => 'ADD INDEX `user_ip_index` (`user_ip`)',
|
||||
'status_index' => 'ADD INDEX `status_index` (`status`)',
|
||||
'is_read_index' => 'ADD INDEX `is_read_index` (`is_read`)',
|
||||
'created_at_gmt_index' => 'ADD INDEX `created_at_gmt_index` (`created_at_gmt`)',
|
||||
'updated_at_gmt_index' => 'ADD INDEX `updated_at_gmt_index` (`updated_at_gmt`)',
|
||||
'created_at_index' => 'ADD INDEX `created_at_index` (`created_at`)',
|
||||
'updated_at_index' => 'ADD INDEX `updated_at_index` (`updated_at`)',
|
||||
'referer_index' => "ADD INDEX `referer_index` (`referer` ({$max_index_length}))",
|
||||
'referer_title_index' => "ADD INDEX `referer_title_index` (`referer_title` ({$max_index_length}))",
|
||||
] ),
|
||||
$this->query->get_table_submissions_values() => new Collection( [
|
||||
'submission_id_index' => 'ADD INDEX `submission_id_index` (`submission_id`)',
|
||||
'key_index' => "ADD INDEX `key_index` (`key` ({$max_index_length}))",
|
||||
] ),
|
||||
$this->query->get_table_form_actions_log() => new Collection( [
|
||||
'submission_id_index' => 'ADD INDEX `submission_id_index` (`submission_id`)',
|
||||
'action_name_index' => "ADD INDEX `action_name_index` (`action_name` ({$max_index_length}))",
|
||||
'status_index' => 'ADD INDEX `status_index` (`status`)',
|
||||
'created_at_gmt_index' => 'ADD INDEX `created_at_gmt_index` (`created_at_gmt`)',
|
||||
'updated_at_gmt_index' => 'ADD INDEX `updated_at_gmt_index` (`updated_at_gmt`)',
|
||||
'created_at_index' => 'ADD INDEX `created_at_index` (`created_at`)',
|
||||
'updated_at_index' => 'ADD INDEX `updated_at_index` (`updated_at`)',
|
||||
] ),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
namespace ElementorPro\Modules\Forms\Submissions\Database\Migrations;
|
||||
|
||||
class Initial extends Base_Migration {
|
||||
public function run() {
|
||||
$this->create_tables();
|
||||
$this->add_indexes();
|
||||
}
|
||||
|
||||
private function create_tables() {
|
||||
$charset_collate = $this->wpdb->get_charset_collate();
|
||||
|
||||
$e_submission_table = "CREATE TABLE `{$this->query->get_table_submissions()}` (
|
||||
id bigint(20) unsigned auto_increment primary key,
|
||||
type varchar(60) null,
|
||||
hash_id varchar(60) not null,
|
||||
main_meta_id bigint(20) unsigned not null comment 'Id of main field. to represent the main meta field',
|
||||
post_id bigint(20) unsigned not null,
|
||||
referer varchar(500) not null,
|
||||
element_id varchar(20) not null,
|
||||
form_name varchar(60) not null,
|
||||
campaign_id bigint(20) unsigned not null,
|
||||
user_id bigint(20) unsigned null,
|
||||
user_ip varchar(46) not null,
|
||||
user_agent text not null,
|
||||
actions_count INT DEFAULT 0,
|
||||
actions_succeeded_count INT DEFAULT 0,
|
||||
status varchar(20) not null,
|
||||
is_read tinyint(1) default 0 not null,
|
||||
meta text null,
|
||||
created_at_gmt datetime not null,
|
||||
updated_at_gmt datetime not null,
|
||||
created_at datetime not null,
|
||||
updated_at datetime not null
|
||||
) {$charset_collate};";
|
||||
|
||||
$e_submission_values_table = "CREATE TABLE `{$this->query->get_table_submissions_values()}` (
|
||||
id bigint(20) unsigned auto_increment primary key,
|
||||
submission_id bigint(20) unsigned not null default 0,
|
||||
`key` varchar(60) null,
|
||||
value longtext null
|
||||
) {$charset_collate};";
|
||||
|
||||
$e_submission_actions_log_table = "CREATE TABLE `{$this->query->get_table_form_actions_log()}` (
|
||||
id bigint(20) unsigned auto_increment primary key,
|
||||
submission_id bigint(20) unsigned not null,
|
||||
action_name varchar(60) not null,
|
||||
action_label varchar(60) null,
|
||||
status varchar(20) not null,
|
||||
log text null,
|
||||
created_at_gmt datetime not null,
|
||||
updated_at_gmt datetime not null,
|
||||
created_at datetime not null,
|
||||
updated_at datetime not null
|
||||
) {$charset_collate};";
|
||||
|
||||
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
|
||||
|
||||
dbDelta( $e_submission_table . $e_submission_values_table . $e_submission_actions_log_table );
|
||||
}
|
||||
|
||||
private function add_indexes() {
|
||||
// phpcs:disable
|
||||
$this->wpdb->query( "ALTER TABLE `{$this->query->get_table_submissions()}`
|
||||
ADD INDEX `main_meta_id_index` (`main_meta_id`),
|
||||
ADD UNIQUE INDEX `hash_id_unique_index` (`hash_id`),
|
||||
ADD INDEX `hash_id_index` (`hash_id`),
|
||||
ADD INDEX `type_index` (`type`),
|
||||
ADD INDEX `post_id_index` (`post_id`),
|
||||
ADD INDEX `element_id_index` (`element_id`),
|
||||
ADD INDEX `campaign_id_index` (`campaign_id`),
|
||||
ADD INDEX `user_id_index` (`user_id`),
|
||||
ADD INDEX `user_ip_index` (`user_ip`),
|
||||
ADD INDEX `status_index` (`status`),
|
||||
ADD INDEX `is_read_index` (`is_read`),
|
||||
ADD INDEX `created_at_gmt_index` (`created_at_gmt`),
|
||||
ADD INDEX `updated_at_gmt_index` (`updated_at_gmt`),
|
||||
ADD INDEX `created_at_index` (`created_at`),
|
||||
ADD INDEX `updated_at_index` (`updated_at`)
|
||||
" );
|
||||
|
||||
$this->wpdb->query( "ALTER TABLE `{$this->query->get_table_submissions_values()}`
|
||||
ADD INDEX `submission_id_index` (`submission_id`),
|
||||
ADD INDEX `key_index` (`key`)
|
||||
" );
|
||||
|
||||
$this->wpdb->query( "ALTER TABLE `{$this->query->get_table_form_actions_log()}`
|
||||
ADD INDEX `submission_id_index` (`submission_id`),
|
||||
ADD INDEX `action_name_index` (`action_name`),
|
||||
ADD INDEX `status_index` (`status`),
|
||||
ADD INDEX `created_at_gmt_index` (`created_at_gmt`),
|
||||
ADD INDEX `updated_at_gmt_index` (`updated_at_gmt`),
|
||||
ADD INDEX `created_at_index` (`created_at`),
|
||||
ADD INDEX `updated_at_index` (`updated_at`)
|
||||
" );
|
||||
// phpcs:enable
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
namespace ElementorPro\Modules\Forms\Submissions\Database\Migrations;
|
||||
|
||||
class Referer_Extra extends Base_Migration {
|
||||
public function run() {
|
||||
$max_index_length = static::MAX_INDEX_LENGTH;
|
||||
|
||||
// phpcs:disable
|
||||
$this->wpdb->query("
|
||||
ALTER TABLE `{$this->query->get_table_submissions()}`
|
||||
ADD COLUMN `referer_title` varchar(300) null AFTER `referer`;
|
||||
");
|
||||
|
||||
$this->wpdb->query("
|
||||
ALTER TABLE `{$this->query->get_table_submissions()}`
|
||||
ADD INDEX `referer_index` (`referer`({$max_index_length})),
|
||||
ADD INDEX `referer_title_index` (`referer_title`({$max_index_length}));
|
||||
");
|
||||
// phpcs:enable
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,904 @@
|
||||
<?php
|
||||
namespace ElementorPro\Modules\Forms\Submissions\Database;
|
||||
|
||||
use Elementor\Core\Base\Base_Object;
|
||||
use ElementorPro\Plugin;
|
||||
use Elementor\Core\Utils\Collection;
|
||||
use Exception;
|
||||
use ElementorPro\Modules\Forms\Classes\Action_Base;
|
||||
use ElementorPro\Modules\Forms\Submissions\Database\Repositories\Form_Snapshot_Repository;
|
||||
|
||||
class Query extends Base_Object {
|
||||
const STATUS_NEW = 'new';
|
||||
const STATUS_TRASH = 'trash';
|
||||
|
||||
const ACTIONS_LOG_STATUS_SUCCESS = 'success';
|
||||
const ACTIONS_LOG_STATUS_FAILED = 'failed';
|
||||
|
||||
const TYPE_SUBMISSION = 'submission';
|
||||
|
||||
/**
|
||||
* @var Query
|
||||
*/
|
||||
private static $instance = null;
|
||||
|
||||
const E_SUBMISSIONS_ACTIONS_LOG = 'e_submissions_actions_log';
|
||||
const E_SUBMISSIONS_VALUES = 'e_submissions_values';
|
||||
const E_SUBMISSIONS = 'e_submissions';
|
||||
|
||||
/**
|
||||
* @var \wpdb
|
||||
*/
|
||||
private $wpdb;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $table_submissions;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $table_submissions_values;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $table_submissions_actions_log;
|
||||
|
||||
public static function get_instance() {
|
||||
if ( ! self::$instance ) {
|
||||
self::$instance = new Query();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public function get_submissions( $args = [] ) {
|
||||
$args = wp_parse_args( $args, [
|
||||
'page' => 1,
|
||||
'per_page' => 10,
|
||||
'filters' => [],
|
||||
'order' => [],
|
||||
'with_meta' => false,
|
||||
'with_form_fields' => false,
|
||||
] );
|
||||
|
||||
$page = $args['page'];
|
||||
$per_page = $args['per_page'];
|
||||
$filters = $args['filters'];
|
||||
$order = $args['order'];
|
||||
$with_meta = $args['with_meta'];
|
||||
$with_form_fields = $args['with_form_fields'];
|
||||
|
||||
$result = [
|
||||
'data' => [],
|
||||
'meta' => [],
|
||||
];
|
||||
|
||||
$where_sql = $this->apply_filter( $filters );
|
||||
$order_sql = '';
|
||||
|
||||
$total = (int) $this->wpdb->get_var("SELECT COUNT(*) FROM `{$this->get_table_submissions()}` t_submissions {$where_sql}" );// phpcs:ignore
|
||||
|
||||
$last_page = 0 < $total && 0 < $per_page ? (int) ceil( $total / $per_page ) : 1;
|
||||
|
||||
if ( $page > $last_page ) {
|
||||
$page = $last_page;
|
||||
}
|
||||
|
||||
$offset = (int) ceil( ( $page - 1 ) * $per_page );
|
||||
|
||||
$result['meta']['pagination'] = [
|
||||
'current_page' => $page,
|
||||
'per_page' => $per_page,
|
||||
'total' => $total,
|
||||
'last_page' => $last_page,
|
||||
];
|
||||
|
||||
$this->handle_order( $order, $order_sql );
|
||||
|
||||
$per_page = (int) $per_page;
|
||||
|
||||
$q = "SELECT t_submissions.* FROM `{$this->get_table_submissions()}` t_submissions {$where_sql} {$order_sql} LIMIT {$per_page} OFFSET {$offset}";
|
||||
|
||||
$submissions = $this->wpdb->get_results( $q );// phpcs:ignore
|
||||
|
||||
$data = new Collection( [] );
|
||||
|
||||
foreach ( $submissions as $current_submission ) {
|
||||
$data[] = $this->get_submission_body( $current_submission, $with_form_fields );
|
||||
}
|
||||
|
||||
$submissions_meta = $this
|
||||
->get_submissions_meta( $data, ! $with_meta )
|
||||
->group_by( 'submission_id' );
|
||||
|
||||
$result['data'] = $data
|
||||
->map( function ( $submission ) use ( $submissions_meta ) {
|
||||
$current_submission_meta = $submissions_meta->get( $submission['id'], [] );
|
||||
|
||||
foreach ( $current_submission_meta as $meta ) {
|
||||
$extract_meta = $this->extract( $meta, [ 'id', 'key', 'value' ], [ 'id:int' ] );
|
||||
|
||||
if ( $meta->id === $submission['main_meta_id'] ) {
|
||||
$submission['main'] = $extract_meta;
|
||||
}
|
||||
|
||||
$submission['values'][] = $extract_meta;
|
||||
}
|
||||
|
||||
return $submission;
|
||||
} )
|
||||
->all();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get count by status.
|
||||
*
|
||||
* @param $filter
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function count_submissions_by_status( $filter = [] ) {
|
||||
// Should not filter by status.
|
||||
unset( $filter['status'] );
|
||||
|
||||
$where_sql = $this->apply_filter( $filter );
|
||||
|
||||
$trash_status = '"' . static::STATUS_TRASH . '"';
|
||||
|
||||
$sql = "
|
||||
SELECT
|
||||
SUM(IF(`status` != {$trash_status}, 1, 0)) AS `all`,
|
||||
SUM(IF(`status` = {$trash_status}, 1, 0)) AS `trash`,
|
||||
SUM(IF(is_read = 1 AND `status` != {$trash_status}, 1, 0)) AS `read`,
|
||||
SUM(IF(is_read = 0 AND `status` != {$trash_status}, 1, 0)) AS `unread`
|
||||
FROM {$this->get_table_submissions()} AS `t_submissions` {$where_sql};
|
||||
";
|
||||
|
||||
$sql_result = $this->wpdb->get_row( $sql, ARRAY_A ); // phpcs:ignore
|
||||
|
||||
$result = new Collection( [
|
||||
'all' => 0,
|
||||
'trash' => 0,
|
||||
'read' => 0,
|
||||
'unread' => 0,
|
||||
] );
|
||||
|
||||
if ( ! $sql_result ) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
return $result->map( function ( $count, $key ) use ( $sql_result ) {
|
||||
if ( ! isset( $sql_result[ $key ] ) ) {
|
||||
return $count;
|
||||
}
|
||||
|
||||
return intval( $sql_result[ $key ] );
|
||||
} );
|
||||
}
|
||||
|
||||
public function get_submissions_by_email( $email, $include_submission_values = false ) {
|
||||
$user = get_user_by( 'email', $email );
|
||||
$user_filter = '';
|
||||
|
||||
if ( $user ) {
|
||||
$user_filter = $this->wpdb->prepare( 't_submissions.user_id = %s OR', $user->ID );
|
||||
}
|
||||
|
||||
$query = "
|
||||
SELECT DISTINCT(t_submissions.id), t_submissions.*
|
||||
FROM `{$this->get_table_submissions()}` t_submissions
|
||||
INNER JOIN {$this->get_table_submissions_values()} t_submissions_meta
|
||||
ON t_submissions.id = t_submissions_meta.submission_id
|
||||
WHERE
|
||||
{$user_filter}
|
||||
t_submissions_meta.value = %s
|
||||
;
|
||||
";
|
||||
|
||||
$data = $this->wpdb->get_results(
|
||||
$this->wpdb->prepare( $query, $email ) // phpcs:ignore
|
||||
);
|
||||
|
||||
if ( ! $data ) {
|
||||
return new Collection( [] );
|
||||
}
|
||||
|
||||
$data = new Collection( $data );
|
||||
|
||||
if ( $include_submission_values ) {
|
||||
$submissions_meta = $this->get_submissions_meta( $data )
|
||||
->group_by( 'submission_id' );
|
||||
|
||||
$data->map( function ( $submission ) use ( $submissions_meta ) {
|
||||
$submission->values = $submissions_meta->get( $submission->id, [] );
|
||||
|
||||
return $submission;
|
||||
} );
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $delete_timestamp
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_trashed_submission_ids_to_delete( $delete_timestamp ) {
|
||||
$date = gmdate( 'Y-m-d H:i:s', $delete_timestamp );
|
||||
|
||||
$sql = "
|
||||
SELECT s.id FROM `{$this->get_table_submissions()}` s
|
||||
WHERE s.status = %s AND s.updated_at_gmt < %s;
|
||||
";
|
||||
|
||||
return $this->wpdb->get_col(
|
||||
$this->wpdb->prepare( $sql, static::STATUS_TRASH, $date ) // phpcs:ignore
|
||||
);
|
||||
}
|
||||
|
||||
public function get_submission( $id ) {
|
||||
$result = [
|
||||
'data' => [],
|
||||
'meta' => [],
|
||||
];
|
||||
|
||||
$q = "SELECT * FROM `{$this->get_table_submissions()}` WHERE id=%d";
|
||||
$submission = $this->wpdb->get_row( $this->wpdb->prepare( $q, [ $id ] ) );// phpcs:ignore
|
||||
|
||||
if ( ! $submission ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$result['data'] = $this->get_submission_body( $submission, true );
|
||||
|
||||
$current_submission_meta = $this->get_submissions_meta( $submission, false )->all();
|
||||
|
||||
foreach ( $current_submission_meta as $meta ) {
|
||||
$extract_meta = $this->extract( $meta, [ 'id', 'key', 'value' ], [ 'id:int' ] );
|
||||
|
||||
if ( $meta->id === $result['data']['main_meta_id'] ) {
|
||||
$result['data']['main'] = $extract_meta;
|
||||
}
|
||||
|
||||
$result['data']['values'][] = $extract_meta;
|
||||
}
|
||||
|
||||
$result['data']['form_actions_log'] = ( new Collection( $this->get_submissions_actions_log( $id ) ) )
|
||||
->map( function ( $value ) {
|
||||
return $this->extract(
|
||||
$value,
|
||||
[ 'id', 'action_name', 'action_label', 'status', 'log', 'created_at', 'updated_at' ],
|
||||
[ 'id:int', 'name', 'label' ]
|
||||
);
|
||||
} )
|
||||
->all();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function get_referrers( $search = '', $value = '' ) {
|
||||
$where = '';
|
||||
|
||||
if ( $search ) {
|
||||
$search = '%' . $this->wpdb->esc_like( $search ) . '%';
|
||||
|
||||
$where = $this->wpdb->prepare( ' AND s.referer_title LIKE %s', $search );
|
||||
}
|
||||
|
||||
if ( $value ) {
|
||||
$where = $this->wpdb->prepare( ' AND s.referer = %s', $value ); // phpcs:ignore
|
||||
}
|
||||
|
||||
$where = 'WHERE 1=1' . $where;
|
||||
|
||||
$query = "
|
||||
SELECT DISTINCT (s.referer), s.referer_title
|
||||
FROM {$this->get_table_submissions()} s
|
||||
{$where};
|
||||
";
|
||||
|
||||
$result = $this->wpdb->get_results( $query, ARRAY_A ); // phpcs:ignore
|
||||
|
||||
if ( ! $result ) {
|
||||
return new Collection( [] );
|
||||
}
|
||||
|
||||
return new Collection( $result );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $submissions
|
||||
* @param false $only_main
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function get_submissions_meta( $submissions, $only_main = false ) {
|
||||
if ( ! ( $submissions instanceof Collection ) ) {
|
||||
$submissions = new Collection(
|
||||
is_array( $submissions ) ? $submissions : [ $submissions ]
|
||||
);
|
||||
}
|
||||
|
||||
if ( $submissions->is_empty() ) {
|
||||
return new Collection( [] );
|
||||
}
|
||||
|
||||
if ( $only_main ) {
|
||||
$column = 'id';
|
||||
$ids = $submissions->pluck( 'main_meta_id' );
|
||||
} else {
|
||||
$column = 'submission_id';
|
||||
$ids = $submissions->pluck( 'id' );
|
||||
}
|
||||
|
||||
$placeholders = $ids->map( function () {
|
||||
return '%d';
|
||||
} )->implode( ',' );
|
||||
|
||||
$q = "SELECT * FROM `{$this->get_table_submissions_values()}` WHERE `{$column}` IN ({$placeholders})";
|
||||
|
||||
$result = $this->wpdb->get_results( $this->wpdb->prepare( $q, $ids->all() ) ); // phpcs:ignore
|
||||
|
||||
if ( ! $result ) {
|
||||
return new Collection( [] );
|
||||
}
|
||||
|
||||
return new Collection( $result );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $post_id
|
||||
* @param $element_id
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function get_submissions_value_keys( $post_id, $element_id ) {
|
||||
$sql = "
|
||||
SELECT DISTINCT(wesv.`key`)
|
||||
FROM {$this->get_table_submissions_values()} wesv
|
||||
INNER JOIN {$this->get_table_submissions()} wes
|
||||
ON wes.id = wesv.submission_id
|
||||
WHERE wes.post_id = %s AND wes.element_id = %s;
|
||||
";
|
||||
|
||||
$result = $this->wpdb->get_col( $this->wpdb->prepare( $sql, $post_id, $element_id ) ); // phpcs:ignore
|
||||
|
||||
$form = Form_Snapshot_Repository::instance()->find( $post_id, $element_id );
|
||||
if ( $form ) {
|
||||
$ordered_keys = array_map( function( $field ) {
|
||||
return $field['id'];
|
||||
}, $form->fields );
|
||||
|
||||
$result = array_merge( array_intersect( $ordered_keys, $result ), array_diff( $result, $ordered_keys ) );
|
||||
}
|
||||
|
||||
if ( ! $result ) {
|
||||
return new Collection( [] );
|
||||
}
|
||||
|
||||
return new Collection( $result );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $submission_id
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function get_submissions_actions_log( $submission_id ) {
|
||||
$query = "SELECT * FROM `{$this->get_table_form_actions_log()}` WHERE `submission_id` = %d;";
|
||||
|
||||
return $this->wpdb->get_results(
|
||||
$this->wpdb->prepare( $query, (int) $submission_id ), // phpcs:ignore
|
||||
ARRAY_A
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add submission.
|
||||
*
|
||||
* @param array $submission_data
|
||||
* @param array $fields_data
|
||||
*
|
||||
* @return int id or 0
|
||||
*/
|
||||
public function add_submission( array $submission_data, array $fields_data ) {
|
||||
global $wpdb;
|
||||
|
||||
$result = 0;
|
||||
|
||||
$submission_data = $this->get_new_submission_initial_data( $submission_data );
|
||||
|
||||
try {
|
||||
$wpdb->query( 'START TRANSACTION' );
|
||||
|
||||
$submission_success = $wpdb->insert( $this->get_table_submissions(), $submission_data );
|
||||
|
||||
if ( ! $submission_success ) {
|
||||
throw new Exception( $wpdb->last_error );
|
||||
}
|
||||
|
||||
$submission_id = $wpdb->insert_id;
|
||||
|
||||
// Meta's keys/values.
|
||||
$main_meta_id = 0;
|
||||
$first_submissions_meta_id = 0;
|
||||
foreach ( $fields_data as $field ) {
|
||||
$wpdb->insert( $this->get_table_submissions_values(), [
|
||||
'submission_id' => $submission_id,
|
||||
'key' => $field['id'],
|
||||
'value' => $field['value'],
|
||||
] );
|
||||
|
||||
if ( ! $first_submissions_meta_id ) {
|
||||
$first_submissions_meta_id = $wpdb->insert_id;
|
||||
}
|
||||
|
||||
if ( 0 === $main_meta_id && 'email' === $field['type'] ) {
|
||||
$main_meta_id = $wpdb->insert_id;
|
||||
}
|
||||
}
|
||||
|
||||
// If `$main_meta_id` not determined.
|
||||
if ( ! $main_meta_id ) {
|
||||
$main_meta_id = $first_submissions_meta_id;
|
||||
}
|
||||
|
||||
// Update main_meta_id.
|
||||
$wpdb->update( $this->get_table_submissions(),
|
||||
[
|
||||
'main_meta_id' => $main_meta_id,
|
||||
],
|
||||
[
|
||||
'id' => $submission_id,
|
||||
]
|
||||
);
|
||||
|
||||
$wpdb->query( 'COMMIT' );
|
||||
|
||||
$result = $submission_id;
|
||||
} catch ( Exception $e ) {
|
||||
$wpdb->query( 'ROLLBACK' );
|
||||
|
||||
Plugin::elementor()->logger->get_logger()->error( 'Save submission failed, db error: ' . $e->getMessage() );
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $submission_id
|
||||
* @param array $data
|
||||
* @param array $values
|
||||
*
|
||||
* @return bool|int affected rows
|
||||
*/
|
||||
public function update_submission( $submission_id, array $data, $values = [] ) {
|
||||
if ( $values ) {
|
||||
foreach ( $values as $key => $value ) {
|
||||
$this->wpdb->update(
|
||||
$this->get_table_submissions_values(),
|
||||
[ 'value' => $value ],
|
||||
[
|
||||
'submission_id' => $submission_id,
|
||||
'key' => $key,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$data['updated_at_gmt'] = current_time( 'mysql', true );
|
||||
$data['updated_at'] = get_date_from_gmt( $data['updated_at_gmt'] );
|
||||
|
||||
return $this->wpdb->update(
|
||||
$this->get_table_submissions(),
|
||||
$data,
|
||||
[ 'id' => $submission_id ]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move single submission to trash
|
||||
*
|
||||
* @param $id
|
||||
*
|
||||
* @return bool|int number of affected rows or false if failed
|
||||
*/
|
||||
public function move_to_trash_submission( $id ) {
|
||||
return $this->update_submission(
|
||||
$id,
|
||||
[ 'status' => static::STATUS_TRASH ]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a single submission.
|
||||
*
|
||||
* @param $id
|
||||
*
|
||||
* @return bool|int number of affected rows or false if failed
|
||||
*/
|
||||
public function delete_submission( $id ) {
|
||||
$this->wpdb->delete(
|
||||
$this->get_table_submissions_values(),
|
||||
[ 'submission_id' => $id ]
|
||||
);
|
||||
|
||||
$this->wpdb->delete(
|
||||
$this->get_table_form_actions_log(),
|
||||
[ 'submission_id' => $id ]
|
||||
);
|
||||
|
||||
return $this->wpdb->delete(
|
||||
$this->get_table_submissions(),
|
||||
[ 'id' => $id ]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore a single submission.
|
||||
*
|
||||
* @param $id
|
||||
*
|
||||
* @return bool|int number of affected rows or false if failed
|
||||
*/
|
||||
public function restore( $id ) {
|
||||
return $this->wpdb->update(
|
||||
$this->get_table_submissions(),
|
||||
[ 'status' => self::STATUS_NEW ],
|
||||
[ 'id' => $id ]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $submission_id
|
||||
* @param Action_Base $action Should be class based on ActionBase (do not type hint to support third party plugins)
|
||||
* @param $status
|
||||
* @param null $log_message
|
||||
*
|
||||
* @return bool|int
|
||||
*/
|
||||
public function add_action_log( $submission_id, $action, $status, $log_message = null ) {
|
||||
$current_datetime_gmt = current_time( 'mysql', true );
|
||||
$current_datetime = get_date_from_gmt( $current_datetime_gmt );
|
||||
|
||||
return $this->wpdb->insert(
|
||||
$this->get_table_form_actions_log(),
|
||||
[
|
||||
'submission_id' => $submission_id,
|
||||
'action_name' => $action->get_name(),
|
||||
'action_label' => $action->get_label(),
|
||||
'status' => $status,
|
||||
'log' => $log_message,
|
||||
'created_at_gmt' => $current_datetime_gmt,
|
||||
'updated_at_gmt' => $current_datetime_gmt,
|
||||
'created_at' => $current_datetime,
|
||||
'updated_at' => $current_datetime,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function get_last_error() {
|
||||
$this->wpdb->last_error;
|
||||
}
|
||||
|
||||
public function get_table_submissions() {
|
||||
return $this->table_submissions;
|
||||
}
|
||||
|
||||
public function get_table_submissions_values() {
|
||||
return $this->table_submissions_values;
|
||||
}
|
||||
|
||||
public function get_table_form_actions_log() {
|
||||
return $this->table_submissions_actions_log;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract data from `$target` by selected `$keys`.
|
||||
* `$as` used to convert extracted data with different keys.
|
||||
* `$as` support converting to types. using ':' after the key name.
|
||||
*
|
||||
* Example: `$target = [ 'someId' => '5' ];`
|
||||
* `$keys = [ 'someId' ]`
|
||||
* `$as = [ 'id:int' ]`
|
||||
* Output will be `[ 'id' => 5 ];`
|
||||
*
|
||||
* @param array|\stdClass $target
|
||||
* @param array $keys
|
||||
* @param array $as
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function extract( $target, $keys, $as = [] ) {
|
||||
$result = [];
|
||||
|
||||
$as_types = [];
|
||||
$as_count = 0;
|
||||
|
||||
if ( is_object( $target ) ) {
|
||||
$target = (array) $target;
|
||||
}
|
||||
|
||||
foreach ( $as as $key => $as_item ) {
|
||||
$exploded = explode( ':', $as_item );
|
||||
|
||||
if ( count( $exploded ) > 1 ) {
|
||||
$as_types [] = $exploded[1];
|
||||
$as[ $key ] = $exploded[0];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $keys as $key ) {
|
||||
if ( isset( $target[ $key ] ) ) {
|
||||
if ( isset( $as[ $as_count ] ) ) {
|
||||
$value = $target[ $key ];
|
||||
if ( isset( $as_types[ $as_count ] ) ) {
|
||||
settype( $value, $as_types[ $as_count ] );
|
||||
}
|
||||
$result[ $as[ $as_count ] ] = $value;
|
||||
} else {
|
||||
$result[ $key ] = $target[ $key ];
|
||||
}
|
||||
}
|
||||
++$as_count;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function handle_and_for_where_query( &$target_where_query ) {
|
||||
if ( $target_where_query ) {
|
||||
$target_where_query .= ' AND ';
|
||||
}
|
||||
}
|
||||
|
||||
private function filter_after_and_before( $filters, &$target_where_query ) {
|
||||
// Filters 'after' & 'before' known in advance, and could handled systematically.
|
||||
if ( isset( $filters['after'] ) || isset( $filters['before'] ) ) {
|
||||
$this->handle_and_for_where_query( $target_where_query );
|
||||
|
||||
// TODO: This logic applies only for 'date' format not 'date-time' format.
|
||||
$after = '0000-01-01 00:00:00';
|
||||
$before = '9999-12-31 23:59:59';
|
||||
|
||||
if ( isset( $filters['after'] ) ) {
|
||||
$after = $filters['after']['value'] . ' 00:00:00';
|
||||
}
|
||||
|
||||
if ( isset( $filters['before'] ) ) {
|
||||
$before = $filters['before']['value'] . ' 23:59:59';
|
||||
}
|
||||
|
||||
$after = get_gmt_from_date( $after );
|
||||
$before = get_gmt_from_date( $before );
|
||||
|
||||
$target_where_query .= $this->wpdb->prepare( 'created_at_gmt BETWEEN %s and %s', [ $after, $before ] );
|
||||
}
|
||||
}
|
||||
|
||||
private function filter_status( $filters, &$target_where_query ) {
|
||||
if ( isset( $filters['status'] ) ) {
|
||||
$this->handle_and_for_where_query( $target_where_query );
|
||||
|
||||
switch ( $filters['status']['value'] ) {
|
||||
case 'all':
|
||||
$target_where_query .= 'status != \'' . self::STATUS_TRASH . '\'';
|
||||
break;
|
||||
case 'unread':
|
||||
$target_where_query .= 'status = \'' . self::STATUS_NEW . '\' AND is_read = 0';
|
||||
break;
|
||||
case 'read':
|
||||
$target_where_query .= 'status = \'' . self::STATUS_NEW . '\' AND is_read > 0';
|
||||
break;
|
||||
case 'trash':
|
||||
$target_where_query .= 'status = \'' . self::STATUS_TRASH . '\'';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function filter_ids( $filters, &$target_where_query ) {
|
||||
if ( ! isset( $filters['ids'] ) || empty( $filters['ids'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->handle_and_for_where_query( $target_where_query );
|
||||
|
||||
$ids_collection = new Collection( $filters['ids']['value'] );
|
||||
|
||||
$placeholder = $ids_collection->map( function () {
|
||||
return '%d';
|
||||
} )->implode( ', ' );
|
||||
|
||||
$target_where_query .= $this->wpdb->prepare( "`id` IN ({$placeholder})", $ids_collection->all() ); // phpcs:ignore
|
||||
}
|
||||
|
||||
private function filter_search( $filters, &$target_where_query ) {
|
||||
if ( isset( $filters['search'] ) ) {
|
||||
$this->handle_and_for_where_query( $target_where_query );
|
||||
|
||||
$like = '%' . $this->wpdb->esc_like( $filters['search']['value'] ) . '%';
|
||||
$meta_table_name = $this->get_table_submissions_values();
|
||||
|
||||
$search = $this->wpdb->prepare( 'LIKE %s OR t_submissions.id LIKE %s', [ $like, $like ] );
|
||||
|
||||
$target_where_query .= "(
|
||||
(
|
||||
SELECT GROUP_CONCAT({$meta_table_name}.value)
|
||||
FROM `{$meta_table_name}`
|
||||
WHERE {$meta_table_name}.submission_id = t_submissions.id
|
||||
GROUP BY {$meta_table_name}.submission_id
|
||||
) {$search}
|
||||
)";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter bu element_id and post_id
|
||||
*
|
||||
* @param $filters
|
||||
* @param $target_where_query
|
||||
*/
|
||||
private function filter_by_form( $filters, &$target_where_query ) {
|
||||
if ( ! isset( $filters['form']['value'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->handle_and_for_where_query( $target_where_query );
|
||||
|
||||
list( $post_id, $form_id ) = explode( '_', $filters['form']['value'] );
|
||||
|
||||
$target_where_query .= $this->wpdb->prepare(
|
||||
'post_id = %d AND element_id = %s',
|
||||
$post_id,
|
||||
$form_id
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $filters
|
||||
* @param $target_where_query
|
||||
*/
|
||||
private function filter_by_referer( $filters, &$target_where_query ) {
|
||||
if ( ! isset( $filters['referer']['value'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->handle_and_for_where_query( $target_where_query );
|
||||
|
||||
$target_where_query .= $this->wpdb->prepare(
|
||||
'referer = %s',
|
||||
$filters['referer']['value']
|
||||
);
|
||||
}
|
||||
|
||||
private function handle_order( $order, &$target_order_query ) {
|
||||
if ( ! empty( $order ) ) {
|
||||
$order['by'] = esc_sql( $order['by'] );
|
||||
$order['order'] = strtoupper( $order['order'] );
|
||||
|
||||
if ( ! in_array( $order['order'], [ 'ASC', 'DESC' ], true ) ) {
|
||||
$order['order'] = 'ASC';
|
||||
}
|
||||
|
||||
$target_order_query = 'ORDER BY ' . $order['by'] . ' ' . $order['order'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \stdClass $submission
|
||||
* @param bool $with_form_fields
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_submission_body( $submission, $with_form_fields = false ) {
|
||||
$id = (int) $submission->id;
|
||||
|
||||
$result = [
|
||||
'id' => $id,
|
||||
];
|
||||
|
||||
$result['post'] = $this->extract( $submission, [ 'post_id' ], [ 'id:int' ] );
|
||||
$result['form'] = $this->extract( $submission, [ 'form_name', 'element_id' ], [ 'name' ] );
|
||||
|
||||
$edit_post_id = $submission->post_id;
|
||||
|
||||
// TODO: Should be removed if there is an ability to edit "global widgets"
|
||||
$meta = json_decode( $submission->meta ?? '', true );
|
||||
|
||||
if ( isset( $meta['edit_post_id'] ) ) {
|
||||
$edit_post_id = $meta['edit_post_id'];
|
||||
}
|
||||
|
||||
$document = Plugin::elementor()->documents->get( $edit_post_id );
|
||||
|
||||
if ( $document ) {
|
||||
$result['post']['edit_url'] = $document->get_edit_url();
|
||||
}
|
||||
|
||||
if ( $with_form_fields ) {
|
||||
$form = Form_Snapshot_Repository::instance()
|
||||
->find( $submission->post_id, $submission->element_id );
|
||||
|
||||
if ( $form ) {
|
||||
$result['form']['fields'] = $form->fields;
|
||||
}
|
||||
}
|
||||
|
||||
$user = get_user_by( 'id', $submission->user_id );
|
||||
|
||||
$result['actions_count'] = (int) $submission->actions_count;
|
||||
$result['actions_succeeded_count'] = (int) $submission->actions_succeeded_count;
|
||||
$result['referer'] = $submission->referer;
|
||||
$result['referer_title'] = $submission->referer_title;
|
||||
$result['element_id'] = $submission->element_id;
|
||||
$result['main_meta_id'] = $submission->main_meta_id;
|
||||
$result['user_id'] = $submission->user_id;
|
||||
$result['user_agent'] = $submission->user_agent;
|
||||
$result['user_ip'] = $submission->user_ip;
|
||||
$result['user_name'] = $user ? $user->display_name : null;
|
||||
|
||||
$result['created_at_gmt'] = $submission->created_at_gmt;
|
||||
$result['updated_at_gmt'] = $submission->updated_at_gmt;
|
||||
|
||||
// Return the the dates according to WP current selected timezone.
|
||||
$result['created_at'] = get_date_from_gmt( $submission->created_at_gmt );
|
||||
$result['updated_at'] = get_date_from_gmt( $submission->updated_at_gmt );
|
||||
|
||||
$result['status'] = $submission->status;
|
||||
$result['is_read'] = (bool) $submission->is_read;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function get_new_submission_initial_data( array $submission_data ) {
|
||||
$current_datetime_gmt = current_time( 'mysql', true );
|
||||
$current_datetime = get_date_from_gmt( $current_datetime_gmt );
|
||||
|
||||
$submission_data['hash_id'] = wp_generate_uuid4();
|
||||
|
||||
$submission_data = array_merge( [
|
||||
'created_at_gmt' => $current_datetime_gmt,
|
||||
'updated_at_gmt' => $current_datetime_gmt,
|
||||
'created_at' => $current_datetime,
|
||||
'updated_at' => $current_datetime,
|
||||
'type' => self::TYPE_SUBMISSION,
|
||||
'status' => self::STATUS_NEW,
|
||||
], $submission_data );
|
||||
|
||||
return $submission_data;
|
||||
}
|
||||
|
||||
private function apply_filter( array $filter ) {
|
||||
$where_sql = '';
|
||||
|
||||
$this->filter_after_and_before( $filter, $where_sql );
|
||||
$this->filter_status( $filter, $where_sql );
|
||||
$this->filter_search( $filter, $where_sql );
|
||||
$this->filter_by_form( $filter, $where_sql );
|
||||
$this->filter_ids( $filter, $where_sql );
|
||||
$this->filter_by_referer( $filter, $where_sql );
|
||||
|
||||
if ( ! $where_sql ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return 'WHERE ' . $where_sql;
|
||||
}
|
||||
|
||||
public function __construct() {
|
||||
global $wpdb;
|
||||
|
||||
$this->wpdb = $wpdb;
|
||||
|
||||
$this->table_submissions = $wpdb->prefix . self::E_SUBMISSIONS;
|
||||
$this->table_submissions_values = $wpdb->prefix . self::E_SUBMISSIONS_VALUES;
|
||||
$this->table_submissions_actions_log = $wpdb->prefix . self::E_SUBMISSIONS_ACTIONS_LOG;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
namespace ElementorPro\Modules\Forms\Submissions\Database\Repositories;
|
||||
|
||||
use Elementor\Core\Base\Base_Object;
|
||||
use Elementor\Core\Utils\Collection;
|
||||
use ElementorPro\Modules\Forms\Submissions\Database\Entities\Form_Snapshot;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Form_Snapshot_Repository extends Base_Object {
|
||||
// There are two underscore prefix to avoid duplicate the meta when the post will be published.
|
||||
const POST_META_KEY = '__elementor_forms_snapshot';
|
||||
|
||||
/**
|
||||
* @var static
|
||||
*/
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* @var Collection
|
||||
*/
|
||||
private $cache;
|
||||
|
||||
/**
|
||||
* @return static
|
||||
*/
|
||||
public static function instance() {
|
||||
if ( null === static::$instance ) {
|
||||
static::$instance = new static();
|
||||
}
|
||||
|
||||
return static::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get specific form.
|
||||
*
|
||||
* @param $post_id
|
||||
* @param $form_id
|
||||
* @param bool $from_cache
|
||||
*
|
||||
* @return Form_Snapshot|null
|
||||
*/
|
||||
public function find( $post_id, $form_id, $from_cache = true ) {
|
||||
$key = Form_Snapshot::generate_key( $post_id, $form_id );
|
||||
|
||||
if ( $from_cache && $this->cache->get( $key, false ) ) {
|
||||
return $this->cache->get( $key, false );
|
||||
}
|
||||
|
||||
return $this->save_in_cache(
|
||||
$this->get_post_forms( $post_id )
|
||||
)->get( $key );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the forms.
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function all() {
|
||||
global $wpdb;
|
||||
|
||||
$result = $wpdb->get_results(
|
||||
$wpdb->prepare(
|
||||
"SELECT pm.meta_value, pm.post_id FROM {$wpdb->postmeta} pm WHERE pm.meta_key = %s",
|
||||
static::POST_META_KEY
|
||||
)
|
||||
);
|
||||
|
||||
if ( ! $result ) {
|
||||
return new Collection( [] );
|
||||
}
|
||||
|
||||
foreach ( $result as $post_forms ) {
|
||||
$this->save_in_cache(
|
||||
$this->parse_meta( $post_forms->meta_value, $post_forms->post_id )
|
||||
);
|
||||
}
|
||||
|
||||
return $this->cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $post_id
|
||||
* @param $form_id
|
||||
* @param $data
|
||||
*
|
||||
* @return Form_Snapshot
|
||||
*/
|
||||
public function create_or_update( $post_id, $form_id, $data ) {
|
||||
$forms = $this->get_post_forms( $post_id )
|
||||
->filter( function ( Form_Snapshot $form ) use ( $form_id ) {
|
||||
return $form->id !== $form_id;
|
||||
} );
|
||||
|
||||
$form = new Form_Snapshot( $post_id, $data + [ 'id' => $form_id ] );
|
||||
$forms[] = $form;
|
||||
|
||||
update_post_meta(
|
||||
$post_id,
|
||||
self::POST_META_KEY,
|
||||
// Use `wp_slash` in order to avoid the unslashing during the `update_post_meta`
|
||||
wp_slash( wp_json_encode( $forms->values() ) )
|
||||
);
|
||||
|
||||
$this->save_in_cache( $forms );
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
public function clear_cache() {
|
||||
$this->cache = new Collection( [] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $post_id
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
private function get_post_forms( $post_id ) {
|
||||
$meta_value = get_post_meta( $post_id, self::POST_META_KEY, true );
|
||||
|
||||
if ( ! $meta_value ) {
|
||||
return new Collection( [] );
|
||||
}
|
||||
|
||||
return $this->parse_meta( $meta_value, $post_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive a meta value and transform it to an array of Form objects.
|
||||
*
|
||||
* @param $meta_value
|
||||
* @param $post_id
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
private function parse_meta( $meta_value, $post_id ) {
|
||||
return ( new Collection( json_decode( $meta_value, true ) ) )
|
||||
->map( function ( $item ) use ( $post_id ) {
|
||||
return new Form_Snapshot( $post_id, $item );
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $forms
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
private function save_in_cache( Collection $forms ) {
|
||||
/** @var Form_Snapshot $form */
|
||||
foreach ( $forms as $form ) {
|
||||
$this->cache[ $form->get_key() ] = $form;
|
||||
}
|
||||
|
||||
return $this->cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forms_Repository constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->cache = new Collection( [] );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user