Google Ads & Marketing by Kliken - Version 1.0.2

Version Description

  • Minor bug fixes.
Download this release

Release Info

Developer sitewit
Plugin Icon 128x128 Google Ads & Marketing by Kliken
Version 1.0.2
Comparing to
See all releases

Version 1.0.2

assets/admin-script.js ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(document).ready(function() {
2
+ // Only care about those notice messages with "data-message-name" attribute, and "is-dimissible" class
3
+ jQuery(".wrap").on("click", "div[data-message-name].is-dismissible button.notice-dismiss", function(event) {
4
+ event.preventDefault();
5
+ event.stopPropagation();
6
+
7
+ if (confirm(dimissibleNotice.confirmMessage)) {
8
+ jQuery.post( ajaxurl, {
9
+ action: dimissibleNotice.action,
10
+ message_name: jQuery(this).parent().attr('data-message-name'),
11
+ _ajax_nonce: dimissibleNotice.nonce
12
+ })
13
+ }
14
+ });
15
+ });
changelog.txt ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ *** Google Ads for WooCommerce Changelog ***
2
+
3
+ 2019-07-02 - version 1.0.2
4
+ * Minor bug fixes.
5
+
6
+ 2019-04-02 - version 1.0.1
7
+ * Minor UI improvements.
8
+ * Minor bug fixes.
9
+
10
+ 2019-02-20 - version 1.0.0
11
+ * First release.
classes/class-helper.php ADDED
@@ -0,0 +1,428 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Helper class to provide some common functionalities
4
+ *
5
+ * @package Kliken Marketing for Google
6
+ */
7
+
8
+ namespace Kliken\WcPlugin;
9
+
10
+ defined( 'ABSPATH' ) || exit;
11
+
12
+ /**
13
+ * Helper class
14
+ */
15
+ class Helper {
16
+ /**
17
+ * Name of the option that WooCommerce will save the plugin settings into.
18
+ *
19
+ * @var string
20
+ */
21
+ private static $_option_key = null;
22
+
23
+ /**
24
+ * A wrapper around WC_Logger log method.
25
+ *
26
+ * @param string $level Log level.
27
+ * @param string $message Message to log.
28
+ * @param boolean $force Force logging without the need of WP_DEBUG mode.
29
+ */
30
+ public static function wc_log( $level, $message, $force = false ) {
31
+ if ( ( WP_DEBUG || $force )
32
+ && function_exists( 'wc_get_logger' )
33
+ && $message
34
+ && in_array( $level, [ 'debug', 'info', 'notice', 'warning', 'error', 'critical', 'alert', 'emergency' ], true ) ) {
35
+ wc_get_logger()->log( $level, $message, [ 'source' => 'kliken-marketing-for-google' ] );
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Get plugin option key, because of WooCommerce Integration Settings API.
41
+ *
42
+ * @return string
43
+ */
44
+ public static function get_option_key() {
45
+ if ( null === self::$_option_key ) {
46
+ if ( function_exists( 'wc' ) && class_exists( '\WC_Integration' ) ) {
47
+ self::$_option_key = ( new WC_Integration( false ) )->get_option_key();
48
+ } else {
49
+ // In the weird case when WooCommerce is not available.
50
+ self::$_option_key = 'woocommerce_kk_wcintegration_settings';
51
+ }
52
+ }
53
+
54
+ return self::$_option_key;
55
+ }
56
+
57
+ /**
58
+ * Get plugin options, being an integration with WooCommerce Settings API.
59
+ *
60
+ * @return array
61
+ */
62
+ public static function get_plugin_options() {
63
+ return get_option( self::get_option_key(), [] );
64
+ }
65
+
66
+ /**
67
+ * Check if provided id can be considered a valid account id.
68
+ * Should be an integer.
69
+ *
70
+ * @param mixed $id Account Id.
71
+ * @return boolean
72
+ */
73
+ public static function is_valid_account_id( $id ) {
74
+ return ! empty( $id ) && ctype_digit( strval( $id ) );
75
+ }
76
+
77
+ /**
78
+ * Check if the provided token can be considered a valid application token.
79
+ * Should be not empty, for now.
80
+ *
81
+ * @param string $token Application token.
82
+ * @return boolean
83
+ */
84
+ public static function is_valid_app_token( $token ) {
85
+ $token = sanitize_text_field( $token );
86
+ return ! empty( $token );
87
+ }
88
+
89
+ /**
90
+ * Check if current page is a page of the plugin, matching the provided page slug.
91
+ *
92
+ * @param string $page_slug Page slug.
93
+ * @return boolean
94
+ */
95
+ public static function is_plugin_page( $page_slug = null ) {
96
+ global $pagenow;
97
+
98
+ if ( 'admin.php' !== $pagenow ) {
99
+ return false;
100
+ }
101
+
102
+ if ( null !== $page_slug ) {
103
+ return ( isset( $_GET['page'] ) && $page_slug === $_GET['page'] ); // WPCS: CSRF ok, input var ok.
104
+ } else {
105
+ return (
106
+ isset( $_GET['page'] ) && 'wc-settings' === $_GET['page']
107
+ && isset( $_GET['tab'] ) && 'integration' === $_GET['tab']
108
+ && isset( $_GET['section'] ) && KK_WC_INTEGRATION_PAGE_ID === $_GET['section']
109
+ ); // WPCS: CSRF ok, input var ok.
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Get plugin page URL.
115
+ */
116
+ public static function get_plugin_page() {
117
+ return 'admin.php?page=wc-settings&tab=integration&section=' . KK_WC_INTEGRATION_PAGE_ID;
118
+ }
119
+
120
+ /**
121
+ * Get base64 encoded data url for the logo image.
122
+ *
123
+ * @return string
124
+ */
125
+ public static function get_base64_icon() {
126
+ // phpcs:ignore
127
+ return 'data:image/svg+xml;base64,' . base64_encode( file_get_contents( KK_WC_PLUGIN_DIR . 'assets/logo-small.svg' ) );
128
+ }
129
+
130
+ /**
131
+ * Get a list of supported languages (WP style) with a map to .NET style
132
+ *
133
+ * @return array
134
+ */
135
+ public static function get_wl_supported_languages() {
136
+ return [
137
+ 'en_US' => 'en',
138
+ 'es_ES' => 'es',
139
+ 'de_DE' => 'de',
140
+ 'de_CH' => 'de-CH',
141
+ 'fr_FR' => 'fr',
142
+ 'nl_NL' => 'nl',
143
+ ];
144
+ }
145
+
146
+ /**
147
+ * Get locale of WordPress site/user, in .NET style.
148
+ * Default to English if not supported by the plugin.
149
+ *
150
+ * @return string
151
+ */
152
+ public static function get_wl_locale() {
153
+ if ( function_exists( 'get_user_locale' ) ) {
154
+ $wp_locale = get_user_locale();
155
+ } else {
156
+ $wp_locale = get_locale();
157
+ }
158
+
159
+ $supported_langs = self::get_wl_supported_languages();
160
+
161
+ if ( array_key_exists( $wp_locale, $supported_langs ) ) {
162
+ return $supported_langs[ $wp_locale ];
163
+ }
164
+
165
+ return 'en';
166
+ }
167
+
168
+ /**
169
+ * Get locale of WordPress site/user, with a postfix.
170
+ *
171
+ * @param string $postfix Postfix to be used. Defaults to "-".
172
+ * @return string
173
+ */
174
+ public static function get_locale_postfix( $postfix = '-' ) {
175
+ if ( function_exists( 'get_user_locale' ) ) {
176
+ $wp_locale = get_user_locale();
177
+ } else {
178
+ $wp_locale = get_locale();
179
+ }
180
+
181
+ $supported_langs = self::get_wl_supported_languages();
182
+
183
+ if ( array_key_exists( $wp_locale, $supported_langs ) ) {
184
+ if ( 'en_US' === $wp_locale ) {
185
+ return '';
186
+ } else {
187
+ return $postfix . $wp_locale;
188
+ }
189
+ }
190
+
191
+ return '';
192
+ }
193
+
194
+ /**
195
+ * Add SiteWit tracking script to page.
196
+ */
197
+ public static function add_tracking_script() {
198
+ global $wp;
199
+
200
+ $saved_settings = self::get_plugin_options();
201
+
202
+ if ( ! self::is_valid_account_id( $saved_settings['account_id'] ) ) {
203
+ return;
204
+ }
205
+
206
+ // Check if is order received page.
207
+ if ( is_order_received_page() ) {
208
+ $order_id = isset( $wp->query_vars['order-received'] ) ? $wp->query_vars['order-received'] : 0;
209
+ $trans = self::build_transaction_data( $order_id );
210
+
211
+ if ( null !== $trans ) {
212
+ include_once KK_WC_PLUGIN_DIR . 'pages/transactionscript.php';
213
+ }
214
+ }
215
+
216
+ $account_id = $saved_settings['account_id'];
217
+ include_once KK_WC_PLUGIN_DIR . 'pages/trackingscript.php';
218
+ }
219
+
220
+ /**
221
+ * Add Google Verification Token to page meta.
222
+ */
223
+ public static function add_google_verification_token() {
224
+ $saved_settings = self::get_plugin_options();
225
+
226
+ // Sanitize the saved string again just in case.
227
+ $token = sanitize_text_field( $saved_settings['google_token'] );
228
+
229
+ if ( $token ) {
230
+ printf( '<!-- Kliken Google Site Verification Token Tag -->' );
231
+ printf( '<meta name="google-site-verification" content="%s" />', esc_attr( $token ) );
232
+ }
233
+ }
234
+
235
+ /**
236
+ * Build transaction/order data preparing to be recorded by our tracking script
237
+ *
238
+ * @param int $order_id WooCommerce Order Id.
239
+ * @return array|null
240
+ */
241
+ public static function build_transaction_data( $order_id ) {
242
+ // Get the order detail.
243
+ $order = wc_get_order( $order_id );
244
+
245
+ if ( ! $order ) {
246
+ return null;
247
+ }
248
+
249
+ // We don't care about these statuses.
250
+ $status = $order->get_status();
251
+ if ( 'cancelled' === $status || 'refunded' === $status || 'failed' === $status ) {
252
+ return null;
253
+ }
254
+
255
+ $transaction = [
256
+ 'order_id' => $order_id,
257
+ 'currency' => $order->get_currency(),
258
+ 'affiliate' => null,
259
+ 'sub_total' => $order->get_subtotal(),
260
+ 'tax' => $order->get_total_tax(),
261
+ 'city' => $order->get_billing_city(),
262
+ 'state' => $order->get_billing_state(),
263
+ 'country' => $order->get_billing_country(),
264
+ 'total' => $order->get_total(),
265
+ 'items' => [],
266
+ ];
267
+
268
+ $order_items = $order->get_items();
269
+
270
+ // Cache category info, because in the order, there might be multiple items under same category.
271
+ $category_cache = [];
272
+
273
+ foreach ( $order_items as $index => $item ) {
274
+ $product = $item->get_product();
275
+
276
+ if ( ! $product ) continue;
277
+
278
+ $product_categories = $product->get_category_ids();
279
+ $category_name = '';
280
+
281
+ foreach ( $product_categories as $index => $id ) {
282
+ if ( array_key_exists( $id, $category_cache ) ) {
283
+ $category_name = $category_cache[ $id ];
284
+ } else {
285
+ $term = get_term_by( 'id', $id, 'product_cat' );
286
+ if ( $term ) {
287
+ $category_name = $term->name;
288
+ $category_cache[ $id ] = $category_name;
289
+ }
290
+ }
291
+ }
292
+
293
+ array_push(
294
+ $transaction['items'], [
295
+ 'sku' => $product->get_sku(),
296
+ 'name' => $product->get_name(),
297
+ 'category' => $category_name,
298
+ 'price' => $product->get_price(),
299
+ 'quantity' => $item->get_quantity(),
300
+ ]
301
+ );
302
+ }
303
+
304
+ return $transaction;
305
+ }
306
+
307
+ /**
308
+ * Build the WooCommerce authorization URL
309
+ * Doc: https://woocommerce.github.io/woocommerce-rest-api-docs/#rest-api-keys
310
+ *
311
+ * @param int $account_id Account Id.
312
+ * @param string $application_token Application Token.
313
+ * @return string|null
314
+ */
315
+ public static function build_authorization_url( $account_id, $application_token ) {
316
+ if ( empty( $account_id ) || empty( $application_token ) ) {
317
+ return null;
318
+ }
319
+
320
+ $authorization_url = get_site_url() . '/wc-auth/v1/authorize'
321
+ . '?app_name=' . rawurlencode( 'Google Ads for WooCommerce' )
322
+ . '&scope=read_write'
323
+ . '&user_id=' . base64_encode( $account_id . ':' . $application_token )
324
+ . '&return_url=' . rawurlencode( 'bit.ly/2OweS8h' ) // This links back to woo.kliken.com. We just need to do this to shorten the link because some WordPress hostings seem to dislike long links.
325
+ . '&callback_url=' . rawurlencode( KK_WC_AUTH_CALLBACK_URL );
326
+
327
+ return $authorization_url;
328
+ }
329
+
330
+ /**
331
+ * Build sign up link.
332
+ */
333
+ public static function build_signup_link() {
334
+ $current_user = wp_get_current_user();
335
+
336
+ return sprintf(
337
+ KK_WC_WOOKLIKEN_BASE_URL . 'auth/woo/?u=%s&n=%s&e=%s&t=%s&return=%s',
338
+ rawurlencode( get_site_url() ),
339
+ rawurlencode( $current_user->display_name ),
340
+ rawurlencode( $current_user->user_email ),
341
+ wp_create_nonce( KK_WC_ACTION_SAVE_ACCOUNT ),
342
+ get_admin_url() . 'admin.php?action=' . KK_WC_ACTION_SAVE_ACCOUNT
343
+ );
344
+ }
345
+
346
+ /**
347
+ * Save account information.
348
+ *
349
+ * @param int $account_id Account Id.
350
+ * @param string $application_token Application Token.
351
+ * @return string|null WooCommerce authorization URL to redirect to after saving account info.
352
+ */
353
+ public static function save_account_info( $account_id, $application_token ) {
354
+ if ( self::is_valid_account_id( $account_id )
355
+ && self::is_valid_app_token( $application_token )
356
+ ) {
357
+ $saved_settings = self::get_plugin_options();
358
+
359
+ $google_token = '';
360
+ if ( ! empty( $saved_settings['google_token'] ) ) {
361
+ $google_token = $saved_settings['google_token'];
362
+ }
363
+
364
+ update_option(
365
+ self::get_option_key(), [
366
+ 'account_id' => intval( $account_id ),
367
+ 'app_token' => sanitize_text_field( $application_token ),
368
+ 'google_token' => $google_token,
369
+ ]
370
+ );
371
+
372
+ return self::build_authorization_url( $account_id, $application_token );
373
+ }
374
+
375
+ return null;
376
+ }
377
+
378
+ /**
379
+ * Redirect to WooCommerce Authorization page if needed.
380
+ */
381
+ public static function check_redirect_for_wc_auth() {
382
+ // Bail if activating from network, or bulk.
383
+ if ( is_network_admin() ) {
384
+ return;
385
+ }
386
+
387
+ // If WooCommerce is not available/active, don't bother.
388
+ if ( ! function_exists( 'wc' ) ) {
389
+ return;
390
+ }
391
+
392
+ $authorization_url = get_site_transient( KK_WC_TRANSIENT_AUTH_REDIRECT );
393
+
394
+ if ( ! empty( $authorization_url ) ) {
395
+ delete_site_transient( KK_WC_TRANSIENT_AUTH_REDIRECT );
396
+
397
+ if ( wp_safe_redirect( $authorization_url ) ) {
398
+ exit;
399
+ }
400
+ }
401
+ }
402
+
403
+ /**
404
+ * Get the formatted, clean, safe onboarding message.
405
+ */
406
+ public static function get_onboarding_message() {
407
+ return sprintf(
408
+ wp_kses(
409
+ /* translators: %s: A hyperlink */
410
+ __( '<strong>Google Ads for WooCommerce plugin is almost ready.</strong> <a href="%s">Click here</a> to get started.', 'kliken-marketing-for-google' ),
411
+ [
412
+ 'strong' => [],
413
+ 'a' => [ 'href' => [] ],
414
+ ]
415
+ ), esc_url( self::get_plugin_page() )
416
+ );
417
+ }
418
+
419
+ /**
420
+ * Get image link.
421
+ *
422
+ * @param string $image_base_name Base name of the image, without the extension.
423
+ * @param string $image_extension Extension of the image, including the ".".
424
+ */
425
+ public static function get_image_link( $image_base_name, $image_extension ) {
426
+ return KK_WC_PLUGIN_URL . 'assets/' . $image_base_name . self::get_locale_postfix() . $image_extension;
427
+ }
428
+ }
classes/class-message.php ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Message class to provide shortcuts for display various types of WordPress admin notices
4
+ *
5
+ * @package Kliken WooCommerce Google Ads
6
+ */
7
+
8
+ namespace Kliken\WcPlugin;
9
+
10
+ defined( 'ABSPATH' ) || exit;
11
+
12
+ /**
13
+ * Message class
14
+ */
15
+ class Message {
16
+ /**
17
+ * Build a message to HTML code to be displayed on a page
18
+ *
19
+ * @param string $type Message type.
20
+ * @param string $message Message, translated, to show to user.
21
+ * @param string $message_name The name of the message. This is only used for message that will be permanently dismissible.
22
+ */
23
+ private static function show_message( $type = 'info', $message = '', $message_name = '' ) {
24
+ $data_attr = $message_name ? 'data-message-name="' . $message_name . '"' : '';
25
+
26
+ // Do not escape the message here as it's already escaped.
27
+ // Mark this as XSS ok for PHPCodesniffer because the message MUST be escaped beforehand.
28
+ printf(
29
+ '<div class="notice notice-%1$s is-dismissible" %2$s><p>%3$s</p></div>',
30
+ esc_attr( $type ),
31
+ $data_attr,
32
+ $message
33
+ ); // WPCS: XSS ok.
34
+ }
35
+
36
+ /**
37
+ * Show an info message
38
+ *
39
+ * @param string $message Message, translated, to be displayed.
40
+ * @param string $message_name The name of the message. This is only used for message that will be permanently dismissible.
41
+ */
42
+ public static function show_info( $message = '', $message_name = '' ) {
43
+ if ( $message ) {
44
+ self::show_message( 'info', $message, $message_name );
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Show a success message
50
+ *
51
+ * @param string $message Message, translated, to be displayed.
52
+ * @param string $message_name The name of the message. This is only used for message that will be permanently dismissible.
53
+ */
54
+ public static function show_success( $message = '', $message_name = '' ) {
55
+ if ( $message ) {
56
+ self::show_message( 'success', $message, $message_name );
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Show a warning message
62
+ *
63
+ * @param string $message Message, translated, to be displayed.
64
+ * @param string $message_name The name of the message. This is only used for message that will be permanently dismissible.
65
+ */
66
+ public static function show_warning( $message = '', $message_name = '' ) {
67
+ if ( $message ) {
68
+ self::show_message( 'warning', $message, $message_name );
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Show an error message
74
+ *
75
+ * @param string $message Message, translated, to be displayed.
76
+ * @param string $message_name The name of the message. This is only used for message that will be permanently dismissible.
77
+ */
78
+ public static function show_error( $message = '', $message_name = '' ) {
79
+ if ( $message ) {
80
+ self::show_message( 'error', $message, $message_name );
81
+ }
82
+ }
83
+ }
classes/class-plugin.php ADDED
@@ -0,0 +1,299 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Main plugin class to bootstrap everything else
4
+ *
5
+ * @package Kliken Marketing for Google
6
+ */
7
+
8
+ namespace Kliken\WcPlugin;
9
+
10
+ defined( 'ABSPATH' ) || exit;
11
+
12
+ /**
13
+ * Plugin class
14
+ */
15
+ class Plugin {
16
+
17
+ const ALREADY_BOOTSTRAPED = 1;
18
+ const DEPENDENCIES_UNSATISFIED = 2;
19
+ const DISMISS_POSTFIX = '_dismissed';
20
+
21
+ /**
22
+ * Flag to indicate the plugin has been boostrapped.
23
+ *
24
+ * @var bool
25
+ */
26
+ private $_bootstrapped = false;
27
+
28
+ /**
29
+ * Try to register important hooks, with main stuff inside "plugins_loaded"
30
+ * so we can have checks for requirements/dependencies.
31
+ */
32
+ public function maybe_run() {
33
+ register_activation_hook( KK_WC_PLUGIN_FILE, [ $this, 'activate' ] );
34
+
35
+ add_action( 'plugins_loaded', [ $this, 'bootstrap' ] );
36
+
37
+ add_filter( 'plugin_action_links_' . plugin_basename( KK_WC_PLUGIN_FILE ), [ $this, 'plugin_action_links' ] );
38
+ add_action( 'admin_enqueue_scripts', [ $this, 'admin_enqueue_scripts' ] );
39
+
40
+ if ( is_admin() ) {
41
+ add_action( 'wp_ajax_' . KK_WC_ACTION_DISMISS_NOTICE, [ $this, 'ajax_dismiss_notice' ] );
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Bootstrap the execution of the plugin.
47
+ *
48
+ * @throws \Exception Throw an exception if the plugin has been called before.
49
+ */
50
+ public function bootstrap() {
51
+ try {
52
+ load_plugin_textdomain( 'kliken-marketing-for-google', false, KK_WC_PLUGIN_REL_PATH . '/languages' );
53
+
54
+ if ( $this->_bootstrapped ) {
55
+ throw new \Exception( __( 'Google Ads for WooCommerce plugin can only be called once', 'kliken-marketing-for-google' ), self::ALREADY_BOOTSTRAPED );
56
+ }
57
+
58
+ $this->check_dependencies();
59
+ $this->run();
60
+
61
+ $this->_bootstrapped = true;
62
+ } catch ( \Exception $e ) {
63
+ if ( in_array( $e->getCode(), [ self::ALREADY_BOOTSTRAPED, self::DEPENDENCIES_UNSATISFIED ] ) ) {
64
+ set_site_transient( KK_WC_BOOTSTRAP_MESSAGE, $e->getMessage(), MONTH_IN_SECONDS );
65
+ }
66
+
67
+ add_action( 'admin_notices', [ $this, 'show_bootstrap_message' ] );
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Run when the plugin is activated.
73
+ */
74
+ public function activate() {
75
+ $saved_settings = Helper::get_plugin_options();
76
+ if ( empty( $saved_settings ) ) {
77
+ update_option( Helper::get_option_key(), [
78
+ 'account_id' => 0,
79
+ 'app_token' => '',
80
+ 'google_token' => '',
81
+ ] );
82
+
83
+ $has_account_id = false;
84
+ } else {
85
+ $has_account_id = Helper::is_valid_account_id( $saved_settings['account_id'] );
86
+ }
87
+
88
+ if ( ! $has_account_id ) {
89
+ // Try to find account id baked into the plugin zip.
90
+ $config_path = KK_WC_PLUGIN_DIR . 'config.php';
91
+ if ( file_exists( $config_path ) ) {
92
+ include_once $config_path;
93
+
94
+ // $kk_account_id and $kk_app_token are defined in the config file.
95
+ $authorization_url = Helper::save_account_info( $kk_account_id, $kk_app_token );
96
+
97
+ // Remove the file.
98
+ unlink( $config_path ); // phpcs:ignore WordPress.VIP.FileSystemWritesDisallow.file_ops_unlink
99
+
100
+ // Here we need to know if after activation we need to redirect to the WooCommerce Auth page or not.
101
+ if ( ! empty( $authorization_url ) ) {
102
+ set_site_transient( KK_WC_TRANSIENT_AUTH_REDIRECT, $authorization_url, MINUTE_IN_SECONDS );
103
+ }
104
+ } else {
105
+ // Seems to not have account info, we will try to show onboarding message, for a month, at top.
106
+ set_site_transient( KK_WC_WELCOME_MESSAGE, true, MONTH_IN_SECONDS );
107
+ }
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Add more links to plugin's "row" in the WordPress' plugin list,
113
+ * for more actions that user can do.
114
+ *
115
+ * @param array $links A list of links already registered.
116
+ * @return array
117
+ */
118
+ public function plugin_action_links( $links ) {
119
+ $plugin_links = [];
120
+
121
+ if ( function_exists( 'wc' ) ) {
122
+ $plugin_links[] = '<a href="' . esc_url( Helper::get_plugin_page() ) . '">' . __( 'Dashboard', 'kliken-marketing-for-google' ) . '</a>';
123
+ }
124
+
125
+ $plugin_links[] = '<a href="https://kliken.com/contact/">' . __( 'Support', 'kliken-marketing-for-google' ) . '</a>';
126
+
127
+ return array_merge( $plugin_links, $links );
128
+ }
129
+
130
+ /**
131
+ * Show bootstrap (warning) message if needed.
132
+ */
133
+ public function show_bootstrap_message() {
134
+ $message = get_site_transient( KK_WC_BOOTSTRAP_MESSAGE );
135
+
136
+ if ( ! empty( $message )
137
+ && false === get_option( KK_WC_BOOTSTRAP_MESSAGE . self::DISMISS_POSTFIX, false )
138
+ && current_user_can( 'manage_options' ) ) {
139
+ Message::show_warning( $message, 'bsmessage' );
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Show onboarding (info) message if needed.
145
+ */
146
+ public function show_onboarding_message() {
147
+ $settings = Helper::get_plugin_options();
148
+
149
+ if ( false !== get_site_transient( KK_WC_WELCOME_MESSAGE, false )
150
+ && false === get_option( KK_WC_WELCOME_MESSAGE . self::DISMISS_POSTFIX, false )
151
+ && current_user_can( 'manage_options' ) && ! Helper::is_plugin_page()
152
+ && ! Helper::is_valid_account_id( $settings['account_id'] ) ) {
153
+ Message::show_info( Helper::get_onboarding_message(), 'welcome' );
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Add a new integration to WooCommerce.
159
+ *
160
+ * @param array $integrations List of integrations.
161
+ * @return array
162
+ */
163
+ public function woocommerce_integrations( $integrations ) {
164
+ $integrations[] = '\\Kliken\\WcPlugin\\WC_Integration';
165
+
166
+ return $integrations;
167
+ }
168
+
169
+ /**
170
+ * Add Tracking Script and Google Verification Token to site's header if available.
171
+ */
172
+ public function wp_head() {
173
+ // Inject Google verification token.
174
+ Helper::add_google_verification_token();
175
+
176
+ // Inject tracking script.
177
+ Helper::add_tracking_script();
178
+ }
179
+
180
+ /**
181
+ * Register custom REST API endpoints for WooCommerce.
182
+ */
183
+ public function rest_api_init() {
184
+ ( new REST_Misc_Controller() )->register_routes();
185
+ ( new REST_Products_Controller() )->register_routes();
186
+ ( new REST_Orders_Controller() )->register_routes();
187
+ }
188
+
189
+ /**
190
+ * Stuff to do during admin initialization.
191
+ */
192
+ public function admin_init() {
193
+ Helper::check_redirect_for_wc_auth();
194
+ }
195
+
196
+ /**
197
+ * Enqueue script(s) needed for admin pages.
198
+ */
199
+ public function admin_enqueue_scripts() {
200
+ wp_enqueue_script( 'kk-admin-script', KK_WC_PLUGIN_URL . 'assets/admin-script.js', [ 'jquery' ] );
201
+
202
+ wp_localize_script(
203
+ 'kk-admin-script',
204
+ 'dimissibleNotice',
205
+ [
206
+ 'action' => KK_WC_ACTION_DISMISS_NOTICE,
207
+ 'nonce' => wp_create_nonce( KK_WC_ACTION_DISMISS_NOTICE ),
208
+ 'confirmMessage' => esc_html__( 'This will dismiss the message permanently. Are you sure?', 'kliken-marketing-for-google' ),
209
+ ]
210
+ );
211
+ }
212
+
213
+ /**
214
+ * Action to save account information after signing up.
215
+ */
216
+ public function save_account() {
217
+ if ( ! isset( $_GET['t'] ) || ! wp_verify_nonce( sanitize_key( $_GET['t'] ), KK_WC_ACTION_SAVE_ACCOUNT ) ) { // WPCS: input var ok.
218
+ wp_nonce_ays( KK_WC_ACTION_SAVE_ACCOUNT );
219
+ }
220
+
221
+ // Default to redirect back to dashboard page.
222
+ $url = Helper::get_plugin_page();
223
+
224
+ if ( isset( $_GET['maid'], $_GET['appt'] ) ) { // WPCS: input var ok.
225
+ // Once account info is saved, we will have to do WooCommerce API Authorization again.
226
+ $authorization_url = Helper::save_account_info(
227
+ sanitize_text_field( wp_unslash( $_GET['maid'] ) ), // WPCS: input var ok.
228
+ sanitize_text_field( wp_unslash( $_GET['appt'] ) ) // WPCS: input var ok.
229
+ );
230
+
231
+ if ( $authorization_url ) {
232
+ $url = $authorization_url;
233
+ }
234
+ }
235
+
236
+ if ( $url ) {
237
+ wp_safe_redirect( $url );
238
+ exit;
239
+ }
240
+ }
241
+
242
+ /**
243
+ * Action to handle AJAX request to permanently dismiss a notice.
244
+ */
245
+ public function ajax_dismiss_notice() {
246
+ check_ajax_referer( KK_WC_ACTION_DISMISS_NOTICE );
247
+
248
+ if ( isset( $_POST['message_name'] ) ) { // WPCS: input var ok.
249
+ $message_name = sanitize_key( $_POST['message_name'] ); // WPCS: input var ok.
250
+
251
+ switch ( $message_name ) {
252
+ case 'welcome':
253
+ update_option( KK_WC_WELCOME_MESSAGE . self::DISMISS_POSTFIX, true );
254
+ break;
255
+ case 'bsmessage':
256
+ update_option( KK_WC_BOOTSTRAP_MESSAGE . self::DISMISS_POSTFIX, true );
257
+ delete_site_transient( KK_WC_BOOTSTRAP_MESSAGE );
258
+ break;
259
+ }
260
+ }
261
+
262
+ wp_die();
263
+ }
264
+
265
+ /**
266
+ * Check if the dependencies of the plugin are satisfied.
267
+ * Here we need WooCommerce 3.0 or above, to be installed, and active.
268
+ *
269
+ * @throws \Exception Throw an exception if the dependency is not satisfied.
270
+ */
271
+ private function check_dependencies() {
272
+ if ( ! function_exists( 'wc' ) ) {
273
+ throw new \Exception( __( 'Google Ads for WooCommerce requires WooCommerce to be activated', 'kliken-marketing-for-google' ), self::DEPENDENCIES_UNSATISFIED );
274
+ }
275
+
276
+ $required_woo_version = '3.0';
277
+ if ( version_compare( wc()->version, $required_woo_version, '<' ) ) {
278
+ /* translators: %s: Version number of WooCommerce required to run plugin. Do not translate. */
279
+ throw new \Exception( sprintf( __( 'Google Ads for WooCommerce requires WooCommerce version %s or greater', 'kliken-marketing-for-google' ), $required_woo_version ), self::DEPENDENCIES_UNSATISFIED );
280
+ }
281
+ }
282
+
283
+ /**
284
+ * Main running point of the plugins. Responsible for hooking into different parts of WordPress and WooCommerce.
285
+ */
286
+ private function run() {
287
+ add_filter( 'woocommerce_integrations', [ $this, 'woocommerce_integrations' ] );
288
+ add_action( 'wp_head', [ $this, 'wp_head' ] );
289
+ add_action( 'rest_api_init', [ $this, 'rest_api_init' ] );
290
+ add_action( 'admin_init', [ $this, 'admin_init' ] );
291
+ add_action( 'admin_notices', [ $this, 'show_onboarding_message' ] );
292
+
293
+ // Additional admin actions registered for handling requests.
294
+ if ( is_admin() ) {
295
+ // Handle request to save account info coming back from Kliken sign up page.
296
+ add_action( 'admin_action_' . KK_WC_ACTION_SAVE_ACCOUNT, [ $this, 'save_account' ] );
297
+ }
298
+ }
299
+ }
classes/class-rest-misc-controller.php ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * REST controller for miscellaneous endpoints.
4
+ *
5
+ * @package Kliken Marketing for Google
6
+ */
7
+
8
+ namespace Kliken\WcPlugin;
9
+
10
+ defined( 'ABSPATH' ) || exit;
11
+
12
+ /**
13
+ * REST API Misc controller class.
14
+ *
15
+ * @extends WC_REST_CRUD_Controller
16
+ */
17
+ class REST_Misc_Controller extends \WC_REST_CRUD_Controller {
18
+ /**
19
+ * Endpoint namespace.
20
+ *
21
+ * @var string
22
+ */
23
+ protected $namespace = 'wc-kliken/v1';
24
+
25
+ /**
26
+ * Register the routes we need.
27
+ */
28
+ public function register_routes() {
29
+ register_rest_route(
30
+ $this->namespace, '/google-token', [
31
+ [
32
+ 'methods' => \WP_REST_Server::CREATABLE,
33
+ 'callback' => [ $this, 'save_google_token' ],
34
+ 'permission_callback' => function() {
35
+ return current_user_can( 'manage_options' );
36
+ },
37
+ ],
38
+ ]
39
+ );
40
+
41
+ register_rest_route(
42
+ $this->namespace, '/shipping/wc-services', [
43
+ [
44
+ 'methods' => \WP_REST_Server::READABLE,
45
+ 'callback' => [ $this, 'get_wc_services_shipping_methods' ],
46
+ 'permission_callback' => [ $this, 'get_shipping_methods_permissions_check' ],
47
+ ],
48
+ ]
49
+ );
50
+ }
51
+
52
+ /**
53
+ * Save Google Verification Token to database so we can later on display it as a header's meta.
54
+ *
55
+ * @param WP_REST_Request $request Full details about the request.
56
+ * @return WP_Error|WP_REST_Response
57
+ */
58
+ public function save_google_token( $request ) {
59
+ // If we know more about Google's token specifications, we can have more strict rules
60
+ // For now, just sanitize the token as a text string (no tags, no simple injections).
61
+ $token = sanitize_text_field( $request->get_param( 'token' ) );
62
+ if ( ! $token ) {
63
+ return new \WP_Error( 'rest_bad_request', __( 'Invalid Data', 'kliken-marketing-for-google' ), [ 'status' => 400 ] );
64
+ }
65
+
66
+ $saved_settings = Helper::get_plugin_options();
67
+ $saved_settings['google_token'] = $token;
68
+
69
+ // Save the token to database.
70
+ update_option( Helper::get_option_key(), $saved_settings );
71
+
72
+ // But WP_REST_Response is only available since 4.4?
73
+ return new \WP_REST_Response( null, 201 );
74
+ }
75
+
76
+ /**
77
+ * Get shipping services associated with a WooCommerce Services Carrier shipping method.
78
+ * Because for some reason, WooCommerce does not return that with their API endpoints.
79
+ *
80
+ * @param WP_REST_Request $request Full details about the request.
81
+ * @return WP_Error|WP_REST_Response
82
+ */
83
+ public function get_wc_services_shipping_methods( $request ) {
84
+ $id = (int) $request['id'];
85
+ $carrier_name = $request['carrier'];
86
+
87
+ // Get the option from database.
88
+ $data = get_option( "woocommerce_wc_services_{$carrier_name}_{$id}_form_settings" );
89
+
90
+ if ( ! $data || empty( $data ) || empty( $data->services ) ) {
91
+ return new \WP_Error( 'no_wc_services', __( 'Invalid Data', 'kliken-marketing-for-google' ), [ 'status' => 404 ] );
92
+ }
93
+
94
+ $carrier_services = [];
95
+
96
+ foreach ( $data->services as $key => $service ) {
97
+ if ( true !== $service['enabled'] ) {
98
+ continue;
99
+ }
100
+
101
+ array_push(
102
+ $carrier_services, [
103
+ 'service_name' => $service['id'],
104
+ 'adjustment' => $service['adjustment'],
105
+ 'adjustment_type' => $service['adjustment_type'],
106
+ ]
107
+ );
108
+ }
109
+
110
+ $response = rest_ensure_response(
111
+ [
112
+ 'carrier_name' => $data->title,
113
+ 'carrier_services' => $carrier_services,
114
+ ]
115
+ );
116
+
117
+ return $response;
118
+ }
119
+
120
+ /**
121
+ * Check whether a given request has permission to view shipping methods.
122
+ *
123
+ * @param WP_REST_Request $request Full details about the request.
124
+ * @return WP_Error|boolean
125
+ */
126
+ public function get_shipping_methods_permissions_check( $request ) {
127
+ if ( ! wc_rest_check_manager_permissions( 'shipping_methods', 'read' ) ) {
128
+ return new \WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'kliken-marketing-for-google' ), [ 'status' => rest_authorization_required_code() ] );
129
+ }
130
+ return true;
131
+ }
132
+ }
classes/class-rest-orders-controller.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * REST controller for getting orders, extending of what WooCommerce
4
+ * does not provide at the moment.
5
+ *
6
+ * @package Kliken Marketing for Google
7
+ */
8
+
9
+ namespace Kliken\WcPlugin;
10
+
11
+ defined( 'ABSPATH' ) || exit;
12
+
13
+ /**
14
+ * REST API Orders controller class.
15
+ *
16
+ * @extends WC_REST_Orders_Controller
17
+ */
18
+ class REST_Orders_Controller extends \WC_REST_Orders_Controller {
19
+ /**
20
+ * Endpoint namespace.
21
+ *
22
+ * @var string
23
+ */
24
+ protected $namespace = 'wc-kliken/v1';
25
+
26
+ /**
27
+ * Register the routes for products.
28
+ */
29
+ public function register_routes() {
30
+ register_rest_route(
31
+ $this->namespace, '/orders/modified', [
32
+ [
33
+ 'methods' => \WP_REST_Server::READABLE,
34
+ 'callback' => [ $this, 'get_items' ],
35
+ 'permission_callback' => [ $this, 'get_items_permissions_check' ],
36
+ ],
37
+ ]
38
+ );
39
+ }
40
+
41
+ /**
42
+ * Prepare objects query to get modified orders.
43
+ *
44
+ * @since 3.0.0
45
+ * @param WP_REST_Request $request Full details about the request.
46
+ * @return array
47
+ */
48
+ protected function prepare_objects_query( $request ) {
49
+ $args = parent::prepare_objects_query( $request );
50
+
51
+ // Reset the date query to look up post_modified column instead.
52
+ if ( ! empty( $args['date_query'] ) ) {
53
+ if ( isset( $request['before'] ) ) {
54
+ $args['date_query'][0]['column'] = 'post_modified';
55
+ $args['date_query'][0]['before'] = $request['before'];
56
+ }
57
+
58
+ if ( isset( $request['after'] ) ) {
59
+ $args['date_query'][0]['column'] = 'post_modified';
60
+ $args['date_query'][0]['after'] = $request['after'];
61
+ }
62
+ }
63
+
64
+ return $args;
65
+ }
66
+ }
classes/class-rest-products-controller.php ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * REST controller for getting products, extending of what WooCommerce
4
+ * does not provide at the moment.
5
+ *
6
+ * @package Kliken Marketing for Google
7
+ */
8
+
9
+ namespace Kliken\WcPlugin;
10
+
11
+ defined( 'ABSPATH' ) || exit;
12
+
13
+ /**
14
+ * REST API Products controller class.
15
+ *
16
+ * @extends WC_REST_Products_Controller_Compat
17
+ */
18
+ class REST_Products_Controller extends WC_REST_Products_Controller_Compat {
19
+ /**
20
+ * Endpoint namespace.
21
+ *
22
+ * @var string
23
+ */
24
+ protected $namespace = 'wc-kliken/v1';
25
+
26
+ /**
27
+ * Register the routes for products.
28
+ */
29
+ public function register_routes() {
30
+ register_rest_route(
31
+ $this->namespace, '/products/modified', [
32
+ [
33
+ 'methods' => \WP_REST_Server::READABLE,
34
+ 'callback' => [ $this, 'get_items' ],
35
+ 'permission_callback' => [ $this, 'get_items_permissions_check' ],
36
+ ],
37
+ ]
38
+ );
39
+
40
+ register_rest_route(
41
+ $this->namespace, '/products/deleted', [
42
+ [
43
+ 'methods' => \WP_REST_Server::READABLE,
44
+ 'callback' => [ $this, 'get_deleted_products' ],
45
+ 'permission_callback' => [ $this, 'get_items_permissions_check' ],
46
+ ],
47
+ ]
48
+ );
49
+ }
50
+
51
+ /**
52
+ * Get deleted/trashed products.
53
+ *
54
+ * @param WP_REST_Request $request Full details about the request.
55
+ * @return WP_Error|WP_REST_Response
56
+ */
57
+ public function get_deleted_products( $request ) {
58
+ $page = intval( $request['page'] );
59
+ $per_page = intval( $request['per_page'] );
60
+
61
+ $query_args = [
62
+ 'fields' => 'ids', // We only need post ids for this.
63
+ 'post_type' => $this->post_type,
64
+ 'post_status' => 'trash',
65
+ 'ignore_sticky_posts' => true,
66
+ 'paged' => 0 === $page ? 1 : $page,
67
+ 'posts_per_page' => 0 === $per_page ? 100 : $per_page,
68
+ 'orderby' => 'modified',
69
+ 'order' => 'ASC',
70
+ ];
71
+
72
+ $query_args['date_query'] = [];
73
+ // Set before into date query. Date query must be specified as an array of an array.
74
+ if ( isset( $request['before'] ) ) {
75
+ $query_args['date_query'][0]['column'] = 'post_modified';
76
+ $query_args['date_query'][0]['before'] = $request['before'];
77
+ }
78
+
79
+ // Set after into date query. Date query must be specified as an array of an array.
80
+ if ( isset( $request['after'] ) ) {
81
+ $query_args['date_query'][0]['column'] = 'post_modified';
82
+ $query_args['date_query'][0]['after'] = $request['after'];
83
+ }
84
+
85
+ $query = new \WP_Query();
86
+ $result = $query->query( $query_args );
87
+
88
+ $total_posts = $query->found_posts;
89
+ if ( $total_posts < 1 ) {
90
+ // Out-of-bounds, run the query again without LIMIT for total count.
91
+ unset( $query_args['paged'] );
92
+ $count_query = new \WP_Query();
93
+ $count_query->query( $query_args );
94
+ $total_posts = $count_query->found_posts;
95
+ }
96
+
97
+ $response = rest_ensure_response( $result );
98
+ $response->header( 'X-WP-Total', (int) $total_posts );
99
+ $response->header( 'X-WP-TotalPages', (int) ceil( $total_posts / (int) $query_args['posts_per_page'] ) );
100
+
101
+ return $response;
102
+ }
103
+
104
+ /**
105
+ * Prepare objects query to get modified products.
106
+ *
107
+ * @since 3.0.0
108
+ * @param WP_REST_Request $request Full details about the request.
109
+ * @return array
110
+ */
111
+ protected function prepare_objects_query( $request ) {
112
+ $args = parent::prepare_objects_query( $request );
113
+
114
+ // Reset the date query to look up post_modified column instead.
115
+ if ( ! empty( $args['date_query'] ) ) {
116
+ if ( isset( $request['before'] ) ) {
117
+ $args['date_query'][0]['column'] = 'post_modified';
118
+ $args['date_query'][0]['before'] = $request['before'];
119
+ }
120
+
121
+ if ( isset( $request['after'] ) ) {
122
+ $args['date_query'][0]['column'] = 'post_modified';
123
+ $args['date_query'][0]['after'] = $request['after'];
124
+ }
125
+ }
126
+
127
+ return $args;
128
+ }
129
+ }
classes/class-wc-integration.php ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Integration class to hook plugin settings into WooCommerce's Settings Integration tab
4
+ *
5
+ * @package Kliken Marketing for Google
6
+ */
7
+
8
+ namespace Kliken\WcPlugin;
9
+
10
+ defined( 'ABSPATH' ) || exit;
11
+
12
+ /**
13
+ * WooCommerce Integration class
14
+ */
15
+ class WC_Integration extends \WC_Integration {
16
+ /**
17
+ * Constructor. Default to also init.
18
+ *
19
+ * @param boolean $init If class initialization is also needed.
20
+ */
21
+ public function __construct( $init = true ) {
22
+ global $woocommerce;
23
+
24
+ $this->id = KK_WC_INTEGRATION_PAGE_ID;
25
+ $this->method_title = __( 'Google Ads for WooCommerce', 'kliken-marketing-for-google' ); // This is for the sub-section text.
26
+
27
+ if ( true === $init ) {
28
+ // Load the settings.
29
+ $this->init_form_fields();
30
+ $this->init_settings();
31
+
32
+ $this->account_id = $this->get_option( 'account_id' );
33
+ $this->app_token = $this->get_option( 'app_token' );
34
+
35
+ // Action to save the options.
36
+ add_action( 'woocommerce_update_options_integration_' . $this->id, [ $this, 'process_admin_options' ] );
37
+ add_filter( 'woocommerce_settings_api_sanitized_fields_' . $this->id, [ $this, 'sanitize_settings' ] );
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Initialize integration settings form fields.
43
+ */
44
+ public function init_form_fields() {
45
+ $this->form_fields = [
46
+ 'account_id' => [
47
+ 'title' => __( 'Account Id', 'kliken-marketing-for-google' ),
48
+ 'type' => 'text',
49
+ 'default' => '',
50
+ ],
51
+ 'app_token' => [
52
+ 'title' => __( 'Application Token', 'kliken-marketing-for-google' ),
53
+ 'type' => 'text',
54
+ 'default' => '',
55
+ ],
56
+ 'google_token' => [
57
+ 'title' => __( 'Google Verification Token', 'kliken-marketing-for-google' ),
58
+ 'type' => 'text',
59
+ 'default' => '',
60
+ ],
61
+ 'guide_link' => [
62
+ 'type' => 'desc',
63
+ 'description' => '<a href="https://support.sitewit.com/hc/en-us/articles/360016107933-WooCommerce-Advanced-Options" target="_blank" rel="noopener noreferrer">' . __( 'What are these options?', 'kliken-marketing-for-google' ) . '</a>',
64
+ 'desc_tip' => '',
65
+ ],
66
+ ];
67
+ }
68
+
69
+ /**
70
+ * Display "options" area.
71
+ */
72
+ public function admin_options() {
73
+ Helper::check_redirect_for_wc_auth();
74
+
75
+ // So this is the way WordPress hide the save button *facepalming*.
76
+ $GLOBALS['hide_save_button'] = true;
77
+
78
+ include_once KK_WC_PLUGIN_DIR . 'pages/admin-style.php';
79
+
80
+ if ( Helper::is_valid_account_id( $this->settings['account_id'] )
81
+ && Helper::is_valid_app_token( $this->settings['app_token'] )
82
+ ) {
83
+ // Already have account info. Show dashboard.
84
+ include_once KK_WC_PLUGIN_DIR . 'pages/dashboard.php';
85
+ } else {
86
+ // Show sign up form.
87
+ include_once KK_WC_PLUGIN_DIR . 'pages/getstarted.php';
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Generate HTML code for "desc" field.
93
+ * This is a custom field for display a descriptive info line alongside other fields.
94
+ *
95
+ * @param string $key Field key.
96
+ * @param array $data Field data.
97
+ */
98
+ public function generate_desc_html( $key, $data ) {
99
+ ob_start();
100
+ ?>
101
+ <tr valign="top">
102
+ <th scope="row" class="titledesc" style="padding: 0;">
103
+ </th>
104
+ <td class="forminp" style="padding: 0 0 0 10px;">
105
+ <fieldset>
106
+ <?php echo $this->get_description_html( $data ); // WPCS: XSS ok. ?>
107
+ </fieldset>
108
+ </td>
109
+ </tr>
110
+ <?php
111
+ return ob_get_clean();
112
+ }
113
+
114
+ /**
115
+ * Sanitize setting values before saving to the database
116
+ *
117
+ * @param array $settings Array of fields setting values being submitted.
118
+ * @return array
119
+ */
120
+ public function sanitize_settings( $settings ) {
121
+ // Validate account id as an integer.
122
+ $settings['account_id'] = intval( $settings['account_id'] );
123
+
124
+ // Sanitize token.
125
+ $settings['app_token'] = sanitize_text_field( $settings['app_token'] );
126
+
127
+ // Sanitize google verification token.
128
+ $settings['google_token'] = sanitize_text_field( $settings['google_token'] );
129
+
130
+ if ( 0 === $settings['account_id'] || '' === $settings['app_token'] ) {
131
+ // No (valid) values were entered, we should show onboarding/welcome message.
132
+ set_site_transient( KK_WC_WELCOME_MESSAGE, true, MONTH_IN_SECONDS );
133
+ } elseif ( intval( $this->account_id ) !== $settings['account_id']
134
+ || $settings['app_token'] !== $this->app_token
135
+ ) {
136
+ // If the values changed, we want to redirect them to the WooCommerce Authorization page immediately after save.
137
+ $authorization_url = Helper::build_authorization_url( $settings['account_id'], $settings['app_token'] );
138
+ if ( ! empty( $authorization_url ) ) {
139
+ set_site_transient( KK_WC_TRANSIENT_AUTH_REDIRECT, $authorization_url, MINUTE_IN_SECONDS );
140
+ }
141
+ }
142
+
143
+ return $settings;
144
+ }
145
+ }
classes/class-wc-rest-products-controller-compat.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Adapter class to provide compatibility to WooCommerce version < 3.5.0, when
4
+ * the version 3 of WooCommerce REST API was introduced.
5
+ *
6
+ * @package Kliken Marketing for Google
7
+ */
8
+
9
+ namespace Kliken\WcPlugin;
10
+
11
+ defined( 'ABSPATH' ) || exit;
12
+
13
+ if ( class_exists( '\WC_REST_Products_V2_Controller' ) ) {
14
+ /**
15
+ * WooCommerce at > v2. We still try to extend from the v2 of the class.
16
+ */
17
+ class WC_REST_Products_Controller_Compat extends \WC_REST_Products_V2_Controller { }
18
+ } else {
19
+ /**
20
+ * WooCommerce at v2. This might become legacy soon.
21
+ */
22
+ class WC_REST_Products_Controller_Compat extends \WC_REST_Products_Controller { }
23
+ }
kliken-marketing-for-google.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ Plugin Name: Google Ads for WooCommerce
4
+ Plugin URI: https://woo.kliken.com/
5
+ Description: The automated Google Shopping solution to get your products found on Google, and grow your WooCommerce Store!
6
+ Version: 1.0.2
7
+ Author: Kliken
8
+ Author URI: http://kliken.com/
9
+ Developer: Kliken
10
+ Developer URI: http://kliken.com/
11
+ Text Domain: kliken-marketing-for-google
12
+ Domain path: /languages
13
+
14
+ WC requires at least: 3.0
15
+ WC tested up to: 3.9
16
+
17
+ License: GNU General Public License v3.0
18
+ License URI: http://www.gnu.org/licenses/gpl-3.0.html
19
+
20
+ @package Kliken Marketing for Google
21
+ */
22
+
23
+ defined( 'ABSPATH' ) || exit;
24
+
25
+ define( 'KK_WC_PLUGIN_FILE', __FILE__ );
26
+ define( 'KK_WC_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
27
+ define( 'KK_WC_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
28
+ define( 'KK_WC_PLUGIN_REL_PATH', basename( dirname( __FILE__ ) ) );
29
+ define( 'KK_WC_AFFILIATE_ID', '82E7B644-DB42-40E9-9EDF-6FD10A4BAFB3' );
30
+ define( 'KK_WC_WOOKLIKEN_BASE_URL', 'https://woo.kliken.com/' );
31
+ define( 'KK_WC_AUTH_CALLBACK_URL', 'https://app.mysite-analytics.com/WebHooks/WooCommerceAuth/' );
32
+ define( 'KK_WC_AUTH_APP_NAME', 'Google Ads for WooCommerce' );
33
+
34
+ define( 'KK_WC_INTEGRATION_PAGE_ID', 'kk_wcintegration' );
35
+
36
+ define( 'KK_WC_ACTION_SAVE_ACCOUNT', 'kkwc_saveaccount' );
37
+ define( 'KK_WC_ACTION_DISMISS_NOTICE', 'kkwc_dismissnotice' );
38
+ define( 'KK_WC_ACTION_CHECK_ACCOUNT', 'kkwc_checkaccount' );
39
+
40
+ define( 'KK_WC_TRANSIENT_AUTH_REDIRECT', 'kk_wc_activation_redirect' );
41
+
42
+ define( 'KK_WC_WELCOME_MESSAGE', 'kk_wc_welcome_message' );
43
+ define( 'KK_WC_BOOTSTRAP_MESSAGE', 'kk_wc_bootstrap_message' );
44
+
45
+ require 'vendor/autoload.php';
46
+
47
+ /**
48
+ * Starting point. Try to initiate the main instance of the plugin.
49
+ */
50
+ function kk_wc_plugin() {
51
+ static $plugin;
52
+
53
+ if ( ! isset( $plugin ) ) {
54
+ $plugin = new \Kliken\WcPlugin\Plugin();
55
+ }
56
+
57
+ return $plugin;
58
+ }
59
+
60
+ // Adopt this nice method from WooCommerce.
61
+ kk_wc_plugin()->maybe_run();
languages/kliken-marketing-for-google-de_CH.mo ADDED
Binary file
languages/kliken-marketing-for-google-de_DE.mo ADDED
Binary file
languages/kliken-marketing-for-google-es_ES.mo ADDED
Binary file
languages/kliken-marketing-for-google-fr_FR.mo ADDED
Binary file
languages/kliken-marketing-for-google-nl_NL.mo ADDED
Binary file
languages/kliken-marketing-for-google.pot ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2020 Kliken
2
+ # This file is distributed under the GNU General Public License v3.0.
3
+ msgid ""
4
+ msgstr ""
5
+ "Project-Id-Version: Google Ads for WooCommerce 1.0.2\n"
6
+ "Report-Msgid-Bugs-To: http://www.sitewit.com/contact/\n"
7
+ "POT-Creation-Date: 2020-02-18 16:30:32+00:00\n"
8
+ "MIME-Version: 1.0\n"
9
+ "Content-Type: text/plain; charset=utf-8\n"
10
+ "Content-Transfer-Encoding: 8bit\n"
11
+ "PO-Revision-Date: 2020-MO-DA HO:MI+ZONE\n"
12
+ "Last-Translator: SiteWit <wordpress@sitewit.com>\n"
13
+ "Language-Team: SiteWit <wordpress@sitewit.com>\n"
14
+ "Language: en\n"
15
+ "Plural-Forms: nplurals=2; plural=n !=\n"
16
+ "X-Poedit-Country: United States\n"
17
+ "X-Poedit-SourceCharset: UTF-8\n"
18
+ "X-Poedit-KeywordsList: __;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;"
19
+ "_nx_noop:1,2,3c;esc_attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;"
20
+ "esc_html_x:1,2c;\n"
21
+ "X-Poedit-Basepath: ../\n"
22
+ "X-Poedit-SearchPath-0: .\n"
23
+ "X-Poedit-Bookmarks: \n"
24
+ "X-Textdomain-Support: yes\n"
25
+ "X-Generator: grunt-wp-i18n1.0.2\n"
26
+
27
+ #. translators: %s: A hyperlink
28
+ #: classes/class-helper.php:410
29
+ msgid ""
30
+ "<strong>Google Ads for WooCommerce plugin is almost ready.</strong> <a href="
31
+ "\"%s\">Click here</a> to get started."
32
+ msgstr ""
33
+
34
+ #: classes/class-plugin.php:55
35
+ msgid "Google Ads for WooCommerce plugin can only be called once"
36
+ msgstr ""
37
+
38
+ #: classes/class-plugin.php:122
39
+ msgid "Dashboard"
40
+ msgstr ""
41
+
42
+ #: classes/class-plugin.php:125
43
+ msgid "Support"
44
+ msgstr ""
45
+
46
+ #: classes/class-plugin.php:208
47
+ msgid "This will dismiss the message permanently. Are you sure?"
48
+ msgstr ""
49
+
50
+ #: classes/class-plugin.php:273
51
+ msgid "Google Ads for WooCommerce requires WooCommerce to be activated"
52
+ msgstr ""
53
+
54
+ #. translators: %s: Version number of WooCommerce required to run plugin. Do
55
+ #. not translate.
56
+ #: classes/class-plugin.php:279
57
+ msgid "Google Ads for WooCommerce requires WooCommerce version %s or greater"
58
+ msgstr ""
59
+
60
+ #: classes/class-rest-misc-controller.php:63
61
+ #: classes/class-rest-misc-controller.php:91
62
+ msgid "Invalid Data"
63
+ msgstr ""
64
+
65
+ #: classes/class-rest-misc-controller.php:128
66
+ msgid "Sorry, you cannot list resources."
67
+ msgstr ""
68
+
69
+ #. Plugin Name of the plugin/theme
70
+ msgid "Google Ads for WooCommerce"
71
+ msgstr ""
72
+
73
+ #: classes/class-wc-integration.php:47
74
+ msgid "Account Id"
75
+ msgstr ""
76
+
77
+ #: classes/class-wc-integration.php:52
78
+ msgid "Application Token"
79
+ msgstr ""
80
+
81
+ #: classes/class-wc-integration.php:57
82
+ msgid "Google Verification Token"
83
+ msgstr ""
84
+
85
+ #: classes/class-wc-integration.php:63
86
+ msgid "What are these options?"
87
+ msgstr ""
88
+
89
+ #: pages/dashboard.php:15
90
+ msgid "Launch Google Shopping ads and get your products found online easily."
91
+ msgstr ""
92
+
93
+ #: pages/dashboard.php:26
94
+ msgid "Your store is connected."
95
+ msgstr ""
96
+
97
+ #: pages/dashboard.php:28
98
+ msgid "Your WooCommerce store is connected to your Kliken account."
99
+ msgstr ""
100
+
101
+ #: pages/dashboard.php:34
102
+ msgid "Campaign Dashboard"
103
+ msgstr ""
104
+
105
+ #: pages/dashboard.php:36
106
+ msgid "Open your dashboard to review your campaign's performance"
107
+ msgstr ""
108
+
109
+ #: pages/dashboard.php:41
110
+ msgid "Create a New Google Shopping Campaign"
111
+ msgstr ""
112
+
113
+ #: pages/dashboard.php:43
114
+ msgid ""
115
+ "Build a campaign in a few minutes, and sell to customers as they search for "
116
+ "your products on Google."
117
+ msgstr ""
118
+
119
+ #: pages/dashboard.php:48
120
+ msgid "Manage Campaigns"
121
+ msgstr ""
122
+
123
+ #: pages/dashboard.php:50
124
+ msgid ""
125
+ "Make changes to your active campaigns, purchase one you built, or reinstate "
126
+ "your cancelled campaigns."
127
+ msgstr ""
128
+
129
+ #: pages/dashboard.php:56
130
+ msgid "Advanced Options"
131
+ msgstr ""
132
+
133
+ #: pages/dashboard.php:67
134
+ msgid "Enable Edit"
135
+ msgstr ""
136
+
137
+ #: pages/dashboard.php:68
138
+ msgid "Save Changes"
139
+ msgstr ""
140
+
141
+ #: pages/dashboard.php:72
142
+ msgid "Authorize API Access"
143
+ msgstr ""
144
+
145
+ #: pages/getstarted.php:30
146
+ msgid "Launch Google Shopping Ads and get your products found online easily."
147
+ msgstr ""
148
+
149
+ #: pages/getstarted.php:41
150
+ msgid "Increase sales and revenue with Google Shopping Ads"
151
+ msgstr ""
152
+
153
+ #: pages/getstarted.php:43
154
+ msgid "Use this WooCommerce and Google Ads integration to:"
155
+ msgstr ""
156
+
157
+ #: pages/getstarted.php:45
158
+ msgid "Find more customers on Google"
159
+ msgstr ""
160
+
161
+ #: pages/getstarted.php:46
162
+ msgid "Automate bidding to maximize sales for your marketing budget"
163
+ msgstr ""
164
+
165
+ #: pages/getstarted.php:47
166
+ msgid "Keep your marketing and store in sync"
167
+ msgstr ""
168
+
169
+ #: pages/getstarted.php:48
170
+ msgid "Create perfect shopping campaigns in minutes"
171
+ msgstr ""
172
+
173
+ #: pages/getstarted.php:51
174
+ msgid "Get Started"
175
+ msgstr ""
176
+
177
+ #. Plugin URI of the plugin/theme
178
+ msgid "https://woo.kliken.com/"
179
+ msgstr ""
180
+
181
+ #. Description of the plugin/theme
182
+ msgid ""
183
+ "The automated Google Shopping solution to get your products found on Google, "
184
+ "and grow your WooCommerce Store!"
185
+ msgstr ""
186
+
187
+ #. Author of the plugin/theme
188
+ msgid "Kliken"
189
+ msgstr ""
190
+
191
+ #. Author URI of the plugin/theme
192
+ msgid "http://kliken.com/"
193
+ msgstr ""
pages/admin-style.php ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <style>
2
+ .kk-wrapper {
3
+ font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
4
+ font-size: 13pt;
5
+ font-style: normal;
6
+ font-variant: normal;
7
+ }
8
+
9
+ .kk-wrapper h1 {
10
+ font-size: 18pt!important;
11
+ font-weight: bold!important;
12
+ }
13
+
14
+ .kk-wrapper h2 {
15
+ font-size: 17pt!important;
16
+ }
17
+
18
+ .kk-wrapper p {
19
+ font-size: 13pt;
20
+ }
21
+
22
+ .kk-wrapper .subhdr {
23
+ font-size: 14pt;
24
+ }
25
+
26
+ .kk-wrapper ul {
27
+ list-style-type: disc;
28
+ margin: 16pt 16pt 38pt 16pt;
29
+ }
30
+
31
+ .kk-wrapper .kk-box {
32
+ max-width: 825px;
33
+ margin-top: 20pt;
34
+ }
35
+
36
+ .kk-wrapper .kk-box-header {
37
+ border: 1px solid #cccccc;
38
+ background-color: #f6f6f6;
39
+ }
40
+
41
+ .kk-wrapper .kk-box-content {
42
+ background-color: white;
43
+ border: 1px solid #cccccc;
44
+ padding: 30px;
45
+ }
46
+
47
+ .kk-wrapper .sub-heading {
48
+ font-size: 14pt;
49
+ text-decoration: none;
50
+ font-weight: bold;
51
+ margin-top: 10px;
52
+ color: #4883af!important;
53
+ }
54
+
55
+ .kk-wrapper .sub-note {
56
+ margin-top: 0;
57
+ }
58
+
59
+ .kk-wrapper .kk-img-container {
60
+ margin: 20px;
61
+ }
62
+
63
+ .kk-wrapper .kk-google-img {
64
+ height: 40px;
65
+ display: block;
66
+ margin-bottom: -5px;
67
+ }
68
+
69
+ .kk-wrapper .kk-poweredby-img {
70
+ margin-left: 65px;
71
+ }
72
+
73
+ .kk-wrapper .kk-link {
74
+ margin-top: 30px;
75
+ }
76
+
77
+ .kk-wrapper summary {
78
+ margin-top: 25px;
79
+ margin-bottom: 10px;
80
+ color: #4883af!important;
81
+ cursor: pointer;
82
+ }
83
+
84
+ .kk-wrapper summary:focus {
85
+ outline: none;
86
+ }
87
+
88
+ .kk-wrapper .input-text.regular-input {
89
+ width: 100%!important;
90
+ max-width: 400px!important;
91
+ }
92
+ </style>
pages/dashboard.php ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Dashboard page.
4
+ *
5
+ * @package Kliken Marketing for Google
6
+ */
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ ?>
11
+
12
+ <div class="wrap kk-wrapper">
13
+ <h2><?php esc_html_e( 'Google Ads for WooCommerce', 'kliken-marketing-for-google' ); ?></h2>
14
+
15
+ <p><?php esc_html_e( 'Launch Google Shopping ads and get your products found online easily.', 'kliken-marketing-for-google' ); ?></p>
16
+
17
+ <div class="kk-box">
18
+ <div class="kk-box-header">
19
+ <div class="kk-img-container">
20
+ <img src="https://d9hhrg4mnvzow.cloudfront.net/start.kliken.com/woo/40c24363-googleheader-logo.svg" alt="Google Logo" height="40" class="kk-google-img">
21
+ <img src="https://d9hhrg4mnvzow.cloudfront.net/start.kliken.com/woo/1ab26a95-powderedbykliken_03600c03500c000000.png" alt="Powered by Kliken" class="kk-poweredby-img">
22
+ </div>
23
+ </div>
24
+
25
+ <div class="kk-box-content">
26
+ <h1><?php esc_html_e( 'Your store is connected.', 'kliken-marketing-for-google' ); ?></h1>
27
+
28
+ <p class="subhdr"><?php esc_html_e( 'Your WooCommerce store is connected to your Kliken account.', 'kliken-marketing-for-google' ); ?></p>
29
+
30
+ <hr>
31
+
32
+ <div class="kk-link">
33
+ <a class="sub-heading" href="<?php echo esc_url( KK_WC_WOOKLIKEN_BASE_URL . 'smb/shopping/dashboard' ); ?>">
34
+ <?php esc_html_e( 'Campaign Dashboard', 'kliken-marketing-for-google' ); ?>
35
+ </a>
36
+ <p class="sub-note"><?php esc_html_e( 'Open your dashboard to review your campaign\'s performance', 'kliken-marketing-for-google' ); ?></p>
37
+ </div>
38
+
39
+ <div class="kk-link">
40
+ <a class="sub-heading" href="<?php echo esc_url( KK_WC_WOOKLIKEN_BASE_URL . 'smb/shopping/create' ); ?>">
41
+ <?php esc_html_e( 'Create a New Google Shopping Campaign', 'kliken-marketing-for-google' ); ?>
42
+ </a>
43
+ <p class="sub-note"><?php esc_html_e( 'Build a campaign in a few minutes, and sell to customers as they search for your products on Google.', 'kliken-marketing-for-google' ); ?></p>
44
+ </div>
45
+
46
+ <div class="kk-link">
47
+ <a class="sub-heading" href="<?php echo esc_url( KK_WC_WOOKLIKEN_BASE_URL . 'smb/shopping/manage' ); ?>">
48
+ <?php esc_html_e( 'Manage Campaigns', 'kliken-marketing-for-google' ); ?>
49
+ </a>
50
+ <p class="sub-note"><?php esc_html_e( 'Make changes to your active campaigns, purchase one you built, or reinstate your cancelled campaigns.', 'kliken-marketing-for-google' ); ?></p>
51
+ </div>
52
+
53
+ <hr>
54
+
55
+ <details class="primer advanced-settings">
56
+ <summary><?php esc_html_e( 'Advanced Options', 'kliken-marketing-for-google' ); ?></summary>
57
+
58
+ <div>
59
+ <input type="hidden" name="section" value="<?php echo esc_attr( $this->id ); ?>" />
60
+ </div>
61
+ <table class="form-table">
62
+ <?php
63
+ // NOTE: these methods are only available if this file is included within a WC_Integration extended class.
64
+ $this->generate_settings_html( $this->get_form_fields() ); // WPCS: XSS ok.
65
+ ?>
66
+ </table>
67
+ <button type="button" class="button button-default" id="enable-settings-edit"><?php esc_html_e( 'Enable Edit', 'kliken-marketing-for-google' ); ?></button>
68
+ <button type="submit" class="button-primary woocommerce-save-button" name="save" id="submit" value="Save changes"><?php esc_html_e( 'Save Changes', 'kliken-marketing-for-google' ); ?></button>
69
+
70
+ <hr>
71
+ <a href="<?php echo esc_url( \Kliken\WcPlugin\Helper::build_authorization_url( $this->account_id, $this->app_token ) ); ?>">
72
+ <button type="button" class="button button-primary" id="authorize-api-access"><?php esc_html_e( 'Authorize API Access', 'kliken-marketing-for-google' ); ?></button>
73
+ </a>
74
+ </details>
75
+ </div>
76
+ </div>
77
+ </div>
78
+
79
+ <script>
80
+ jQuery(document).ready(function() {
81
+ // Disable setting inputs, and hide submit button
82
+ jQuery(".advanced-settings input[type=text]").prop("disabled", true);
83
+ jQuery("#submit").prop("disabled", true).hide();
84
+
85
+ jQuery("#enable-settings-edit").click(function() {
86
+ jQuery(".advanced-settings input[type=text]").prop("disabled", false);
87
+ jQuery("#submit").prop("disabled", false).show();
88
+
89
+ // Hide the button itself
90
+ jQuery(this).hide();
91
+ });
92
+ });
93
+ </script>
pages/getstarted.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Get started page.
4
+ *
5
+ * @package Kliken Marketing for Google
6
+ */
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ ?>
11
+
12
+ <style>
13
+ #getstarted_btn {
14
+ background-color: #ff6600;
15
+ color: #fff;
16
+ padding: 10px 20px;
17
+ font-size: 14px;
18
+ text-decoration: none;
19
+ font-family: "Lato",sans-serif;
20
+ line-height: 35px;
21
+ font-weight: 700;
22
+ letter-spacing: 2px !important;
23
+ text-transform: uppercase;
24
+ }
25
+ </style>
26
+
27
+ <div class="wrap kk-wrapper">
28
+ <h2><?php esc_html_e( 'Google Ads for WooCommerce', 'kliken-marketing-for-google' ); ?></h2>
29
+
30
+ <p><?php esc_html_e( 'Launch Google Shopping Ads and get your products found online easily.', 'kliken-marketing-for-google' ); ?></p>
31
+
32
+ <div class="kk-box">
33
+ <div class="kk-box-header">
34
+ <div class="kk-img-container">
35
+ <img src="https://d9hhrg4mnvzow.cloudfront.net/start.kliken.com/woo/40c24363-googleheader-logo.svg" alt="Google Logo" height="40" class="kk-google-img">
36
+ <img src="https://d9hhrg4mnvzow.cloudfront.net/start.kliken.com/woo/1ab26a95-powderedbykliken_03600c03500c000000.png" alt="Powered by Kliken" class="kk-poweredby-img">
37
+ </div>
38
+ </div>
39
+
40
+ <div class="kk-box-content">
41
+ <h1><?php esc_html_e( 'Increase sales and revenue with Google Shopping Ads', 'kliken-marketing-for-google' ); ?></h1>
42
+
43
+ <p class="subhdr"><?php esc_html_e( 'Use this WooCommerce and Google Ads integration to:', 'kliken-marketing-for-google' ); ?></h5>
44
+ <ul>
45
+ <li><?php esc_html_e( 'Find more customers on Google', 'kliken-marketing-for-google' ); ?></li>
46
+ <li><?php esc_html_e( 'Automate bidding to maximize sales for your marketing budget', 'kliken-marketing-for-google' ); ?></li>
47
+ <li><?php esc_html_e( 'Keep your marketing and store in sync', 'kliken-marketing-for-google' ); ?></li>
48
+ <li><?php esc_html_e( 'Create perfect shopping campaigns in minutes', 'kliken-marketing-for-google' ); ?></li>
49
+ </ul>
50
+
51
+ <a id="getstarted_btn" href="<?php echo esc_url( \Kliken\WcPlugin\Helper::build_signup_link() ); ?>"><?php esc_html_e( 'Get Started', 'kliken-marketing-for-google' ); ?></a>
52
+ </div>
53
+ </div>
54
+ </div>
pages/trackingscript.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Contains JS script to include our main tracking script.
4
+ *
5
+ * @package Kliken Marketing for Google
6
+ */
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ ?>
11
+
12
+ <script type="text/javascript">
13
+ var _swaMa=["<?php echo esc_attr( $account_id ); ?>"];"undefined"==typeof sw&&!function(e,s,a){function t(){for(;o[0]&&"loaded"==o[0][d];)i=o.shift(),i[w]=!c.parentNode.insertBefore(i,c)}for(var r,n,i,o=[],c=e.scripts[0],w="onreadystatechange",d="readyState";r=a.shift();)n=e.createElement(s),"async"in c?(n.async=!1,e.head.appendChild(n)):c[d]?(o.push(n),n[w]=t):e.write("<"+s+' src="'+r+'" defer></'+s+">"),n.src=r}(document,"script",["//analytics.sitewit.com/v3/"+_swaMa[0]+"/sw.js"]);
14
+ </script>
pages/transactionscript.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Contains JS script to register a transaction up on order received page.
4
+ *
5
+ * @package Kliken Marketing for Google
6
+ */
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ ?>
11
+
12
+ <script type="text/javascript">
13
+ var swPreRegister = function() {
14
+ sw.gawCurrency = "<?php echo esc_attr( $trans['currency'] ); ?>";
15
+
16
+ var trans = sw.create_transaction(
17
+ "<?php echo esc_attr( $trans['order_id'] ); ?>",
18
+ "<?php echo esc_attr( $trans['affiliate'] ); ?>",
19
+ "<?php echo esc_attr( $trans['sub_total'] ); ?>",
20
+ "<?php echo esc_attr( $trans['tax'] ); ?>",
21
+ "<?php echo esc_attr( $trans['city'] ); ?>",
22
+ "<?php echo esc_attr( $trans['state'] ); ?>",
23
+ "<?php echo esc_attr( $trans['country'] ); ?>",
24
+ "<?php echo esc_attr( $trans['total'] ); ?>",
25
+ );
26
+
27
+ <?php foreach ( $trans['items'] as $index => $item ) : ?>
28
+ trans.add_item(
29
+ "<?php echo esc_attr( $item['sku'] ); ?>",
30
+ "<?php echo esc_attr( $item['name'] ); ?>",
31
+ "<?php echo esc_attr( $item['category'] ); ?>",
32
+ "<?php echo esc_attr( $item['price'] ); ?>",
33
+ "<?php echo esc_attr( $item['quantity'] ); ?>",
34
+ );
35
+ <?php endforeach; ?>
36
+
37
+ sw.hit.set_page("ORDER_CONFIRMATION");
38
+ };
39
+ </script>
readme.txt ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === Google Ads for WooCommerce ===
2
+ Contributors: sitewit
3
+ Tags: advertising, adwords, analytics, bing, google, leads, marketing, marketing platform, metrics, online advertising, online marketing, plugin, ppc, PPC, google shopping, shopping ads, product ads, ROI, conversions
4
+ Requires at least: 4.4
5
+ Tested up to: 5.3
6
+ Requires PHP: 5.6
7
+ Stable tag: 1.0.2
8
+ License: GPLv3 or later License
9
+ URI: http://www.gnu.org/licenses/gpl-3.0.html
10
+
11
+ Google Shopping Ads are the most effective way to advertise your Woo store and increase sales and revenue.
12
+
13
+
14
+ == Description ==
15
+ In a few simple steps, generate a fully optimized Google Shopping Ads campaign. Select what categories you want to advertise and we automatically generate smart Google Shopping feeds, automate bidding to maximize your return on advertising spend, and measure conversions and sales for your marketing.
16
+
17
+ = Benefits =
18
+
19
+ * Find more customers on Google when they are searching for your products.
20
+ * Get the most out of your marketing through our automated bidding which maximizes sales for your marketing budget.
21
+ * Keep your marketing and store in sync, ensuring you only market products that are in inventory and keeping the Google campaigns and Woo store in sync automatically.
22
+ * Create perfect shopping campaigns in minutes using best practices to maximize effectiveness, including smart product feeds and advanced bidding by product.
23
+
24
+ = How does it work? =
25
+
26
+ 1. Start building a campaign. No credit card necessary to build your marketing campaign.
27
+ 2. Select the geographical location you want to target.
28
+ 3. Select what store categories you want to advertise.
29
+ 4. Select a budget.
30
+ 5. Purchase your campaign and start marketing on Google Shopping
31
+
32
+ No long term contract, you can cancel at any time.
33
+
34
+ Google Ads for WooCommerce is the easiest, lowest risk way to advertise your Woo store on Google Shopping and is brought to you by Kliken in collaboration with WooCommerce and Google, working together to bring you the best Google Ads experience fully integrated into your WooCommerce store, optimized to maximize your online sales.
35
+
36
+
37
+ == Frequently Asked Questions ==
38
+
39
+ = How does billing work? =
40
+ Your Google Shopping campaign is billed monthly to help keep your product ads online. You will be billed the same amount each time for the package level you choose. This way you know the exact costs of your campaign each month.
41
+
42
+ = How do I cancel my subscription? =
43
+ The Google Shopping campaign is a contract free service. You can cancel your subscription at anytime from the Google Shopping app under the Manage section. When a campaign is cancelled, it will continue to run the remainder of the billed month, or until it has used the remaining Google Ads balance.
44
+
45
+ = What is the Kliken Fee? =
46
+ This is our monthly fee for creating, managing, and optimizing all aspects of your Google Shopping campaign.
47
+
48
+
49
+ == Changelog ==
50
+
51
+ = 1.0.2 =
52
+ * Minor bug fixes.
53
+
54
+ = 1.0.1 =
55
+ * Minor UI improvements.
56
+ * Minor bug fixes.
57
+
58
+ = 1.0.0 =
59
+ * First release.
uninstall.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Uninstall script.
4
+ *
5
+ * @package Kliken Marketing for Google
6
+ */
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ defined( 'WP_UNINSTALL_PLUGIN' ) || exit;
11
+
12
+ delete_option( 'kk_wc_settings' );
13
+ delete_option( 'woocommerce_kk_wcintegration_settings' );
14
+
15
+ delete_site_transient( 'kk_wc_activation_redirect' );
16
+
17
+ delete_site_transient( 'kk_wc_welcome_message' );
18
+ delete_option( 'kk_wc_welcome_message_dismissed' );
19
+ delete_site_transient( 'kk_wc_bootstrap_message' );
20
+ delete_option( 'kk_wc_bootstrap_message_dismissed' );
vendor/autoload.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload.php @generated by Composer
4
+
5
+ require_once __DIR__ . '/composer/autoload_real.php';
6
+
7
+ return ComposerAutoloaderInit8d05e6ff15e07dd67db7d411953e0c56::getLoader();
vendor/composer/ClassLoader.php ADDED
@@ -0,0 +1,445 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Composer.
5
+ *
6
+ * (c) Nils Adermann <naderman@naderman.de>
7
+ * Jordi Boggiano <j.boggiano@seld.be>
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ namespace Composer\Autoload;
14
+
15
+ /**
16
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
17
+ *
18
+ * $loader = new \Composer\Autoload\ClassLoader();
19
+ *
20
+ * // register classes with namespaces
21
+ * $loader->add('Symfony\Component', __DIR__.'/component');
22
+ * $loader->add('Symfony', __DIR__.'/framework');
23
+ *
24
+ * // activate the autoloader
25
+ * $loader->register();
26
+ *
27
+ * // to enable searching the include path (eg. for PEAR packages)
28
+ * $loader->setUseIncludePath(true);
29
+ *
30
+ * In this example, if you try to use a class in the Symfony\Component
31
+ * namespace or one of its children (Symfony\Component\Console for instance),
32
+ * the autoloader will first look for the class under the component/
33
+ * directory, and it will then fallback to the framework/ directory if not
34
+ * found before giving up.
35
+ *
36
+ * This class is loosely based on the Symfony UniversalClassLoader.
37
+ *
38
+ * @author Fabien Potencier <fabien@symfony.com>
39
+ * @author Jordi Boggiano <j.boggiano@seld.be>
40
+ * @see http://www.php-fig.org/psr/psr-0/
41
+ * @see http://www.php-fig.org/psr/psr-4/
42
+ */
43
+ class ClassLoader
44
+ {
45
+ // PSR-4
46
+ private $prefixLengthsPsr4 = array();
47
+ private $prefixDirsPsr4 = array();
48
+ private $fallbackDirsPsr4 = array();
49
+
50
+ // PSR-0
51
+ private $prefixesPsr0 = array();
52
+ private $fallbackDirsPsr0 = array();
53
+
54
+ private $useIncludePath = false;
55
+ private $classMap = array();
56
+ private $classMapAuthoritative = false;
57
+ private $missingClasses = array();
58
+ private $apcuPrefix;
59
+
60
+ public function getPrefixes()
61
+ {
62
+ if (!empty($this->prefixesPsr0)) {
63
+ return call_user_func_array('array_merge', $this->prefixesPsr0);
64
+ }
65
+
66
+ return array();
67
+ }
68
+
69
+ public function getPrefixesPsr4()
70
+ {
71
+ return $this->prefixDirsPsr4;
72
+ }
73
+
74
+ public function getFallbackDirs()
75
+ {
76
+ return $this->fallbackDirsPsr0;
77
+ }
78
+
79
+ public function getFallbackDirsPsr4()
80
+ {
81
+ return $this->fallbackDirsPsr4;
82
+ }
83
+
84
+ public function getClassMap()
85
+ {
86
+ return $this->classMap;
87
+ }
88
+
89
+ /**
90
+ * @param array $classMap Class to filename map
91
+ */
92
+ public function addClassMap(array $classMap)
93
+ {
94
+ if ($this->classMap) {
95
+ $this->classMap = array_merge($this->classMap, $classMap);
96
+ } else {
97
+ $this->classMap = $classMap;
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Registers a set of PSR-0 directories for a given prefix, either
103
+ * appending or prepending to the ones previously set for this prefix.
104
+ *
105
+ * @param string $prefix The prefix
106
+ * @param array|string $paths The PSR-0 root directories
107
+ * @param bool $prepend Whether to prepend the directories
108
+ */
109
+ public function add($prefix, $paths, $prepend = false)
110
+ {
111
+ if (!$prefix) {
112
+ if ($prepend) {
113
+ $this->fallbackDirsPsr0 = array_merge(
114
+ (array) $paths,
115
+ $this->fallbackDirsPsr0
116
+ );
117
+ } else {
118
+ $this->fallbackDirsPsr0 = array_merge(
119
+ $this->fallbackDirsPsr0,
120
+ (array) $paths
121
+ );
122
+ }
123
+
124
+ return;
125
+ }
126
+
127
+ $first = $prefix[0];
128
+ if (!isset($this->prefixesPsr0[$first][$prefix])) {
129
+ $this->prefixesPsr0[$first][$prefix] = (array) $paths;
130
+
131
+ return;
132
+ }
133
+ if ($prepend) {
134
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
135
+ (array) $paths,
136
+ $this->prefixesPsr0[$first][$prefix]
137
+ );
138
+ } else {
139
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
140
+ $this->prefixesPsr0[$first][$prefix],
141
+ (array) $paths
142
+ );
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Registers a set of PSR-4 directories for a given namespace, either
148
+ * appending or prepending to the ones previously set for this namespace.
149
+ *
150
+ * @param string $prefix The prefix/namespace, with trailing '\\'
151
+ * @param array|string $paths The PSR-4 base directories
152
+ * @param bool $prepend Whether to prepend the directories
153
+ *
154
+ * @throws \InvalidArgumentException
155
+ */
156
+ public function addPsr4($prefix, $paths, $prepend = false)
157
+ {
158
+ if (!$prefix) {
159
+ // Register directories for the root namespace.
160
+ if ($prepend) {
161
+ $this->fallbackDirsPsr4 = array_merge(
162
+ (array) $paths,
163
+ $this->fallbackDirsPsr4
164
+ );
165
+ } else {
166
+ $this->fallbackDirsPsr4 = array_merge(
167
+ $this->fallbackDirsPsr4,
168
+ (array) $paths
169
+ );
170
+ }
171
+ } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
172
+ // Register directories for a new namespace.
173
+ $length = strlen($prefix);
174
+ if ('\\' !== $prefix[$length - 1]) {
175
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
176
+ }
177
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
178
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
179
+ } elseif ($prepend) {
180
+ // Prepend directories for an already registered namespace.
181
+ $this->prefixDirsPsr4[$prefix] = array_merge(
182
+ (array) $paths,
183
+ $this->prefixDirsPsr4[$prefix]
184
+ );
185
+ } else {
186
+ // Append directories for an already registered namespace.
187
+ $this->prefixDirsPsr4[$prefix] = array_merge(
188
+ $this->prefixDirsPsr4[$prefix],
189
+ (array) $paths
190
+ );
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Registers a set of PSR-0 directories for a given prefix,
196
+ * replacing any others previously set for this prefix.
197
+ *
198
+ * @param string $prefix The prefix
199
+ * @param array|string $paths The PSR-0 base directories
200
+ */
201
+ public function set($prefix, $paths)
202
+ {
203
+ if (!$prefix) {
204
+ $this->fallbackDirsPsr0 = (array) $paths;
205
+ } else {
206
+ $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Registers a set of PSR-4 directories for a given namespace,
212
+ * replacing any others previously set for this namespace.
213
+ *
214
+ * @param string $prefix The prefix/namespace, with trailing '\\'
215
+ * @param array|string $paths The PSR-4 base directories
216
+ *
217
+ * @throws \InvalidArgumentException
218
+ */
219
+ public function setPsr4($prefix, $paths)
220
+ {
221
+ if (!$prefix) {
222
+ $this->fallbackDirsPsr4 = (array) $paths;
223
+ } else {
224
+ $length = strlen($prefix);
225
+ if ('\\' !== $prefix[$length - 1]) {
226
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
227
+ }
228
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
229
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Turns on searching the include path for class files.
235
+ *
236
+ * @param bool $useIncludePath
237
+ */
238
+ public function setUseIncludePath($useIncludePath)
239
+ {
240
+ $this->useIncludePath = $useIncludePath;
241
+ }
242
+
243
+ /**
244
+ * Can be used to check if the autoloader uses the include path to check
245
+ * for classes.
246
+ *
247
+ * @return bool
248
+ */
249
+ public function getUseIncludePath()
250
+ {
251
+ return $this->useIncludePath;
252
+ }
253
+
254
+ /**
255
+ * Turns off searching the prefix and fallback directories for classes
256
+ * that have not been registered with the class map.
257
+ *
258
+ * @param bool $classMapAuthoritative
259
+ */
260
+ public function setClassMapAuthoritative($classMapAuthoritative)
261
+ {
262
+ $this->classMapAuthoritative = $classMapAuthoritative;
263
+ }
264
+
265
+ /**
266
+ * Should class lookup fail if not found in the current class map?
267
+ *
268
+ * @return bool
269
+ */
270
+ public function isClassMapAuthoritative()
271
+ {
272
+ return $this->classMapAuthoritative;
273
+ }
274
+
275
+ /**
276
+ * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
277
+ *
278
+ * @param string|null $apcuPrefix
279
+ */
280
+ public function setApcuPrefix($apcuPrefix)
281
+ {
282
+ $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
283
+ }
284
+
285
+ /**
286
+ * The APCu prefix in use, or null if APCu caching is not enabled.
287
+ *
288
+ * @return string|null
289
+ */
290
+ public function getApcuPrefix()
291
+ {
292
+ return $this->apcuPrefix;
293
+ }
294
+
295
+ /**
296
+ * Registers this instance as an autoloader.
297
+ *
298
+ * @param bool $prepend Whether to prepend the autoloader or not
299
+ */
300
+ public function register($prepend = false)
301
+ {
302
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
303
+ }
304
+
305
+ /**
306
+ * Unregisters this instance as an autoloader.
307
+ */
308
+ public function unregister()
309
+ {
310
+ spl_autoload_unregister(array($this, 'loadClass'));
311
+ }
312
+
313
+ /**
314
+ * Loads the given class or interface.
315
+ *
316
+ * @param string $class The name of the class
317
+ * @return bool|null True if loaded, null otherwise
318
+ */
319
+ public function loadClass($class)
320
+ {
321
+ if ($file = $this->findFile($class)) {
322
+ includeFile($file);
323
+
324
+ return true;
325
+ }
326
+ }
327
+
328
+ /**
329
+ * Finds the path to the file where the class is defined.
330
+ *
331
+ * @param string $class The name of the class
332
+ *
333
+ * @return string|false The path if found, false otherwise
334
+ */
335
+ public function findFile($class)
336
+ {
337
+ // class map lookup
338
+ if (isset($this->classMap[$class])) {
339
+ return $this->classMap[$class];
340
+ }
341
+ if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
342
+ return false;
343
+ }
344
+ if (null !== $this->apcuPrefix) {
345
+ $file = apcu_fetch($this->apcuPrefix.$class, $hit);
346
+ if ($hit) {
347
+ return $file;
348
+ }
349
+ }
350
+
351
+ $file = $this->findFileWithExtension($class, '.php');
352
+
353
+ // Search for Hack files if we are running on HHVM
354
+ if (false === $file && defined('HHVM_VERSION')) {
355
+ $file = $this->findFileWithExtension($class, '.hh');
356
+ }
357
+
358
+ if (null !== $this->apcuPrefix) {
359
+ apcu_add($this->apcuPrefix.$class, $file);
360
+ }
361
+
362
+ if (false === $file) {
363
+ // Remember that this class does not exist.
364
+ $this->missingClasses[$class] = true;
365
+ }
366
+
367
+ return $file;
368
+ }
369
+
370
+ private function findFileWithExtension($class, $ext)
371
+ {
372
+ // PSR-4 lookup
373
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
374
+
375
+ $first = $class[0];
376
+ if (isset($this->prefixLengthsPsr4[$first])) {
377
+ $subPath = $class;
378
+ while (false !== $lastPos = strrpos($subPath, '\\')) {
379
+ $subPath = substr($subPath, 0, $lastPos);
380
+ $search = $subPath.'\\';
381
+ if (isset($this->prefixDirsPsr4[$search])) {
382
+ $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
383
+ foreach ($this->prefixDirsPsr4[$search] as $dir) {
384
+ if (file_exists($file = $dir . $pathEnd)) {
385
+ return $file;
386
+ }
387
+ }
388
+ }
389
+ }
390
+ }
391
+
392
+ // PSR-4 fallback dirs
393
+ foreach ($this->fallbackDirsPsr4 as $dir) {
394
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
395
+ return $file;
396
+ }
397
+ }
398
+
399
+ // PSR-0 lookup
400
+ if (false !== $pos = strrpos($class, '\\')) {
401
+ // namespaced class name
402
+ $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
403
+ . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
404
+ } else {
405
+ // PEAR-like class name
406
+ $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
407
+ }
408
+
409
+ if (isset($this->prefixesPsr0[$first])) {
410
+ foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
411
+ if (0 === strpos($class, $prefix)) {
412
+ foreach ($dirs as $dir) {
413
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
414
+ return $file;
415
+ }
416
+ }
417
+ }
418
+ }
419
+ }
420
+
421
+ // PSR-0 fallback dirs
422
+ foreach ($this->fallbackDirsPsr0 as $dir) {
423
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
424
+ return $file;
425
+ }
426
+ }
427
+
428
+ // PSR-0 include paths.
429
+ if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
430
+ return $file;
431
+ }
432
+
433
+ return false;
434
+ }
435
+ }
436
+
437
+ /**
438
+ * Scope isolated include.
439
+ *
440
+ * Prevents access to $this/self from included files.
441
+ */
442
+ function includeFile($file)
443
+ {
444
+ include $file;
445
+ }
vendor/composer/LICENSE ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
2
+ Upstream-Name: Composer
3
+ Upstream-Contact: Jordi Boggiano <j.boggiano@seld.be>
4
+ Source: https://github.com/composer/composer
5
+
6
+ Files: *
7
+ Copyright: 2016, Nils Adermann <naderman@naderman.de>
8
+ 2016, Jordi Boggiano <j.boggiano@seld.be>
9
+ License: Expat
10
+
11
+ Files: src/Composer/Util/TlsHelper.php
12
+ Copyright: 2016, Nils Adermann <naderman@naderman.de>
13
+ 2016, Jordi Boggiano <j.boggiano@seld.be>
14
+ 2013, Evan Coury <me@evancoury.com>
15
+ License: Expat and BSD-2-Clause
16
+
17
+ License: BSD-2-Clause
18
+ Redistribution and use in source and binary forms, with or without modification,
19
+ are permitted provided that the following conditions are met:
20
+ .
21
+ * Redistributions of source code must retain the above copyright notice,
22
+ this list of conditions and the following disclaimer.
23
+ .
24
+ * Redistributions in binary form must reproduce the above copyright notice,
25
+ this list of conditions and the following disclaimer in the documentation
26
+ and/or other materials provided with the distribution.
27
+ .
28
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
29
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
30
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
31
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
32
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
33
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
34
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
35
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
37
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
+
39
+ License: Expat
40
+ Permission is hereby granted, free of charge, to any person obtaining a copy
41
+ of this software and associated documentation files (the "Software"), to deal
42
+ in the Software without restriction, including without limitation the rights
43
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
44
+ copies of the Software, and to permit persons to whom the Software is furnished
45
+ to do so, subject to the following conditions:
46
+ .
47
+ The above copyright notice and this permission notice shall be included in all
48
+ copies or substantial portions of the Software.
49
+ .
50
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
51
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
52
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
53
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
54
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
55
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
56
+ THE SOFTWARE.
vendor/composer/autoload_classmap.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_classmap.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ 'Kliken\\WcPlugin\\Helper' => $baseDir . '/classes/class-helper.php',
10
+ 'Kliken\\WcPlugin\\Message' => $baseDir . '/classes/class-message.php',
11
+ 'Kliken\\WcPlugin\\Plugin' => $baseDir . '/classes/class-plugin.php',
12
+ 'Kliken\\WcPlugin\\REST_Misc_Controller' => $baseDir . '/classes/class-rest-misc-controller.php',
13
+ 'Kliken\\WcPlugin\\REST_Orders_Controller' => $baseDir . '/classes/class-rest-orders-controller.php',
14
+ 'Kliken\\WcPlugin\\REST_Products_Controller' => $baseDir . '/classes/class-rest-products-controller.php',
15
+ 'Kliken\\WcPlugin\\WC_Integration' => $baseDir . '/classes/class-wc-integration.php',
16
+ 'Kliken\\WcPlugin\\WC_REST_Products_Controller_Compat' => $baseDir . '/classes/class-wc-rest-products-controller-compat.php',
17
+ );
vendor/composer/autoload_namespaces.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_namespaces.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ );
vendor/composer/autoload_psr4.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_psr4.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ );
vendor/composer/autoload_real.php ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_real.php @generated by Composer
4
+
5
+ class ComposerAutoloaderInit8d05e6ff15e07dd67db7d411953e0c56
6
+ {
7
+ private static $loader;
8
+
9
+ public static function loadClassLoader($class)
10
+ {
11
+ if ('Composer\Autoload\ClassLoader' === $class) {
12
+ require __DIR__ . '/ClassLoader.php';
13
+ }
14
+ }
15
+
16
+ public static function getLoader()
17
+ {
18
+ if (null !== self::$loader) {
19
+ return self::$loader;
20
+ }
21
+
22
+ spl_autoload_register(array('ComposerAutoloaderInit8d05e6ff15e07dd67db7d411953e0c56', 'loadClassLoader'), true, true);
23
+ self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
+ spl_autoload_unregister(array('ComposerAutoloaderInit8d05e6ff15e07dd67db7d411953e0c56', 'loadClassLoader'));
25
+
26
+ $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
27
+ if ($useStaticLoader) {
28
+ require_once __DIR__ . '/autoload_static.php';
29
+
30
+ call_user_func(\Composer\Autoload\ComposerStaticInit8d05e6ff15e07dd67db7d411953e0c56::getInitializer($loader));
31
+ } else {
32
+ $map = require __DIR__ . '/autoload_namespaces.php';
33
+ foreach ($map as $namespace => $path) {
34
+ $loader->set($namespace, $path);
35
+ }
36
+
37
+ $map = require __DIR__ . '/autoload_psr4.php';
38
+ foreach ($map as $namespace => $path) {
39
+ $loader->setPsr4($namespace, $path);
40
+ }
41
+
42
+ $classMap = require __DIR__ . '/autoload_classmap.php';
43
+ if ($classMap) {
44
+ $loader->addClassMap($classMap);
45
+ }
46
+ }
47
+
48
+ $loader->register(true);
49
+
50
+ return $loader;
51
+ }
52
+ }
vendor/composer/autoload_static.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_static.php @generated by Composer
4
+
5
+ namespace Composer\Autoload;
6
+
7
+ class ComposerStaticInit8d05e6ff15e07dd67db7d411953e0c56
8
+ {
9
+ public static $classMap = array (
10
+ 'Kliken\\WcPlugin\\Helper' => __DIR__ . '/../..' . '/classes/class-helper.php',
11
+ 'Kliken\\WcPlugin\\Message' => __DIR__ . '/../..' . '/classes/class-message.php',
12
+ 'Kliken\\WcPlugin\\Plugin' => __DIR__ . '/../..' . '/classes/class-plugin.php',
13
+ 'Kliken\\WcPlugin\\REST_Misc_Controller' => __DIR__ . '/../..' . '/classes/class-rest-misc-controller.php',
14
+ 'Kliken\\WcPlugin\\REST_Orders_Controller' => __DIR__ . '/../..' . '/classes/class-rest-orders-controller.php',
15
+ 'Kliken\\WcPlugin\\REST_Products_Controller' => __DIR__ . '/../..' . '/classes/class-rest-products-controller.php',
16
+ 'Kliken\\WcPlugin\\WC_Integration' => __DIR__ . '/../..' . '/classes/class-wc-integration.php',
17
+ 'Kliken\\WcPlugin\\WC_REST_Products_Controller_Compat' => __DIR__ . '/../..' . '/classes/class-wc-rest-products-controller-compat.php',
18
+ );
19
+
20
+ public static function getInitializer(ClassLoader $loader)
21
+ {
22
+ return \Closure::bind(function () use ($loader) {
23
+ $loader->classMap = ComposerStaticInit8d05e6ff15e07dd67db7d411953e0c56::$classMap;
24
+
25
+ }, null, ClassLoader::class);
26
+ }
27
+ }
vendor/composer/installed.json ADDED
@@ -0,0 +1 @@
 
1
+ []