* @copyright Copyright (c) 2018 Ekomi ltd (http://www.ekomi.de) * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) * @link http://www.ekomi.de * * Plugin Name: eKomi Integration * Plugin URI: ekomi.de * Description: This plugin allows you to integrate your WordPress shop easily with eKomi system to collect verified reviews. * Version: 3.4.0 * Author: eKomi * Author URI: ekomi.de * Text Domain: ekomi-integration */ namespace ekomi_integration; /** * Plugin version. */ const PLUGIN_VERSION = '3.4.0'; /** * Constant values. */ const TIMESTAMP_DAYS = 15; const ONE_DAY_IN_SECONDS = 86400; const EXPORT_METHOD_STATUS = 'status'; const EXPORT_METHOD_CRON = 'cron'; const ENABLE_VALUE = '1'; const DISABLE_VALUE = '0'; /** * eKomi SFF (Shop Feedback Form) constants. */ const SFF_OPTION_ENABLE = 'EKOMISFF_ENABLE'; const SFF_OPTION_FORM_ID = 'EKOMISFF_FORM_ID'; const SFF_OPTION_WIDGET_WIDTH = 'EKOMISFF_WIDGET_WIDTH'; const SFF_OPTION_WIDGET_HEIGHT = 'EKOMISFF_WIDGET_HEIGHT'; const SFF_OPTION_TRANSACTION_PREF = 'EKOMISFF_TRANSACTION_ID_PREFIX'; /** * eKomi Floating Widget constants. */ const FLOATING_OPTION_ENABLE = 'EKOMIFLOATING_ENABLE'; const FLOATING_OPTION_CODE = 'EKOMIFLOATING_CODE'; /** * Base URL for the eKomi SFF iframe. * * @since 3.4.0 */ const SFF_BASE_URL = 'https://smartforms.ekomi.com'; /** * The URL where the order data is sent. */ const URL_TO_SEND_DATA = 'https://plugins-dashboard.ekomiapps.de/api/v1/order'; /** * The URL to validate the shop id and password. */ const URL_GET_SETTINGS = 'https://api.ekomi.de/v3/getSettings'; /** * Product identifier options. */ const PRODUCT_IDENTIFIER_ID = 'id'; const PRODUCT_IDENTIFIER_SKU = 'sku'; if ( ! defined( 'ABSPATH' ) ) { exit; } /** * Checks if WooCommerce is installed and activated. * * @return void */ function ekomi_integration() { if ( ! is_plugin_active( 'woocommerce/woocommerce.php' ) && current_user_can( 'activate_plugins' ) ) { wp_die( 'Sorry, but this plugin requires the WooCommerce Plugin to be installed and active.' ); } } register_activation_hook( __FILE__, __NAMESPACE__ . '\\ekomi_integration' ); /** * Adds data on admin menu page. * * @return void */ function ekomi_integration_config() { add_menu_page( __( 'eKomi Integration Configuration', 'ekomi-integration' ), __( 'eKomi Integration', 'ekomi-integration' ), 'manage_options', 'ekomi-integration-plugin', __NAMESPACE__ . '\\ekomi_integration_init', '', 100 ); } add_action( 'admin_menu', __NAMESPACE__ . '\\ekomi_integration_config' ); /** * Processes and validates form data. * * @return void */ function ekomi_integration_init() { if ( ! current_user_can( 'manage_options' ) ) { die( esc_attr( __( 'You don\'t have the required permissions to edit this Plugin' ) ) ); } wp_register_style( 'ekomi-integration', plugins_url( 'css/configuration.css', __FILE__ ), $deps = array(), $ver = false, $media = 'all' ); wp_enqueue_style( 'ekomi-integration' ); $order_statuses = ei_get_order_statuses(); $data = array(); $response['is_submited'] = false; $default_lang = ei_get_default_lang_code(); load_plugin_textdomain( 'ekomi-integration', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' ); $lang_code = $default_lang; if ( isset( $_GET['lang'] ) ) { $lang_code = sanitize_key( $_GET['lang'] ); } $lang_code = explode( '_', $lang_code )[0]; ei_load_textdomain( 'ekomi-integration', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/', $lang_code ); if ( isset( $_POST['ee_shop_id'] ) && isset( $_POST['ee_shop_pw'] ) ) { if ( ! check_admin_referer( 'ekomi-integration-nonce', 'ee_nonce_field' ) ) { die( esc_attr( __( 'Validation Error!' ) ) ); } $sanitized_data = ei_sanitize_configuration_data( $_POST ); $validation_errors = ei_validate_configuration_data( $sanitized_data ); if ( ! empty( $validation_errors ) ) { $response = array( 'status' => 'error', 'message' => implode( ' ', $validation_errors ), 'is_submited' => true, ); } else { $response = ei_authenticate( $sanitized_data['shop_id'], $sanitized_data['shop_pw'] ); $response['is_submited'] = true; if ( 'success' === $response['status'] ) { ei_update_options_data( $sanitized_data, $lang_code ); /** * Registering Hooks on selected order statuses. */ ei_register_hooks( $sanitized_data['statuses'] ); } } } $languages_arr = ei_prepare_lang_array( $default_lang ); $data = ei_get_options_data( $lang_code ); include 'configuration.php'; } /** * Sanitizes the configurations data. * * @param array $post_data Form post data. * * @return array */ function ei_sanitize_configuration_data( $post_data ) { $configurations = array(); $configurations['shop_id'] = intval( $post_data['ee_shop_id'] ); $configurations['shop_pw'] = sanitize_text_field( $post_data['ee_shop_pw'] ); $configurations['active'] = isset( $post_data['ee_active'] ) ? intval( $post_data['ee_active'] ) : 0; $configurations['t_and_c'] = isset( $post_data['ee_t_and_c'] ) ? intval( $post_data['ee_t_and_c'] ) : 1; $configurations['reviews'] = isset( $post_data['ee_product_reviews'] ) ? intval( $post_data['ee_product_reviews'] ) : 0; $configurations['mode'] = isset( $post_data['ee_mode'] ) ? sanitize_text_field( wp_unslash( $post_data['ee_mode'] ) ) : 'email'; $configurations['show_prc_widget'] = isset( $post_data['ee_show_prc_widget'] ) ? intval( $post_data['ee_show_prc_widget'] ) : 0; $configurations['statuses'] = array(); $post_order_statuses = ( isset( $post_data['ee_order_statuses'] ) ) ? wp_unslash( $post_data['ee_order_statuses'] ) : array(); if ( is_array( $post_order_statuses ) ) { foreach ( $post_order_statuses as $order_status_id => $order_status ) { $configurations['statuses'][] = sanitize_text_field( $order_status ); } } $configurations['active'] = is_numeric( $configurations['active'] ) ? $configurations['active'] : 0; $configurations['reviews'] = is_numeric( $configurations['reviews'] ) ? $configurations['reviews'] : 0; $configurations['show_prc_widget'] = is_numeric( $configurations['show_prc_widget'] ) ? $configurations['show_prc_widget'] : 0; $modes_list = array( 'email', 'sms', 'fallback' ); $configurations['mode'] = in_array( $configurations['mode'], $modes_list, true ) ? $configurations['mode'] : 'email'; $configurations['product_identifier'] = isset( $post_data['ee_product_identifier'] ) ? sanitize_text_field( wp_unslash( $post_data['ee_product_identifier'] ) ) : PRODUCT_IDENTIFIER_ID; $configurations['exclude_products'] = isset( $post_data['ee_exclude_products'] ) ? sanitize_text_field( wp_unslash( $post_data['ee_exclude_products'] ) ) : ']'; // Optional slider widget HTML/JS snippet (not required). // Trust admins: store raw snippet to preserve inline styles like 'display: none;'. $configurations['slider_widget_snippet'] = isset( $post_data['ee_slider_widget_snippet'] ) ? wp_unslash( $post_data['ee_slider_widget_snippet'] ) : ''; $configurations['prc_widget_token'] = isset( $post_data['ee_prc_widget_token'] ) ? sanitize_text_field( wp_unslash( $post_data['ee_prc_widget_token'] ) ) : ']'; $configurations['export_method'] = isset( $post_data['ee_export_method'] ) ? sanitize_text_field( wp_unslash( $post_data['ee_export_method'] ) ) : 'status'; $configurations['turnaround_time'] = isset( $post_data['ee_turnaround_time'] ) ? intval( $post_data['ee_turnaround_time'] ) : 7; // eKomi SFF (Shop Feedback Form) configuration. $configurations['sff_enable'] = isset( $post_data[ SFF_OPTION_ENABLE ] ) ? intval( $post_data[ SFF_OPTION_ENABLE ] ) : 0; $configurations['sff_form_id'] = isset( $post_data[ SFF_OPTION_FORM_ID ] ) ? sanitize_text_field( wp_unslash( $post_data[ SFF_OPTION_FORM_ID ] ) ) : ''; $configurations['sff_widget_width'] = isset( $post_data[ SFF_OPTION_WIDGET_WIDTH ] ) ? sanitize_text_field( wp_unslash( $post_data[ SFF_OPTION_WIDGET_WIDTH ] ) ) : ''; $configurations['sff_widget_height'] = isset( $post_data[ SFF_OPTION_WIDGET_HEIGHT ] ) ? sanitize_text_field( wp_unslash( $post_data[ SFF_OPTION_WIDGET_HEIGHT ] ) ) : ''; $configurations['sff_transaction_prefix'] = isset( $post_data[ SFF_OPTION_TRANSACTION_PREF ] ) ? sanitize_text_field( wp_unslash( $post_data[ SFF_OPTION_TRANSACTION_PREF ] ) ) : ''; // eKomi Floating Widget configuration. $configurations['floating_enable'] = isset( $post_data[ FLOATING_OPTION_ENABLE ] ) ? intval( $post_data[ FLOATING_OPTION_ENABLE ] ) : 0; $configurations['floating_code'] = isset( $post_data[ FLOATING_OPTION_CODE ] ) ? wp_unslash( $post_data[ FLOATING_OPTION_CODE ] ) : ''; return $configurations; } /** * Validates the configuration data. * * @param array $configurations Sanitized configuration data. * * @return array Array of validation error messages. */ function ei_validate_configuration_data( $configurations ) { $errors = array(); if ( ! empty( $configurations['floating_enable'] ) && ENABLE_VALUE === (string) $configurations['floating_enable'] ) { if ( empty( trim( $configurations['floating_code'] ) ) ) { $errors[] = __( 'Floating Widget Code is required when the Floating Widget is enabled.', 'ekomi-integration' ); } } if ( ! empty( $configurations['sff_enable'] ) && ENABLE_VALUE === (string) $configurations['sff_enable'] ) { if ( empty( trim( $configurations['sff_form_id'] ) ) ) { $errors[] = __( 'SFF Form ID is required when the SFF Popup is enabled.', 'ekomi-integration' ); } } return $errors; } /** * Update the plugins options data. * * @param array $configurations Sanitized configuration data. * @param string $lang_code The selected language iso code. * * @return void */ function ei_update_options_data( $configurations, $lang_code ) { update_option( $lang_code . '_ee_active', $configurations['active'] ); update_option( $lang_code . '_ee_shop_id', $configurations['shop_id'] ); update_option( $lang_code . '_ee_shop_pw', $configurations['shop_pw'] ); update_option( $lang_code . '_ee_product_reviews', $configurations['reviews'] ); update_option( $lang_code . '_ee_mode', $configurations['mode'] ); update_option('_ee_order_statuses', $configurations['statuses'] ); update_option( $lang_code . '_ee_show_prc_widget', $configurations['show_prc_widget'] ); update_option( $lang_code . '_ee_prc_widget_token', $configurations['prc_widget_token'] ); update_option( $lang_code . '_ee_product_identifier', $configurations['product_identifier'] ); update_option( $lang_code . '_ee_exclude_products', $configurations['exclude_products'] ); update_option( $lang_code . '_ee_slider_widget_snippet', $configurations['slider_widget_snippet'] ); update_option('_ee_export_method', $configurations['export_method'] ); update_option( $lang_code . '_ee_t_and_c', $configurations['t_and_c'] ); update_option('_ee_turnaround_time', $configurations['turnaround_time'] ); // eKomi SFF (Shop Feedback Form) options – not language specific. update_option( SFF_OPTION_ENABLE, $configurations['sff_enable'] ); update_option( SFF_OPTION_FORM_ID, $configurations['sff_form_id'] ); update_option( SFF_OPTION_WIDGET_WIDTH, $configurations['sff_widget_width'] ); update_option( SFF_OPTION_WIDGET_HEIGHT, $configurations['sff_widget_height'] ); update_option( SFF_OPTION_TRANSACTION_PREF, $configurations['sff_transaction_prefix'] ); // eKomi Floating Widget options – not language specific. update_option( FLOATING_OPTION_ENABLE, $configurations['floating_enable'] ); update_option( FLOATING_OPTION_CODE, $configurations['floating_code'] ); } /** * Gets default lang code. * * @return string */ function ei_get_default_lang_code() { $wpml_options = ei_get_wpml_settings(); $default_lang = get_locale(); if ( $wpml_options ) { $default_lang = $wpml_options['default_language']; if ( empty( $default_lang ) ) { $default_lang = get_locale(); } } $default_lang = explode( '_', $default_lang )[0]; return $default_lang; } /** * Prepares the shop language codes. * * @param string $default_lang The language iso code. * * @return array */ function ei_prepare_lang_array( $default_lang ) { $wpml_options = ei_get_wpml_settings(); if ( $wpml_options ) { $available_langs = $wpml_options['active_languages']; } else { $available_langs = array(); } $arr = array(); if ( ! empty( $default_lang ) ) { $arr[ $default_lang ] = $default_lang; } foreach ( $available_langs as $key => $value ) { $arr[ $value ] = $value; } return $arr; } /** * Fetch order statuses from WooCommerce. * * @return array Order Status list from WooCommerce */ function ei_get_order_statuses() { return wc_get_order_statuses(); } /** * Verifies eKomi account. * * @param int $shop_id The shop id. * @param string $shop_secret The shop password. * * @return array */ function ei_authenticate( $shop_id, $shop_secret ) { $query_string = "auth={$shop_id}|{$shop_secret}&version=cust-1.0.0&type=request&charset=iso&app=PD-Woocommerce"; $url = URL_GET_SETTINGS . '?' . $query_string; $response = wp_remote_get( $url ); $server_output = wp_remote_retrieve_body( $response ); if ( 'Access denied' === $server_output ) { $response = array( 'status' => 'error', 'message' => __( 'Shop ID & Password Required', 'ekomi-integration' ), ); } else { $response = array( 'status' => 'success', 'message' => __( 'Settings Updated', 'ekomi-integration' ), ); } return $response; } /** * Gets the configurations. * * @param string $lang_code The language code. * * @return array */ function ei_get_options_data( $lang_code ) { return array( 'ee_active' => get_option( $lang_code . '_ee_active', '0' ), 'ee_shop_id' => get_option( $lang_code . '_ee_shop_id', '' ), 'ee_shop_pw' => get_option( $lang_code . '_ee_shop_pw', '' ), 'ee_product_reviews' => get_option( $lang_code . '_ee_product_reviews', '0' ), 'ee_mode' => get_option( $lang_code . '_ee_mode', '0' ), 'ee_order_statuses' => get_option('_ee_order_statuses', '' ), 'ee_product_identifier' => get_option( $lang_code . '_ee_product_identifier', '' ), 'ee_exclude_products' => get_option( $lang_code . '_ee_exclude_products', '' ), 'ee_slider_widget_snippet' => get_option( $lang_code . '_ee_slider_widget_snippet', '' ), 'ee_show_prc_widget' => get_option( $lang_code . '_ee_show_prc_widget', '0' ), 'ee_prc_widget_token' => get_option( $lang_code . '_ee_prc_widget_token', '' ), 'ee_export_method' => get_option('_ee_export_method', 'status' ), 'ee_t_and_c' => get_option( $lang_code . '_ee_t_and_c', '1' ), 'ee_turnaround_time' => get_option('_ee_turnaround_time', '7' ), // eKomi SFF (Shop Feedback Form) options. 'ekomisff_enable' => get_option( SFF_OPTION_ENABLE, '0' ), 'ekomisff_form_id' => get_option( SFF_OPTION_FORM_ID, '' ), 'ekomisff_widget_width' => get_option( SFF_OPTION_WIDGET_WIDTH, '' ), 'ekomisff_widget_height' => get_option( SFF_OPTION_WIDGET_HEIGHT, '' ), 'ekomisff_transaction_prefix' => get_option( SFF_OPTION_TRANSACTION_PREF, '' ), // eKomi Floating Widget options. 'ekomifloating_enable' => get_option( FLOATING_OPTION_ENABLE, '0' ), 'ekomifloating_code' => get_option( FLOATING_OPTION_CODE, '' ), ); } /** * Gets common configurations for all languages. * * @return array */ function ei_get_common_options() { return array( 'ee_order_statuses' => get_option('_ee_order_statuses', '' ), 'ee_export_method' => get_option('_ee_export_method', 'status' ), 'ee_turnaround_time' => get_option('_ee_turnaround_time', '7' ), ); } /** * Registers woocommerce hooks on selected order statuses. * * @param array $statuses Order statuses. * * @return void */ function ei_register_hooks( $statuses ) { if ( is_array( $statuses ) ) { foreach ( $statuses as $status ) { $status_hook = substr( $status, 3 ); add_action( 'woocommerce_order_status_' . $status_hook, 'ekomiIntegratioExportOrderData' ); } } } /** * Gets wpml settings. * * @return mixed|null|void */ function ei_get_wpml_settings() { $active_plugins = (array) get_option( 'active_plugins', array() ); if ( ! empty( $active_plugins ) && in_array( 'sitepress-multilingual-cms/sitepress.php', $active_plugins, true ) ) { return get_option( 'icl_sitepress_settings' ); } return null; } /** * Loads text domain. * * @param string $domain Text domain. * @param bool $deprecated Deprication. * @param bool $plugin_rel_path The plugin url path. * @param string $locale The locale id. * * @return bool */ function ei_load_textdomain( $domain, $deprecated = false, $plugin_rel_path = false, $locale ) { $mofile = $domain . '-' . $locale . '.mo'; /** * Try to load from the languages directory first. */ if ( load_textdomain( $domain, WP_LANG_DIR . '/plugins/' . $mofile ) ) { return true; } if ( false !== $plugin_rel_path ) { $path = WP_PLUGIN_DIR . '/' . trim( $plugin_rel_path, '/' ); } elseif ( false !== $deprecated ) { _deprecated_argument( __FUNCTION__, '2.7.0' ); $path = ABSPATH . trim( $deprecated, '/' ); } else { $path = WP_PLUGIN_DIR; } return load_textdomain( $domain, $path . '/' . $mofile ); } add_filter( 'cron_schedules', __NAMESPACE__ . '\\ei_register_cron' ); /** * Registers cron task. * * @param array $schedules Cron schedules. * * @return mixed */ function ei_register_cron( $schedules ) { $schedules['1min'] = array( 'interval' => 60, // 1 minute in seconds 'display' => __( 'Once Every Minute' ), ); return $schedules; } /** * Schedule an action if it's not already scheduled. */ if ( ! wp_next_scheduled( 'ei_export_orders_cron' ) ) { wp_schedule_event( time(), 'daily', 'ei_export_orders_cron' ); } require __DIR__ . '/export.php'; /** * Hook into that action that'll fire daily. */ add_action( 'ei_export_orders_cron', __NAMESPACE__ . '\\ei_export_orders' ); /** * Exports orders for eKomi. * * @return void */ function ei_export_orders() { $configurations = ei_get_common_options(); if ( EXPORT_METHOD_CRON === $configurations['ee_export_method'] ) { $days_in_seconds = $configurations['ee_turnaround_time'] * ONE_DAY_IN_SECONDS; $args = array( 'date_created' => '>' . ( time() - $days_in_seconds ), 'limit' => -1 ); $orders = wc_get_orders( $args ); foreach ( $orders as $order ) { ei_export_order_data( $order->get_id(), true ); } echo 'eKomi integration orders exported.'; } } /** * Outputs the eKomi SFF popup on the WooCommerce Thank You page. * * @param int $order_id WooCommerce order id. * * @return void */ function ei_render_sff_popup( $order_id ) { if ( ! $order_id ) { return; } // Check if SFF feature is enabled. $sff_enable = get_option( SFF_OPTION_ENABLE, '0' ); if ( ENABLE_VALUE !== (string) $sff_enable ) { return; } // Get SFF settings. $form_id = get_option( SFF_OPTION_FORM_ID, '' ); $widget_width = get_option( SFF_OPTION_WIDGET_WIDTH, '600px' ); $widget_height = get_option( SFF_OPTION_WIDGET_HEIGHT, '600px' ); $transaction_prefix = get_option( SFF_OPTION_TRANSACTION_PREF, '' ); // Ensure width and height have units (default to px if missing). if ( ! empty( $widget_width ) && ! preg_match( '/\d+(px|%|em|rem)$/i', $widget_width ) ) { $widget_width = $widget_width . 'px'; } if ( ! empty( $widget_height ) && ! preg_match( '/\d+(px|%|em|rem)$/i', $widget_height ) ) { $widget_height = $widget_height . 'px'; } // Set defaults if empty. $widget_width = ! empty( $widget_width ) ? $widget_width : '600px'; $widget_height = ! empty( $widget_height ) ? $widget_height : '600px'; if ( empty( $form_id ) ) { return; } $transaction_id = $transaction_prefix . $order_id; // Resolve shop ID from existing configuration (use default language context). $default_lang = ei_get_default_lang_code(); $config = ei_get_options_data( $default_lang ); $shop_id = isset( $config['ee_shop_id'] ) ? $config['ee_shop_id'] : ''; // Collect product IDs from the order. $product_ids = array(); if ( class_exists( 'WC_Order' ) ) { $order = wc_get_order( $order_id ); if ( $order ) { foreach ( $order->get_items() as $item ) { $product = $item->get_product(); if ( $product ) { $product_ids[] = (string) $product->get_id(); } } } } ?>
$method, 'sslverify' => true, 'timeout' => 15, 'headers' => $headers, 'body' => wp_json_encode( $post_fields ), ); $response = wp_remote_request( $url, $arguments ); $body = wp_remote_retrieve_body( $response ); return $body; } /** * Enqueues frontend styles for eKomi widgets. * * @return void */ function ei_enqueue_frontend_styles() { if ( ! is_product() ) { return; } $floating_enable = get_option( FLOATING_OPTION_ENABLE, '0' ); if ( ENABLE_VALUE !== (string) $floating_enable ) { return; } wp_enqueue_style( 'ekomi-floating-widget', plugins_url( 'css/ekomi-floating-widget.css', __FILE__ ), array(), PLUGIN_VERSION, 'all' ); } add_action( 'wp_enqueue_scripts', __NAMESPACE__ . '\\ei_enqueue_frontend_styles' ); /** * Register ShortCodes. */ require_once 'ekomi-prc-widget.php'; require_once 'ekomi-slider-widget.php'; require_once 'ekomi-floating-widget.php';