mirror of
https://github.com/proelements/proelements.git
synced 2026-05-05 17:15:58 +00:00
v4.0.4.1
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
namespace ElementorPro\Modules\AtomicForm\Actions;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
abstract class Action_Base {
|
||||
|
||||
/**
|
||||
* Get the action type identifier.
|
||||
*
|
||||
* @return string Action type (e.g., 'email', 'webhook', 'collect-submissions')
|
||||
*/
|
||||
abstract public function get_type(): string;
|
||||
|
||||
/**
|
||||
* Execute the action with the provided form data and widget settings.
|
||||
*
|
||||
* @param array $form_data Sanitized form data submitted by the user.
|
||||
* Example: ['name' => 'John', 'email' => 'john@example.com']
|
||||
* @param array $widget_settings Full widget settings - action extracts what it needs.
|
||||
* Example: ['email_to' => 'admin@site.com', 'email_subject' => 'New form', ...]
|
||||
* @param array $context Additional context (post_id, form_id, form_name).
|
||||
* Example: ['post_id' => 123, 'form_id' => 'contact', 'form_name' => 'Contact Form']
|
||||
* @return array Result array with 'status' and optional data.
|
||||
* Success: ['status' => 'success', 'message' => '...', ...]
|
||||
* Failure: ['status' => 'failed', 'error' => '...', ...]
|
||||
*/
|
||||
abstract public function execute( array $form_data, array $widget_settings, array $context ): array;
|
||||
|
||||
/**
|
||||
* Validate widget settings for this action.
|
||||
*
|
||||
* @param array $widget_settings Widget settings to validate.
|
||||
* @return bool|\WP_Error True if valid, WP_Error otherwise.
|
||||
*/
|
||||
protected function validate_settings( array $widget_settings ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a success result.
|
||||
*
|
||||
* @param string $message Success message.
|
||||
* @param array $additional_data Additional data to include.
|
||||
* @return array
|
||||
*/
|
||||
protected function success( string $message, array $additional_data = [] ): array {
|
||||
return array_merge(
|
||||
[
|
||||
'status' => 'success',
|
||||
'message' => $message,
|
||||
],
|
||||
$additional_data
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a failure result.
|
||||
*
|
||||
* @param string $error Error message.
|
||||
* @param array $additional_data Additional data to include.
|
||||
* @return array
|
||||
*/
|
||||
protected function failure( string $error, array $additional_data = [] ): array {
|
||||
return array_merge(
|
||||
[
|
||||
'status' => 'failed',
|
||||
'error' => $error,
|
||||
],
|
||||
$additional_data
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
namespace ElementorPro\Modules\AtomicForm\Actions;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Action_Runner {
|
||||
|
||||
/**
|
||||
* Registered actions.
|
||||
*
|
||||
* @var Action_Base[]
|
||||
*/
|
||||
private static $actions = [];
|
||||
|
||||
/**
|
||||
* Register an action.
|
||||
*
|
||||
* @param Action_Base $action Action instance to register.
|
||||
* @return void
|
||||
*/
|
||||
public static function register_action( Action_Base $action ): void {
|
||||
self::$actions[ $action->get_type() ] = $action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an action instance by type.
|
||||
*
|
||||
* @param string $type Action type.
|
||||
* @return Action_Base|null Action instance or null if not found.
|
||||
*/
|
||||
public static function create_action( string $type ): ?Action_Base {
|
||||
if ( ! isset( self::$actions[ $type ] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return self::$actions[ $type ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all registered actions.
|
||||
*
|
||||
* @return Action_Base[] Array of registered actions.
|
||||
*/
|
||||
public static function get_registered_actions(): array {
|
||||
return self::$actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an action type is registered.
|
||||
*
|
||||
* @param string $type Action type.
|
||||
* @return bool
|
||||
*/
|
||||
public static function has_action( string $type ): bool {
|
||||
return isset( self::$actions[ $type ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute multiple actions and gather results.
|
||||
*
|
||||
* @param string[] $actions Array of action type strings.
|
||||
* @param array $form_data Sanitized form data.
|
||||
* @param array $widget_settings Full widget settings for actions to extract what they need.
|
||||
* @param array $context Form context (post_id, form_id, form_name).
|
||||
* @return array Results containing actionResults, allActionsSucceeded, failedActions, and optional submissionId.
|
||||
*/
|
||||
public static function execute_actions( array $actions, array $form_data, array $widget_settings, array $context ): array {
|
||||
$action_results = [];
|
||||
$failed_actions = [];
|
||||
|
||||
foreach ( $actions as $action_type ) {
|
||||
|
||||
if ( ! Action_Type::is_valid( $action_type ) ) {
|
||||
$action_results[] = [
|
||||
'type' => $action_type,
|
||||
'status' => 'failed',
|
||||
'error' => sprintf( __( 'Invalid action type: %s', 'elementor-pro' ), $action_type ),
|
||||
];
|
||||
$failed_actions[] = $action_type;
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$action = self::create_action( $action_type );
|
||||
|
||||
if ( ! $action ) {
|
||||
throw new \Exception( sprintf( __( 'Could not create action: %s', 'elementor-pro' ), $action_type ) );
|
||||
}
|
||||
|
||||
$result = $action->execute( $form_data, $widget_settings, $context );
|
||||
|
||||
$action_results[] = array_merge(
|
||||
[ 'type' => $action_type ],
|
||||
$result
|
||||
);
|
||||
} catch ( \Exception $e ) {
|
||||
$action_results[] = [
|
||||
'type' => $action_type,
|
||||
'status' => 'failed',
|
||||
'error' => $e->getMessage(),
|
||||
];
|
||||
$failed_actions[] = $action_type;
|
||||
}
|
||||
}
|
||||
|
||||
$all_actions_succeeded = empty( $failed_actions );
|
||||
|
||||
$response = [
|
||||
'actionResults' => $action_results,
|
||||
'allActionsSucceeded' => $all_actions_succeeded,
|
||||
'failedActions' => $failed_actions,
|
||||
];
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize default actions.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init(): void {
|
||||
self::register_action( new Email_Action() );
|
||||
self::register_action( new Collect_Submissions_Action() );
|
||||
self::register_action( new Webhook_Action() );
|
||||
|
||||
/**
|
||||
* Allow registering custom actions.
|
||||
*
|
||||
* @param Action_Factory $factory The action factory instance.
|
||||
*/
|
||||
do_action( 'elementor_pro/atomic_forms/actions/register', __CLASS__ );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
namespace ElementorPro\Modules\AtomicForm\Actions;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Action_Type {
|
||||
const EMAIL = 'email';
|
||||
const COLLECT_SUBMISSIONS = 'collect-submissions';
|
||||
const WEBHOOK = 'webhook';
|
||||
|
||||
/**
|
||||
* Get all registered action types.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_all_types(): array {
|
||||
return [
|
||||
self::EMAIL,
|
||||
self::COLLECT_SUBMISSIONS,
|
||||
self::WEBHOOK,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an action type is valid.
|
||||
*
|
||||
* @param string $type Action type to validate.
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_valid( string $type ): bool {
|
||||
return in_array( $type, self::get_all_types(), true );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
namespace ElementorPro\Modules\AtomicForm\Actions;
|
||||
|
||||
use ElementorPro\Core\Utils;
|
||||
use ElementorPro\Modules\Forms\Submissions\Database\Query;
|
||||
use ElementorPro\Modules\Forms\Submissions\Database\Repositories\Form_Snapshot_Repository;
|
||||
use Elementor\Utils as ElementorUtils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Collect_Submissions_Action extends Action_Base {
|
||||
|
||||
public function get_type(): string {
|
||||
return Action_Type::COLLECT_SUBMISSIONS;
|
||||
}
|
||||
|
||||
public function execute( array $form_data, array $widget_settings, array $context ): array {
|
||||
$metadata_keys = $this->normalize_metadata_keys( $widget_settings['submissions_metadata'] ?? [] );
|
||||
|
||||
$field_metadata = $context['field_metadata'] ?? [];
|
||||
$fields = $this->prepare_fields( $form_data, $field_metadata );
|
||||
|
||||
$submission_id = Query::get_instance()->add_submission(
|
||||
[
|
||||
'main_meta_id' => 0,
|
||||
'post_id' => $context['post_id'],
|
||||
'referer' => $this->get_referer(),
|
||||
'referer_title' => $this->get_referer_title(),
|
||||
'element_id' => $context['form_id'],
|
||||
'form_name' => $context['form_name'],
|
||||
'campaign_id' => 0,
|
||||
'user_id' => get_current_user_id(),
|
||||
'user_ip' => in_array( 'remote_ip', $metadata_keys, true ) ? Utils::get_client_ip() : '',
|
||||
'user_agent' => in_array( 'user_agent', $metadata_keys, true ) ? $this->get_user_agent() : '',
|
||||
'actions_count' => 0,
|
||||
'actions_succeeded_count' => 0,
|
||||
'meta' => wp_json_encode( [] ),
|
||||
],
|
||||
$fields
|
||||
);
|
||||
|
||||
if ( ! $submission_id ) {
|
||||
return $this->failure( __( 'Failed to save submission to database', 'elementor-pro' ) );
|
||||
}
|
||||
|
||||
$this->store_form_snapshot( $context, $fields );
|
||||
|
||||
return $this->success(
|
||||
__( 'Submission saved successfully', 'elementor-pro' ),
|
||||
[ 'submissionId' => $submission_id ]
|
||||
);
|
||||
}
|
||||
|
||||
private function normalize_metadata_keys( array $raw ): array {
|
||||
$allowed = [ 'remote_ip', 'user_agent' ];
|
||||
|
||||
return array_values( array_intersect( $raw, $allowed ) );
|
||||
}
|
||||
|
||||
private function prepare_fields( array $form_data, array $field_metadata = [] ): array {
|
||||
$fields = [];
|
||||
|
||||
foreach ( $form_data as $key => $value ) {
|
||||
$meta = $field_metadata[ $key ] ?? [];
|
||||
$label = ! empty( $meta['label'] ) ? $meta['label'] : ucwords( str_replace( [ '_', '-' ], ' ', $key ) );
|
||||
$type = ! empty( $meta['type'] ) ? $meta['type'] : $this->guess_field_type( $key, $value );
|
||||
|
||||
$fields[] = [
|
||||
'id' => $key,
|
||||
'type' => $type,
|
||||
'label' => $label,
|
||||
'value' => is_array( $value ) ? implode( ', ', $value ) : $value,
|
||||
];
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
private function guess_field_type( string $key, $value ): string {
|
||||
$key_lower = strtolower( $key );
|
||||
|
||||
if ( strpos( $key_lower, 'email' ) !== false ) {
|
||||
return 'email';
|
||||
}
|
||||
|
||||
if ( strpos( $key_lower, 'phone' ) !== false || strpos( $key_lower, 'tel' ) !== false ) {
|
||||
return 'tel';
|
||||
}
|
||||
|
||||
if ( is_array( $value ) ) {
|
||||
return 'checkbox';
|
||||
}
|
||||
|
||||
if ( strpos( $key_lower, 'message' ) !== false || ( is_string( $value ) && strlen( $value ) > 100 ) ) {
|
||||
return 'textarea';
|
||||
}
|
||||
|
||||
if ( strpos( $key_lower, 'url' ) !== false || strpos( $key_lower, 'website' ) !== false ) {
|
||||
return 'url';
|
||||
}
|
||||
|
||||
return 'text';
|
||||
}
|
||||
|
||||
private function store_form_snapshot( array $context, array $fields ): void {
|
||||
$snapshot_fields = array_map(
|
||||
function ( $field ) {
|
||||
return [
|
||||
'id' => $field['id'],
|
||||
'type' => $field['type'],
|
||||
'label' => $field['label'],
|
||||
];
|
||||
},
|
||||
$fields
|
||||
);
|
||||
|
||||
Form_Snapshot_Repository::instance()->create_or_update(
|
||||
$context['post_id'],
|
||||
$context['form_id'],
|
||||
[
|
||||
'name' => $context['form_name'],
|
||||
'fields' => $snapshot_fields,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
private function get_referer(): string {
|
||||
$referer = ElementorUtils::get_super_global_value( $_SERVER, 'HTTP_REFERER' );
|
||||
if ( $referer ) {
|
||||
return esc_url_raw( wp_unslash( $referer ) );
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
private function get_referer_title(): string {
|
||||
// For now, return empty as we don't have access to the frontend page title
|
||||
return '';
|
||||
}
|
||||
|
||||
private function get_user_agent(): string {
|
||||
$user_agent = ElementorUtils::get_super_global_value( $_SERVER, 'HTTP_USER_AGENT' );
|
||||
if ( $user_agent ) {
|
||||
return sanitize_textarea_field( wp_unslash( $user_agent ) );
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
namespace ElementorPro\Modules\AtomicForm\Actions;
|
||||
|
||||
use ElementorPro\Modules\AtomicForm\Actions\Email_Settings;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Email_Action extends Action_Base {
|
||||
|
||||
public function get_type(): string {
|
||||
return Action_Type::EMAIL;
|
||||
}
|
||||
|
||||
public function execute( array $form_data, array $widget_settings, array $context ): array {
|
||||
$validation = $this->validate_settings( $widget_settings );
|
||||
|
||||
if ( is_wp_error( $validation ) ) {
|
||||
return $this->failure( $validation->get_error_message() );
|
||||
}
|
||||
|
||||
$email_settings = new Email_Settings( $widget_settings );
|
||||
|
||||
$to = $email_settings->to();
|
||||
$from = $email_settings->from();
|
||||
$from_name = $email_settings->from_name();
|
||||
$message = $email_settings->message();
|
||||
$subject = $email_settings->subject();
|
||||
$reply_to = $email_settings->reply_to();
|
||||
$cc = $email_settings->cc();
|
||||
$bcc = $email_settings->bcc();
|
||||
$content_type = $email_settings->content_type();
|
||||
|
||||
$field_metadata = $context['field_metadata'] ?? [];
|
||||
$message = $this->replace_shortcodes( $message, $form_data, 'html' === $content_type, $field_metadata );
|
||||
|
||||
$headers = [];
|
||||
$headers[] = sprintf( 'From: %s <%s>', $from_name, $from );
|
||||
$headers[] = sprintf( 'Reply-To: %s', $reply_to );
|
||||
|
||||
if ( 'html' === $content_type ) {
|
||||
$headers[] = 'Content-Type: text/html; charset=UTF-8';
|
||||
}
|
||||
|
||||
if ( ! empty( $cc ) ) {
|
||||
$headers[] = sprintf( 'Cc: %s', $cc );
|
||||
}
|
||||
|
||||
if ( ! empty( $bcc ) ) {
|
||||
$headers[] = sprintf( 'Bcc: %s', $bcc );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter email headers for atomic forms.
|
||||
*
|
||||
* @param array $headers Email headers.
|
||||
* @param array $form_data Form data.
|
||||
* @param array $widget_settings Widget settings.
|
||||
*/
|
||||
$headers = apply_filters( 'elementor_pro/atomic_forms/email_headers', $headers, $form_data, $widget_settings );
|
||||
|
||||
/**
|
||||
* Filter email message for atomic forms.
|
||||
*
|
||||
* @param string $message Email message.
|
||||
* @param array $form_data Form data.
|
||||
* @param array $widget_settings Widget settings.
|
||||
*/
|
||||
$message = apply_filters( 'elementor_pro/atomic_forms/email_message', $message, $form_data, $widget_settings );
|
||||
|
||||
$email_sent = wp_mail( $to, $subject, $message, $headers );
|
||||
|
||||
if ( ! $email_sent ) {
|
||||
return $this->failure( __( 'Failed to send email', 'elementor-pro' ) );
|
||||
}
|
||||
|
||||
return $this->success( __( 'Email sent successfully', 'elementor-pro' ) );
|
||||
}
|
||||
|
||||
protected function validate_settings( array $widget_settings ) {
|
||||
$email_settings = new Email_Settings( $widget_settings );
|
||||
$email_to = $email_settings->to();
|
||||
|
||||
if ( ! empty( $email_to ) && ! is_email( $email_to ) ) {
|
||||
$emails = array_map( 'trim', explode( ',', $email_to ) );
|
||||
foreach ( $emails as $email ) {
|
||||
if ( ! is_email( $email ) ) {
|
||||
return new \WP_Error(
|
||||
'invalid_email',
|
||||
sprintf(
|
||||
/* translators: %s: Invalid email address. */
|
||||
__( 'Invalid email address: %s', 'elementor-pro' ),
|
||||
$email
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function replace_shortcodes( string $message, array $form_data, bool $is_html, array $field_metadata = [] ): string {
|
||||
$line_break = $is_html ? '<br>' : "\n";
|
||||
|
||||
if ( strpos( $message, '[all-fields]' ) !== false ) {
|
||||
$all_fields_text = '';
|
||||
|
||||
foreach ( $form_data as $key => $value ) {
|
||||
$meta = $field_metadata[ $key ] ?? [];
|
||||
$formatted_key = ! empty( $meta['label'] ) ? $meta['label'] : ucwords( str_replace( [ '_', '-' ], ' ', $key ) );
|
||||
$formatted_value = is_array( $value ) ? implode( ', ', $value ) : $value;
|
||||
|
||||
if ( $is_html ) {
|
||||
$formatted_key = esc_html( $formatted_key );
|
||||
|
||||
if ( is_string( $formatted_value ) ) {
|
||||
$formatted_value = nl2br( esc_html( $formatted_value ) );
|
||||
}
|
||||
}
|
||||
|
||||
$all_fields_text .= sprintf(
|
||||
'%s: %s%s',
|
||||
$formatted_key,
|
||||
$formatted_value,
|
||||
$line_break
|
||||
);
|
||||
}
|
||||
|
||||
$message = str_replace( '[all-fields]', $all_fields_text, $message );
|
||||
}
|
||||
|
||||
$message = preg_replace_callback(
|
||||
'/\[field[^\]]*id=["\']([^"\']+)["\'][^\]]*\]/',
|
||||
function ( $matches ) use ( $form_data ) {
|
||||
$field_id = $matches[1];
|
||||
|
||||
if ( isset( $form_data[ $field_id ] ) ) {
|
||||
$value = $form_data[ $field_id ];
|
||||
|
||||
return is_array( $value ) ? implode( ', ', $value ) : $value;
|
||||
}
|
||||
|
||||
return '';
|
||||
},
|
||||
$message
|
||||
);
|
||||
|
||||
return $message;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
namespace ElementorPro\Modules\AtomicForm\Actions;
|
||||
|
||||
use ElementorPro\Core\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Email_Settings {
|
||||
private $email_settings;
|
||||
|
||||
public function __construct( array $widget_settings ) {
|
||||
$this->email_settings = $widget_settings['email'] ?? [];
|
||||
}
|
||||
|
||||
public function to() {
|
||||
return $this->email_settings['to'] ?? get_option( 'admin_email' );
|
||||
}
|
||||
|
||||
public function from() {
|
||||
return $this->email_settings['from'] ?? 'noreply@' . Utils::get_site_domain();
|
||||
}
|
||||
|
||||
public function from_name() {
|
||||
return $this->email_settings['from-name'] ?? get_bloginfo( 'name' );
|
||||
}
|
||||
|
||||
public function subject() {
|
||||
return $this->email_settings['subject'] ?? sprintf(
|
||||
/* translators: %s: Site title. */
|
||||
__( 'New message from "%s"', 'elementor-pro' ),
|
||||
get_bloginfo( 'name' )
|
||||
);
|
||||
}
|
||||
|
||||
public function message() {
|
||||
return $this->email_settings['message'] ?? '[all-fields]';
|
||||
}
|
||||
|
||||
public function reply_to() {
|
||||
return $this->email_settings['reply-to'] ?? $this->from();
|
||||
}
|
||||
|
||||
public function cc() {
|
||||
return $this->email_settings['cc'] ?? '';
|
||||
}
|
||||
|
||||
public function bcc() {
|
||||
return $this->email_settings['bcc'] ?? '';
|
||||
}
|
||||
|
||||
public function content_type() {
|
||||
return $this->email_settings['send-as'] ?? 'html';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
namespace ElementorPro\Modules\AtomicForm\Actions;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Webhook_Action extends Action_Base {
|
||||
|
||||
public function get_type(): string {
|
||||
return Action_Type::WEBHOOK;
|
||||
}
|
||||
|
||||
public function execute( array $form_data, array $widget_settings, array $context ): array {
|
||||
$validation = $this->validate_settings( $widget_settings );
|
||||
if ( is_wp_error( $validation ) ) {
|
||||
return $this->failure( $validation->get_error_message() );
|
||||
}
|
||||
|
||||
$url = $widget_settings['webhook_url'];
|
||||
$method = strtoupper( $widget_settings['webhook_method'] ?? 'POST' );
|
||||
$timeout = isset( $widget_settings['webhook_timeout'] ) ? absint( $widget_settings['webhook_timeout'] ) : 30;
|
||||
|
||||
$payload = [
|
||||
'formData' => $form_data,
|
||||
'postId' => $context['post_id'],
|
||||
'formId' => $context['form_id'],
|
||||
'formName' => $context['form_name'],
|
||||
'timestamp' => current_time( 'mysql' ),
|
||||
'siteUrl' => get_site_url(),
|
||||
];
|
||||
|
||||
/**
|
||||
* Filter webhook payload for atomic forms.
|
||||
*
|
||||
* @param array $payload Webhook payload.
|
||||
* @param array $form_data Form data.
|
||||
* @param array $widget_settings Widget settings.
|
||||
* @param array $context Form context.
|
||||
*/
|
||||
$payload = apply_filters(
|
||||
'elementor_pro/atomic_forms/webhook_payload',
|
||||
$payload,
|
||||
$form_data,
|
||||
$widget_settings,
|
||||
$context
|
||||
);
|
||||
|
||||
$args = [
|
||||
'method' => $method,
|
||||
'timeout' => $timeout,
|
||||
'headers' => [
|
||||
'Content-Type' => 'application/json',
|
||||
'User-Agent' => 'Elementor Pro Atomic Forms/' . ELEMENTOR_PRO_VERSION,
|
||||
],
|
||||
'body' => wp_json_encode( $payload ),
|
||||
];
|
||||
|
||||
if ( ! empty( $widget_settings['webhook_headers'] ) && is_array( $widget_settings['webhook_headers'] ) ) {
|
||||
$args['headers'] = array_merge( $args['headers'], $widget_settings['webhook_headers'] );
|
||||
}
|
||||
|
||||
$response = wp_remote_request( $url, $args );
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
return $this->failure(
|
||||
sprintf(
|
||||
/* translators: %s: Error message. */
|
||||
__( 'Webhook request failed: %s', 'elementor-pro' ),
|
||||
$response->get_error_message()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$response_code = wp_remote_retrieve_response_code( $response );
|
||||
$response_body = wp_remote_retrieve_body( $response );
|
||||
|
||||
if ( $response_code >= 200 && $response_code < 300 ) {
|
||||
return $this->success(
|
||||
__( 'Webhook delivered successfully', 'elementor-pro' ),
|
||||
[
|
||||
'responseCode' => $response_code,
|
||||
'responseBody' => $response_body,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
return $this->failure(
|
||||
sprintf(
|
||||
/* translators: %d: HTTP status code. */
|
||||
__( 'Webhook returned error status code: %d', 'elementor-pro' ),
|
||||
$response_code
|
||||
),
|
||||
[
|
||||
'responseCode' => $response_code,
|
||||
'responseBody' => $response_body,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
protected function validate_settings( array $widget_settings ) {
|
||||
if ( empty( $widget_settings['webhook_url'] ) ) {
|
||||
return new \WP_Error(
|
||||
'missing_url',
|
||||
__( 'Webhook URL is required', 'elementor-pro' )
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! filter_var( $widget_settings['webhook_url'], FILTER_VALIDATE_URL ) ) {
|
||||
return new \WP_Error(
|
||||
'invalid_url',
|
||||
__( 'Invalid webhook URL', 'elementor-pro' )
|
||||
);
|
||||
}
|
||||
|
||||
if ( isset( $widget_settings['webhook_method'] ) ) {
|
||||
$allowed_methods = [ 'GET', 'POST', 'PUT', 'PATCH', 'DELETE' ];
|
||||
if ( ! in_array( strtoupper( $widget_settings['webhook_method'] ), $allowed_methods, true ) ) {
|
||||
return new \WP_Error(
|
||||
'invalid_method',
|
||||
__( 'Invalid HTTP method', 'elementor-pro' )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
<?php
|
||||
namespace ElementorPro\Modules\AtomicForm;
|
||||
|
||||
use Elementor\Modules\AtomicWidgets\DynamicTags\Dynamic_Prop_Type;
|
||||
use Elementor\Utils as ElementorUtils;
|
||||
use ElementorPro\Modules\AtomicForm\Actions\Action_Runner;
|
||||
use ElementorPro\Modules\AtomicWidgets\Settings_Resolver;
|
||||
use ElementorPro\Modules\Forms\Classes\Ajax_Handler;
|
||||
use ElementorPro\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Atomic_Form_Controller {
|
||||
const NONCE_ACTION = 'elementor_pro_atomic_forms_send_form';
|
||||
|
||||
public static function is_form_submitted(): bool {
|
||||
// phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce is validated in ajax_send_form.
|
||||
return wp_doing_ajax()
|
||||
&& 'elementor_pro_atomic_forms_send_form' === ElementorUtils::get_super_global_value( $_POST, 'action' );
|
||||
// phpcs:enable WordPress.Security.NonceVerification.Missing
|
||||
}
|
||||
|
||||
public function ajax_send_form(): void {
|
||||
|
||||
// phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce is validated below.
|
||||
$post_data = [
|
||||
'_nonce' => ElementorUtils::get_super_global_value( $_POST, '_nonce' ),
|
||||
'post_id' => ElementorUtils::get_super_global_value( $_POST, 'post_id' ),
|
||||
'form_id' => ElementorUtils::get_super_global_value( $_POST, 'form_id' ),
|
||||
'form_name' => ElementorUtils::get_super_global_value( $_POST, 'form_name' ),
|
||||
'form_fields' => ElementorUtils::get_super_global_value( $_POST, 'form_fields' ) ?? [],
|
||||
];
|
||||
// phpcs:enable WordPress.Security.NonceVerification.Missing
|
||||
|
||||
if ( ! $this->is_nonce_valid( $post_data ) ) {
|
||||
$this->send_invalid_form_response();
|
||||
}
|
||||
|
||||
$post_id = absint( $post_data['post_id'] ?? 0 );
|
||||
$form_id = sanitize_text_field( $post_data['form_id'] ?? '' );
|
||||
$form_fields = $post_data['form_fields'] ?? [];
|
||||
|
||||
if ( ! $post_id || ! $form_id || empty( $form_fields ) ) {
|
||||
$this->send_invalid_form_response();
|
||||
}
|
||||
|
||||
$form_data = $this->convert_form_fields_to_data( $form_fields );
|
||||
|
||||
if ( empty( $form_data ) ) {
|
||||
$this->send_invalid_form_response();
|
||||
}
|
||||
|
||||
$field_metadata = $this->extract_field_metadata( $form_fields );
|
||||
|
||||
$widget_settings = $this->get_widget_settings( $post_id, $form_id );
|
||||
|
||||
if ( is_wp_error( $widget_settings ) ) {
|
||||
$this->send_error_response( $widget_settings->get_error_message() );
|
||||
}
|
||||
|
||||
$posted_form_name = sanitize_text_field( $post_data['form_name'] ?? '' );
|
||||
$form_name = $this->resolve_form_name( $posted_form_name, $form_id );
|
||||
|
||||
$spam_check = apply_filters(
|
||||
'elementor_pro/atomic_forms/spam_check',
|
||||
false,
|
||||
$form_fields,
|
||||
$widget_settings,
|
||||
$post_id
|
||||
);
|
||||
|
||||
if ( $spam_check ) {
|
||||
$this->send_error_response(
|
||||
__( 'Your submission was flagged as spam. Please try again or contact the site administrator.', 'elementor-pro' )
|
||||
);
|
||||
}
|
||||
|
||||
$actions = $widget_settings['actions-after-submit'] ?? [];
|
||||
|
||||
if ( empty( $actions ) ) {
|
||||
$this->send_error_response( __( 'No actions configured for this form', 'elementor-pro' ) );
|
||||
}
|
||||
|
||||
$results = Action_Runner::execute_actions(
|
||||
$actions,
|
||||
$form_data,
|
||||
$widget_settings,
|
||||
[
|
||||
'post_id' => $post_id,
|
||||
'form_id' => $form_id,
|
||||
'form_name' => $form_name,
|
||||
'field_metadata' => $field_metadata,
|
||||
]
|
||||
);
|
||||
|
||||
$this->send_response(
|
||||
$results['actionResults'],
|
||||
$results['allActionsSucceeded'],
|
||||
$results['failedActions']
|
||||
);
|
||||
}
|
||||
|
||||
private function is_nonce_valid( array $post_data ): bool {
|
||||
$nonce = $post_data['_nonce'] ?? '';
|
||||
|
||||
if ( ! $nonce ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return wp_verify_nonce( $nonce, self::NONCE_ACTION );
|
||||
}
|
||||
|
||||
private function convert_form_fields_to_data( array $form_fields ): array {
|
||||
$form_data = [];
|
||||
|
||||
foreach ( $form_fields as $field ) {
|
||||
if ( ! is_array( $field ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$id = sanitize_text_field( $field['id'] ?? '' );
|
||||
$value = $field['value'] ?? '';
|
||||
|
||||
if ( ! $id ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( is_array( $value ) ) {
|
||||
$form_data[ $id ] = array_map( 'sanitize_text_field', $value );
|
||||
} else {
|
||||
$type = sanitize_text_field( $field['type'] ?? 'text' );
|
||||
|
||||
if ( 'textarea' === $type ) {
|
||||
$form_data[ $id ] = sanitize_textarea_field( $value );
|
||||
} else {
|
||||
$form_data[ $id ] = sanitize_text_field( $value );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $form_data;
|
||||
}
|
||||
|
||||
private function resolve_form_name( string $posted_form_name, string $form_id ): string {
|
||||
return ! empty( $posted_form_name ) ? $posted_form_name : $form_id;
|
||||
}
|
||||
|
||||
private function extract_field_metadata( array $form_fields ): array {
|
||||
$metadata = [];
|
||||
|
||||
foreach ( $form_fields as $field ) {
|
||||
if ( ! is_array( $field ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$id = sanitize_text_field( $field['id'] ?? '' );
|
||||
|
||||
if ( ! $id ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$metadata[ $id ] = [
|
||||
'label' => sanitize_text_field( $field['label'] ?? '' ),
|
||||
'type' => sanitize_text_field( $field['type'] ?? '' ),
|
||||
];
|
||||
}
|
||||
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
private function get_widget_settings( int $post_id, string $form_id ) {
|
||||
$document = Plugin::elementor()->documents->get( $post_id );
|
||||
|
||||
if ( ! $document ) {
|
||||
return new \WP_Error(
|
||||
'document_not_found',
|
||||
__( 'Document not found', 'elementor-pro' )
|
||||
);
|
||||
}
|
||||
|
||||
$element_data = $document->get_elements_data();
|
||||
$form_element = ElementorUtils::find_element_recursive( $element_data, $form_id );
|
||||
|
||||
if ( empty( $form_element ) ) {
|
||||
return new \WP_Error(
|
||||
'form_not_found',
|
||||
__( 'Form element not found', 'elementor-pro' )
|
||||
);
|
||||
}
|
||||
|
||||
$settings = $form_element['settings'] ?? [];
|
||||
|
||||
$settings = $this->resolve_dynamic_tags_in_settings( $settings, $post_id );
|
||||
|
||||
$resolved = Settings_Resolver::resolve( $settings );
|
||||
|
||||
if ( ! isset( $resolved['actions-after-submit'] ) && isset( $resolved['email'] ) ) {
|
||||
$resolved['actions-after-submit'] = [ 'email' ];
|
||||
}
|
||||
|
||||
return $resolved;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|string $value
|
||||
* @param int $post_id
|
||||
* @return array|string|null
|
||||
*/
|
||||
private function resolve_dynamic_tags_in_settings( $value, int $post_id ) {
|
||||
if ( ! is_array( $value ) ) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if ( Dynamic_Prop_Type::is_dynamic_prop_value( $value ) ) {
|
||||
if ( ! empty( $value['disabled'] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$tag_data = $value['value'] ?? [];
|
||||
$tag_name = $tag_data['name'] ?? '';
|
||||
$tag_settings = $tag_data['settings'] ?? [];
|
||||
|
||||
if ( empty( $tag_name ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Plugin::elementor()->db->switch_to_post( $post_id );
|
||||
|
||||
try {
|
||||
return Plugin::elementor()->dynamic_tags->get_tag_data_content(
|
||||
null,
|
||||
$tag_name,
|
||||
$tag_settings
|
||||
);
|
||||
} finally {
|
||||
Plugin::elementor()->db->restore_current_post();
|
||||
}
|
||||
}
|
||||
|
||||
return array_map( function ( $item ) use ( $post_id ) {
|
||||
return $this->resolve_dynamic_tags_in_settings( $item, $post_id );
|
||||
}, $value );
|
||||
}
|
||||
|
||||
private function send_invalid_form_response(): void {
|
||||
wp_send_json_error( [
|
||||
'message' => Ajax_Handler::get_default_message( Ajax_Handler::INVALID_FORM, [] ),
|
||||
] );
|
||||
}
|
||||
|
||||
private function send_error_response( string $message = '' ): void {
|
||||
wp_send_json_error( [
|
||||
'message' => $message ?? Ajax_Handler::get_default_message( Ajax_Handler::ERROR, [] ),
|
||||
] );
|
||||
}
|
||||
|
||||
private function send_response( array $action_results, bool $all_actions_succeeded, array $failed_actions ): void {
|
||||
$response_data = [
|
||||
'actionResults' => $action_results,
|
||||
'allActionsSucceeded' => $all_actions_succeeded,
|
||||
'failedActions' => $failed_actions,
|
||||
];
|
||||
|
||||
if ( $all_actions_succeeded ) {
|
||||
wp_send_json_success( [
|
||||
'message' => Ajax_Handler::get_default_message( Ajax_Handler::SUCCESS, [] ),
|
||||
'data' => $response_data,
|
||||
] );
|
||||
} else {
|
||||
$has_success = ! empty( $action_results ) && count( $failed_actions ) < count( $action_results );
|
||||
|
||||
if ( $has_success ) {
|
||||
wp_send_json_success( [
|
||||
'message' => Ajax_Handler::get_default_message( Ajax_Handler::SUCCESS, [] ),
|
||||
'data' => $response_data,
|
||||
] );
|
||||
} else {
|
||||
wp_send_json_error( [
|
||||
'message' => Ajax_Handler::get_default_message( Ajax_Handler::ERROR, [] ),
|
||||
'data' => $response_data,
|
||||
] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function __construct() {
|
||||
add_action( 'wp_ajax_elementor_pro_atomic_forms_send_form', [ $this, 'ajax_send_form' ] );
|
||||
add_action( 'wp_ajax_nopriv_elementor_pro_atomic_forms_send_form', [ $this, 'ajax_send_form' ] );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{% set classes = settings.classes | merge( [ base_styles.base ] ) | join(' ') | trim %}
|
||||
{% set id_attribute = settings._cssid is not empty ? 'id=' ~ settings._cssid | e('html_attr') : '' %}
|
||||
{% set interactions_attribute = interactions is not empty ? 'data-interactions=' ~ interactions | json_encode | e('html_attr') : '' %}
|
||||
{% set placeholder_attribute = settings.placeholder is not empty ? 'placeholder=' ~ settings.placeholder | e('html_attr') : '' %}
|
||||
{% set required_attribute = settings.required ? 'required' : '' %}
|
||||
{% set checked_attribute = settings.checked ? 'checked' : '' %}
|
||||
{% set name = settings.name is not empty ? settings.name : settings._cssid is not empty ? 'checkbox_' ~ settings._cssid : 'checkbox_' ~ id %}
|
||||
{% set name_attribute = 'name=' ~ name | e('html_attr') %}
|
||||
{% set value_attribute = settings.value is not empty ? 'value=' ~ settings.value | e('html_attr') : '' %}
|
||||
<input
|
||||
{{ id_attribute }}
|
||||
{{ name_attribute }}
|
||||
{{ value_attribute }}
|
||||
class="{{ classes }}"
|
||||
type="checkbox"
|
||||
data-interaction-id="{{ interaction_id | default(id) }}"
|
||||
{{ settings.attributes | raw }}
|
||||
{{ interactions_attribute }}
|
||||
{{ placeholder_attribute | raw }}
|
||||
{{ required_attribute }}
|
||||
{{ checked_attribute }}
|
||||
/>
|
||||
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
namespace ElementorPro\Modules\AtomicForm\Checkbox;
|
||||
|
||||
use Elementor\Modules\AtomicWidgets\Controls\Section;
|
||||
use Elementor\Modules\AtomicWidgets\Controls\Types\Switch_Control;
|
||||
use Elementor\Modules\AtomicWidgets\Controls\Types\Text_Control;
|
||||
use Elementor\Modules\AtomicWidgets\Elements\Base\Atomic_Widget_Base;
|
||||
use Elementor\Modules\AtomicWidgets\Elements\Base\Has_Template;
|
||||
use Elementor\Modules\AtomicWidgets\PropTypes\Attributes_Prop_Type;
|
||||
use Elementor\Modules\AtomicWidgets\PropTypes\Classes_Prop_Type;
|
||||
use Elementor\Modules\AtomicWidgets\PropTypes\Primitives\Boolean_Prop_Type;
|
||||
use Elementor\Modules\AtomicWidgets\PropTypes\Primitives\String_Prop_Type;
|
||||
use Elementor\Modules\AtomicWidgets\Styles\Style_States;
|
||||
use Elementor\Modules\Components\PropTypes\Overridable_Prop_Type;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Checkbox extends Atomic_Widget_Base {
|
||||
use Has_Template;
|
||||
|
||||
public static $widget_description = 'Display a checkbox input with required, readonly, and attributes.';
|
||||
|
||||
public static function get_element_type(): string {
|
||||
return 'e-form-checkbox';
|
||||
}
|
||||
|
||||
public function get_title(): string {
|
||||
return esc_html__( 'Checkbox', 'elementor-pro' );
|
||||
}
|
||||
|
||||
public function get_icon(): string {
|
||||
return 'eicon-atomic-checkbox';
|
||||
}
|
||||
|
||||
public function get_categories(): array {
|
||||
return [ 'atomic-form' ];
|
||||
}
|
||||
|
||||
public function get_keywords() {
|
||||
return [ 'atomic', 'form', 'checkbox' ];
|
||||
}
|
||||
|
||||
protected static function define_props_schema(): array {
|
||||
return [
|
||||
'classes' => Classes_Prop_Type::make()
|
||||
->default( [] ),
|
||||
'name' => String_Prop_Type::make()
|
||||
->default( '' ),
|
||||
'value' => String_Prop_Type::make()
|
||||
->default( '' ),
|
||||
'required' => Boolean_Prop_Type::make()
|
||||
->default( false ),
|
||||
'checked' => Boolean_Prop_Type::make()
|
||||
->default( false ),
|
||||
'attributes' => Attributes_Prop_Type::make()->meta( Overridable_Prop_Type::ignore() ),
|
||||
];
|
||||
}
|
||||
|
||||
protected function define_atomic_controls(): array {
|
||||
return [
|
||||
Section::make()
|
||||
->set_label( __( 'Content', 'elementor-pro' ) )
|
||||
->set_items( [
|
||||
Text_Control::bind_to( 'name' )
|
||||
->set_label( __( 'Group name', 'elementor-pro' ) )
|
||||
->set_placeholder( __( 'Enter checkbox group name', 'elementor-pro' ) )
|
||||
->set_meta( [
|
||||
'layout' => 'two-columns',
|
||||
] ),
|
||||
Text_Control::bind_to( 'value' )
|
||||
->set_label( __( 'Choice value', 'elementor-pro' ) )
|
||||
->set_placeholder( __( 'Enter choice value', 'elementor-pro' ) )
|
||||
->set_meta( [
|
||||
'layout' => 'two-columns',
|
||||
] ),
|
||||
Switch_Control::bind_to( 'required' )
|
||||
->set_label( __( 'Required', 'elementor-pro' ) ),
|
||||
Switch_Control::bind_to( 'checked' )
|
||||
->set_label( __( 'Checked', 'elementor-pro' ) ),
|
||||
] ),
|
||||
Section::make()
|
||||
->set_label( __( 'Settings', 'elementor-pro' ) )
|
||||
->set_id( 'settings' )
|
||||
->set_items( $this->get_settings_controls() ),
|
||||
];
|
||||
}
|
||||
|
||||
protected function get_settings_controls(): array {
|
||||
return [
|
||||
Text_Control::bind_to( '_cssid' )
|
||||
->set_label( __( 'ID', 'elementor-pro' ) )
|
||||
->set_meta( $this->get_css_id_control_meta() ),
|
||||
];
|
||||
}
|
||||
|
||||
protected function get_templates(): array {
|
||||
return [
|
||||
'checkbox' => __DIR__ . '/checkbox.html.twig',
|
||||
];
|
||||
}
|
||||
|
||||
protected function define_base_styles(): array {
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function get_css_id_control_meta(): array {
|
||||
return [
|
||||
'layout' => 'two-columns',
|
||||
'topDivider' => false,
|
||||
];
|
||||
}
|
||||
|
||||
protected function define_atomic_pseudo_states(): array {
|
||||
return [
|
||||
Style_States::get_pseudo_states_map()['checked'],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
namespace ElementorPro\Modules\AtomicForm\Classes;
|
||||
|
||||
use Elementor\Modules\AtomicWidgets\Controls\Section;
|
||||
use Elementor\Modules\AtomicWidgets\Controls\Types\Switch_Control;
|
||||
use Elementor\Modules\AtomicWidgets\Elements\Atomic_Form\Atomic_Form;
|
||||
use Elementor\Modules\AtomicWidgets\PropTypes\Primitives\Boolean_Prop_Type;
|
||||
use ElementorPro\Core\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Akismet {
|
||||
|
||||
public function __construct() {
|
||||
add_filter( 'elementor/atomic-widgets/props-schema', [ $this, 'register_props' ] );
|
||||
add_filter( 'elementor/atomic-widgets/controls', [ $this, 'register_controls' ], 10, 2 );
|
||||
add_filter( 'elementor_pro/atomic_forms/spam_check', [ $this, 'is_spam' ], 10, 4 );
|
||||
}
|
||||
|
||||
public function register_props( array $schema ): array {
|
||||
if ( ! isset( $schema['actions-after-submit'] ) ) {
|
||||
return $schema;
|
||||
}
|
||||
|
||||
if ( ! $this->is_akismet_active() ) {
|
||||
return $schema;
|
||||
}
|
||||
|
||||
$schema['akismet-enabled'] = Boolean_Prop_Type::make()->default( false );
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
public function register_controls( array $controls, $element ): array {
|
||||
if ( ! ( $element instanceof Atomic_Form ) ) {
|
||||
return $controls;
|
||||
}
|
||||
|
||||
if ( ! $this->is_akismet_active() ) {
|
||||
return $controls;
|
||||
}
|
||||
|
||||
$controls[] = Section::make()
|
||||
->set_label( __( 'Akismet', 'elementor-pro' ) )
|
||||
->set_items( [
|
||||
Switch_Control::bind_to( 'akismet-enabled' )
|
||||
->set_label( __( 'Akismet spam protection', 'elementor-pro' ) ),
|
||||
] );
|
||||
|
||||
return $controls;
|
||||
}
|
||||
|
||||
public function is_akismet_active(): bool {
|
||||
if ( ! class_exists( '\Akismet' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$akismet_key = \Akismet::get_api_key();
|
||||
|
||||
return ! empty( $akismet_key );
|
||||
}
|
||||
|
||||
public function is_spam( bool $is_spam, array $form_fields, array $widget_settings, int $post_id = 0 ): bool {
|
||||
if ( $is_spam ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( empty( $widget_settings['akismet-enabled'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! $this->is_akismet_active() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$mapped = $this->map_fields( $form_fields );
|
||||
$params = $this->build_params( $mapped, $post_id );
|
||||
|
||||
return $this->remote_check_is_spam( $params );
|
||||
}
|
||||
|
||||
private function map_fields( array $form_fields ): array {
|
||||
$mapped = [
|
||||
'comment_author' => '',
|
||||
'comment_author_email' => '',
|
||||
'comment_author_url' => '',
|
||||
'comment_content' => '',
|
||||
];
|
||||
|
||||
$text_fields = [];
|
||||
$textarea_fields = [];
|
||||
|
||||
foreach ( $form_fields as $field ) {
|
||||
if ( ! is_array( $field ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$type = sanitize_text_field( $field['type'] ?? 'text' );
|
||||
$value = sanitize_text_field( $field['value'] ?? '' );
|
||||
$label = sanitize_text_field( $field['label'] ?? '' );
|
||||
|
||||
switch ( $type ) {
|
||||
case 'email':
|
||||
if ( empty( $mapped['comment_author_email'] ) ) {
|
||||
$mapped['comment_author_email'] = $value;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'url':
|
||||
if ( empty( $mapped['comment_author_url'] ) ) {
|
||||
$mapped['comment_author_url'] = $value;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'textarea':
|
||||
$textarea_fields[] = [
|
||||
'label' => $label,
|
||||
'value' => sanitize_textarea_field( $field['value'] ?? '' ),
|
||||
];
|
||||
break;
|
||||
|
||||
case 'text':
|
||||
$text_fields[] = [
|
||||
'label' => $label,
|
||||
'value' => $value,
|
||||
];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$mapped['comment_author'] = $this->concatenate_fields( $text_fields );
|
||||
$mapped['comment_content'] = $this->concatenate_fields( $textarea_fields );
|
||||
|
||||
return $mapped;
|
||||
}
|
||||
|
||||
private function concatenate_fields( array $fields ): string {
|
||||
if ( empty( $fields ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ( 1 === count( $fields ) ) {
|
||||
return $fields[0]['value'];
|
||||
}
|
||||
|
||||
$parts = [];
|
||||
foreach ( $fields as $field ) {
|
||||
$label = ! empty( $field['label'] ) ? $field['label'] : 'Field';
|
||||
$parts[] = $label . ': ' . $field['value'];
|
||||
}
|
||||
|
||||
return implode( ' | ', $parts );
|
||||
}
|
||||
|
||||
private function build_params( array $mapped, int $post_id = 0 ): array {
|
||||
$params = $mapped;
|
||||
|
||||
$params['blog'] = get_option( 'home' );
|
||||
$params['blog_lang'] = get_locale();
|
||||
$params['blog_charset'] = get_option( 'blog_charset' );
|
||||
|
||||
if ( $post_id ) {
|
||||
$params['permalink'] = get_permalink( $post_id );
|
||||
}
|
||||
|
||||
$params['user_ip'] = Utils::get_client_ip();
|
||||
$params['referrer'] = wp_get_referer();
|
||||
|
||||
if ( ! empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
|
||||
$params['user_agent'] = sanitize_textarea_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) );
|
||||
}
|
||||
|
||||
$params['comment_type'] = 'contact-form';
|
||||
|
||||
$ignore = [ 'HTTP_COOKIE', 'HTTP_COOKIE2', 'PHP_AUTH_PW' ];
|
||||
foreach ( $_SERVER as $key => $value ) {
|
||||
if ( ! in_array( $key, $ignore, true ) && is_string( $value ) ) {
|
||||
$params[ $key ] = sanitize_text_field( wp_unslash( $value ) );
|
||||
}
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
private function remote_check_is_spam( array $params ): bool {
|
||||
$response = \Akismet::http_post( _http_build_query( $params, '', '&' ), 'comment-check' );
|
||||
|
||||
return 'true' === $response[1];
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -11,10 +11,10 @@
|
||||
{{ name_attribute }}
|
||||
class="{{ classes }}"
|
||||
type="{{ settings.type }}"
|
||||
data-interaction-id="{{ id }}"
|
||||
data-interaction-id="{{ interaction_id | default(id) }}"
|
||||
{{ settings.attributes | raw }}
|
||||
{{ interactions_attribute }}
|
||||
{{ placeholder_attribute }}
|
||||
{{ placeholder_attribute | raw }}
|
||||
{{ required_attribute }}
|
||||
{{ readonly_attribute }}
|
||||
/>
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
namespace ElementorPro\Modules\AtomicForm\Widgets;
|
||||
namespace ElementorPro\Modules\AtomicForm\Input;
|
||||
|
||||
use Elementor\Modules\AtomicWidgets\Controls\Section;
|
||||
use Elementor\Modules\AtomicWidgets\Controls\Types\Select_Control;
|
||||
@@ -12,7 +12,11 @@ use Elementor\Modules\AtomicWidgets\PropTypes\Classes_Prop_Type;
|
||||
use Elementor\Modules\AtomicWidgets\PropTypes\Primitives\Boolean_Prop_Type;
|
||||
use Elementor\Modules\AtomicWidgets\PropTypes\Primitives\String_Prop_Type;
|
||||
use Elementor\Modules\AtomicWidgets\Styles\Style_Definition;
|
||||
|
||||
use Elementor\Modules\AtomicWidgets\Styles\Style_Variant;
|
||||
use Elementor\Modules\AtomicWidgets\PropTypes\Size_Prop_Type;
|
||||
use Elementor\Modules\AtomicWidgets\PropTypes\Color_Prop_Type;
|
||||
use Elementor\Modules\AtomicWidgets\Styles\Style_States;
|
||||
use Elementor\Modules\Components\PropTypes\Overridable_Prop_Type;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
@@ -40,7 +44,7 @@ class Input extends Atomic_Widget_Base {
|
||||
}
|
||||
|
||||
public function get_keywords() {
|
||||
return [ 'atomic', 'form', 'input', 'text', 'email' ];
|
||||
return [ 'atomic', 'form', 'input', 'text', 'email', 'number', 'tel', 'password' ];
|
||||
}
|
||||
|
||||
protected static function define_props_schema(): array {
|
||||
@@ -51,12 +55,12 @@ class Input extends Atomic_Widget_Base {
|
||||
->default( '' ),
|
||||
'type' => String_Prop_Type::make()
|
||||
->default( 'text' )
|
||||
->enum( [ 'text', 'email' ] ),
|
||||
->enum( [ 'text', 'email', 'number', 'tel', 'password' ] ),
|
||||
'required' => Boolean_Prop_Type::make()
|
||||
->default( false ),
|
||||
'readonly' => Boolean_Prop_Type::make()
|
||||
->default( false ),
|
||||
'attributes' => Attributes_Prop_Type::make(),
|
||||
'attributes' => Attributes_Prop_Type::make()->meta( Overridable_Prop_Type::ignore() ),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -79,6 +83,18 @@ class Input extends Atomic_Widget_Base {
|
||||
'label' => __( 'Email', 'elementor-pro' ),
|
||||
'value' => 'email',
|
||||
],
|
||||
[
|
||||
'label' => __( 'Number', 'elementor-pro' ),
|
||||
'value' => 'number',
|
||||
],
|
||||
[
|
||||
'label' => __( 'Tel', 'elementor-pro' ),
|
||||
'value' => 'tel',
|
||||
],
|
||||
[
|
||||
'label' => __( 'Password', 'elementor-pro' ),
|
||||
'value' => 'password',
|
||||
],
|
||||
] ),
|
||||
Switch_Control::bind_to( 'required' )
|
||||
->set_label( __( 'Required', 'elementor-pro' ) ),
|
||||
@@ -102,13 +118,52 @@ class Input extends Atomic_Widget_Base {
|
||||
|
||||
protected function get_templates(): array {
|
||||
return [
|
||||
'input' => __DIR__ . '/../templates/input.html.twig',
|
||||
'input' => __DIR__ . '/input.html.twig',
|
||||
];
|
||||
}
|
||||
|
||||
protected function define_base_styles(): array {
|
||||
$border_radius_value = Size_Prop_Type::generate( [
|
||||
'size' => 0,
|
||||
'unit' => 'px',
|
||||
] );
|
||||
|
||||
$height_value = Size_Prop_Type::generate( [
|
||||
'size' => 36,
|
||||
'unit' => 'px',
|
||||
] );
|
||||
|
||||
$border_color_value = Color_Prop_Type::generate( '#D6D5D5' );
|
||||
|
||||
return [
|
||||
'base' => Style_Definition::make(),
|
||||
'base' => Style_Definition::make()
|
||||
->add_variant(
|
||||
Style_Variant::make()
|
||||
->add_props( [
|
||||
'border-radius' => $border_radius_value,
|
||||
'height' => $height_value,
|
||||
'border-color' => $border_color_value,
|
||||
'font-size' => Size_Prop_Type::generate( [
|
||||
'size' => 12,
|
||||
'unit' => 'px',
|
||||
] ),
|
||||
] ),
|
||||
)
|
||||
->add_variant(
|
||||
Style_Variant::make()
|
||||
->set_state( Style_States::FOCUS )
|
||||
->add_props( [
|
||||
'border-color' => Color_Prop_Type::generate( '#706F6F' ),
|
||||
'outline-style' => String_Prop_Type::generate( 'none' ),
|
||||
] ),
|
||||
),
|
||||
'base::placeholder' => Style_Definition::make() // this should be changed once we support placeholder/pseudo-elements styles in the styles system.
|
||||
->add_variant(
|
||||
Style_Variant::make()
|
||||
->add_props( [
|
||||
'color' => Color_Prop_Type::generate( '#9DA5AE' ),
|
||||
] ),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -118,4 +173,5 @@ class Input extends Atomic_Widget_Base {
|
||||
'topDivider' => false,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
{{ id_attribute }}
|
||||
class="{{ classes }}"
|
||||
{{ for_attribute }}
|
||||
data-interaction-id="{{ id }}"
|
||||
data-interaction-id="{{ interaction_id | default(id) }}"
|
||||
{{ settings.attributes | raw }}
|
||||
{{ interactions_attribute }}
|
||||
>{{ settings.text | striptags(allowed_tags) | raw }}</label>
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
namespace ElementorPro\Modules\AtomicForm\Widgets;
|
||||
namespace ElementorPro\Modules\AtomicForm\Label;
|
||||
|
||||
use Elementor\Modules\AtomicWidgets\Controls\Section;
|
||||
use Elementor\Modules\AtomicWidgets\Controls\Types\Text_Control;
|
||||
@@ -9,9 +9,12 @@ use Elementor\Modules\AtomicWidgets\Elements\Base\Has_Template;
|
||||
use Elementor\Modules\AtomicWidgets\PropTypes\Attributes_Prop_Type;
|
||||
use Elementor\Modules\AtomicWidgets\PropTypes\Classes_Prop_Type;
|
||||
use Elementor\Modules\AtomicWidgets\PropTypes\Primitives\String_Prop_Type;
|
||||
use Elementor\Modules\AtomicWidgets\PropTypes\Html_Prop_Type;
|
||||
|
||||
|
||||
use Elementor\Modules\AtomicWidgets\PropTypes\Html_V3_Prop_Type;
|
||||
use Elementor\Modules\AtomicWidgets\Styles\Style_Definition;
|
||||
use Elementor\Modules\AtomicWidgets\Styles\Style_Variant;
|
||||
use Elementor\Modules\AtomicWidgets\PropTypes\Size_Prop_Type;
|
||||
use Elementor\Modules\AtomicWidgets\PropTypes\Color_Prop_Type;
|
||||
use Elementor\Modules\Components\PropTypes\Overridable_Prop_Type;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
@@ -44,13 +47,18 @@ class Label extends Atomic_Widget_Base {
|
||||
|
||||
protected static function define_props_schema(): array {
|
||||
return [
|
||||
'tag' => String_Prop_Type::make()
|
||||
->default( 'label' ),
|
||||
'classes' => Classes_Prop_Type::make()
|
||||
->default( [] ),
|
||||
'text' => Html_Prop_Type::make()
|
||||
->default( 'Form label' ),
|
||||
'text' => Html_V3_Prop_Type::make()
|
||||
->default( [
|
||||
'content' => String_Prop_Type::generate( 'Form label' ),
|
||||
'children' => [],
|
||||
] ),
|
||||
'input-id' => String_Prop_Type::make()
|
||||
->default( '' ),
|
||||
'attributes' => Attributes_Prop_Type::make(),
|
||||
'attributes' => Attributes_Prop_Type::make()->meta( Overridable_Prop_Type::ignore() ),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -84,7 +92,7 @@ class Label extends Atomic_Widget_Base {
|
||||
|
||||
protected function get_templates(): array {
|
||||
return [
|
||||
'label' => __DIR__ . '/../templates/label.html.twig',
|
||||
'label' => __DIR__ . '/label.html.twig',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -94,4 +102,24 @@ class Label extends Atomic_Widget_Base {
|
||||
'topDivider' => false,
|
||||
];
|
||||
}
|
||||
|
||||
protected function define_base_styles(): array {
|
||||
|
||||
$text_color_value = Color_Prop_Type::generate( '#0c0d0e' );
|
||||
$font_size_value = Size_Prop_Type::generate( [
|
||||
'size' => 14,
|
||||
'unit' => 'px',
|
||||
] );
|
||||
|
||||
return [
|
||||
'base' => Style_Definition::make()
|
||||
->add_variant(
|
||||
Style_Variant::make()
|
||||
->add_props( [
|
||||
'color' => $text_color_value,
|
||||
'font-size' => $font_size_value,
|
||||
] ),
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{% set classes = settings.classes | merge( [ base_styles.base ] ) | join(' ') | trim %}
|
||||
{% set id_attribute = settings._cssid is not empty ? 'id=' ~ settings._cssid | e('html_attr') : '' %}
|
||||
{% set interactions_attribute = interactions is not empty ? 'data-interactions=' ~ interactions | json_encode | e('html_attr') : '' %}
|
||||
<button
|
||||
{{ id_attribute }}
|
||||
class="{{ classes }}"
|
||||
type="submit"
|
||||
data-interaction-id="{{ interaction_id | default(id) }}"
|
||||
{{ settings.attributes | raw }}
|
||||
{{ interactions_attribute }}
|
||||
>{{ settings.label | e }}</button>
|
||||
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
namespace ElementorPro\Modules\AtomicForm\Submit_Button;
|
||||
|
||||
use Elementor\Modules\AtomicWidgets\Controls\Section;
|
||||
use Elementor\Modules\AtomicWidgets\Controls\Types\Text_Control;
|
||||
use Elementor\Modules\AtomicWidgets\Elements\Base\Atomic_Widget_Base;
|
||||
use Elementor\Modules\AtomicWidgets\Elements\Base\Has_Template;
|
||||
use Elementor\Modules\AtomicWidgets\PropTypes\Attributes_Prop_Type;
|
||||
use Elementor\Modules\AtomicWidgets\PropTypes\Background_Prop_Type;
|
||||
use Elementor\Modules\AtomicWidgets\PropTypes\Classes_Prop_Type;
|
||||
use Elementor\Modules\AtomicWidgets\PropTypes\Color_Prop_Type;
|
||||
use Elementor\Modules\AtomicWidgets\PropTypes\Dimensions_Prop_Type;
|
||||
use Elementor\Modules\AtomicWidgets\PropTypes\Primitives\String_Prop_Type;
|
||||
use Elementor\Modules\AtomicWidgets\PropTypes\Size_Prop_Type;
|
||||
use Elementor\Modules\AtomicWidgets\Styles\Style_Definition;
|
||||
use Elementor\Modules\AtomicWidgets\Styles\Style_Variant;
|
||||
use Elementor\Modules\Components\PropTypes\Overridable_Prop_Type;
|
||||
use Elementor\Modules\AtomicWidgets\Styles\Style_States;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Submit_Button extends Atomic_Widget_Base {
|
||||
use Has_Template;
|
||||
|
||||
public static $widget_description = 'Display a submit button with customizable label text.';
|
||||
private static $button_background_color_hover = '#323232';
|
||||
|
||||
public static function get_element_type(): string {
|
||||
return 'e-form-submit-button';
|
||||
}
|
||||
|
||||
public function get_title(): string {
|
||||
return esc_html__( 'Submit button', 'elementor-pro' );
|
||||
}
|
||||
|
||||
public function get_icon(): string {
|
||||
return 'eicon-atomic-submit-button';
|
||||
}
|
||||
|
||||
public function get_categories(): array {
|
||||
return [ 'atomic-form' ];
|
||||
}
|
||||
|
||||
public function get_keywords() {
|
||||
return [ 'atomic', 'form', 'button', 'submit', 'send' ];
|
||||
}
|
||||
|
||||
protected static function define_props_schema(): array {
|
||||
return [
|
||||
'classes' => Classes_Prop_Type::make()
|
||||
->default( [] ),
|
||||
'label' => String_Prop_Type::make()
|
||||
->default( 'Submit' ),
|
||||
'attributes' => Attributes_Prop_Type::make()->meta( Overridable_Prop_Type::ignore() ),
|
||||
];
|
||||
}
|
||||
|
||||
protected function define_atomic_controls(): array {
|
||||
return [
|
||||
Section::make()
|
||||
->set_label( __( 'Content', 'elementor-pro' ) )
|
||||
->set_items( [
|
||||
Text_Control::bind_to( 'label' )
|
||||
->set_label( __( 'Button Text', 'elementor-pro' ) )
|
||||
->set_placeholder( 'Submit' ),
|
||||
] ),
|
||||
Section::make()
|
||||
->set_label( __( 'Settings', 'elementor-pro' ) )
|
||||
->set_id( 'settings' )
|
||||
->set_items( $this->get_settings_controls() ),
|
||||
];
|
||||
}
|
||||
|
||||
protected function get_settings_controls(): array {
|
||||
return [
|
||||
Text_Control::bind_to( '_cssid' )
|
||||
->set_label( __( 'ID', 'elementor-pro' ) )
|
||||
->set_meta( $this->get_css_id_control_meta() ),
|
||||
];
|
||||
}
|
||||
|
||||
protected function get_templates(): array {
|
||||
return [
|
||||
'submit_button' => __DIR__ . '/submit-button.html.twig',
|
||||
];
|
||||
}
|
||||
|
||||
protected function define_base_styles(): array {
|
||||
$background_color_value = Background_Prop_Type::generate( [
|
||||
'color' => Color_Prop_Type::generate( '#000' ),
|
||||
] );
|
||||
$text_color_value = Color_Prop_Type::generate( '#fff' );
|
||||
$display_value = String_Prop_Type::generate( 'flex' );
|
||||
$padding_value = Dimensions_Prop_Type::generate( [
|
||||
'block-start' => Size_Prop_Type::generate( [
|
||||
'size' => 10,
|
||||
'unit' => 'px',
|
||||
]),
|
||||
'inline-end' => Size_Prop_Type::generate( [
|
||||
'size' => 30,
|
||||
'unit' => 'px',
|
||||
]),
|
||||
'block-end' => Size_Prop_Type::generate( [
|
||||
'size' => 10,
|
||||
'unit' => 'px',
|
||||
]),
|
||||
'inline-start' => Size_Prop_Type::generate( [
|
||||
'size' => 28,
|
||||
'unit' => 'px',
|
||||
]),
|
||||
]);
|
||||
$justify_content_value = String_Prop_Type::generate( 'center' );
|
||||
$align_items_value = String_Prop_Type::generate( 'center' );
|
||||
$background_color_hover_value = Background_Prop_Type::generate( [
|
||||
'color' => Color_Prop_Type::generate( self::$button_background_color_hover ),
|
||||
] );
|
||||
$border_base_size = Size_Prop_Type::generate( [
|
||||
'size' => 0,
|
||||
'unit' => 'px',
|
||||
] );
|
||||
return [
|
||||
'base' => Style_Definition::make()
|
||||
->add_variant(
|
||||
Style_Variant::make()
|
||||
->add_prop( 'background', $background_color_value )
|
||||
->add_prop( 'color', $text_color_value )
|
||||
->add_prop( 'display', $display_value )
|
||||
->add_prop( 'padding', $padding_value )
|
||||
->add_prop( 'justify-content', $justify_content_value )
|
||||
->add_prop( 'align-items', $align_items_value )
|
||||
->add_prop( 'border-radius', $border_base_size )
|
||||
->add_prop( 'border-width', $border_base_size )
|
||||
)
|
||||
->add_variant(
|
||||
Style_Variant::make()
|
||||
->set_state( Style_States::HOVER )
|
||||
->add_prop( 'background', $background_color_hover_value )
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
protected function get_css_id_control_meta(): array {
|
||||
return [
|
||||
'layout' => 'two-columns',
|
||||
'topDivider' => false,
|
||||
];
|
||||
}
|
||||
|
||||
public static function get_inline_styles(): string {
|
||||
$base_class = self::get_element_type() . '-base';
|
||||
return ".{$base_class} { cursor: pointer; box-sizing: border-box; }";
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -14,10 +14,10 @@
|
||||
{{ id_attribute }}
|
||||
{{ name_attribute }}
|
||||
class="{{ classes }}"
|
||||
data-interaction-id="{{ id }}"
|
||||
data-interaction-id="{{ interaction_id | default(id) }}"
|
||||
{{ settings.attributes | raw }}
|
||||
{{ interactions_attribute }}
|
||||
{{ placeholder_attribute }}
|
||||
{{ placeholder_attribute | raw }}
|
||||
{{ required_attribute }}
|
||||
{{ readonly_attribute }}
|
||||
{{ rows_attribute }}
|
||||
+50
-4
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
namespace ElementorPro\Modules\AtomicForm\Widgets;
|
||||
namespace ElementorPro\Modules\AtomicForm\Textarea;
|
||||
|
||||
use Elementor\Modules\AtomicWidgets\Controls\Section;
|
||||
use Elementor\Modules\AtomicWidgets\Controls\Types\Number_Control;
|
||||
@@ -13,6 +13,11 @@ use Elementor\Modules\AtomicWidgets\PropTypes\Primitives\Boolean_Prop_Type;
|
||||
use Elementor\Modules\AtomicWidgets\PropTypes\Primitives\Number_Prop_Type;
|
||||
use Elementor\Modules\AtomicWidgets\PropTypes\Primitives\String_Prop_Type;
|
||||
use Elementor\Modules\AtomicWidgets\Styles\Style_Definition;
|
||||
use Elementor\Modules\AtomicWidgets\Styles\Style_Variant;
|
||||
use Elementor\Modules\AtomicWidgets\PropTypes\Size_Prop_Type;
|
||||
use Elementor\Modules\AtomicWidgets\PropTypes\Color_Prop_Type;
|
||||
use Elementor\Modules\AtomicWidgets\Styles\Style_States;
|
||||
use Elementor\Modules\Components\PropTypes\Overridable_Prop_Type;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
@@ -59,7 +64,7 @@ class Textarea extends Atomic_Widget_Base {
|
||||
->default( true ),
|
||||
'minlength' => Number_Prop_Type::make(),
|
||||
'maxlength' => Number_Prop_Type::make(),
|
||||
'attributes' => Attributes_Prop_Type::make(),
|
||||
'attributes' => Attributes_Prop_Type::make()->meta( Overridable_Prop_Type::ignore() ),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -107,13 +112,46 @@ class Textarea extends Atomic_Widget_Base {
|
||||
|
||||
protected function get_templates(): array {
|
||||
return [
|
||||
'textarea' => __DIR__ . '/../templates/textarea.html.twig',
|
||||
'textarea' => __DIR__ . '/textarea.html.twig',
|
||||
];
|
||||
}
|
||||
|
||||
protected function define_base_styles(): array {
|
||||
$border_radius_value = Size_Prop_Type::generate( [
|
||||
'size' => 0,
|
||||
'unit' => 'px',
|
||||
] );
|
||||
|
||||
$border_color_value = Color_Prop_Type::generate( '#D6D5D5' );
|
||||
|
||||
return [
|
||||
'base' => Style_Definition::make(),
|
||||
'base' => Style_Definition::make()
|
||||
->add_variant(
|
||||
Style_Variant::make()
|
||||
->add_props( [
|
||||
'border-radius' => $border_radius_value,
|
||||
'border-color' => $border_color_value,
|
||||
'font-size' => Size_Prop_Type::generate( [
|
||||
'size' => 12,
|
||||
'unit' => 'px',
|
||||
] ),
|
||||
] ),
|
||||
)
|
||||
->add_variant(
|
||||
Style_Variant::make()
|
||||
->set_state( Style_States::FOCUS )
|
||||
->add_props( [
|
||||
'border-color' => Color_Prop_Type::generate( '#706F6F' ),
|
||||
'outline-style' => String_Prop_Type::generate( 'none' ),
|
||||
] ),
|
||||
),
|
||||
'base::placeholder' => Style_Definition::make() // this should be changed once we support placeholder/pseudo-elements styles in the styles system.
|
||||
->add_variant(
|
||||
Style_Variant::make()
|
||||
->add_props( [
|
||||
'color' => Color_Prop_Type::generate( '#9DA5AE' ),
|
||||
] ),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -123,4 +161,12 @@ class Textarea extends Atomic_Widget_Base {
|
||||
'topDivider' => false,
|
||||
];
|
||||
}
|
||||
|
||||
public static function get_inline_styles(): string {
|
||||
$base_class = self::get_element_type() . '-base';
|
||||
// Default html textarea is resizable, but we want control over it from settings.
|
||||
$inline_css = ".{$base_class}:not([data-resizable]) { resize: none; }";
|
||||
|
||||
return $inline_css;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user