Call Now Button - Version 0.4.4

Version Description

= * UI improvements

Download this release

Release Info

Developer jasperroel
Plugin Icon 128x128 Call Now Button
Version 0.4.4
Comparing to
See all releases

Code changes from version 0.4.3 to 0.4.4

Files changed (43) hide show
  1. call-now-button.css +0 -95
  2. call-now-button.js +0 -46
  3. call-now-button.php +13 -711
  4. index.php +1 -0
  5. readme.txt +18 -16
  6. resources/images/button-new-full.png +0 -0
  7. resources/images/button-new-multi.png +0 -0
  8. resources/images/button-new-single.png +0 -0
  9. resources/images/favicon.png +0 -0
  10. resources/js/call-now-button.js +288 -0
  11. resources/style/call-now-button.css +248 -0
  12. src/admin/action-edit.php +597 -0
  13. src/admin/action-overview.php +423 -0
  14. src/admin/admin-ajax.php +29 -0
  15. src/admin/api/CnbAdminCloud.php +631 -0
  16. src/admin/api/CnbAppRemote.php +634 -0
  17. src/admin/api/CnbAppRemotePayment.php +46 -0
  18. src/admin/api/RemoteTrace.php +65 -0
  19. src/admin/api/RemoteTracer.php +56 -0
  20. src/admin/apikey-overview.php +365 -0
  21. src/admin/button-edit.php +569 -0
  22. src/admin/button-overview.php +589 -0
  23. src/admin/condition-edit.php +233 -0
  24. src/admin/condition-overview.php +408 -0
  25. src/admin/domain-edit.php +370 -0
  26. src/admin/domain-overview.php +352 -0
  27. src/admin/domain-upgrade.php +198 -0
  28. src/admin/legacy-edit.php +246 -0
  29. src/admin/partials/admin-footer.php +49 -0
  30. src/admin/partials/admin-functions.php +100 -0
  31. src/admin/partials/admin-header.php +69 -0
  32. src/admin/settings.php +471 -0
  33. src/autoload.php +31 -0
  34. src/call-now-button.php +299 -0
  35. src/renderers/classic/wp_foot.php +85 -0
  36. src/renderers/classic/wp_head.php +35 -0
  37. src/renderers/cloud/wp_foot.php +1 -0
  38. src/renderers/cloud/wp_head.php +12 -0
  39. src/renderers/modern/wp_foot.php +2 -0
  40. src/renderers/modern/wp_head.php +64 -0
  41. src/utils/notices.php +94 -0
  42. src/utils/utils.php +371 -0
  43. uninstall.php +16 -0
call-now-button.css DELETED
@@ -1,95 +0,0 @@
1
- .wrap {
2
- max-width: 1050px;
3
- }
4
- .version {
5
- font-weight: 200;
6
- }
7
- .form-table td p.description,
8
- .inline-helper,
9
- label.small-italic {
10
- font-size: 12px;
11
- }
12
-
13
- table.form-table {
14
- display:none;
15
- }
16
-
17
- table.form-table.nav-tab-active {
18
- display: initial;
19
- }
20
-
21
- .cnb-url a {
22
- text-decoration: none;
23
- font-weight: 200;
24
- font-size: 28px;
25
- color: black;
26
- }
27
- .cnb-center {
28
- text-align: center;
29
- }
30
- #settings {
31
- display: none;
32
- }
33
- .cnb-container input[type="radio"] + label {
34
- font-weight: 300;
35
- }
36
- .cnb-container input[type="radio"]:checked + label,
37
- .cnb-container input[type="checkbox"]:checked + label {
38
- font-weight: bold;
39
- }
40
- .radio-item {
41
- margin: 10px 0;
42
- }
43
- .cnb-extra-placement {
44
- display: none;
45
- }
46
- .cnb-extra-placement.cnb-extra-active {
47
- display: block;
48
- }
49
- .check-settings,
50
- .cnb-switch-back {
51
- color: #0073aa;
52
- text-decoration: underline;
53
- cursor: pointer;
54
- }
55
- .red-background {
56
- background-color: #ffdbdb !important;
57
- }
58
- #cnb_slider_value,
59
- #cnb_order_value {
60
- font-weight: normal;
61
- }
62
- input[type='range'] {
63
- position: relative;
64
- top: 8px;
65
- }
66
- @-moz-document url-prefix() {
67
- input[type='range'] {
68
- top: 12px;
69
- }
70
- }
71
- .cnb_slider_value {
72
- font-weight: normal;
73
- }
74
-
75
- @media screen and (min-width: 960px) {
76
- .radio-item {
77
- margin: 5px 0;
78
- }
79
- }
80
- .nounderscore {
81
- text-decoration: none;
82
- }
83
-
84
- a#cnb_donate {
85
- display: inline-block;
86
- padding: 5px 10px;
87
- text-decoration: none;
88
- background: #ffc439;
89
- border-radius: 15px;
90
- color: #000;
91
- font-weight: bold;
92
- border-style: outset none;
93
- border-color: #ffdc8a;
94
- border-width: 1px;
95
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
call-now-button.js DELETED
@@ -1,46 +0,0 @@
1
- jQuery(document).ready(function($){
2
- // Add color picker
3
- $('.cnb-color-field').wpColorPicker();
4
- $('.cnb-iconcolor-field').wpColorPicker();
5
-
6
- // Reveal additional button placements when clicking "more"
7
- $("#cnb-more-placements").click(function(e){
8
- e.preventDefault();
9
- $(".cnb-extra-placement").css("display","block");
10
- $("#cnb-more-placements").remove();
11
- });
12
-
13
- // Option to Hide Icon is only visible when the full width button is selected
14
- var radioValue = $("input[name='cnb[appearance]']:checked").val();
15
- var textValue = $("input[name='cnb[text]']").val();
16
- if(radioValue != 'full' && radioValue != 'tfull') {
17
- $('#hideIconTR').hide();
18
- } else if(textValue.length < 1) {
19
- $('#hideIconTR').hide();
20
- }
21
- $('input[name="cnb[appearance]"]').on("change",function(){
22
- var radioValue = $("input[name='cnb[appearance]']:checked").val();
23
- var textValue = $("input[name='cnb[text]']").val();
24
- if(radioValue != 'full' && radioValue != 'tfull') {
25
- $('#hideIconTR').hide();
26
- } else if(textValue.length > 0 ) {
27
- $('#hideIconTR').show();
28
- }
29
-
30
- });
31
- });
32
- // Zoom slider - show percentage
33
- var cnb_slider = document.getElementById("cnb_slider");
34
- var cnb_slider_value = document.getElementById("cnb_slider_value");
35
- cnb_slider_value.innerHTML = Math.round(cnb_slider.value * 100) + "%";
36
- cnb_slider.oninput = function() {
37
- cnb_slider_value.innerHTML = Math.round(this.value * 100) + "%";
38
- }
39
-
40
- // Z-index slider - show steps
41
- var cnb_order_slider = document.getElementById("cnb_order_slider");
42
- var cnb_order_value = document.getElementById("cnb_order_value");
43
- cnb_order_value.innerHTML = cnb_order_slider.value;
44
- cnb_order_slider.oninput = function() {
45
- cnb_order_value.innerHTML = this.value;
46
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
call-now-button.php CHANGED
@@ -3,16 +3,17 @@
3
  Plugin Name: Call Now Button
4
  Plugin URI: https://callnowbutton.com
5
  Description: Mobile visitors will see a <strong>Call Now Button</strong> on your website. Easy to use but flexible to meet more demanding requirements. Change placement and color, hide on specific pages, track how many people click them or conversions of your Google Ads campaigns. It's all optional but possible.
6
- Version: 0.4.3
7
  Author: Jerry Rietveld
8
- Author URI: http://www.callnowbutton.com
 
9
  License: GPL2
10
  */
11
 
12
- /* Copyright 2013-2021 Jerry Rietveld (email : jerry@callnowbutton.com)
13
 
14
  This program is free software; you can redistribute it and/or modify
15
- it under the terms of the GNU General Public License, version 2, as
16
  published by the Free Software Foundation.
17
 
18
  This program is distributed in the hope that it will be useful,
@@ -24,712 +25,13 @@ License: GPL2
24
  along with this program; if not, write to the Free Software
25
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
26
  */
27
- ?>
28
- <?php
29
- define('CNB_VERSION','0.4.3');
30
- define('CNB_BASENAME', plugin_basename( __FILE__ ) );
31
- define('CNB_BASEFOLDER', plugin_basename( dirname( __FILE__ ) ) );
32
- define('CNB_FILENAME', str_replace( CNB_BASEFOLDER.'/', '', CNB_BASENAME ) );
33
- define('CNB_WEBSITE','https://callnowbutton.com/');
34
- define('CNB_SUPPORT', CNB_WEBSITE . 'support/');
35
- add_action('admin_menu', 'cnb_register_admin_page');
36
- add_action('admin_init', 'cnb_options_init');
37
-
38
- $cnb_changelog =
39
- array(
40
- array(
41
- '4.3' => 'Critical fix',
42
- '4.2' => 'Button styling adjustments, security improvements',
43
- '4.0' => 'Text bubbles for standard buttons, set the icon color, Google Ads conversion tracking, tabbed admin interface, 6 additional button locations, small button design changes, added support articles for (nearly) all settings, control visibility on front page, plus a bunch of smaller fixes. Enjoy!',
44
- '3.6' => 'Small validation fixes and zoom now controls icon size in full width buttons.',
45
- '3.5' => 'Small JS fix',
46
- '3.4' => 'Option to resize your button and change where it sits in the stack order (z-index).',
47
- '3.3' => 'Some small improvements.',
48
- '3.2' => 'Option to hide icon in text button, small bug fixes.',
49
- '3.1' => 'You can now add text to your button and it\'s possible to switch between including and excluding specific pages.',
50
- '3.0' => 'Option to add text to your button.',
51
- '2.1' => 'Some small fixes',
52
- '2.0' => 'The Call Now Button has a new look!'
53
- )
54
- );
55
-
56
- $cnb_settings = cnb_get_options(); // Grabbing the settins and checking for latest version OR creating the options file for first time installations
57
- $cnb_options = $cnb_settings['options'];
58
- $cnb_updated = $cnb_settings['updated'];
59
-
60
-
61
- $cnb_options['active'] = isset($cnb_options['active']) ? 1 : 0;
62
- $cnb_options['classic'] = isset($cnb_options['classic']) ? 1 : 0;
63
- $cnb_options['hideIcon'] = isset($cnb_options['hideIcon']) ? $cnb_options['hideIcon'] : 0;
64
- $cnb_options['frontpage'] = isset($cnb_options['frontpage']) ? $cnb_options['frontpage'] : 0;
65
-
66
- $plugin_title = apply_filters( 'cnb_plugin_title', 'Call Now Button');
67
-
68
- if(isset($_GET['page']) && strpos($_GET['page'], 'call-now-button') !== false) {
69
- add_action( 'admin_enqueue_scripts', 'cnb_enqueue_color_picker' ); // add the color picker
70
- }
71
-
72
- function cnb_register_admin_page() {
73
- global $plugin_title;
74
- $page = add_submenu_page('options-general.php', $plugin_title, $plugin_title, 'manage_options', 'call-now-button', 'cnb_admin_settings_page');
75
- add_action( 'admin_print_styles-' . $page , 'cnb_admin_styling' );
76
- }
77
- function cnb_enqueue_color_picker( $hook_suffix ) {
78
- wp_enqueue_style( 'wp-color-picker' );
79
- wp_enqueue_script( 'cnb-script-handle', plugins_url('call-now-button.js', __FILE__ ), array( 'wp-color-picker' ), CNB_VERSION, true );
80
- }
81
- function cnb_admin_styling() {
82
- wp_enqueue_style( 'cnb_styling' );
83
- }
84
-
85
- function cnb_plugin_meta($links, $file) {
86
- if ( $file == CNB_BASENAME ) {
87
- $cnb_new_links = array(
88
- sprintf( '<a href="options-general.php?page=%s">%s</a>', CNB_BASEFOLDER, __('Settings')),
89
- '<a href="'.CNB_SUPPORT.'">Support</a>');
90
- array_push(
91
- $links,
92
- $cnb_new_links[0],
93
- $cnb_new_links[1]
94
- );
95
- }
96
- return $links;
97
- }
98
- add_filter( 'plugin_row_meta', 'cnb_plugin_meta', 10, 2 );
99
-
100
- function cnb_plugin_add_settings_link( $links ) {
101
- array_unshift( $links, sprintf( '<a href="options-general.php?page=%s">%s</a>', CNB_BASEFOLDER, __('Settings') ) );
102
- return $links;
103
- }
104
- add_filter( 'plugin_action_links_'. CNB_BASENAME, 'cnb_plugin_add_settings_link' );
105
-
106
- function cnb_options_init() {
107
- register_setting('cnb_options','cnb');
108
- wp_register_style( 'cnb_styling', plugins_url('call-now-button.css', __FILE__), false, CNB_VERSION, 'all' );
109
- }
110
- function cnb_admin_settings_page() {
111
- global $cnb_options;
112
- global $plugin_title;
113
- global $cnb_updated;
114
- global $cnb_changelog;
115
- ?>
116
-
117
- <div class="wrap">
118
- <h1>Call Now Button <span class="version">v<?php echo CNB_VERSION;?></span></h1>
119
-
120
- <!--## NOTIFICATION BARS ## -->
121
- <?php
122
- // Display notification that the button is active or inactive
123
- if(!$cnb_options['active']==1) {
124
- echo '<div class="notice-error notice"><p>The Call Now Button is currently <b>inactive</b>.</p></div>';
125
- }
126
-
127
- // Display notification that there's a caching plugin active
128
- if(isset($_GET['settings-updated'])) {
129
- $cnb_caching_check = cnb_check_for_caching();
130
- if($cnb_caching_check[0] == TRUE) {
131
- echo '<div class="notice-error notice"><p><span class="dashicons dashicons-warning"></span> Your website is using a <strong><i>Caching Plugin</i></strong> ('.$cnb_caching_check[1].'). If you\'re not seeing your button or your changes, make sure you empty your cache first.</p></div>';
132
- }
133
- }
134
-
135
- // inform exisiting users about update to the button design
136
- if($cnb_updated[0]) { ?>
137
- <div class="notice-warning notice is-dismissible">
138
- <?php
139
- $cnb_old_version = substr($cnb_updated[1],2);
140
- echo "<h3>The Call Now Button has been updated!</h3><h4>What's new?</h4>";
141
- foreach ($cnb_changelog[0] as $key => $value) { // Only on first run after update show list of changes since last update
142
- if($key > $cnb_old_version) {
143
- echo '<p><span class="dashicons dashicons-yes"></span> ' . $value . '</p>';
144
- }
145
- }
146
- ?>
147
- </div>
148
- <?php }
149
-
150
-
151
- if( isset( $_GET[ 'tab' ] ) ) {
152
- $cnb_admin_tabs = array (
153
- "basic_options",
154
- "extra_options",
155
- "advanced_options"
156
- );
157
- $get_tab = sanitize_key($_GET[ 'tab' ]);
158
- if(in_array($get_tab, $cnb_admin_tabs)) {
159
- $active_tab = $get_tab;
160
- } else {
161
- $active_tab = "basic_options";
162
- }
163
- } else {
164
- $active_tab = "basic_options";
165
- } // end if
166
-
167
-
168
- ?>
169
- <h2 class="nav-tab-wrapper">
170
- <a href="?page=call-now-button&tab=basic_options" class="nav-tab <?php echo $active_tab == 'basic_options' ? 'nav-tab-active' : ''; ?>">Basics</a>
171
- <a href="?page=call-now-button&tab=extra_options" class="nav-tab <?php echo $active_tab == 'extra_options' ? 'nav-tab-active' : ''; ?>">Presentation</a>
172
- <a href="?page=call-now-button&tab=advanced_options" class="nav-tab <?php echo $active_tab == 'advanced_options' ? 'nav-tab-active' : ''; ?>">Advanced</a>
173
- </h2>
174
- <form method="post" action="options.php" class="cnb-container">
175
- <?php settings_fields('cnb_options'); ?>
176
- <table class="form-table <?php echo $active_tab == 'basic_options' ? 'nav-tab-active' : ''; ?>">
177
- <tr>
178
- <th colspan="2"><h2>Basic Settings</h2></th>
179
- </tr>
180
- <tr valign="top">
181
- <th scope="row">Button status:</th>
182
- <td class="activated">
183
- <input id="activated" name="cnb[active]" type="checkbox" value="1" <?php checked('1', $cnb_options['active']); ?> /> <label title="Enable" for="activated">Enabled</label> &nbsp; &nbsp;
184
- </td>
185
- </tr>
186
- <tr valign="top">
187
- <th scope="row">Phone number:<a href="<?php echo CNB_SUPPORT; ?>phone-number/<?php cnb_utm_params("question-mark", "phone-number"); ?>" target="_blank" class="nounderscore">
188
- <span class="dashicons dashicons-editor-help"></span>
189
- </a></th>
190
- <td><input type="text" name="cnb[number]" value="<?php echo $cnb_options['number']; ?>" /></td>
191
- </tr>
192
- <tr valign="top" class="button-text">
193
- <th scope="row">Button text <small style="font-weight: 400">(optional)</small>:<a href="<?php echo CNB_SUPPORT; ?>using-text-buttons/<?php cnb_utm_params("question-mark", "using-text-buttons"); ?>" target="_blank" class="nounderscore">
194
- <span class="dashicons dashicons-editor-help"></span>
195
- </a></th>
196
- <td>
197
- <input id="buttonTextField" type="text" name="cnb[text]" value="<?php echo $cnb_options['text']; ?>" maxlength="30" />
198
- <p class="description">Leave this field empty to only show an icon.</p>
199
- </td>
200
- </tr>
201
- </table>
202
-
203
- <table class="form-table <?php echo $active_tab == 'extra_options' ? 'nav-tab-active' : ''; ?>">
204
- <tr>
205
- <th colspan="2"><h2>Presentation Settings</h2></th>
206
- </tr>
207
-
208
- <tr valign="top">
209
- <th scope="row">Button color:</th>
210
- <td><input name="cnb[color]" type="text" value="<?php echo $cnb_options['color']; ?>" class="cnb-color-field" data-default-color="#009900" /></td>
211
- </tr>
212
- <tr valign="top">
213
- <th scope="row">Icon color:</th>
214
- <td><input name="cnb[iconcolor]" type="text" value="<?php echo $cnb_options['iconcolor']; ?>" class="cnb-iconcolor-field" data-default-color="#ffffff" /></td>
215
- </tr>
216
- <tr valign="top">
217
- <th scope="row">Position: <a href="<?php echo CNB_SUPPORT; ?>button-position/<?php cnb_utm_params("question-mark", "button-position"); ?>" target="_blank" class="nounderscore">
218
- <span class="dashicons dashicons-editor-help"></span>
219
- </a></th>
220
- <td class="appearance">
221
- <div class="appearance-options">
222
- <div class="radio-item">
223
- <input type="radio" id="appearance1" name="cnb[appearance]" value="right" <?php checked('right', $cnb_options['appearance']); ?>>
224
- <label title="right" for="appearance1">Right corner</label>
225
- </div>
226
- <div class="radio-item">
227
- <input type="radio" id="appearance2" name="cnb[appearance]" value="left" <?php checked('left', $cnb_options['appearance']); ?>>
228
- <label title="left" for="appearance2">Left corner</label>
229
- </div>
230
- <div class="radio-item">
231
- <input type="radio" id="appearance3" name="cnb[appearance]" value="middle" <?php checked('middle', $cnb_options['appearance']); ?>>
232
- <label title="middle" for="appearance3">Center bottom</label>
233
- </div>
234
- <div class="radio-item">
235
- <input type="radio" id="appearance4" name="cnb[appearance]" value="full" <?php checked('full', $cnb_options['appearance']); ?>>
236
- <label title="full" for="appearance4">Full bottom</label>
237
- </div>
238
-
239
- <!-- Extra placement options -->
240
- <br class="cnb-extra-placement">
241
- <div class="radio-item cnb-extra-placement <?php echo $cnb_options['appearance'] == "mright" ? "cnb-extra-active" : ""; ?>">
242
- <input type="radio" id="appearance5" name="cnb[appearance]" value="mright" <?php checked('mright', $cnb_options['appearance']); ?>>
243
- <label title="mright" for="appearance5">Middle right</label>
244
- </div>
245
- <div class="radio-item cnb-extra-placement <?php echo $cnb_options['appearance'] == "mleft" ? "cnb-extra-active" : ""; ?>">
246
- <input type="radio" id="appearance6" name="cnb[appearance]" value="mleft" <?php checked('mleft', $cnb_options['appearance']); ?>>
247
- <label title="mleft" for="appearance6">Middle left </label>
248
- </div>
249
- <br class="cnb-extra-placement">
250
- <div class="radio-item cnb-extra-placement <?php echo $cnb_options['appearance'] == "tright" ? "cnb-extra-active" : ""; ?>">
251
- <input type="radio" id="appearance7" name="cnb[appearance]" value="tright" <?php checked('tright', $cnb_options['appearance']); ?>>
252
- <label title="tright" for="appearance7">Top right corner</label>
253
- </div>
254
- <div class="radio-item cnb-extra-placement <?php echo $cnb_options['appearance'] == "tleft" ? "cnb-extra-active" : ""; ?>">
255
- <input type="radio" id="appearance8" name="cnb[appearance]" value="tleft" <?php checked('tleft', $cnb_options['appearance']); ?>>
256
- <label title="tleft" for="appearance8">Top left corner</label>
257
- </div>
258
- <div class="radio-item cnb-extra-placement <?php echo $cnb_options['appearance'] == "tmiddle" ? "cnb-extra-active" : ""; ?>">
259
- <input type="radio" id="appearance9" name="cnb[appearance]" value="tmiddle" <?php checked('tmiddle', $cnb_options['appearance']); ?>>
260
- <label title="tmiddle" for="appearance9">Center top</label>
261
- </div>
262
- <div class="radio-item cnb-extra-placement <?php echo $cnb_options['appearance'] == "tfull" ? "cnb-extra-active" : ""; ?>">
263
- <input type="radio" id="appearance10" name="cnb[appearance]" value="tfull" <?php checked('tfull', $cnb_options['appearance']); ?>>
264
- <label title="tfull" for="appearance10">Full top</label>
265
- </div>
266
- <a href="#" id="cnb-more-placements">More placement options...</a>
267
- <!-- END extra placement options -->
268
- </div>
269
-
270
- <div id="hideIconTR">
271
- <br>
272
- <input id="hide_icon" type="checkbox" name="cnb[hideIcon]" value="1" <?php checked('1', $cnb_options['hideIcon']); ?>>
273
- <label title="right" for="hide_icon">Remove icon</label>
274
- </div>
275
- </td>
276
- </tr>
277
- <tr valign="top" class="appearance">
278
- <th scope="row">Limit appearance: <a href="<?php echo CNB_SUPPORT; ?>limit-appearance/<?php cnb_utm_params("question-mark", "limit-appearance"); ?>" target="_blank" class="nounderscore">
279
- <span class="dashicons dashicons-editor-help"></span>
280
- </a></th>
281
- <td>
282
- <input type="text" name="cnb[show]" value="<?php echo $cnb_options['show']; ?>" placeholder="E.g. 14, 345" />
283
- <p class="description">Enter IDs of the posts &amp; pages, separated by commas (leave blank for all). <a href="<?php echo CNB_SUPPORT; ?>limit-appearance/<?php cnb_utm_params("question-mark", "limit-appearance"); ?>">Learn more...</a></p>
284
- <div class="radio-item">
285
- <input id="limit1" type="radio" name="cnb[limit]" value="include" <?php checked('include', $cnb_options['limit']);?> />
286
- <label for="limit1">Limit to these posts and pages.</label>
287
- </div>
288
- <div class="radio-item">
289
- <input id="limit2" type="radio" name="cnb[limit]" value="exclude" <?php checked('exclude', $cnb_options['limit']);?> />
290
- <label for="limit2">Exclude these posts and pages.</label>
291
- </div>
292
- <br>
293
- <div>
294
- <input id="frontpage" type="checkbox" name="cnb[frontpage]" value="1" <?php checked('1', $cnb_options['frontpage']); ?>>
295
- <label title="right" for="frontpage">Hide button on front page</label>
296
- </div>
297
- </td>
298
- </tr>
299
- </table>
300
- <table class="form-table <?php echo $active_tab == 'advanced_options' ? 'nav-tab-active' : ''; ?>">
301
- <tr>
302
- <th colspan="2"><h2>Advanced Settings</h2></th>
303
- </tr>
304
- <tr valign="top">
305
- <th scope="row">Click tracking: <a href="<?php echo CNB_SUPPORT; ?>click-tracking/<?php cnb_utm_params("question-mark", "click-tracking"); ?>" target="_blank" class="nounderscore">
306
- <span class="dashicons dashicons-editor-help"></span>
307
- </a></th>
308
- <td>
309
- <div class="radio-item">
310
- <input id="tracking3" type="radio" name="cnb[tracking]" value="0" <?php checked('0', $cnb_options['tracking']); ?> />
311
- <label for="tracking3">Disabled</label>
312
- </div>
313
- <div class="radio-item">
314
- <input id="tracking4" type="radio" name="cnb[tracking]" value="3" <?php checked('3', $cnb_options['tracking']); ?> />
315
- <label for="tracking4">Latest Google Analytics (gtag.js)</label>
316
- </div>
317
- <div class="radio-item">
318
- <input id="tracking1" type="radio" name="cnb[tracking]" value="2" <?php checked('2', $cnb_options['tracking']); ?> />
319
- <label for="tracking1">Google Universal Analytics (analytics.js)</label>
320
- </div>
321
- <div class="radio-item">
322
- <input id="tracking2" type="radio" name="cnb[tracking]" value="1" <?php checked('1', $cnb_options['tracking']); ?> />
323
- <label for="tracking2">Classic Google Analytics (ga.js)</label>
324
- </div>
325
- <p class="description">Using Google Tag Manager? Set up click tracking in GTM. <a href="<?php echo CNB_SUPPORT; ?>click-tracking/google-tag-manager-event-tracking/<?php cnb_utm_params("description_link", "google-tag-manager-event-tracking"); ?>" target="_blank">Learn how to do this...</a></p>
326
- </td>
327
- </tr>
328
- <tr valign="top">
329
- <th scope="row">Google Ads: <a href="<?php echo CNB_SUPPORT; ?>google-ads/<?php cnb_utm_params("question-mark", "google-ads"); ?>" target="_blank" class="nounderscore">
330
- <span class="dashicons dashicons-editor-help"></span>
331
- </a></th>
332
- <td class="conversions">
333
- <div class="radio-item">
334
- <input name="cnb[conversions]" type="radio" value="0" <?php checked('0', $cnb_options['conversions']); ?> /> <label for="conversions">Off </label>
335
- </div>
336
- <div class="radio-item">
337
- <input name="cnb[conversions]" type="radio" value="1" <?php checked('1', $cnb_options['conversions']); ?> /> <label for="conversions">Conversion Tracking using Google's global site tag </label>
338
- </div>
339
- <div class="radio-item">
340
- <input name="cnb[conversions]" type="radio" value="2" <?php checked('2', $cnb_options['conversions']); ?> /> <label for="conversions">Conversion Tracking using JavaScript</label>
341
- </div>
342
- <p class="description">Select this option if you want to track clicks on the button as Google Ads conversions. This option requires the Event snippet to be present on the page. <a href="https">Learn more...</a></p>
343
- </td>
344
- </tr>
345
- <tr valign="top" class="zoom">
346
- <th scope="row">Button size (<span id="cnb_slider_value"></span>):</th>
347
- <td>
348
- <label class="cnb_slider_value">Smaller&nbsp;&laquo;&nbsp;</label>
349
- <input type="range" min="0.7" max="1.3" name="cnb[zoom]" value="<?php echo $cnb_options['zoom']; ?>" class="slider" id="cnb_slider" step="0.1">
350
- <label class="cnb_slider_value">&nbsp;&raquo;&nbsp;Bigger</label>
351
- </td>
352
- </tr>
353
- <tr valign="top" class="z-index">
354
- <th scope="row">Order (<span id="cnb_order_value"></span>): <a href="https://callnowbutton.com/set-order/" target="_blank" class="nounderscore">
355
- <span class="dashicons dashicons-editor-help"></span>
356
- </a></th>
357
- <td>
358
- <label class="cnb_slider_value">Backwards&nbsp;&laquo;&nbsp;</label>
359
- <input type="range" min="1" max="10" name="cnb[z-index]" value="<?php echo $cnb_options['z-index']; ?>" class="slider2" id="cnb_order_slider" step="1">
360
- <label class="cnb_slider_value">&nbsp;&raquo;&nbsp;Front</label>
361
- <p class="description">The default (and recommended) value is all the way to the front so the button sits on top of everything else. In case you have a specific usecase where you want something else to sit in front of the Call Now Button (e.g. a chat window or a cookie notice) you can move this backwards one step at a time to adapt it to your situation.</p>
362
- </td>
363
- </tr>
364
- <?php if($cnb_options['classic'] == 1) { ?>
365
- <tr valign="top" class="classic">
366
- <th scope="row">Classic button: <a href="https://callnowbutton.com/new-button-design/<?php cnb_utm_params("question-mark", "new-button-design"); ?>" target="_blank" class="nounderscore">
367
- <span class="dashicons dashicons-editor-help"></span>
368
- </a></th>
369
- <td>
370
- <input id="classic" name="cnb[classic]" type="checkbox" value="1" <?php checked('1', $cnb_options['classic']); ?> /> <label title="Enable" for="classic">Active</label>
371
- </td>
372
- </tr>
373
- <?php } ?>
374
- </table>
375
-
376
- <input type="hidden" name="cnb[version]" value="<?php echo CNB_VERSION; ?>" />
377
- <p class="submit"><input type="submit" class="button-primary" value="<?php _e('Save Changes') ?>" /></p>
378
- </form>
379
-
380
- <div class="feedback-collection">
381
- <div class="cnb-clear"></div>
382
- <p class="cnb-url cnb-center"><a href="https://callnowbutton.com" target="_blank">callnowbutton.com</a></p>
383
-
384
- <p class="cnb-center cnb-spacing">
385
- <a href="<?php echo CNB_SUPPORT; cnb_utm_params("footer-links", "support"); ?>" target="_blank" title="Support">Support</a> &middot;
386
- <a href="<?php echo CNB_WEBSITE; ?>feature-request/<?php cnb_utm_params("footer-links", "suggestions"); ?>" target="_blank" title="Feature Requests">Suggestions</a> &middot;
387
- <a href="https://www.paypal.com/paypalme/jgrietveld">Donate</a> &middot;
388
- <a href="<?php echo CNB_WEBSITE; ?>praise/<?php cnb_utm_params("footer-links", "thanks"); ?>" target="_blank" title="Praise">Just say thanks :-)</a>
389
- </p>
390
- <!--// Display notification about the testing program -->
391
- <div class="postbox cnb-alert-box cnb-center">
392
- <p>The Call&nbsp;Now&nbsp;Button&nbsp;<b>Pro</b> is imminent.
393
- <a class="cnb-external" href="https://callnowbutton.com/be-notified-call-now-button-pro/<?php cnb_utm_params("footer-links", "notify-of-pro"); ?>" rel="help" target="_blank">Be the first to know!</a>
394
- </p>
395
- </div>
396
-
397
- <div class="donate cnb-center">
398
- <a id="cnb_donate" title="Thank you!!" href="https://www.paypal.com/paypalme/jgrietveld">Donate</a>
399
- </div><!--.donate-->
400
- </div>
401
-     </div>
402
- <?php }
403
- if(get_option('cnb') && !is_admin()) {
404
-
405
- $cnb_options = get_option('cnb');
406
- $cnb_enabled = (isset($cnb_options['active'])) ? true : false;
407
 
408
- if($cnb_enabled) {
409
- function cnb_head() {
410
- global $cnb_options;
411
- $cnb_has_text = ($cnb_options['text'] == '') ? false : true;
412
- $cnb_is_full_width = $cnb_options['appearance'] == "full" || $cnb_options['appearance'] == "tfull" ? true : false;
413
- $cnb_is_classic = isset($cnb_options['classic']) && $cnb_options['classic'] == 1 && !$cnb_has_text ? true : false;
414
- $cnb_button_css = "";
415
-
416
-
417
- $ButtonExtra = "";
418
- if($cnb_is_classic) {
419
-
420
- // OLD BUTTON DESIGN
421
- if($cnb_options['appearance'] == 'full' || $cnb_options['appearance'] == 'middle' || $cnb_has_text) {
422
- $cnb_button_appearance = "width:100%;left:0;";
423
- $ButtonExtra = "body {padding-bottom:60px;}";
424
- }
425
- elseif($cnb_options['appearance'] == 'left') {
426
- $cnb_button_appearance = "width:100px;left:0;border-bottom-right-radius:40px; border-top-right-radius:40px;";
427
- } else {
428
- $cnb_button_appearance = "width:100px;right:0;border-bottom-left-radius:40px; border-top-left-radius:40px;";
429
- }
430
-
431
- $cnb_button_css .= "<style>#callnowbutton, #callnowbutton span {display:none;} @media screen and (max-width:650px){#callnowbutton .NoButtonText{display:none;}#callnowbutton {display:block; ".$cnb_button_appearance." height:80px; position:fixed; bottom:-20px; border-top:2px solid ".changeColor($cnb_options['color'],'lighter')."; background:url(data:image/svg+xml;base64,".svg(changeColor($cnb_options['color'], 'darker'),$cnb_options['iconcolor'] ).") center 2px no-repeat ".$cnb_options['color']."; text-decoration:none; box-shadow:0 0 5px #888; z-index:".zindex($cnb_options['z-index']).";background-size:58px 58px}".$ButtonExtra."}</style>\n";
432
-
433
- } else {
434
-
435
- // NEW BUTTON DESIGN
436
- $cnb_button_shape = "width:55px; height:55px; border-radius:50%; box-shadow: 0 3px 6px rgba(0, 0, 0, 0.3);transform: scale(" . $cnb_options['zoom'] . ");";
437
- $cnb_button_positions = array(
438
- 'middle' => 'bottom:15px; left:50%; margin-left:-28px;',
439
- 'left' => 'bottom:15px; left:20px;',
440
- 'right' => 'bottom:15px; right:20px;',
441
- 'mleft' => 'top:50%; margin-top:-28px; left:20px;',
442
- 'mright' => 'top:50%; margin-top:-28px; right:20px;',
443
- 'tleft' => 'top:15px; left:20px;',
444
- 'tmiddle' => 'top:15px; left:50%; margin-left:-28px;',
445
- 'tright' => 'top:15px; right:20px;',
446
- );
447
-
448
- if($cnb_options['appearance'] == 'full' || $cnb_options['appearance'] == 'tfull') {
449
- $cnb_top_or_bottom = ($cnb_options['appearance']) == 'full' ? "bottom" : "top";
450
-
451
- $cnb_button_appearance = "width:100%;left:0;".$cnb_top_or_bottom.":0;height:60px;";
452
-
453
- $ButtonExtra = "body {padding-".$cnb_top_or_bottom.":60px;}#callnowbutton img {transform: scale(" . $cnb_options['zoom'] . ");}";
454
- if($cnb_has_text) {
455
- $cnb_button_appearance .= "text-align:center;color:#fff; font-weight:600; font-size:120%; overflow: hidden;";
456
- if(isset($cnb_options['hideIcon']) && $cnb_options['hideIcon'] == 1) {
457
- $cnb_button_appearance .= 'padding-right:20px;';
458
- }
459
- }
460
- } else {
461
- $cnb_button_appearance = $cnb_button_shape . $cnb_button_positions[$cnb_options['appearance']];
462
- }
463
-
464
- $cnb_label_side = ltrim(ltrim($cnb_options['appearance'],"m"),"t");
465
-
466
- if($cnb_has_text && ($cnb_options['appearance'] == 'middle' || $cnb_options['appearance'] == 'tmiddle')) { // don't show the label in this situation
467
- $circularButtonTextCSS = "#callnowbutton span{display: none;";
468
- } elseif($cnb_has_text && !$cnb_is_full_width){
469
- $circularButtonTextCSS = "\n#callnowbutton span {-moz-osx-font-smoothing: grayscale; -webkit-user-select: none; -ms-user-select: none; user-select: none; display: block; width: auto; background-color: rgba(70,70,70,.9); position: absolute; ".$cnb_label_side.": 68px; border-radius: 2px; font-family: Helvetica,Arial,sans-serif; padding: 6px 8px; font-size: 13px; font-weight:700; color: #ececec; top: 15px; box-shadow: 0 1px 2px rgba(0,0,0,.15); word-break: keep-all; line-height: 1em; text-overflow: ellipsis; vertical-align: middle; }";
470
- } elseif(!$cnb_is_full_width) {
471
- $circularButtonTextCSS = "#callnowbutton span{display:none;}";
472
- } else {
473
- $circularButtonTextCSS = "";
474
- }
475
-
476
- $cnb_button_css .= "#callnowbutton {display:none;} @media screen and (max-width:650px){#callnowbutton {display:block; position:fixed; text-decoration:none; z-index:".zindex($cnb_options['z-index']).";";
477
- $cnb_button_css .= $cnb_button_appearance;
478
- if($cnb_is_full_width) {
479
- $cnb_button_css .= "background:".$cnb_options['color'].";display: flex; justify-content: center; align-items: center;text-shadow: 0 1px 0px rgba(0, 0, 0, 0.18);";
480
- } else {
481
- $cnb_button_css .= "background:url(data:image/svg+xml;base64,".svg(changeColor($cnb_options['color'], 'darker'),$cnb_options['iconcolor'] ).") center/35px 35px no-repeat ".$cnb_options['color'].";";
482
- }
483
- $cnb_button_css .= "}" . $ButtonExtra . "}" . $circularButtonTextCSS;
484
-
485
-
486
- }
487
- echo "\n<!-- Call Now Button ".CNB_VERSION." by Jerry Rietveld (callnowbutton.com) -->\n <style>" . esc_html($cnb_button_css) . "</style>\n";
488
- }
489
- add_action('wp_head', 'cnb_head');
490
-
491
- function cnb_footer() {
492
- global $cnb_options;
493
-
494
- $cnb_hide_icon = isset($cnb_options['hideIcon']) && $cnb_options['hideIcon'] == 1 ? true : false;
495
- $cnb_has_text = ($cnb_options['text'] == '') ? false : true;
496
- $cnb_is_classic = isset($cnb_options['classic']) && $cnb_options['classic'] == 1 ? true : false;
497
- $cnb_show_limited = isset($cnb_options['show']) && $cnb_options['show'] != '' ? true : false;
498
- $cnb_show_included = $cnb_options['limit'] == 'include' ? true : false;
499
- $cnb_click_tracking = $cnb_options['tracking'] > 0 ? true : false;
500
- $cnb_is_full_width = $cnb_options['appearance'] == 'full' || $cnb_options['appearance'] == 'tfull' ? true : false;
501
- $cnb_hide_frontpage = isset($cnb_options['frontpage']) && $cnb_options['frontpage'] == 1 ? true : false;
502
- $cnb_conversion_tracking = $cnb_options['conversions'] > 0 ? true : false;
503
-
504
- if($cnb_show_limited) {
505
- $cnb_show_ids = explode(',', str_replace(' ', '' ,$cnb_options['show']));
506
- }
507
-
508
- if($cnb_click_tracking) {
509
- $cnb_tracking_code[1] = "_gaq.push(['_trackEvent', 'Contact', 'Call Now Button', 'Phone']);";
510
- $cnb_tracking_code[2] = "ga('send', 'event', 'Contact', 'Call Now Button', 'Phone');";
511
- $cnb_tracking_code[3] = "gtag('event', 'Call Now Button', {event_category: 'contact', event_label: 'phone'});";
512
- $cnb_tracking_code = $cnb_tracking_code[$cnb_options['tracking']];
513
- } else {
514
- $cnb_tracking_code = "";
515
- }
516
-
517
- if($cnb_conversion_tracking) {
518
- $cnb_conversion_code[1] = "return gtag_report_conversion('tel:".$cnb_options['number']."');";
519
- $cnb_conversion_code[2] = "goog_report_conversion('tel:".$cnb_options['number']."');";
520
- $cnb_conversion_code = $cnb_conversion_code[$cnb_options['conversions']];
521
- } else {
522
- $cnb_conversion_code = "";
523
- }
524
-
525
- $cnb_onclick_events = $cnb_click_tracking || $cnb_conversion_tracking ? $cnb_tracking_code . $cnb_conversion_code : "";
526
-
527
- $cnb_button_text = str_replace(" ", "&nbsp;", $cnb_options['text']);
528
- if(!$cnb_has_text && !$cnb_is_full_width) {
529
- $cnb_button_text = 'Call Now Button';
530
- $cnb_button_content = 10; // text only
531
- } elseif(!$cnb_has_text && $cnb_is_full_width) {
532
- $cnb_button_text = '';
533
- $cnb_button_content = 20; // image only
534
- } elseif($cnb_hide_icon && $cnb_is_full_width) {
535
- $cnb_button_content = 11; // text only with flexible color
536
- } elseif($cnb_is_full_width) {
537
- $cnb_button_content = 31; // text and image both flexible colors
538
- } else {
539
- $cnb_button_content = 10; // text only
540
- }
541
-
542
-
543
-
544
- if(is_front_page()) {
545
- if(!$cnb_hide_frontpage) {
546
- echo cnb_button_output($cnb_options['number'],$cnb_onclick_events,$cnb_button_text,$cnb_button_content,$cnb_options['color'],$cnb_options['iconcolor']);
547
- }
548
- } elseif($cnb_show_limited) {
549
- if($cnb_show_included) {
550
- if(is_single($cnb_show_ids) || is_page($cnb_show_ids)) {
551
- echo cnb_button_output($cnb_options['number'],$cnb_onclick_events,$cnb_button_text,$cnb_button_content,$cnb_options['color'],$cnb_options['iconcolor']);
552
- }
553
- } else {
554
- if(!is_single($cnb_show_ids) && !is_page($cnb_show_ids)) {
555
- echo cnb_button_output($cnb_options['number'],$cnb_onclick_events,$cnb_button_text,$cnb_button_content,$cnb_options['color'],$cnb_options['iconcolor']);
556
- }
557
- }
558
- } else {
559
- echo cnb_button_output($cnb_options['number'],$cnb_onclick_events,$cnb_button_text,$cnb_button_content,$cnb_options['color'],$cnb_options['iconcolor']);
560
- }
561
- }
562
- add_action('wp_footer', 'cnb_footer');
563
- }
564
- }
565
- function cnb_button_output($number, $onclick, $text, $content, $color, $icon) {
566
- if($content == 10){
567
- return '<a href="tel:'.esc_attr($number).'" id="callnowbutton" onclick="'.$onclick.'""><span>'.esc_html($text).'</span></a>';
568
- } elseif($content == 11) {
569
- return '<a href="tel:'.esc_attr($number).'" id="callnowbutton" onclick="'.$onclick.'""><span style="color:'.$icon.'">'.esc_html($text).'</span></a>';
570
- } elseif($content == 20) {
571
- return '<a href="tel:'.esc_attr($number).'" id="callnowbutton" onclick="'.$onclick.'""><img alt="Call Now Button" src="data:image/svg+xml;base64,'.svg(changeColor($color, 'darker'), $icon).'" width="40"></a>';
572
- } elseif($content == 31) {
573
- return '<a href="tel:'.esc_attr($number).'" id="callnowbutton" onclick="'.$onclick.'""><img alt="Call Now Button" src="data:image/svg+xml;base64,'.svg(changeColor($color, 'darker'), $icon).'" width="40"><span style="color:'.$icon.'">'.esc_html($text).'</span></a>';
574
- }
575
-
576
- }
577
-
578
- function cnb_get_options() { // Grabbing existing settings and creating them if it's a first time installation
579
- if(!get_option('cnb')) { // Doesn't exist -> set defaults
580
- $default_options = array(
581
- 'active',
582
- 'number' => '',
583
- 'text' => '',
584
- 'hideIcon' => 0,
585
- 'color' => '#00bb00',
586
- 'iconcolor' => '#ffffff',
587
- 'appearance' => 'right',
588
- 'tracking' => 0,
589
- 'conversions' => 0,
590
- 'show' => '',
591
- 'limit' => 'include',
592
- 'frontpage' => 0,
593
- 'zoom' => '1',
594
- 'z-index' => '10',
595
- 'version' => CNB_VERSION
596
- );
597
- add_option('cnb',$default_options);
598
- } else { // Does exist -> see if update is needed
599
- $updated = cnb_update_options();
600
- }
601
- $cnb_options['options'] = get_option('cnb');
602
- $cnb_options['updated'] = isset($updated) ? $updated : array(false, substr(CNB_VERSION, 0, 3));
603
- return $cnb_options;
604
- }
605
- function cnb_update_needed() { //compares version numbers
606
- $cnb_options = get_option('cnb');
607
- $pluginVersion = CNB_VERSION;
608
- $setupVersion = array_key_exists('version', $cnb_options) ? $cnb_options['version'] : 0.1;
609
- if($pluginVersion == $setupVersion) {
610
- $output = false;
611
- } elseif(substr($pluginVersion,0,3) > substr($setupVersion,0,3)) {
612
- $output = true;
613
- } elseif(substr($pluginVersion,0,3) == substr($setupVersion,0,3)) {
614
- $output = (substr($pluginVersion,-1) > substr($setupVersion,-1)) ? true : false;
615
- } else {
616
- $output = false;
617
- }
618
- return $output;
619
- }
620
- function cnb_update_options() {
621
- $cnb_options = get_option('cnb');
622
- if(cnb_update_needed()) { // Check current version and if it needs an update
623
- $cnb_options['active'] = isset($cnb_options['active']) ? 1 : 0;
624
- $cnb_options['text'] = isset($cnb_options['text']) ? $cnb_options['text'] : "";
625
- $cnb_options['iconcolor'] = isset($cnb_options['iconcolor']) ? $cnb_options['iconcolor'] : '#ffffff';
626
- $cnb_options['appearance'] = $cnb_options['text'] != "" ? "full" : $cnb_options['appearance'];
627
- $cnb_options['hideIcon'] = isset($cnb_options['hideIcon']) ? $cnb_options['hideIcon'] : 0;
628
- $cnb_options['limit'] = isset($cnb_options['limit']) ? $cnb_options['limit'] : 'include';
629
- $cnb_options['frontpage'] = isset($cnb_options['frontpage']) ? $cnb_options['frontpage'] : 0;
630
- $cnb_options['conversions'] = isset($cnb_options['conversions']) ? 1 : 0;
631
- $cnb_options['zoom'] = isset($cnb_options['zoom']) ? $cnb_options['zoom'] : 1;
632
- $cnb_options['z-index'] = isset($cnb_options['z-index']) ? $cnb_options['z-index'] : 10;
633
- $default_options = array(
634
- 'active' => $cnb_options['active'],
635
- 'number' => $cnb_options['number'],
636
- 'text' => $cnb_options['text'],
637
- 'hideIcon' => $cnb_options['hideIcon'],
638
- 'color' => $cnb_options['color'],
639
- 'iconcolor' => $cnb_options['iconcolor'],
640
- 'appearance' => $cnb_options['appearance'],
641
- 'tracking' => $cnb_options['tracking'],
642
- 'conversions' => $cnb_options['conversions'],
643
- 'show' => $cnb_options['show'],
644
- 'limit' => $cnb_options['limit'],
645
- 'frontpage' => $cnb_options['frontpage'],
646
- 'zoom' => $cnb_options['zoom'],
647
- 'z-index' => $cnb_options['z-index'],
648
- 'version' => CNB_VERSION
649
- );
650
- if(array_key_exists('classic', $cnb_options) && $cnb_options['classic'] == 1 ) {
651
- $default_options['classic'] = 1;
652
- }
653
- update_option('cnb',$default_options);
654
- $updated = array(true, $cnb_options['version']); // Updated and previous version number
655
- } else {
656
- $updated = array(false, $cnb_options['version']); // Not updated and current version number
657
- }
658
- return $updated;
659
- }
660
- // Color functions to calculate borders
661
- function changeColor($color, $direction) {
662
- if(!preg_match('/^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i', $color, $parts));
663
- if(!isset($direction) || $direction == "lighter") { $change = 45; } else { $change = -30; }
664
- for($i = 1; $i <= 3; $i++) {
665
- $parts[$i] = hexdec($parts[$i]);
666
- $parts[$i] = round($parts[$i] + $change);
667
- if($parts[$i] > 255) { $parts[$i] = 255; } elseif($parts[$i] < 0) { $parts[$i] = 0; }
668
- $parts[$i] = dechex($parts[$i]);
669
- }
670
- $output = '#' . str_pad($parts[1],2,"0",STR_PAD_LEFT) . str_pad($parts[2],2,"0",STR_PAD_LEFT) . str_pad($parts[3],2,"0",STR_PAD_LEFT);
671
- return $output;
672
- }
673
-
674
- function svg($color, $icon) {
675
- $phone = '<path d="M7.104 14.032l15.586 1.984c0 0-0.019 0.5 0 0.953c0.029 0.756-0.26 1.534-0.809 2.1 l-4.74 4.742c2.361 3.3 16.5 17.4 19.8 19.8l16.813 1.141c0 0 0 0.4 0 1.1 c-0.002 0.479-0.176 0.953-0.549 1.327l-6.504 6.505c0 0-11.261 0.988-25.925-13.674C6.117 25.3 7.1 14 7.1 14" fill="'.$color.'"/><path d="M7.104 13.032l6.504-6.505c0.896-0.895 2.334-0.678 3.1 0.35l5.563 7.8 c0.738 1 0.5 2.531-0.36 3.426l-4.74 4.742c2.361 3.3 5.3 6.9 9.1 10.699c3.842 3.8 7.4 6.7 10.7 9.1 l4.74-4.742c0.897-0.895 2.471-1.026 3.498-0.289l7.646 5.455c1.025 0.7 1.3 2.2 0.4 3.105l-6.504 6.5 c0 0-11.262 0.988-25.925-13.674C6.117 24.3 7.1 13 7.1 13" fill="'.$icon.'"/>';
676
- $svg = '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 60 60">' . $phone . '</svg>';
677
- return base64_encode($svg);
678
- }
679
- function buttonActive() {
680
- $cnb_options = get_option('cnb');
681
- if(isset($cnb_options['active'])) { $output = true; } else { $output = false; }
682
- return $output;
683
- }
684
-
685
- function zindex($value) {
686
- $zindex = array(
687
- 10 => 2147483647,
688
- 9 => 214748365,
689
- 8 => 21474836,
690
- 7 => 2147484,
691
- 6 => 214748,
692
- 5 => 21475,
693
- 4 => 2147,
694
- 3 => 215,
695
- 2 => 21,
696
- 1 => 2
697
- );
698
- return $zindex[$value];
699
- }
700
 
701
- function cnb_check_for_caching() {
702
- $caching_plugins = array(
703
- 'autoptimize/autoptimize.php',
704
- 'breeze/breeze.php',
705
- 'cache-control/cache-control.php',
706
- 'cache-enabler/cache-enabler.php',
707
- 'comet-cache/comet-cache.php',
708
- 'fast-velocity-minify/fvm.php',
709
- 'hyper-cache/plugin.php',
710
- 'litespeed-cache/litespeed-cache.php',
711
- 'simple-cache/simple-cache.php',
712
- 'w3-total-cache/w3-total-cache.php',
713
- 'wp-fastest-cache/wpFastestCache.php',
714
- 'wp-super-cache/wp-cache.php'
715
- );
716
- $active = FALSE; //Default is false
717
- $name = 'none'; // Default name is none
718
- foreach ($caching_plugins as $plugin) {
719
- if ( is_plugin_active( $plugin ) ) {
720
- $active = TRUE;
721
- $name = explode('/', $plugin);
722
- $name = $name[0];
723
- break;
724
- }
725
- }
726
- return array($active,$name);
727
- }
728
- function cnb_utm_params($element, $page) {
729
- $output = "?utm_source=wp-plugin";
730
- $output .= "&utm_medium=referral";
731
- $output .= "&utm_campaign=" . $element;
732
- $output .= "&utm_term=" . $page;
733
- echo $output;
734
- }
735
- ?>
3
  Plugin Name: Call Now Button
4
  Plugin URI: https://callnowbutton.com
5
  Description: Mobile visitors will see a <strong>Call Now Button</strong> on your website. Easy to use but flexible to meet more demanding requirements. Change placement and color, hide on specific pages, track how many people click them or conversions of your Google Ads campaigns. It's all optional but possible.
6
+ Version: 0.4.4
7
  Author: Jerry Rietveld
8
+ Author URI: http://www.jgrietveld.com
9
+ GitHub Plugin URI: https://github.com/callnowbutton/wp-plugin
10
  License: GPL2
11
  */
12
 
13
+ /* Copyright 2013-2020 Jerry Rietveld (email : jerry@jgrietveld.com)
14
 
15
  This program is free software; you can redistribute it and/or modify
16
+ it under the terms of the GNU General Public License, version 2, as
17
  published by the Free Software Foundation.
18
 
19
  This program is distributed in the hope that it will be useful,
25
  along with this program; if not, write to the Free Software
26
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27
  */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
+ define('CNB_VERSION', '0.4.4');
30
+ define('CNB_NAME', 'Call Now Button');
31
+ define('CNB_BASENAME', plugin_basename(__FILE__));
32
+ define('CNB_BASEFOLDER', plugin_basename(dirname(__FILE__)));
33
+ define('CNB_FILENAME', str_replace(CNB_BASEFOLDER . '/', '', CNB_BASENAME));
34
+ define('CNB_WEBSITE', 'https://callnowbutton.com/');
35
+ define('CNB_SUPPORT', CNB_WEBSITE . 'support/');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
37
+ require_once dirname( __FILE__ ) . '/src/call-now-button.php';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden
readme.txt CHANGED
@@ -1,10 +1,11 @@
1
- === Plugin Name ===
2
- Contributors: jgrietveld
3
- Donate link: http://callnowbutton.com/donate/
4
  Tags: call button, click to call, convert, call now button, contact button
5
- Requires at least: 2.7
6
- Tested up to: 5.7
7
- Stable tag: 0.4.3
 
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -14,22 +15,21 @@ A very simple yet very effective and flexible plugin that adds a Call Now Button
14
 
15
  ###What does the plugin do?
16
 
17
- This plugin places a Call Now Button (click-to-call button) to the bottom of the screen which is only visible for your mobile visitors. Because your mobile visitors already have a phone in their hands this plugin will allow them to call you with one simple touch of the button.
18
  No more navigating to the contact page and no more complicated copy/pasting or memorizing the phone number!
19
 
20
  ###Could not be easier!
21
  The settings are very easy: enable and enter your phone number. That's it!
22
 
23
- If you want to add some text to your button, that's possible. Entering text is fully optional - leaving it empty will show a nice circular phone button to your visitors (have a look at the screenshots).
24
 
25
- ####Need more control?
26
- Under the **Presentation tab** you can change the color of the button, move it to a differet location on the screen and limit the pages on which the button should be visible.
27
-
28
- Under the **Advanced tab** you'll find a bunch of settings that allow you to enable click tracking in Google Analytics, fire a conversion tag so a call is registered as a conversion in Google Ads, adjust the size of the button or move the button further backwards in case you want something else to sit on top of it (e.g. your privacy notice).
29
 
 
30
 
31
  ### For mobile optimized (responsive) themes
32
- The button is super light and fully build in CSS. The only requirement is that your website's theme is responsive, meaning it adapts to the size of the screen.
33
 
34
 
35
  == Installation ==
@@ -82,7 +82,7 @@ Another option could be that you have set the Call Now Button to only appear on
82
 
83
  = Do I have to add a country code to my phone number? =
84
 
85
- You don't have to but I recommend that you do to increase your options internationally.
86
 
87
  = Do I start the number with + or 00? =
88
 
@@ -98,7 +98,7 @@ Yes, you can enter the IDs of posts and pages you wish to include or exclude.
98
 
99
  = I need way more flexibility! Isn't there a PRO version that I can use? =
100
 
101
- This is coming very soon. We're currently looking for testers so if you want to give it a try already, please sign up at [callnowbutton.com](https://callnowbutton.com/).
102
 
103
 
104
  == Screenshots ==
@@ -110,13 +110,15 @@ This is coming very soon. We're currently looking for testers so if you want to
110
 
111
 
112
  == Changelog ==
 
 
113
 
114
  = 0.4.3 =
115
  * Critical fix
116
 
117
  = 0.4.2 =
118
  * Button styling adjustments
119
- * Security improvements (input sanitisation, output escaping)
120
 
121
  = 0.4.0 =
122
  * Tabbed admin interface
1
+ === Call Now Button ===
2
+ Contributors: jgrietveld, jasperroel
3
+ Donate link: https://callnowbutton.com/donate/
4
  Tags: call button, click to call, convert, call now button, contact button
5
+ Requires at least: 3.9
6
+ Requires PHP: 5.4
7
+ Tested up to: 5.8
8
+ Stable tag: 0.4.4
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
15
 
16
  ###What does the plugin do?
17
 
18
+ This plugin places a Call Now Button (click-to-call button) to the bottom of the screen which is **only visible for your mobile visitors**. Because your mobile visitors already have a phone in their hands this plugin will allow them to call you with one simple touch of the button.
19
  No more navigating to the contact page and no more complicated copy/pasting or memorizing the phone number!
20
 
21
  ###Could not be easier!
22
  The settings are very easy: enable and enter your phone number. That's it!
23
 
24
+ If you want to add some text to your button, that's possible. Entering text is fully optional - leaving it empty will show a nice circular phone button to your visitors (take a look at the screenshots).
25
 
26
+ ###Need more control?
27
+ Under the **Presentation tab** you can change the color of the button, move it to a different location on the screen and limit the pages on which the button should be visible.
 
 
28
 
29
+ Under the **Settings menu** you'll find a bunch of features that allow you to enable click tracking in Google Analytics, fire a conversion tag so a call is registered as a conversion in Google Ads, adjust the size of the button or move the button further backwards in case you want something else to sit on top of it (e.g. your privacy notice).
30
 
31
  ### For mobile optimized (responsive) themes
32
+ The button is super light and fully build in CSS. The **only requirement is that your website's theme is responsive**, meaning it adapts to the size of the screen.
33
 
34
 
35
  == Installation ==
82
 
83
  = Do I have to add a country code to my phone number? =
84
 
85
+ You don't have to, but I recommend that you do to increase your options internationally.
86
 
87
  = Do I start the number with + or 00? =
88
 
98
 
99
  = I need way more flexibility! Isn't there a PRO version that I can use? =
100
 
101
+ This is coming before the end of this year. We're currently looking for testers so if you want to give it a try already, please sign up at [callnowbutton.com](https://callnowbutton.com/).
102
 
103
 
104
  == Screenshots ==
110
 
111
 
112
  == Changelog ==
113
+ = 0.4.4 =
114
+ * UI improvements
115
 
116
  = 0.4.3 =
117
  * Critical fix
118
 
119
  = 0.4.2 =
120
  * Button styling adjustments
121
+ * Security improvements (input sanitization, output escaping)
122
 
123
  = 0.4.0 =
124
  * Tabbed admin interface
resources/images/button-new-full.png ADDED
Binary file
resources/images/button-new-multi.png ADDED
Binary file
resources/images/button-new-single.png ADDED
Binary file
resources/images/favicon.png ADDED
Binary file
resources/js/call-now-button.js ADDED
@@ -0,0 +1,288 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ function cnb_setup_colors() {
2
+ // Add color picker
3
+ jQuery('.cnb-color-field').wpColorPicker();
4
+ jQuery('.cnb-iconcolor-field').wpColorPicker();
5
+
6
+ // Reveal additional button placements when clicking "more"
7
+ jQuery("#cnb-more-placements").click(function(e){
8
+ e.preventDefault();
9
+ jQuery(".cnb-extra-placement").css("display","block");
10
+ jQuery("#cnb-more-placements").remove();
11
+ });
12
+
13
+ // TODO The input names AND radioValue might have to be changed to reflect new values
14
+ // Option to Hide Icon is only visible when the full width button is selected
15
+ var radioValue = jQuery("input[name='cnb[appearance]']:checked").val();
16
+ var textValue = jQuery("input[name='cnb[text]']").val();
17
+ if(radioValue != 'full' && radioValue != 'tfull') {
18
+ jQuery('#hideIconTR').hide();
19
+ } else if(textValue.length < 1) {
20
+ jQuery('#hideIconTR').hide();
21
+ }
22
+ jQuery('input[name="cnb[appearance]"]').on("change",function(){
23
+ var radioValue = jQuery("input[name='cnb[appearance]']:checked").val();
24
+ var textValue = jQuery("input[name='cnb[text]']").val();
25
+ if(radioValue != 'full' && radioValue != 'tfull') {
26
+ jQuery('#hideIconTR').hide();
27
+ } else if(textValue.length > 0 ) {
28
+ jQuery('#hideIconTR').show();
29
+ }
30
+ });
31
+ }
32
+
33
+ function cnb_setup_sliders() {
34
+ jQuery('#cnb_slider').on("input change", function() {
35
+ cnb_update_sliders();
36
+ });
37
+ jQuery('#cnb_order_slider').on("input change", function() {
38
+ cnb_update_sliders();
39
+ });
40
+ cnb_update_sliders();
41
+ }
42
+
43
+ function cnb_update_sliders() {
44
+ // Zoom slider - show percentage
45
+ var cnb_slider = document.getElementById("cnb_slider");
46
+ if (cnb_slider && cnb_slider.value) {
47
+ var cnb_slider_value = document.getElementById("cnb_slider_value");
48
+ cnb_slider_value.innerHTML = '(' + Math.round(cnb_slider.value * 100) + '%)';
49
+ }
50
+
51
+ // Z-index slider - show steps
52
+ var cnb_order_slider = document.getElementById("cnb_order_slider");
53
+ if (cnb_order_slider && cnb_order_slider.value) {
54
+ var cnb_order_value = document.getElementById("cnb_order_value");
55
+ cnb_order_value.innerHTML = cnb_order_slider.value;
56
+ }
57
+ }
58
+
59
+ function cnb_hide_on_show_always() {
60
+ let show_always_checkbox = document.getElementById('actions_schedule_show_always');
61
+ if (show_always_checkbox) {
62
+ if (show_always_checkbox.checked) {
63
+ // Hide
64
+ jQuery('.cnb_hide_on_show_always').hide();
65
+ } else {
66
+ jQuery('.cnb_hide_on_show_always').show();
67
+ }
68
+ }
69
+ return false;
70
+ }
71
+
72
+ function cnb_disable_api_key_when_cloud_hosting_is_disabled() {
73
+ jQuery('.when-cloud-enabled :input').prop('disabled', !jQuery('#cloud_enabled').is(':checked'));
74
+ }
75
+
76
+ let cnb_add_condition_counter = 0;
77
+ function cnb_add_condition() {
78
+ let template = `
79
+ <th scope="row">Condition: <div class="cnb_font_normal cnb_font_90">new/unsaved</div></th>
80
+ <td>
81
+ <input type="hidden" name="condition[${cnb_add_condition_counter}][id]" value="" />
82
+ <input type="hidden" name="condition[${cnb_add_condition_counter}][conditionType]" value="URL" />
83
+ <input type="hidden" name="condition[${cnb_add_condition_counter}][delete]" id="cnb_condition_${cnb_add_condition_counter}_delete" value="" />
84
+ <select name="condition[${cnb_add_condition_counter}][filterType]">
85
+ <option value="INCLUDE">Include</option>
86
+ <option value="EXCLUDE">Exclude</option>
87
+ </select><br />
88
+
89
+ <select name="condition[${cnb_add_condition_counter}][matchType]">
90
+ <option value="SIMPLE">Page path is:</option>
91
+ <option value="EXACT">Page URL is:</option>
92
+ <option value="SUBSTRING">Page URL contains:</option>
93
+ <option value="REGEX">Page URL matches RegEx:</option>
94
+ </select><br />
95
+
96
+ <input type="text" name="condition[${cnb_add_condition_counter}][matchValue]" value=""/><br />
97
+
98
+ <input type="button" onclick="return cnb_remove_condition('${cnb_add_condition_counter}');" value="Remove Condition" class="button-link button-link-delete"></td>
99
+ </td>
100
+ `;
101
+
102
+ let table = document.getElementById('cnb_form_table_visibility');
103
+ let container = document.getElementById('cnb_form_table_add_condition');
104
+ let rowElement = document.createElement('tr');
105
+ rowElement.className = 'appearance cnb_condition_new';
106
+ rowElement.vAlign = 'top';
107
+ rowElement.id = 'cnb_condition_' + cnb_add_condition_counter;
108
+ rowElement.innerHTML = template;
109
+ table.insertBefore(rowElement, container);
110
+
111
+ cnb_add_condition_counter++;
112
+ return false;
113
+ }
114
+
115
+ function cnb_remove_condition(id) {
116
+ let container = document.getElementById('cnb_condition_' + id);
117
+ let deleteElement = document.getElementById('cnb_condition_' + id + '_delete');
118
+ deleteElement.value = 'true';
119
+ jQuery(container).css("background-color", "#ff726f");
120
+ jQuery(container).fadeOut(function() {
121
+ jQuery(container).css("background-color", "");
122
+ if (container.className.includes('cnb_condition_new')) {
123
+ container.remove();
124
+ }
125
+ });
126
+ }
127
+
128
+ function cnb_action_appearance() {
129
+ jQuery('#cnb_action_type').change(function (obj) {
130
+ cnb_action_update_appearance(obj.target.value);
131
+ });
132
+
133
+ // Setup WHATSAPP integration
134
+ const input = document.querySelector("#cnb_action_value_input_whatsapp");
135
+ if (!input || !window.intlTelInput) {
136
+ return
137
+ }
138
+
139
+ const iti = window.intlTelInput(input, {
140
+ utilsScript: 'https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.12/js/utils.min.js',
141
+ nationalMode: false,
142
+ separateDialCode: true,
143
+ hiddenInput: 'actionValueWhatsappHidden'
144
+ });
145
+
146
+ // here, the index maps to the error code returned from getValidationError - see readme
147
+ const errorMap = [
148
+ 'Invalid number',
149
+ 'Invalid country code',
150
+ 'Too short',
151
+ 'Too long',
152
+ 'Invalid number'];
153
+
154
+ const errorMsg = jQuery('#cnb-error-msg');
155
+ const validMsg = jQuery('#cnb-valid-msg');
156
+
157
+ const reset = function() {
158
+ input.classList.remove('error');
159
+ errorMsg.html('');
160
+ errorMsg.hide();
161
+ validMsg.hide();
162
+ };
163
+
164
+ const onBlur = function() {
165
+ reset();
166
+ if (input.value.trim()) {
167
+ if (iti.isValidNumber()) {
168
+ validMsg.show();
169
+ } else {
170
+ const errorCode = iti.getValidationError();
171
+ if (errorCode < 0) {
172
+ // Unknown error, ignore for now
173
+ return
174
+ }
175
+ input.classList.add('error');
176
+ errorMsg.text(errorMap[errorCode]);
177
+ errorMsg.show();
178
+ }
179
+ } else {
180
+ // Empty
181
+ reset();
182
+ }
183
+ }
184
+
185
+ // on blur: validate
186
+ input.addEventListener('blur', onBlur);
187
+
188
+ // on keyup / change flag: reset
189
+ input.addEventListener('change', onBlur);
190
+ input.addEventListener('keyup', onBlur);
191
+
192
+ // init
193
+ onBlur();
194
+ }
195
+
196
+ function cnb_action_update_appearance(value) {
197
+ jQuery('.cnb-action-properties-email').hide();
198
+ jQuery('.cnb-action-properties-email-extra').hide();
199
+ jQuery('.cnb-action-properties-whatsapp').hide();
200
+ jQuery('.cnb-action-properties-whatsapp-extra').hide();
201
+ jQuery('.cnb-action-properties-map').hide();
202
+ jQuery('.cnb-action-value').show();
203
+ jQuery('#cnb_action_value_input').prop( 'disabled', false );
204
+ jQuery('#cnb_action_value_input_whatsapp').prop( 'disabled', true );
205
+ switch (value) {
206
+ case 'PHONE':
207
+ jQuery('#cnb_action_value').text('Phone number');
208
+ break
209
+ case 'ANCHOR':
210
+ jQuery('#cnb_action_value').text('On-page anchor');
211
+ break
212
+ case 'LINK':
213
+ jQuery('#cnb_action_value').text('Full URL');
214
+ break
215
+ case 'EMAIL':
216
+ jQuery('#cnb_action_value').text('E-mail address');
217
+ jQuery('.cnb-action-properties-email').show()
218
+ break
219
+ case 'WHATSAPP':
220
+ jQuery('#cnb_action_value').text('Whatsapp number');
221
+ jQuery('.cnb-action-value').hide();
222
+ jQuery('#cnb_action_value_input').prop( 'disabled', true );
223
+ jQuery('#cnb_action_value_input_whatsapp').prop( 'disabled', false );
224
+ jQuery('.cnb-action-properties-whatsapp').show();
225
+ break
226
+ case 'MAP':
227
+ jQuery('#cnb_action_value').text('Address');
228
+ jQuery('.cnb-action-properties-map').show();
229
+ break
230
+ default:
231
+ jQuery('#cnb_action_value').text('Action value');
232
+ }
233
+ }
234
+
235
+ function cnb_action_update_map_link(element) {
236
+ jQuery(element).prop("href", "https://maps.google.com?q=" + jQuery('#cnb_action_value_input').val())
237
+ }
238
+
239
+ function cnb_hide_edit_action_if_advanced() {
240
+ var element = jQuery('#toplevel_page_call-now-button li.current a');
241
+ if (element.text() === 'Edit action') {
242
+ element.removeAttr('href');
243
+ element.css('cursor', 'default');
244
+ }
245
+ }
246
+
247
+ function cnb_hide_edit_domain_upgrade_if_advanced() {
248
+ var element = jQuery('#toplevel_page_call-now-button li.current a');
249
+ if (element.text() === 'Upgrade domain') {
250
+ element.removeAttr('href');
251
+ element.css('cursor', 'default');
252
+ }
253
+ }
254
+
255
+ function cnb_hide_on_modal() {
256
+ jQuery('.cnb_hide_on_modal').hide();
257
+ }
258
+
259
+ function show_advanced_view_only() {
260
+ jQuery('.cnb_advanced_view').show();
261
+ }
262
+
263
+ function cnb_strip_beta_from_referrer() {
264
+ var referer = jQuery('input[name="_wp_http_referer"]');
265
+ if (referer && referer.val()) {
266
+ referer.val(referer.val().replace('&beta', ''))
267
+ }
268
+ }
269
+
270
+ jQuery( document ).ready(function() {
271
+ cnb_setup_colors();
272
+ cnb_setup_sliders();
273
+ cnb_hide_on_show_always();
274
+ cnb_disable_api_key_when_cloud_hosting_is_disabled();
275
+ cnb_action_appearance();
276
+ cnb_action_update_appearance(jQuery('#cnb_action_type').val());
277
+ cnb_hide_edit_action_if_advanced();
278
+ cnb_hide_edit_domain_upgrade_if_advanced();
279
+ cnb_strip_beta_from_referrer();
280
+
281
+ if (typeof cnb_hide_on_modal_set !== 'undefined' && cnb_hide_on_modal_set === 1) {
282
+ cnb_hide_on_modal();
283
+ }
284
+
285
+ if (typeof show_advanced_view_only_set !== 'undefined' && show_advanced_view_only_set && show_advanced_view_only_set === 1) {
286
+ show_advanced_view_only()
287
+ }
288
+ });
resources/style/call-now-button.css ADDED
@@ -0,0 +1,248 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Universal CNB admin styling options
3
+ */
4
+
5
+ .cnb_align_center {
6
+ text-align: center;
7
+ }
8
+ .cnb_align_right {
9
+ text-align: right;
10
+ }
11
+ .cnb_align_left {
12
+ text-align: left;
13
+ }
14
+
15
+ .cnb_font_normal {
16
+ font-weight: normal;
17
+ }
18
+ .cnb_font_bold {
19
+ font-weight: 700;
20
+ }
21
+ .cnb_font_thin {
22
+ font-weight: 100;
23
+ }
24
+
25
+ .cnb_font_80 {
26
+ font-size: 80%;
27
+ }
28
+ .cnb_font_90 {
29
+ font-size: 90%;
30
+ }
31
+ .cnb_font_100 {
32
+ font-size: 100%;
33
+ }
34
+ .cnb_font_110 {
35
+ font-size: 110%;
36
+ }
37
+ .cnb_font_120 {
38
+ font-size: 120%;
39
+ }
40
+
41
+ .cnb_padding_0 {
42
+ padding: 0 !important;
43
+ }
44
+ .cnb_cursor_pointer {
45
+ cursor: pointer;
46
+ }
47
+ .cnb_cursor_pointer:hover {
48
+ text-decoration: underline;
49
+ }
50
+
51
+ .cnb_footer_beta {
52
+ border: 1px solid;
53
+ border-radius: 3px;
54
+ margin-left: 5px;
55
+ padding: 0 3px;
56
+ }
57
+
58
+ /*
59
+ Other styling
60
+ */
61
+
62
+ .cnb_promobox_item {
63
+ margin: 5px 10px;
64
+ }
65
+
66
+ #poststuff .postbox .inside {
67
+ margin: 0;
68
+ padding: 0;
69
+ }
70
+
71
+ .cnb-button-name-field {
72
+ padding: 20px 10px 20px 0;
73
+ }
74
+
75
+ #cnb\[name\] {
76
+ padding: 3px 8px;
77
+ font-size: 1.7em;
78
+ line-height: 100%;
79
+ height: 1.7em;
80
+ width: 100%;
81
+ outline: 0;
82
+ margin: 0 0 3px;
83
+ background-color: #fff;
84
+ }
85
+
86
+ #post-body .cnb-side-icon:before {
87
+ font: normal 20px/1 dashicons;
88
+ color: #8c8f94;
89
+ speak: never;
90
+ display: inline-block;
91
+ margin-left: -1px;
92
+ padding-right: 3px;
93
+ vertical-align: top;
94
+ -webkit-font-smoothing: antialiased;
95
+ }
96
+ #post-body .cnb-side-checkbox:before {
97
+ content: "\f147"; /* \f147 = yes (since 4.1), \f12a = yes-alt (since 5.2), \f15e = saved (since 5.5) */
98
+ }
99
+
100
+ .form-table td p.description,
101
+ .inline-helper,
102
+ label.small-italic {
103
+ font-size: 12px;
104
+ }
105
+
106
+ table.form-table,
107
+ .cnb-button-name-field,
108
+ .cnb-button-edit-action-table .tablenav {
109
+ display:none;
110
+ }
111
+
112
+ table.form-table.nav-tab-active,
113
+ .notice table.form-table {
114
+ display: initial;
115
+ }
116
+ #TB_window .cnb-button-name-field,
117
+ .cnb-button-name-field.nav-tab-active {
118
+ display: block;
119
+ }
120
+
121
+ table.form-table.nav-tab-only {
122
+ display: table;
123
+ }
124
+
125
+ .cnb-url a {
126
+ text-decoration: none;
127
+ font-weight: 200;
128
+ font-size: 28px;
129
+ color: black;
130
+ }
131
+ .cnb-center {
132
+ text-align: center;
133
+ }
134
+ .cnb-container input[type="radio"] + label {
135
+ font-weight: 300;
136
+ }
137
+ .cnb-container input[type="radio"]:checked + label,
138
+ .cnb-container input[type="checkbox"]:checked + label {
139
+ font-weight: bold;
140
+ }
141
+ .cnb-radio-item {
142
+ margin: 10px 0;
143
+ }
144
+ .cnb-extra-placement {
145
+ display: none;
146
+ }
147
+ .cnb-extra-placement.cnb-extra-active {
148
+ display: block;
149
+ }
150
+ .cnb-switch-back {
151
+ color: #0073aa;
152
+ text-decoration: underline;
153
+ cursor: pointer;
154
+ }
155
+ #cnb_slider_value,
156
+ #cnb_order_value {
157
+ font-weight: normal;
158
+ }
159
+
160
+ input[type='range'] {
161
+ position: relative;
162
+ top: 8px;
163
+ }
164
+
165
+ @-moz-document url-prefix() {
166
+ input[type='range'] {
167
+ top: 12px;
168
+ }
169
+ }
170
+
171
+ .cnb_slider_value {
172
+ font-weight: normal;
173
+ }
174
+
175
+ @media screen and (min-width: 960px) {
176
+ .cnb-radio-item {
177
+ margin: 5px 0;
178
+ }
179
+ }
180
+ .cnb-nounderscore {
181
+ text-decoration: none;
182
+ }
183
+
184
+ .cnb_advanced_view {
185
+ display: none;
186
+ }
187
+
188
+ @media screen and (max-width: 630px) {
189
+ .wp-admin #TB_window {
190
+ width: auto !important;
191
+ margin-left: auto !important;
192
+ left: auto !important;
193
+ margin-left: 10px !important;
194
+ margin-right: 10px !important;
195
+ }
196
+
197
+ .wp-admin #TB_ajaxContent {
198
+ width: auto !important;
199
+ }
200
+ }
201
+
202
+ #cnb_action_value_input_whatsapp.error {
203
+ color:red;
204
+ }
205
+
206
+ #cnb-valid-msg {
207
+ color: green;
208
+ }
209
+
210
+ #cnb-error-msg {
211
+ color: red;
212
+ }
213
+
214
+ .iti__flag {background-image: url('https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.12/img/flags.png');}
215
+
216
+ @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
217
+ .iti__flag {background-image: url('https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.12/img/flags@2x.png');}
218
+ }
219
+
220
+ .MuiSvgIcon-root {
221
+ -webkit-font-smoothing: antialiased;
222
+ font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";
223
+ font-weight: 400;
224
+ line-height: 1.43;
225
+ color: rgba(0, 0, 0, 0.87);
226
+ text-align: center;
227
+ box-sizing: inherit;
228
+ fill: currentColor;
229
+ width: 1em;
230
+ height: 1em;
231
+ display: inline-block;
232
+ font-size: 1.5rem;
233
+ transition: fill 200ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
234
+ flex-shrink: 0;
235
+ user-select: none;
236
+ margin-right: 4px;
237
+ }
238
+ .cnb-pricing, .cnb-pricing td {
239
+ border: 1px solid #000000;
240
+ text-align: center;
241
+ }
242
+ .cnb-pricing td {
243
+ padding: 10px;
244
+ }
245
+
246
+ .cnb-version {
247
+ font-weight: 200;
248
+ }
src/admin/action-edit.php ADDED
@@ -0,0 +1,597 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname( __FILE__ ) . '/api/CnbAppRemote.php';
4
+ require_once dirname( __FILE__ ) . '/api/CnbAdminCloud.php';
5
+ require_once dirname( __FILE__ ) . '/partials/admin-functions.php';
6
+ require_once dirname( __FILE__ ) . '/partials/admin-header.php';
7
+ require_once dirname( __FILE__ ) . '/partials/admin-footer.php';
8
+ require_once dirname( __FILE__ ) . '/../utils/utils.php';
9
+ require_once dirname( __FILE__ ) . '/button-edit.php';
10
+
11
+ function cnb_add_header_action_edit($action) {
12
+ $id = filter_input( INPUT_GET, 'id', FILTER_SANITIZE_STRING );
13
+ $name = 'New Action';
14
+ if ($action && $action->id !== 'new') {
15
+ $actionTypes = cnb_get_action_types();
16
+ $name = $actionTypes[$action->actionType];
17
+ if ($action->actionValue) {
18
+ $name = $action->actionValue;
19
+ }
20
+ }
21
+ if (strlen($id) > 0 && $id === 'new') {
22
+ echo 'Add action';
23
+ } else {
24
+ echo 'Edit action: "' . esc_html($name) . '"';
25
+ }
26
+ }
27
+
28
+ /**
29
+ * This is called to create an Action
30
+ * via `call-now-button.php#cnb_create_action`
31
+ */
32
+ function cnb_admin_page_action_create_process() {
33
+ global $cnb_slug_base;
34
+ $nonce = filter_input( INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING );
35
+ if( isset( $_REQUEST['_wpnonce'] ) && wp_verify_nonce( $nonce, 'cnb-action-edit') ) {
36
+
37
+ $actions = filter_input(
38
+ INPUT_POST,
39
+ 'actions',
40
+ FILTER_SANITIZE_STRING,
41
+ FILTER_REQUIRE_ARRAY | FILTER_FLAG_NO_ENCODE_QUOTES);
42
+ $action_id = filter_input( INPUT_POST, 'action_id', FILTER_SANITIZE_STRING );
43
+ $action = $actions[$action_id];
44
+
45
+ // Do the processing
46
+ $cnb_cloud_notifications = array();
47
+ if (isset($action['schedule']['daysOfWeek']) &&
48
+ $action['schedule']['daysOfWeek'] !== null &&
49
+ is_array($action['schedule']['daysOfWeek'])) {
50
+ $action['schedule']['daysOfWeek'] = cnb_create_days_of_week_array($action['schedule']['daysOfWeek']);
51
+ }
52
+
53
+ // "Fix" the WHATSAPP values
54
+ if ($action['actionType'] === 'WHATSAPP'
55
+ && isset($action['actionValueWhatsappHidden'])
56
+ && !empty($action['actionValueWhatsappHidden'])) {
57
+ $action['actionValue'] = $action['actionValueWhatsappHidden'];
58
+ }
59
+
60
+ // Remove the "display" value
61
+ unset($action['actionValueWhatsapp']);
62
+ unset($action['actionValueWhatsappHidden']);
63
+
64
+ $new_action = CnbAdminCloud::cnb_create_action( $cnb_cloud_notifications, $action );
65
+ $new_action_id = $new_action->id;
66
+
67
+ $bid = filter_input( INPUT_POST, 'bid', FILTER_SANITIZE_STRING );
68
+ if (!empty($bid)) {
69
+ // Tie this new Action to the provided Button
70
+ $button = CnbAppRemote::cnb_remote_get_button( $bid );
71
+ if (!($button instanceof WP_Error)) {
72
+ $button->actions[] = $new_action_id;
73
+ $button_array = json_decode(json_encode($button), true);
74
+ CnbAdminCloud::cnb_update_button( $cnb_cloud_notifications, $button_array );
75
+ } else {
76
+ // TODO Add error to $cnb_cloud_notifications
77
+ }
78
+ }
79
+
80
+ // redirect the user to the appropriate page
81
+ $transient_id = 'cnb-' . wp_generate_uuid4();
82
+ set_transient($transient_id, $cnb_cloud_notifications, HOUR_IN_SECONDS);
83
+
84
+ // Create link
85
+ $bid = !empty($_GET['bid']) ? sanitize_text_field($_GET['bid']) : null;
86
+ $url = admin_url('admin.php');
87
+
88
+ if (!empty($bid)) {
89
+ $redirect_link =
90
+ add_query_arg(
91
+ array(
92
+ 'page' => 'call-now-button',
93
+ 'action' => 'edit',
94
+ 'id' => $bid,
95
+ 'tid' => $transient_id,
96
+ ),
97
+ $url);
98
+ $redirect_url = esc_url_raw($redirect_link);
99
+ wp_safe_redirect($redirect_url);
100
+ exit;
101
+ } else {
102
+ $redirect_link =
103
+ add_query_arg(
104
+ array(
105
+ 'page' => 'call-now-button-actions',
106
+ 'action' => 'edit',
107
+ 'id' => $new_action_id,
108
+ 'tid' => $transient_id,
109
+ 'bid' => $bid),
110
+ $url);
111
+ $redirect_url = esc_url_raw($redirect_link);
112
+ wp_safe_redirect($redirect_url);
113
+ exit;
114
+ }
115
+ }
116
+ else {
117
+ $url = admin_url('admin.php');
118
+ $redirect_link =
119
+ add_query_arg(
120
+ array(
121
+ 'page' => $cnb_slug_base
122
+ ),
123
+ $url );
124
+ $redirect_url = esc_url_raw($redirect_link);
125
+ wp_die( __( 'Invalid nonce specified', CNB_NAME), __( 'Error', CNB_NAME), array(
126
+ 'response' => 403,
127
+ 'back_link' => $redirect_url,
128
+ ) );
129
+ }
130
+ }
131
+
132
+ /**
133
+ * @param $action
134
+ *
135
+ * @return array
136
+ */
137
+ function cnb_admin_process_action($action) {
138
+ if (isset($action['schedule']['daysOfWeek']) && $action['schedule']['daysOfWeek'] !== null && is_array($action['schedule']['daysOfWeek'])) {
139
+ $action['schedule']['daysOfWeek'] = cnb_create_days_of_week_array($action['schedule']['daysOfWeek']);
140
+ }
141
+
142
+ // "Fix" the WHATSAPP values
143
+ if (isset($action['actionType']) && $action['actionType'] === 'WHATSAPP'
144
+ && isset($action['actionValueWhatsappHidden'])
145
+ && !empty($action['actionValueWhatsappHidden'])) {
146
+ $action['actionValue'] = $action['actionValueWhatsappHidden'];
147
+ }
148
+
149
+ // Remove the "display" value
150
+ unset($action['actionValueWhatsapp']);
151
+ unset($action['actionValueWhatsappHidden']);
152
+
153
+ // Set the correct iconText
154
+ if (isset($action['iconText']) && !empty($action['iconText'])) {
155
+ // Reset the iconText based on type
156
+ $action['iconText'] = cnb_actiontype_to_icontext($action['actionType']);
157
+ }
158
+
159
+ return $action;
160
+ }
161
+ /**
162
+ * This is called to update the action
163
+ * via `call-now-button.php#cnb_update_action`
164
+ */
165
+ function cnb_admin_page_action_edit_process() {
166
+ global $cnb_slug_base;
167
+ $nonce = filter_input( INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING );
168
+ if( isset( $_REQUEST['_wpnonce'] ) && wp_verify_nonce( $nonce, 'cnb-action-edit') ) {
169
+
170
+ // sanitize the input
171
+ $actions = filter_input(
172
+ INPUT_POST,
173
+ 'actions',
174
+ FILTER_SANITIZE_STRING,
175
+ FILTER_REQUIRE_ARRAY | FILTER_FLAG_NO_ENCODE_QUOTES);
176
+ $result = '';
177
+ $cnb_cloud_notifications = array();
178
+
179
+ foreach($actions as $action) {
180
+ $processed_action = cnb_admin_process_action($action);
181
+ // do the processing
182
+ $result = CnbAdminCloud::cnb_update_action( $cnb_cloud_notifications, $processed_action );
183
+ }
184
+
185
+ // redirect the user to the appropriate page
186
+ $transient_id = 'cnb-' . wp_generate_uuid4();
187
+ set_transient($transient_id, $cnb_cloud_notifications, HOUR_IN_SECONDS);
188
+
189
+ // Create link
190
+ $bid = !empty($_GET["bid"]) ? sanitize_text_field($_GET["bid"]) : null;
191
+ $url = admin_url('admin.php');
192
+ if (!empty($bid)) {
193
+ $redirect_link =
194
+ add_query_arg(
195
+ array(
196
+ 'page' => 'call-now-button',
197
+ 'action' => 'edit',
198
+ 'id' => $bid,
199
+ 'tid' => $transient_id,
200
+ ),
201
+ $url);
202
+ $redirect_url = esc_url_raw($redirect_link);
203
+ wp_safe_redirect($redirect_url);
204
+ exit;
205
+ } else {
206
+ $redirect_link =
207
+ add_query_arg(
208
+ array(
209
+ 'page' => 'call-now-button-actions',
210
+ 'action' => 'edit',
211
+ 'id' => $result->id,
212
+ 'tid' => $transient_id,
213
+ 'bid' => $bid),
214
+ $url);
215
+ $redirect_url = esc_url_raw($redirect_link);
216
+ wp_safe_redirect($redirect_url);
217
+ exit;
218
+ }
219
+ }
220
+ else {
221
+ wp_die( __( 'Invalid nonce specified', CNB_NAME), __( 'Error', CNB_NAME), array(
222
+ 'response' => 403,
223
+ // TODO Create proper URL with escaping
224
+ 'back_link' => 'admin.php?page=' . $cnb_slug_base,
225
+ ) );
226
+ }
227
+ }
228
+
229
+ function cnb_action_edit_create_tab_url($button, $tab) {
230
+ $url = admin_url('admin.php');
231
+ $tab_link =
232
+ add_query_arg(
233
+ array(
234
+ 'page' => 'call-now-button',
235
+ 'action' => 'edit',
236
+ 'type' => strtolower($button->type),
237
+ 'id' => $button->id,
238
+ 'tab' => $tab),
239
+ $url );
240
+ return esc_url( $tab_link );
241
+ }
242
+
243
+ /**
244
+ * @param $action
245
+ * @param object? $button
246
+ * @param bool $show_table
247
+ */
248
+ function cnb_render_form($action, $button=null, $show_table=true) {
249
+ /**
250
+ * @global WP_Locale $wp_locale WordPress date and time locale object.
251
+ */
252
+ global $wp_locale;
253
+ // CNB week starts on Monday
254
+ $cnb_days_of_week_order = array(1,2,3,4,5,6,0);
255
+
256
+ if ($button) {
257
+ $url = admin_url('admin.php');
258
+ $upgrade_link =
259
+ add_query_arg(array(
260
+ 'page' => 'call-now-button-domains',
261
+ 'action' => 'upgrade',
262
+ 'id' => $button->domain->id
263
+ ),
264
+ $url);
265
+ $upgrade_url = esc_url($upgrade_link);
266
+ }
267
+ ?>
268
+ <input type="hidden" name="actions[<?php esc_attr_e($action->id) ?>][id]" value="<?php if ($action->id !== null && $action->id !== 'new') { esc_attr_e($action->id); } ?>" />
269
+ <input type="hidden" name="actions[<?php esc_attr_e($action->id) ?>][delete]" id="cnb_action_<?php esc_attr_e($action->id) ?>_delete" value="" />
270
+ <input type="hidden" name="actions[<?php esc_attr_e($action->id) ?>][iconText]" value="<?php if (isset($action->iconText)) { esc_attr_e($action->iconText); } ?>" />
271
+ <?php if ($show_table) { ?>
272
+ <table class="form-table nav-tab-active">
273
+ <?php } ?>
274
+ <?php if (!$button) { ?>
275
+ <tr>
276
+ <th colspan="2"><h2>Action Settings</h2>
277
+ </th>
278
+ </tr>
279
+ <?php } ?>
280
+ <tr class="cnb_hide_on_modal">
281
+ <th></th>
282
+ <td></td>
283
+ </tr>
284
+ <tr class="cnb_hide_on_modal">
285
+ <th scope="row"><label for="cnb_action_type">Button type:</label></th>
286
+ <td>
287
+ <select id="cnb_action_type" name="actions[<?php esc_attr_e($action->id) ?>][actionType]">
288
+ <?php foreach (cnb_get_action_types() as $action_type_key => $action_type_value) { ?>
289
+ <option value="<?php esc_attr_e($action_type_key) ?>"<?php selected($action_type_key, $action->actionType) ?>>
290
+ <?php esc_html_e($action_type_value) ?>
291
+ </option>
292
+ <?php } ?>
293
+ </select>
294
+ </tr>
295
+ <tr class="cnb-action-value cnb_hide_on_modal">
296
+ <th scope="row">
297
+ <label for="cnb_action_value_input">
298
+ <span id="cnb_action_value">Action value</span>
299
+ </label>:
300
+ <a href="<?php echo CNB_SUPPORT; ?>phone-number/<?php cnb_utm_params("question-mark", "phone-number"); ?>"
301
+ target="_blank" class="cnb-nounderscore">
302
+ <span class="dashicons dashicons-editor-help"></span>
303
+ </a>
304
+ </th>
305
+ <td>
306
+ <input type="text" id="cnb_action_value_input" name="actions[<?php esc_attr_e($action->id) ?>][actionValue]" value="<?php esc_attr_e($action->actionValue) ?>"/>
307
+ <p class="description cnb-action-properties-map">Preview via <a href="#" onclick="cnb_action_update_map_link(this)" target="_blank">Google Maps</a></p>
308
+
309
+ </td>
310
+ </tr>
311
+ <tr class="cnb-action-properties-whatsapp">
312
+ <th scope="row"><label for="cnb_action_value_input_whatsapp">Whatsapp Number</label></th>
313
+ <td>
314
+ <input type="tel" id="cnb_action_value_input_whatsapp" name="actions[<?php esc_attr_e($action->id) ?>][actionValueWhatsapp]" value="<?php esc_attr_e($action->actionValue) ?>"/>
315
+ <p class="description" id="cnb-valid-msg">✓ Valid</p>
316
+ <p class="description" id="cnb-error-msg"></p>
317
+ </td>
318
+ </tr>
319
+ <tr class="button-text cnb_hide_on_modal">
320
+ <th scope="row"><label for="buttonTextField">Button label:</label><a
321
+ href="<?php echo CNB_SUPPORT; ?>using-text-buttons/<?php cnb_utm_params("question-mark", "using-text-buttons"); ?>"
322
+ target="_blank" class="cnb-nounderscore">
323
+ <span class="dashicons dashicons-editor-help"></span>
324
+ </a></th>
325
+ <td>
326
+ <input id="buttonTextField" type="text" name="actions[<?php esc_attr_e($action->id) ?>][labelText]"
327
+ value="<?php esc_attr_e($action->labelText) ?>" maxlength="30" placeholder="optional" />
328
+ <p class="description">Leave this field empty to only show an icon.</p>
329
+ </td>
330
+ </tr>
331
+ <tr class="cnb-action-properties-email">
332
+ <th></th>
333
+ <td><a class="cnb_cursor_pointer" onclick="jQuery('.cnb-action-properties-email-extra').show();jQuery(this).parent().parent().hide()">Extra email settings...</a></td>
334
+ </tr>
335
+ <tr class="cnb-action-properties-email-extra">
336
+ <th colspan="2"><hr /></th>
337
+ </tr>
338
+ <tr class="cnb-action-properties-email-extra">
339
+ <th scope="row"><label for="action-properties-subject">Subject</label></th>
340
+ <td><input id="action-properties-subject" name="actions[<?php esc_attr_e($action->id) ?>][properties][subject]" type="text" value="<?php if (isset($action->properties) && isset($action->properties->subject)) { esc_attr_e($action->properties->subject); } ?>" /></td>
341
+ </tr>
342
+ <tr class="cnb-action-properties-email-extra">
343
+ <th scope="row"><label for="action-properties-body">Body</label></th>
344
+ <td><textarea id="action-properties-body" name="actions[<?php esc_attr_e($action->id) ?>][properties][body]" class="large-text code" rows="3"><?php if (isset($action->properties) && isset($action->properties->body)) { echo esc_textarea($action->properties->body); } ?></textarea></td>
345
+
346
+ </tr>
347
+ <tr class="cnb-action-properties-email-extra">
348
+ <th scope="row"><label for="action-properties-cc">CC</label></th>
349
+ <td><input id="action-properties-cc" name="actions[<?php esc_attr_e($action->id) ?>][properties][cc]" type="text" value="<?php if (isset($action->properties) && isset($action->properties->cc)) { esc_attr_e($action->properties->cc); } ?>" /></td>
350
+ </tr>
351
+ <tr class="cnb-action-properties-email-extra">
352
+ <th scope="row"><label for="action-properties-bcc">BCC</label></th>
353
+ <td><input id="action-properties-bcc" name="actions[<?php esc_attr_e($action->id) ?>][properties][bcc]" type="text" value="<?php if (isset($action->properties) && isset($action->properties->bcc)) { esc_attr_e($action->properties->bcc); } ?>" /></td>
354
+ </tr>
355
+ <tr class="cnb-action-properties-email-extra">
356
+ <th colspan="2"><hr /></th>
357
+ </tr>
358
+
359
+ <tr class="cnb-action-properties-whatsapp">
360
+ <th></th>
361
+ <td><a class="cnb_cursor_pointer" onclick="jQuery('.cnb-action-properties-whatsapp-extra').show();jQuery(this).parent().parent().hide()">Extra Whatsapp settings...</a></td>
362
+ </tr>
363
+ <tr class="cnb-action-properties-whatsapp-extra">
364
+ <th colspan="2"><hr /></th>
365
+ </tr>
366
+ <tr class="cnb-action-properties-whatsapp-extra">
367
+ <th scope="row"><label for="action-properties-message">Default message</label></th>
368
+ <td>
369
+ <textarea id="action-properties-message" name="actions[<?php esc_attr_e($action->id) ?>][properties][message]" class="large-text code" rows="3"><?php if (isset($action->properties) && isset($action->properties->message)) { echo esc_textarea($action->properties->message); } ?></textarea>
370
+ </td>
371
+ </tr>
372
+ <tr class="cnb-action-properties-whatsapp-extra">
373
+ <th colspan="2"><hr /></th>
374
+ </tr>
375
+
376
+ <?php if ($button && $button->type === 'SINGLE') { ?>
377
+ <tr class="cnb_hide_on_modal cnb_advanced_view">
378
+ <th colspan="2">
379
+ <h2>Colors for a Single button are defined on the Button, not the action.</h2>
380
+ <input name="actions[<?php esc_attr_e($action->id) ?>][backgroundColor]" type="hidden" value="<?php esc_attr_e($action->backgroundColor) ?>" />
381
+ <input name="actions[<?php esc_attr_e($action->id) ?>][iconColor]" type="hidden" value="<?php esc_attr_e($action->iconColor) ?>" />
382
+ <!-- We always enable the icon when the type if SINGLE, original value is "<?php esc_attr_e($action->iconEnabled) ?>" -->
383
+ <input name="actions[<?php esc_attr_e($action->id) ?>][iconEnabled]" type="hidden" value="1" />
384
+ </th>
385
+ </tr>
386
+ <?php } else { ?>
387
+ <tr class="cnb_hide_on_modal">
388
+ <th></th>
389
+ <td></td>
390
+ </tr>
391
+ <tr>
392
+ <th scope="row"><label for="actions[<?php esc_attr_e($action->id) ?>][backgroundColor]">Background color</label></th>
393
+ <td>
394
+ <input name="actions[<?php esc_attr_e($action->id) ?>][backgroundColor]" id="actions[<?php esc_attr_e($action->id) ?>][backgroundColor]" type="text" value="<?php esc_attr_e($action->backgroundColor) ?>"
395
+ class="cnb-color-field" data-default-color="#009900"/>
396
+ </td>
397
+ </tr>
398
+ <tr>
399
+ <th scope="row"><label for="actions[<?php esc_attr_e($action->id) ?>][iconColor]">Icon color</label></th>
400
+ <td>
401
+ <input name="actions[<?php esc_attr_e($action->id) ?>][iconColor]" id="actions[<?php esc_attr_e($action->id) ?>][iconColor]" type="text" value="<?php esc_attr_e($action->iconColor) ?>"
402
+ class="cnb-iconcolor-field" data-default-color="#FFFFFF"/>
403
+ </td>
404
+ </tr>
405
+ <?php if ($button && $button->type === 'MULTI') { ?>
406
+ <input name="actions[<?php esc_attr_e($action->id) ?>][iconEnabled]" type="hidden" value="1" />
407
+ <?php } else { ?>
408
+ <tr>
409
+ <th scope="row"></th>
410
+ <td>
411
+ <input type="checkbox" name="actions[<?php esc_attr_e($action->id) ?>][iconEnabled]" id="actions[<?php esc_attr_e($action->id) ?>][iconEnabled]" value="true" <?php checked(true, $action->iconEnabled); ?>>
412
+ <label title="right" for="actions[<?php esc_attr_e($action->id) ?>][iconEnabled]">Show icon</label>
413
+ </td>
414
+ </tr>
415
+ <?php } // End Multi/Buttonbar ?>
416
+ <?php } ?>
417
+
418
+ <tr class="cnb_hide_on_modal">
419
+ <th scope="row"><h3>Scheduling</h3> </th>
420
+ <td>
421
+ <div class="cnb-radio-item">
422
+ <?php if (!isset($button) || $button->domain->type !== 'FREE') { ?>
423
+ <input name="actions[<?php esc_attr_e($action->id) ?>][schedule][showAlways]" type="hidden" value="false" />
424
+ <input id="actions_schedule_show_always" onchange="return cnb_hide_on_show_always();" name="actions[<?php esc_attr_e($action->id) ?>][schedule][showAlways]" type="checkbox"
425
+ value="true" <?php checked(true, $action->id === 'new' || $action->schedule->showAlways); ?> />
426
+ <label title="Show always" for="actions_schedule_show_always">Show always</label>
427
+ <?php } else { ?>
428
+ <input id="actions_schedule_show_always" name="actions[<?php esc_attr_e($action->id) ?>][schedule][showAlways]" type="hidden" value="true" />
429
+ <label title="Show always" for="actions_schedule_show_always">Show always.
430
+ <?php
431
+ if ($button->domain->type !== 'PROPLUS') {
432
+ echo '<a href="' . $upgrade_url . '">Upgrade!</a>';
433
+ }
434
+ ?>
435
+ to enable scheduling.</label>
436
+ <?php } ?>
437
+ </div>
438
+ </td>
439
+ </tr>
440
+ <?php if (!isset($button) || $button->domain->type !== 'FREE') { ?>
441
+ <tr class="cnb_hide_on_show_always">
442
+ <th>Show on these days</th>
443
+ <td>
444
+ <?php
445
+ foreach ($cnb_days_of_week_order as $cnb_days_of_week) {
446
+ $selected = '';
447
+ if (isset($action->schedule) && isset($action->schedule->daysOfWeek)) {
448
+ $selected = ($action->schedule->daysOfWeek[$cnb_days_of_week] == true) ? 'checked="checked"' : '';
449
+ }
450
+ echo "<input type='checkbox' name='actions[" . esc_attr($action->id) . "][schedule][daysOfWeek][" . esc_attr($cnb_days_of_week) . "]' value='true' $selected>" . $wp_locale->get_weekday($cnb_days_of_week) . '<br/>';
451
+ }
452
+ ?>
453
+ </td>
454
+ </tr>
455
+ <tr class="cnb_hide_on_show_always">
456
+ <th><label for="actions[<?php esc_attr_e($action->id) ?>][schedule][start]">Start time</label></th>
457
+ <td><input type="time" name="actions[<?php esc_attr_e($action->id) ?>][schedule][start]" id="actions[<?php esc_attr_e($action->id) ?>][schedule][start]" value="<?php if (isset($action->schedule)) { esc_attr_e($action->schedule->start); } ?>"></td>
458
+ </tr>
459
+ <tr class="cnb_hide_on_show_always">
460
+ <th><label for="actions[<?php esc_attr_e($action->id) ?>][schedule][stop]">End time</label></th>
461
+ <td><input type="time" name="actions[<?php esc_attr_e($action->id) ?>][schedule][stop]" id="actions[<?php esc_attr_e($action->id) ?>][schedule][stop]" value="<?php if (isset($action->schedule)) { esc_attr_e($action->schedule->stop); } ?>"></td>
462
+ </tr>
463
+ <tr class="cnb_hide_on_show_always">
464
+ <th><label for="actions[<?php esc_attr_e($action->id) ?>][schedule][timezone]">Timezone</label></th>
465
+ <td>
466
+ <select name="actions[<?php esc_attr_e($action->id) ?>][schedule][timezone]" id="actions[<?php esc_attr_e($action->id) ?>][schedule][timezone]">
467
+ <?php if (isset($action->schedule)) { echo wp_timezone_choice($action->schedule->timezone); } ?>
468
+ </select>
469
+ <p class="description" id="domain_timezone-description">
470
+ <?php if (empty($action->schedule->timezone)) { ?>
471
+ Please select your timezone.
472
+ <?php } else { ?>
473
+ Currently set to <code><?php esc_html_e($action->schedule->timezone)?></code>.
474
+ <?php } ?>
475
+ </p>
476
+ </td>
477
+ </tr>
478
+ <tr class="cnb_hide_on_show_always">
479
+ <th></th>
480
+ <td>
481
+ <input id="actions_schedule_outside_hours" name="actions[<?php esc_attr_e($action->id) ?>][schedule][outsideHours]" type="checkbox"
482
+ value="true" <?php checked(true, isset($action->schedule) && $action->schedule->outsideHours); ?> />
483
+ <label title="Show always" for="actions_schedule_outside_hours">Show button outside these hours</label>
484
+ </td>
485
+ </tr>
486
+ <?php } ?>
487
+ <?php if ($show_table) { ?>
488
+ </table>
489
+ <?php } ?>
490
+ <?php
491
+ }
492
+
493
+ function cnb_admin_page_action_edit_render_main($action, $button, $show_table=true) {
494
+ $bid = !empty($_GET["bid"]) ? sanitize_text_field($_GET["bid"]) : null;
495
+ // Set some sane defaults
496
+ $action->backgroundColor = !empty($action->backgroundColor)
497
+ ? $action->backgroundColor
498
+ : '#009900';
499
+ $action->iconColor = !empty($action->iconColor)
500
+ ? $action->iconColor
501
+ : '#FFFFFF';
502
+ $action->iconEnabled = isset($action->iconEnabled)
503
+ // phpcs:ignore
504
+ ? boolval($action->iconEnabled)
505
+ : true;
506
+ ?>
507
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.12/js/intlTelInput.min.js" integrity="sha512-OnkjbJ4TwPpgSmjXACCb5J4cJwi880VRe+vWpPDlr8M38/L3slN5uUAeOeWU2jN+4vN0gImCXFGdJmc0wO4Mig==" crossorigin="anonymous"></script>
508
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.12/css/intlTelInput.min.css" integrity="sha512-yye/u0ehQsrVrfSd6biT17t39Rg9kNc+vENcCXZuMz2a+LWFGvXUnYuWUW6pbfYj1jcBb/C39UZw2ciQvwDDvg==" crossorigin="anonymous" />
509
+ <input type="hidden" name="bid" value="<?php echo $bid ?>" />
510
+ <input type="hidden" name="action_id" value="<?php echo $action->id ?>" />
511
+ <input type="hidden" name="_wpnonce" value="<?php echo wp_create_nonce('cnb-action-edit')?>" />
512
+ <?php
513
+ cnb_render_form($action, $button, $show_table);
514
+ }
515
+
516
+ function cnb_admin_page_action_edit_render() {
517
+ global $cnb_options, $cnb_settings;
518
+
519
+ $cnb_notices = cnb_get_notices();
520
+ $cnb_changelog = cnb_get_changelog();
521
+
522
+ $action_id = cnb_get_button_id();
523
+ $action = new stdClass();
524
+ $action->id = 'new';
525
+ $action->actionType = 'PHONE';
526
+ $action->actionValue = null;
527
+ $action->labelText = null;
528
+
529
+ if (strlen($action_id) > 0 && $action_id !== 'new') {
530
+ $action = CnbAppRemote::cnb_remote_get_action( $action_id );
531
+ }
532
+
533
+ $button = null;
534
+ $bid = !empty($_GET["bid"]) ? sanitize_text_field($_GET["bid"]) : null;
535
+ if ($bid !== null) {
536
+ $button = CnbAppRemote::cnb_remote_get_button_full( $bid );
537
+
538
+ add_action('cnb_header', function() use($button) {
539
+ cnb_add_header_button_edit($button);
540
+ });
541
+
542
+ // Create back link
543
+ $url = admin_url('admin.php');
544
+ $redirect_link = esc_url(
545
+ add_query_arg(
546
+ array(
547
+ 'page' => 'call-now-button',
548
+ 'action' => 'edit',
549
+ 'id' => $bid),
550
+ $url ));
551
+
552
+ $action_verb = $action->id === 'new' ? 'adding' : 'editing';
553
+ $cnb_notice = '<div class="notice notice-info">
554
+ <p><strong>You are '.$action_verb.' an Action</strong>. Click <a href="'.$redirect_link.'">here</a> to go back to editing the button.</p></div>';
555
+
556
+ $cnb_notices[] = $cnb_notice;
557
+
558
+ } else {
559
+ add_action('cnb_header', function() use($action) {
560
+ cnb_add_header_action_edit($action);
561
+ });
562
+ }
563
+
564
+ $url = admin_url('admin-post.php');
565
+ $form_action = esc_url( $url );
566
+ $redirect_link = add_query_arg(
567
+ array(
568
+ 'bid' => $bid
569
+ ),
570
+ $form_action
571
+ );
572
+
573
+ ?>
574
+ <div class="wrap">
575
+ <?php cnb_admin_header($cnb_options, $cnb_settings, $cnb_notices, $cnb_changelog); ?>
576
+
577
+ <?php if ($bid !== null) { ?>
578
+ <h2 class="nav-tab-wrapper">
579
+ <a href="<?php echo cnb_action_edit_create_tab_url($button, 'basic_options') ?>"
580
+ class="nav-tab">Basics</a>
581
+ <a href="<?php echo cnb_action_edit_create_tab_url($button, 'extra_options') ?>"
582
+ class="nav-tab ">Presentation</a>
583
+ <a href="<?php echo cnb_action_edit_create_tab_url($button, 'visibility') ?>"
584
+ class="nav-tab ">Visibility</a>
585
+ </h2>
586
+ <?php } ?>
587
+ <form action="<?php echo $redirect_link; ?>" method="post">
588
+ <input type="hidden" name="page" value="call-now-button-actions" />
589
+ <input type="hidden" name="action" value="<?php echo $action->id === 'new' ? 'cnb_create_action' :'cnb_update_action' ?>" />
590
+ <?php
591
+ cnb_admin_page_action_edit_render_main($action, $button);
592
+ submit_button();
593
+ ?>
594
+ </form>
595
+ <?php cnb_admin_footer();
596
+ echo '</div>';
597
+ }
src/admin/action-overview.php ADDED
@@ -0,0 +1,423 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname( __FILE__ ) . '/api/CnbAppRemote.php';
4
+ require_once dirname( __FILE__ ) . '/api/CnbAdminCloud.php';
5
+ require_once dirname( __FILE__ ) . '/partials/admin-functions.php';
6
+ require_once dirname( __FILE__ ) . '/partials/admin-header.php';
7
+ require_once dirname( __FILE__ ) . '/partials/admin-footer.php';
8
+
9
+ if(!class_exists('WP_List_Table')) {
10
+ require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
11
+ }
12
+
13
+ function cnb_add_header_action_overview() {
14
+ echo 'Actions ';
15
+ }
16
+
17
+ class Cnb_Action_List_Table extends WP_List_Table {
18
+
19
+ /**
20
+ * CallNowButton Button object
21
+ *
22
+ * @since v0.5.1
23
+ * @var object
24
+ */
25
+ public $button;
26
+
27
+ /**
28
+ * Constructor, we override the parent to pass our own arguments
29
+ * We usually focus on three parameters: singular and plural labels, as well as whether the class supports AJAX.
30
+ * @param array $args (show contain at least a single key called 'button')
31
+ */
32
+ function __construct( $args = array() ) {
33
+ if (isset($args['button'])) {
34
+ $this->button = $args['button'];
35
+ }
36
+
37
+ parent::__construct(array(
38
+ 'singular' => 'cnb_list_action', //Singular label
39
+ 'plural' => 'cnb_list_actions', //plural label, also this well be one of the table css class
40
+ 'ajax' => false //We won't support Ajax for this table
41
+ ));
42
+ }
43
+
44
+ /**
45
+ * Define the columns that are going to be used in the table
46
+ * @return array $columns, the array of columns to use with the table
47
+ */
48
+ function get_columns() {
49
+ $columns = array(
50
+ 'cb' => '<input type="checkbox">',
51
+ 'id' => __('ID'),
52
+ 'actionType' => __('Type'),
53
+ 'actionValue' => __('Value'),
54
+ 'labelText' => __('Label'),
55
+ );
56
+ if ($this->button) { unset($columns['cb']); }
57
+ return $columns;
58
+ }
59
+
60
+ function get_sortable_columns() {
61
+ return array(
62
+ 'actionType' => array('actionType', false),
63
+ 'actionValue' => array('actionValue', false),
64
+ 'labelText' => array('labelText', false),
65
+ );
66
+ }
67
+
68
+ function get_hidden_columns() {
69
+ return array('id');
70
+ }
71
+
72
+ function prepare_items() {
73
+ // Process any Bulk actions before gathering data
74
+ $this->process_bulk_action();
75
+
76
+ /* -- Preparing your query -- */
77
+ $data = $this->get_data();
78
+
79
+ if ($data instanceof WP_Error) {
80
+ return $data;
81
+ }
82
+
83
+ /* -- Ordering parameters -- */
84
+ //Parameters that are going to be used to order the result
85
+ usort( $data, array( &$this, 'sort_data' ) );
86
+
87
+ /* -- Pagination parameters -- */
88
+ //Number of elements in your table?
89
+ $totalitems = count($data); //return the total number of affected rows
90
+ $per_page = 20; //How many to display per page?
91
+ //Which page is this?
92
+ $current_page = !empty($_GET['paged']) ? (int)sanitize_text_field($_GET['paged']) : 1;
93
+
94
+ //Page Number
95
+ if (empty($current_page) || !is_numeric($current_page) || $current_page <= 0) {
96
+ $current_page = 1;
97
+ }
98
+
99
+ //How many pages do we have in total?
100
+ $totalpages = ceil($totalitems / $per_page); //adjust the query to take pagination into account
101
+ if (!empty($current_page) && !empty($per_page)) {
102
+ $offset = ($current_page - 1) * $per_page;
103
+
104
+ /* -- Register the pagination -- */
105
+ $this->set_pagination_args(array(
106
+ 'total_items' => $totalitems,
107
+ 'total_pages' => $totalpages,
108
+ 'per_page' => $per_page,
109
+ ));
110
+ //The pagination links are automatically built according to those parameters
111
+
112
+ /* -- Register the Columns -- */
113
+ $columns = $this->get_columns();
114
+ $hidden_columns = $this->get_hidden_columns();
115
+ $sortable_columns = $this->get_sortable_columns();
116
+ $this->_column_headers = array($columns, $hidden_columns, $sortable_columns, 'actionType');
117
+
118
+ /* -- Register the items -- */
119
+ $data = array_slice($data,$offset,$per_page);
120
+ $this->items = $data;
121
+ }
122
+ return null;
123
+ }
124
+
125
+ function column_default( $item, $column_name )
126
+ {
127
+ switch( $column_name ) {
128
+ case 'id':
129
+ case 'actionValue':
130
+ case 'labelText':
131
+ return !empty($item[$column_name]) ? esc_html($item[$column_name]) : '<em>No value</em>';
132
+ // Handled by column_actionType
133
+ case 'actionType':
134
+ return null;
135
+ default:
136
+ return '<em>Unknown column ' .esc_html($column_name) . '</em>';
137
+ }
138
+ }
139
+
140
+ private function get_data() {
141
+ $actions = array();
142
+ if ($this->button === null) {
143
+ $actions = CnbAppRemote::cnb_remote_get_actions();
144
+ } else {
145
+ // Find ActionIDs for Button
146
+ $button = $this->button;
147
+ if ($button instanceof WP_Error) {
148
+ return $button;
149
+ }
150
+
151
+ if ($button->actions != null) {
152
+ $actions = $button->actions;
153
+ }
154
+ }
155
+
156
+ if ($actions instanceof WP_Error) {
157
+ return $actions;
158
+ }
159
+
160
+ $data = array();
161
+ foreach ($actions as $action) {
162
+ $data[] = array(
163
+ 'id' => $action->id,
164
+ 'actionType' => $action->actionType,
165
+ 'actionValue' => $action->actionValue,
166
+ 'labelText' => $action->labelText
167
+ );
168
+ }
169
+ return $data;
170
+ }
171
+
172
+ /**
173
+ * Allows you to sort the data by the variables set in the $_GET
174
+ *
175
+ * @return Mixed
176
+ */
177
+ private function sort_data( $a, $b ) {
178
+ // If orderby is set, use this as the sort column
179
+ $orderby = !empty($_GET['orderby']) ? sanitize_text_field($_GET['orderby']) : 'actionValue';
180
+ // If order is set use this as the order
181
+ $order = !empty($_GET['order']) ? sanitize_text_field($_GET['order']) : 'asc';
182
+
183
+ $result = strcmp( $a[$orderby], $b[$orderby] );
184
+
185
+ if($order === 'asc') {
186
+ return $result;
187
+ }
188
+ return -$result;
189
+ }
190
+
191
+ /**
192
+ * Custom action for `cb` columns (checkboxes)
193
+ *
194
+ * @param array|object $item
195
+ * @return string|void
196
+ */
197
+ function column_cb($item) {
198
+ return sprintf(
199
+ '<input type="checkbox" name="%1$s[]" value="%2$s" />',
200
+ $this->_args['singular'],
201
+ esc_attr($item['id'])
202
+ );
203
+ }
204
+
205
+ function column_actionType($item) {
206
+ $column_name = 'actionType';
207
+ $bid = $this->button !== null ? $this->button->id : null;
208
+ $tab = $this->button !== null ? 'actions' : null;
209
+
210
+ // Let's build a link
211
+ $url = admin_url('admin.php');
212
+ $edit_link =
213
+ add_query_arg(
214
+ array(
215
+ 'page' => 'call-now-button-actions',
216
+ 'action' => 'edit',
217
+ 'id' => $item['id'],
218
+ 'bid' => $bid,
219
+ 'tab' => $tab),
220
+ $url );
221
+ $edit_url = esc_url( $edit_link );
222
+ $actions = array(
223
+ 'edit' => '<a href="'.$edit_url.'">Edit</a>',
224
+ );
225
+
226
+ $delete_link = wp_nonce_url(
227
+ add_query_arg( array(
228
+ 'page' => 'call-now-button-actions',
229
+ 'action' => 'delete',
230
+ 'id' => $item['id'],
231
+ 'bid' => $bid ),
232
+ $url ),
233
+ 'cnb_delete_action' );
234
+ $delete_url = esc_url( $delete_link );
235
+ $actions['delete'] = '<a href="'.$delete_url.'">Delete</a>';
236
+
237
+ $actionTypes = cnb_get_action_types();
238
+ $value = !empty($item[$column_name]) ? esc_html($actionTypes[$item[$column_name]]) : '<em>No value</em>';
239
+ return sprintf(
240
+ '%1$s %2$s',
241
+ '<a href="'.$edit_url.'">'.$value . '</a>',
242
+ $this->row_actions($actions)
243
+ );
244
+ }
245
+
246
+ function get_bulk_actions() {
247
+ // Hide Bulk Actions if we're on the Button edit page
248
+ if ($this->button) { return array(); }
249
+ return array(
250
+ 'delete' => 'Delete',
251
+ );
252
+ }
253
+
254
+ function process_bulk_action() {
255
+ if ( isset( $_REQUEST['_wpnonce'] ) && ! empty( $_REQUEST['_wpnonce'] ) ) {
256
+ $nonce = filter_input( INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING );
257
+ $action = 'bulk-' . $this->_args['plural'];
258
+
259
+ if ( wp_verify_nonce( $nonce, $action ) ) {
260
+ $actionIds = filter_input(INPUT_POST, 'cnb_list_action', FILTER_SANITIZE_STRING, FILTER_REQUIRE_ARRAY);
261
+ switch ($this->current_action()) {
262
+ case 'delete':
263
+ foreach ($actionIds as $actionId) {
264
+ $action = array('id' => $actionId);
265
+ CnbAppRemote::cnb_remote_delete_action( $action );
266
+ }
267
+ echo '<div class="notice-success notice"><p>' . count($actionIds) . ' Action(s) deleted</p></div>';
268
+ break;
269
+ }
270
+ }
271
+ }
272
+ }
273
+ }
274
+
275
+ function cnb_action_after_header() {
276
+ // Only add the "Add new" action in the overview part
277
+ $id = !empty($_GET['id']) ? sanitize_text_field($_GET['id']) : null;
278
+ $action = !empty($_GET['action']) ? sanitize_text_field($_GET['action']) : null;
279
+ $bid = !empty($_GET["bid"]) ? sanitize_text_field($_GET["bid"]) : null;
280
+ if ($id === null || ($action != 'new' && $action != 'edit')) {
281
+ // Create link
282
+ $url = admin_url('admin.php');
283
+ $new_link =
284
+ add_query_arg(
285
+ array(
286
+ 'page' => 'call-now-button-actions',
287
+ 'action' => 'new',
288
+ 'id' => 'new',
289
+ 'bid' => $bid),
290
+ $url);
291
+ $new_url = esc_url($new_link);
292
+
293
+ echo '<a href="' . $new_url . '" class="page-title-action">Add New</a>';
294
+ }
295
+ }
296
+
297
+ /**
298
+ * Main entrypoint, used by `call-now-button.php`.
299
+ */
300
+ function cnb_admin_page_action_overview_render() {
301
+ $id = !empty($_GET['id']) ? sanitize_text_field($_GET['id']) : null;
302
+ $action = !empty($_GET['action']) ? sanitize_text_field($_GET['action']) : null;
303
+ if ($id === null) {
304
+ cnb_admin_page_action_overview_render_list();
305
+ } else {
306
+ switch ($action) {
307
+ case 'new':
308
+ case 'edit':
309
+ require_once dirname( __FILE__ ) . '/action-edit.php';
310
+ cnb_admin_page_action_edit_render();
311
+ break;
312
+ case 'delete':
313
+ if (isset($_REQUEST['_wpnonce']) && !empty($_REQUEST['_wpnonce'])) {
314
+ $nonce = filter_input(INPUT_GET, '_wpnonce', FILTER_SANITIZE_STRING);
315
+ $action = 'cnb_delete_action';
316
+
317
+ if (wp_verify_nonce($nonce, $action)) {
318
+ $cnb_cloud_notifications = array();
319
+ // If a button is set, remove this ID from the actions array
320
+ $bid = !empty($_GET['bid']) ? sanitize_text_field($_GET['bid']) : null;
321
+ if ($bid !== null) {
322
+ // Get the button
323
+ $button = CnbAppRemote::cnb_remote_get_button( $bid );
324
+
325
+ // Remove the current Action
326
+ $pos = array_search($id, $button->actions);
327
+ unset($button->actions[$pos]);
328
+
329
+ // Convert to array and update
330
+ $button_array = json_decode(json_encode($button), true);
331
+ CnbAdminCloud::cnb_update_button( $cnb_cloud_notifications, $button_array );
332
+ }
333
+
334
+ CnbAdminCloud::cnb_delete_action( $cnb_cloud_notifications, $id );
335
+
336
+ foreach ($cnb_cloud_notifications as $cnb_cloud_notification) {
337
+ echo $cnb_cloud_notification;
338
+ }
339
+ if ($bid !== null) {
340
+ // Create link
341
+ // Create link
342
+ $url = admin_url('admin.php');
343
+ $new_link =
344
+ add_query_arg(
345
+ array(
346
+ 'page' => 'call-now-button',
347
+ 'action' => 'edit',
348
+ 'id' => $bid,
349
+ ),
350
+ $url);
351
+ $new_url = esc_url_raw($new_link);
352
+ echo '<div class="notice-success notice"><p>';
353
+ echo '<p>You will be redirected back to the Button overview in 5 seconds...</p><p>Or click here to go immediately: <a href="'.$new_url.'">'.$new_url.'</a></p>';
354
+ echo '<script type="text/javascript">setTimeout(function(){location.href="' . $new_url .'"} , 5000); </script>';
355
+ echo '</p></div>';
356
+ } else {
357
+ cnb_admin_page_action_overview_render_list();
358
+ }
359
+ }
360
+ }
361
+ break;
362
+ }
363
+ }
364
+ }
365
+
366
+ function cnb_admin_page_action_overview_bid(&$cnb_notices) {
367
+ $bid = !empty($_GET['bid']) ? sanitize_text_field($_GET['bid']) : null;
368
+ $args = array();
369
+ $button = null;
370
+ if ($bid !== null) {
371
+ $button = CnbAppRemote::cnb_remote_get_button( $bid );
372
+ }
373
+
374
+ if ($button && !($button instanceof WP_Error)) {
375
+ $args['button'] = $button;
376
+ $cnb_notices[] = '<div class="notice notice-info"><p>Only actions for Button ID <code>'.esc_html($button->id).'</code> (<strong>'.esc_html($button->name).'</strong>) are shown</p></div>';
377
+ }
378
+ return $args;
379
+ }
380
+ function cnb_admin_page_action_overview_render_list() {
381
+ global $cnb_options, $cnb_settings;
382
+
383
+ add_action('cnb_header', 'cnb_add_header_action_overview');
384
+
385
+ $cnb_notices = cnb_get_notices();
386
+ $cnb_changelog = cnb_get_changelog();
387
+
388
+ $args = cnb_admin_page_action_overview_bid($cnb_notices);
389
+ //Prepare Table of elements
390
+ $wp_list_table = new Cnb_Action_List_Table($args);
391
+ $data = $wp_list_table->prepare_items();
392
+
393
+ if ($data instanceof WP_Error) {
394
+ $cnb_notices[] = cnb_admin_header_get_cloud_error($data);
395
+ } else {
396
+ add_action('cnb_after_header', 'cnb_action_after_header');
397
+ }
398
+
399
+ echo '<div class="wrap">';
400
+ cnb_admin_header($cnb_options, $cnb_settings, $cnb_notices, $cnb_changelog);
401
+
402
+ echo '<form id="wp_list_event" method="post">';
403
+
404
+ //Table of elements
405
+ $wp_list_table->display();
406
+ echo '</form>';
407
+ cnb_admin_footer();
408
+ echo '</div>';
409
+ }
410
+
411
+ /**
412
+ * Used by button-overview
413
+ *
414
+ * @param $args
415
+ */
416
+ function cnb_admin_page_action_overview_render_form($args) {
417
+ //Prepare Table of elements
418
+ $wp_list_table = new Cnb_Action_List_Table($args);
419
+ $wp_list_table->prepare_items();
420
+
421
+ //Table of elements
422
+ $wp_list_table->display();
423
+ }
src/admin/admin-ajax.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once dirname( __FILE__ ) . '/api/CnbAppRemotePayment.php';
3
+ require_once dirname( __FILE__ ) . '/api/CnbAppRemote.php';
4
+
5
+ // part of domain-upgrade
6
+ function cnb_admin_page_domain_upgrade_get_checkout($arg) {
7
+ $planId = filter_input( INPUT_POST, 'planId', FILTER_SANITIZE_STRING );
8
+ $domainId = filter_input( INPUT_POST, 'domainId', FILTER_SANITIZE_STRING );
9
+
10
+ $url = admin_url('admin.php');
11
+ $redirect_link =
12
+ add_query_arg(
13
+ array(
14
+ 'page' => 'call-now-button-domains',
15
+ 'action' => 'upgrade',
16
+ 'id' => $domainId,
17
+ 'upgrade' => 'success'),
18
+ $url );
19
+ $callbackUri = esc_url_raw( $redirect_link );
20
+ $checkoutSession = CnbAppRemotePayment::cnb_remote_post_subscription( $planId, $domainId, $callbackUri );
21
+ if ($checkoutSession instanceof WP_Error) {
22
+ return -1;
23
+ } else {
24
+ // Get link based on Stripe checkoutSessionId
25
+ esc_html_e($checkoutSession->checkoutSessionId);
26
+ }
27
+ wp_die();
28
+ }
29
+ add_action( 'wp_ajax_cnb_get_checkout', 'cnb_admin_page_domain_upgrade_get_checkout' );
src/admin/api/CnbAdminCloud.php ADDED
@@ -0,0 +1,631 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class CnbAdminCloud {
4
+
5
+ /**
6
+ * Called when Cloud Hosting is enabled via settings
7
+ *
8
+ * If no ID has been set yet, it sets a sane default
9
+ *
10
+ * @param $options array
11
+ *
12
+ * @return string The ID to use for the Cloud Button
13
+ */
14
+ public static function cnb_set_default_option_for_cloud( $options ) {
15
+ global $cnb_options;
16
+ if ( isset( $cnb_options['cloud_use_id'] ) && ! empty( $cnb_options['cloud_use_id'] ) ) {
17
+ return $cnb_options['cloud_use_id'];
18
+ }
19
+ if ( ! isset( $cnb_options['api_key'] ) || empty( $cnb_options['api_key'] ) ) {
20
+ $cnb_options['api_key'] = $options['api_key'];
21
+ }
22
+ $user_info = CnbAppRemote::cnb_remote_get_user_info();
23
+ if ( $user_info instanceof WP_Error ) {
24
+ return '';
25
+ }
26
+
27
+ return $user_info->id;
28
+ }
29
+
30
+ /**
31
+ * @param $button
32
+ * @param array $actions Should ONLY contain Ids, not full objects
33
+ * @param array $conditions
34
+ *
35
+ * @return array
36
+ */
37
+ public static function cnb_update_button_and_conditions( $button, $actions = array(), $conditions = array() ) {
38
+ $cnb_cloud_notifications = array();
39
+ // No need to update the Condition, this is done via the Button for now
40
+
41
+ // 2: Update the Condition(s)
42
+ $new_conditions = array();
43
+ foreach ( $conditions as $condition ) {
44
+ if ( $condition['delete'] === 'true' ) {
45
+ // 2.1 Delete now unused Conditions
46
+ CnbAppRemote::cnb_remote_delete_condition( $condition );
47
+ } else if ( $condition['id'] === '' ) {
48
+ // 2.2 Create new Conditions
49
+ $new_conditions[] = self::cnb_create_condition( $cnb_cloud_notifications, $condition );
50
+ } else if ( $condition['id'] !== '' ) {
51
+ // 2.3 Update existing Conditions
52
+ $new_conditions[] = self::cnb_update_condition( $cnb_cloud_notifications, $condition );
53
+ }
54
+ }
55
+
56
+ // 3: Update the Action(s)
57
+ $new_actions = array();
58
+ foreach ( $actions as $action ) {
59
+ if ( isset( $action['delete'] ) && $action['delete'] === 'true' ) {
60
+ // 2.1 Delete now unused Action
61
+ CnbAppRemote::cnb_remote_delete_action( $action );
62
+ } else if ( $action['id'] === '' ) {
63
+ // 2.2 Create new Action
64
+ $new_actions[] = self::cnb_create_action( $cnb_cloud_notifications, $action );
65
+ } else if ( $action['id'] !== '' && isset( $action['actionType'] ) ) {
66
+ // 2.3 Update existing Action (but only if it is provided fully, which is why "actionType" is tested for presence
67
+ $new_actions[] = self::cnb_update_action( $cnb_cloud_notifications, $action );
68
+ } else {
69
+ // 2.4 No update needed, so pass on the action as received
70
+ $new_actions[] = $action;
71
+ }
72
+ }
73
+
74
+ // 4: Update the Button
75
+ self::cnb_update_button_actions_conditions( $cnb_cloud_notifications, $button, $new_actions, $new_conditions );
76
+
77
+ return $cnb_cloud_notifications;
78
+ }
79
+
80
+ /**
81
+ * @param $cnb_cloud_notifications
82
+ * @param $button array Single Button object
83
+ * @param $actions array Action objects
84
+ * @param $conditions array Condition objects
85
+ *
86
+ * @return mixed|WP_Error
87
+ */
88
+ public static function cnb_update_button_actions_conditions( &$cnb_cloud_notifications, $button, $actions, $conditions ) {
89
+ $result = CnbAppRemote::cnb_remote_update_wp_button( $button, $actions, $conditions );
90
+ if ( $result instanceof WP_Error ) {
91
+ $message = self::cnb_admin_get_error_message( 'update', 'button', $result );
92
+ array_push( $cnb_cloud_notifications, $message );
93
+ } else {
94
+ $message = self::cnb_admin_get_success_message( 'updated', 'button', $result->id );
95
+ array_push( $cnb_cloud_notifications, $message );
96
+ }
97
+
98
+ return $result;
99
+ }
100
+
101
+ public static function cnb_update_button( &$cnb_cloud_notifications, $button ) {
102
+ $result = CnbAppRemote::cnb_remote_update_button( $button );
103
+ if ( $result instanceof WP_Error ) {
104
+ $message = self::cnb_admin_get_error_message( 'update', 'button', $result );
105
+ array_push( $cnb_cloud_notifications, $message );
106
+ } else {
107
+ $message = self::cnb_admin_get_success_message( 'updated', 'button', $result->id );
108
+ array_push( $cnb_cloud_notifications, $message );
109
+ }
110
+
111
+ return $result;
112
+ }
113
+
114
+ public static function cnb_update_action( &$cnb_cloud_notifications, $action ) {
115
+ $result = CnbAppRemote::cnb_remote_update_action( $action );
116
+ if ( $result instanceof WP_Error ) {
117
+ $message = self::cnb_admin_get_error_message( 'update', 'action', $result );
118
+ array_push( $cnb_cloud_notifications, $message );
119
+ } else {
120
+ $message = self::cnb_admin_get_success_message( 'updated', 'action', $result->id );
121
+ array_push( $cnb_cloud_notifications, $message );
122
+ }
123
+
124
+ return $result;
125
+ }
126
+
127
+ public static function cnb_update_condition( &$cnb_cloud_notifications, $condition ) {
128
+ $result = CnbAppRemote::cnb_remote_update_condition( $condition );
129
+ if ( $result instanceof WP_Error ) {
130
+ $message = self::cnb_admin_get_error_message( 'update', 'condition', $result );
131
+ array_push( $cnb_cloud_notifications, $message );
132
+ } else {
133
+ $message = self::cnb_admin_get_success_message( 'updated', 'condition', $result->id );
134
+ array_push( $cnb_cloud_notifications, $message );
135
+ }
136
+
137
+ return $result;
138
+ }
139
+
140
+ public static function cnb_update_domain( &$cnb_cloud_notifications, $domain ) {
141
+ $result = CnbAppRemote::cnb_remote_update_domain( $domain );
142
+ if ( $result instanceof WP_Error ) {
143
+ $message = self::cnb_admin_get_error_message( 'update', 'domain', $result );
144
+ array_push( $cnb_cloud_notifications, $message );
145
+ } else {
146
+ $message = self::cnb_admin_get_success_message( 'updated', 'domain', $result->id );
147
+ array_push( $cnb_cloud_notifications, $message );
148
+ }
149
+
150
+ return $result;
151
+ }
152
+
153
+ /**
154
+ * @param $cnb_cloud_notifications
155
+ * @param $button array Single Button object
156
+ *
157
+ * @return object|WP_Error The created Button
158
+ */
159
+ public static function cnb_create_button( &$cnb_cloud_notifications, $button ) {
160
+ $result = CnbAppRemote::cnb_remote_create_button( $button );
161
+ if ( $result instanceof WP_Error ) {
162
+ $message = self::cnb_admin_get_error_message( 'create', 'button', $result );
163
+ array_push( $cnb_cloud_notifications, $message );
164
+ } else {
165
+ $message = self::cnb_admin_get_success_message( 'created', 'button', $result->id );
166
+ array_push( $cnb_cloud_notifications, $message );
167
+ }
168
+
169
+ return $result;
170
+ }
171
+
172
+ /**
173
+ * @param $cnb_cloud_notifications
174
+ * @param $button_id string ID of the button to delete
175
+ *
176
+ * @return mixed|WP_Error
177
+ */
178
+ public static function cnb_delete_button( &$cnb_cloud_notifications, $button_id ) {
179
+ $result = CnbAppRemote::cnb_remote_delete_button( array( 'id' => $button_id ) );
180
+ if ( $result instanceof WP_Error ) {
181
+ $message = self::cnb_admin_get_error_message( 'delete', 'button', $result, 'with ID <code>' . esc_html( $button_id ) . '</code>' );
182
+ array_push( $cnb_cloud_notifications, $message );
183
+ } else {
184
+ $message = self::cnb_admin_get_success_message( 'deleted', 'button', $button_id );
185
+ array_push( $cnb_cloud_notifications, $message );
186
+ }
187
+
188
+ return $result;
189
+ }
190
+
191
+ /**
192
+ * @param $cnb_cloud_notifications
193
+ * @param $domain_id string ID of the domain to delete
194
+ *
195
+ * @return mixed|WP_Error
196
+ */
197
+ public static function cnb_delete_domain( &$cnb_cloud_notifications, $domain_id ) {
198
+ $result = CnbAppRemote::cnb_remote_delete_domain( array( 'id' => $domain_id ) );
199
+ if ( $result instanceof WP_Error ) {
200
+ $message = self::cnb_admin_get_error_message( 'delete', 'domain', $result, 'with ID <code>' . esc_html( $domain_id ) . '</code>' );
201
+ array_push( $cnb_cloud_notifications, $message );
202
+ } else {
203
+ $message = self::cnb_admin_get_success_message( 'deleted', 'domain', $domain_id );
204
+ array_push( $cnb_cloud_notifications, $message );
205
+ }
206
+
207
+ return $result;
208
+ }
209
+
210
+ /**
211
+ * @param $cnb_cloud_notifications
212
+ * @param $action_id string ID of the action to delete
213
+ *
214
+ * @return mixed|WP_Error
215
+ */
216
+ public static function cnb_delete_action( &$cnb_cloud_notifications, $action_id ) {
217
+ $result = CnbAppRemote::cnb_remote_delete_action( array( 'id' => $action_id ) );
218
+ if ( $result instanceof WP_Error ) {
219
+ $message = self::cnb_admin_get_error_message( 'delete', 'action', $result, 'with ID <code>' . esc_html( $action_id ) . '</code>' );
220
+ array_push( $cnb_cloud_notifications, $message );
221
+ } else {
222
+ $message = self::cnb_admin_get_success_message( 'deleted', 'action', $action_id );
223
+ array_push( $cnb_cloud_notifications, $message );
224
+ }
225
+
226
+ return $result;
227
+ }
228
+
229
+ /**
230
+ * @param $cnb_cloud_notifications
231
+ * @param $condition_id string ID of the condition to delete
232
+ *
233
+ * @return mixed|WP_Error
234
+ */
235
+ public static function cnb_delete_condition( &$cnb_cloud_notifications, $condition_id ) {
236
+ $result = CnbAppRemote::cnb_remote_delete_condition( array( 'id' => $condition_id ) );
237
+ if ( $result instanceof WP_Error ) {
238
+ $message = self::cnb_admin_get_error_message( 'delete', 'condition', $result, 'with ID <code>' . esc_html( $condition_id ) . '</code>' );
239
+ array_push( $cnb_cloud_notifications, $message );
240
+ } else {
241
+ $message = self::cnb_admin_get_success_message( 'deleted', 'condition', $condition_id );
242
+ array_push( $cnb_cloud_notifications, $message );
243
+ }
244
+
245
+ return $result;
246
+ }
247
+
248
+ /**
249
+ * @param $cnb_cloud_notifications
250
+ * @param $apikey_id string ID of the Api Key to delete
251
+ *
252
+ * @return mixed|WP_Error
253
+ */
254
+ public static function cnb_delete_apikey( &$cnb_cloud_notifications, $apikey_id ) {
255
+ $result = CnbAppRemote::cnb_remote_delete_apikey( array( 'id' => $apikey_id ) );
256
+ if ( $result instanceof WP_Error ) {
257
+ $message = self::cnb_admin_get_error_message( 'delete', 'apikey', $result, 'with ID <code>' . esc_html( $apikey_id ) . '</code>' );
258
+ array_push( $cnb_cloud_notifications, $message );
259
+ } else {
260
+ $message = self::cnb_admin_get_success_message( 'deleted', 'apikey', $apikey_id );
261
+ array_push( $cnb_cloud_notifications, $message );
262
+ }
263
+
264
+ return $result;
265
+ }
266
+
267
+ public static function cnb_create_domain( &$cnb_cloud_notifications, $domain ) {
268
+ $result = CnbAppRemote::cnb_remote_create_domain( $domain );
269
+ if ( $result instanceof WP_Error ) {
270
+ $message = self::cnb_admin_get_error_message( 'create', 'domain', $result );
271
+ array_push( $cnb_cloud_notifications, $message );
272
+ } else {
273
+ $message = self::cnb_admin_get_success_message( 'created', 'domain', $result->name );
274
+ array_push( $cnb_cloud_notifications, $message );
275
+ }
276
+
277
+ return $result;
278
+ }
279
+
280
+ public static function cnb_create_action( &$cnb_cloud_notifications, $action ) {
281
+ $result = CnbAppRemote::cnb_remote_create_action( $action );
282
+ if ( $result instanceof WP_Error ) {
283
+ $message = self::cnb_admin_get_error_message( 'create', 'action', $result );
284
+ array_push( $cnb_cloud_notifications, $message );
285
+ } else {
286
+ $message = self::cnb_admin_get_success_message( 'created', 'action', $result->id );
287
+ array_push( $cnb_cloud_notifications, $message );
288
+ }
289
+
290
+ return $result;
291
+ }
292
+
293
+ public static function cnb_create_condition( &$cnb_cloud_notifications, $condition ) {
294
+ $result = CnbAppRemote::cnb_remote_create_condition( $condition );
295
+ if ( $result instanceof WP_Error ) {
296
+ $message = self::cnb_admin_get_error_message( 'create', 'condition', $result );
297
+ array_push( $cnb_cloud_notifications, $message );
298
+ } else if ( $result !== null ) {
299
+ $message = self::cnb_admin_get_success_message( 'created', 'condition', $result->id );
300
+ array_push( $cnb_cloud_notifications, $message );
301
+ }
302
+
303
+ return $result;
304
+ }
305
+
306
+ public static function cnb_create_apikey( &$cnb_cloud_notifications, $apikey ) {
307
+ $result = CnbAppRemote::cnb_remote_create_apikey( $apikey );
308
+ if ( $result instanceof WP_Error ) {
309
+ $message = self::cnb_admin_get_error_message( 'create', 'apikey', $result );
310
+ array_push( $cnb_cloud_notifications, $message );
311
+ } else if ( $result !== null ) {
312
+ $message = self::cnb_admin_get_success_message( 'created', 'apikey', $result->id );
313
+ array_push( $cnb_cloud_notifications, $message );
314
+
315
+ $key_msg = '<div class="notice-success notice"><p>' .
316
+ 'Your API key is <strong><code>' . esc_html( $result->key ) . '</code></strong>. This will not be shown again!' .
317
+ '</p></div>';
318
+ array_push( $cnb_cloud_notifications, $key_msg );
319
+ }
320
+
321
+ return $result;
322
+ }
323
+
324
+ /**
325
+ * @param $verb string one of created, updated, deleted
326
+ * @param $type string one of button, action, condition
327
+ * @param $id string The identifier of the $type (could be an actual ID, a name, etc)
328
+ *
329
+ * @return string A Wordpress success notice with all details filled out
330
+ */
331
+ public static function cnb_admin_get_success_message( $verb, $type, $id ) {
332
+ return '<div class="notice-success notice is-dismissible"><p>
333
+ Your ' . $type . ' <strong>' . esc_html( $id ) . '</strong> has been ' . $verb . ' at <strong>' . CnbAppRemote::cnb_get_api_base() . '</strong>!
334
+ </p></div>';
335
+ }
336
+
337
+ /**
338
+ * @param $result WP_Error The WP_Error that was thrown
339
+ *
340
+ * @return string HTML code with additional information (Content has been escaped already)
341
+ */
342
+ public static function cnb_admin_get_error_message_details( $result ) {
343
+ $additional_details = '';
344
+ // Get detail message if possible
345
+ $details = $result->get_error_data( $result->get_error_code() );
346
+ if ( $details ) {
347
+ $details_obj = json_decode( $details );
348
+ if ( json_last_error() == JSON_ERROR_NONE ) {
349
+ if ( $details_obj->message ) {
350
+ $additional_details = '<p>Additional details: <strong>' . esc_html( $details_obj->message ) . '</strong></p>';
351
+ }
352
+ } else {
353
+ $additional_details = '<p>Additional details: <strong>' . esc_html( $details ) . '</strong></p>';
354
+ }
355
+ }
356
+
357
+ if ($result->get_error_message() === '') {
358
+ return '<p>Code: <code>' . esc_html( $result->get_error_code() ) . '.</code></p>' . $additional_details;
359
+ }
360
+ return '<p>Code: <code>' . esc_html( $result->get_error_code() ) . '</code>, message: <code>' . esc_html( $result->get_error_message() ) . '</code></p>' . $additional_details;
361
+
362
+ }
363
+
364
+ /**
365
+ * @param $verb string one of created, updated, deleted
366
+ * @param $type string one of button, action, condition
367
+ * @param $result WP_Error The WP_Error that was thrown
368
+ * @param $extra_info string Allows for some extra details to be added to the error message.
369
+ * This contains HTML and should be escaped already when passed through.
370
+ *
371
+ * @return string A Wordpress error notice with all details filled out (Content has been escaped already)
372
+ */
373
+ public static function cnb_admin_get_error_message( $verb, $type, $result, $extra_info = '' ) {
374
+ $error_details = self::cnb_admin_get_error_message_details( $result );
375
+
376
+ return '<div class="notice-error notice"><p>We could not ' . $verb . ' the ' . $type . ' ' . $extra_info . ' :-(.</p>
377
+ ' . $error_details . '</div>';
378
+ }
379
+
380
+ /**
381
+ *
382
+ * Update the CallNowButton Cloud with a domain matching the Wordpress Domain
383
+ *
384
+ * @param $cnb_user
385
+ *
386
+ * @return array Array of Notifications
387
+ */
388
+ public static function cnb_wp_create_domain( $cnb_user ) {
389
+ $cnb_cloud_notifications = array();
390
+
391
+ if ( $cnb_user instanceof WP_Error ) {
392
+ array_push( $cnb_cloud_notifications,
393
+ '<div class="notice-error notice"><p>Cloud hosting is enabled, but needs to be configured.</p></div>' );
394
+
395
+ return $cnb_cloud_notifications;
396
+ }
397
+
398
+ array_push( $cnb_cloud_notifications,
399
+ '<div class="notice-success notice is-dismissible"><p>Cloud hosting is successfully connected.</p></div>' );
400
+
401
+ $cnb_cloud_create_domain_check = CnbAppRemote::cnb_remote_get_wp_domain();
402
+ if ( $cnb_cloud_create_domain_check instanceof WP_Error ) {
403
+ self::cnb_remote_create_wp_domain( $cnb_cloud_notifications );
404
+ }
405
+
406
+ return $cnb_cloud_notifications;
407
+ }
408
+
409
+ /**
410
+ *
411
+ * This can be used to create the domain matching this Wordpress instance's main domain
412
+ *
413
+ * @param $cnb_cloud_notifications
414
+ *
415
+ * @return mixed|WP_Error
416
+ */
417
+ public static function cnb_remote_create_wp_domain( &$cnb_cloud_notifications ) {
418
+ global $cnb_options;
419
+ $domain = array(
420
+ 'name' => CnbAppRemote::cnb_clean_site_url(),
421
+ 'timezone' => wp_timezone_string(),
422
+ 'trackGA' => isset($cnb_options['tracking'] ) && $cnb_options['tracking'] != 0,
423
+ 'trackConversion' => isset($cnb_options['conversions'] ) && $cnb_options['conversions'] != 0,
424
+ 'properties' => array(),
425
+ 'renew' => false
426
+ );
427
+
428
+ $domain['properties']['zindex'] = zindex($cnb_options['z-index']);
429
+ $domain['properties']['scale'] = $cnb_options['zoom'];
430
+
431
+ return self::cnb_create_domain( $cnb_cloud_notifications, $domain );
432
+ }
433
+
434
+ /**
435
+ * Update the CallNowButton Cloud with the current settings
436
+ *
437
+ * @return array Array of Notifications
438
+ */
439
+ public static function cnb_wp_migrate_button() {
440
+ global $cnb_options;
441
+
442
+ $cnb_cloud_notifications = array();
443
+
444
+ $cnb_user = CnbAppRemote::cnb_remote_get_user_info();
445
+ if ( $cnb_user instanceof WP_Error ) {
446
+ array_push( $cnb_cloud_notifications,
447
+ '<div class="notice-error notice"><p>Cloud hosting is enabled, but needs to be configured.</p></div>' );
448
+
449
+ return $cnb_cloud_notifications;
450
+ }
451
+
452
+ // Initialize the cloud
453
+ $domain = CnbAppRemote::cnb_remote_get_wp_domain();
454
+ if ( $domain instanceof WP_Error ) {
455
+ array_push( $cnb_cloud_notifications,
456
+ '<div class="notice-error notice"><p>Cloud hosting is enabled, but there is no domain matching ' . CnbAppRemote::cnb_clean_site_url() . '</p></div>' );
457
+ }
458
+
459
+ // 1: Create action
460
+ $action = self::cnb_wp_create_action( $cnb_cloud_notifications, $cnb_options );
461
+
462
+ // 2: Create condition
463
+ $condition = self::cnb_wp_create_condition( $cnb_cloud_notifications, $cnb_options );
464
+
465
+ // 3: Create button
466
+ $button = self::cnb_wp_create_button( $cnb_cloud_notifications, $domain, $action, $condition, $cnb_options );
467
+
468
+ if ( ! ( $button instanceof WP_Error ) ) {
469
+ $url = admin_url( 'admin.php' );
470
+ $button_edit_link =
471
+ add_query_arg(
472
+ array(
473
+ 'page' => 'call-now-button',
474
+ 'action' => 'edit',
475
+ 'id' => $button->id,
476
+ ),
477
+ $url );
478
+ $button_edit_url = esc_url( $button_edit_link );
479
+
480
+ array_push( $cnb_cloud_notifications,
481
+ '<div class="notice notice-success"><p><span class="dashicons dashicons-cloud-saved"></span>
482
+ Congratulations, you have successfully migrated your Button to the cloud version!
483
+ Click <a href="' . $button_edit_url . '">here</a> to edit your button.</p></div>' );
484
+ }
485
+
486
+ return $cnb_cloud_notifications;
487
+ }
488
+
489
+ public static function cnb_wp_create_action( &$cnb_cloud_notifications, $options ) {
490
+ $action = array(
491
+ 'actionType' => 'PHONE',
492
+ 'actionValue' => $options['number'],
493
+ 'labelText' => $options['text'],
494
+ 'backgroundColor' => $options['color'],
495
+ 'iconcolor' => $options['iconcolor'],
496
+ 'iconEnabled' => isset( $options['hideIcon'] ) && $options['hideIcon'] == 1 ? false : true,
497
+ 'schedule' => array(
498
+ 'showAlways' => true
499
+ )
500
+ );
501
+
502
+ return self::cnb_create_action( $cnb_cloud_notifications, $action );
503
+ }
504
+
505
+ public static function cnb_wp_create_condition( &$cnb_cloud_notifications, $options ) {
506
+ // frontpage (if == 1, condition: don't show on /)
507
+ if ( ! isset( $options['frontpage'] ) || $options['frontpage'] != 1 ) {
508
+ return null;
509
+ }
510
+
511
+ $condition = array(
512
+ 'conditionType' => 'url',
513
+ 'filterType' => 'EXCLUDE',
514
+ 'matchType' => 'EXACT',
515
+ 'matchValue' => get_home_url(),
516
+ );
517
+
518
+ return self::cnb_create_condition( $cnb_cloud_notifications, $condition );
519
+ }
520
+
521
+ /**
522
+ *
523
+ * @param $cnb_cloud_notifications
524
+ * @param $domain
525
+ * @param $action
526
+ * @param $condition
527
+ * @param $options
528
+ *
529
+ * @return mixed|WP_Error
530
+ */
531
+ public static function cnb_wp_create_button( &$cnb_cloud_notifications, $domain, $action, $condition, $options ) {
532
+ $appearance = 'default';
533
+ $type = 'single';
534
+
535
+ switch ( $options['appearance'] ) {
536
+ case 'right':
537
+ $appearance = 'BOTTOM_RIGHT';
538
+ break;
539
+ case 'left':
540
+ $appearance = 'BOTTOM_LEFT';
541
+ break;
542
+ case 'middle':
543
+ $appearance = 'BOTTOM_CENTER';
544
+ break;
545
+ case 'mright':
546
+ $appearance = 'MIDDLE_RIGHT';
547
+ break;
548
+ case 'mleft':
549
+ $appearance = 'MIDDLE_LEFT';
550
+ break;
551
+ case 'tright':
552
+ $appearance = 'TOP_RIGHT';
553
+ break;
554
+ case 'tleft':
555
+ $appearance = 'TOP_LEFT';
556
+ break;
557
+ case 'tmiddle':
558
+ $appearance = 'TOP_CENTER';
559
+ break;
560
+
561
+ // The 2 "full" options
562
+ case 'full':
563
+ $appearance = 'BOTTOM_CENTER';
564
+ $type = 'full';
565
+ break;
566
+ case 'tfull':
567
+ $appearance = 'TOP_CENTER';
568
+ $type = 'full';
569
+ break;
570
+ }
571
+
572
+ $iconBackgroundColor = null;
573
+ $iconColor = null;
574
+
575
+ $conditions = array();
576
+ if ( $condition != null && isset( $condition->id ) ) {
577
+ $condition_array = json_decode( json_encode( $condition ), true );
578
+ array_push( $conditions, $condition_array );
579
+ }
580
+
581
+ $actions = array();
582
+ if ( $action != null && isset( $action->id ) ) {
583
+ $action_array = json_decode( json_encode( $action ), true );
584
+ array_push( $actions, $action_array );
585
+
586
+ $iconBackgroundColor = $action->backgroundColor;
587
+ $iconColor = $action->iconColor;
588
+ }
589
+
590
+ $button = array(
591
+ 'name' => 'Button created via Wordpress plugin',
592
+ 'domain' => $domain->id,
593
+ 'active' => true,
594
+ 'actions' => $actions,
595
+ 'conditions' => $conditions,
596
+ 'type' => $type,
597
+ 'options' => array(
598
+ 'placement' => $appearance,
599
+ 'iconBackgroundColor' => $iconBackgroundColor,
600
+ 'iconColor' => $iconColor
601
+ )
602
+ );
603
+
604
+ return self::cnb_create_button( $cnb_cloud_notifications, $button );
605
+ }
606
+
607
+ /**
608
+ * NOTE: Currently only be called via button-overview, for a specific listing use case
609
+ *
610
+ * @param array $button The button array as created by the button-overview table class
611
+ * @param int $max (optional) The maximum amount of Actions to retrieve
612
+ *
613
+ * @return array Array of Action objects, between 0 and $max items
614
+ */
615
+ public static function cnb_wp_get_actions_for_button( $button, $max = 3 ) {
616
+ $count = 0;
617
+ if ( $button['actions'] ) {
618
+ $count = count( $button['actions'] );
619
+ }
620
+ $actionCount = min( $count, $max );
621
+ $result = array();
622
+ if ( ! $button || $max <= 0 ) {
623
+ return $result;
624
+ }
625
+
626
+ for ( $i = 0; $i < $actionCount; $i ++ ) {
627
+ $result[] = $button['actions'][ $i ];
628
+ }
629
+
630
+ return $result;
631
+ }}
src/admin/api/CnbAppRemote.php ADDED
@@ -0,0 +1,634 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname( __FILE__ ) . '/RemoteTrace.php';
4
+
5
+ /**
6
+ * Used only by CnbAppRemote
7
+ * @private
8
+ */
9
+ class CnbGet {
10
+ protected $defaultExpiration = 300; // 5 * MINUTE_IN_SECONDS = 5 * 60 = 300
11
+ protected $isCacheHit = false;
12
+ private $useCache = false;
13
+
14
+ public function __construct() {
15
+ global $cnb_options;
16
+ if ($cnb_options['api_caching'] === 1) {
17
+ $this->useCache = true;
18
+ }
19
+ }
20
+
21
+ protected function add($url, $response) {
22
+ set_transient( CnbAppRemote::cnb_get_transient_base() . $url, $response, $this->defaultExpiration);
23
+ return $response;
24
+ }
25
+
26
+ public function isLastCallCached() {
27
+ return $this->isCacheHit;
28
+ }
29
+
30
+ public function get($url, $args) {
31
+ if ($this->useCache) {
32
+ $cache = get_transient( CnbAppRemote::cnb_get_transient_base() . $url );
33
+ if ( $cache ) {
34
+ $this->isCacheHit = true;
35
+
36
+ return $cache;
37
+ }
38
+ }
39
+ $this->isCacheHit = false;
40
+ $response = wp_remote_get($url, $args);
41
+ return $this->add($url, $response);
42
+ }
43
+ }
44
+
45
+ class CnbAppRemote {
46
+
47
+ /**
48
+ * Return a cleaned up version of the Site URL.
49
+ *
50
+ * Removes protocol and port
51
+ *
52
+ * Example:
53
+ * https://www.testdomain.com:8080 becomes www.testdomain.com
54
+ *
55
+ * @return string
56
+ */
57
+ public static function cnb_clean_site_url() {
58
+ return preg_replace( '/:[0-9]+/', '',
59
+ preg_replace( '/.*\/\//', '', get_site_url(), 1 ), 1 );
60
+ }
61
+
62
+ /**
63
+ * @return string usually "https://api.callnowbutton.com"
64
+ */
65
+ public static function cnb_get_api_base() {
66
+ global $cnb_options;
67
+
68
+ return isset( $cnb_options['api_base'] ) ? $cnb_options['api_base'] : 'https://api.callnowbutton.com';
69
+ }
70
+
71
+ /**
72
+ * @return string usually "https://user.callnowbutton.com"
73
+ */
74
+ public static function cnb_get_user_base() {
75
+ return str_replace('api', 'user', CnbAppRemote::cnb_get_api_base());
76
+ }
77
+
78
+ /**
79
+ * @return int 0 if not found, otherwise the current cache key
80
+ */
81
+ public static function cnb__get_transient_base() {
82
+ $val = get_transient( self::cnb_get_api_base() );
83
+ if ( $val ) {
84
+ return (int) $val;
85
+ }
86
+
87
+ return 0;
88
+ }
89
+
90
+ /**
91
+ * Increments the cache key with 1
92
+ */
93
+ public static function cnb_incr_transient_base() {
94
+ set_transient( self::cnb_get_api_base(), self::cnb__get_transient_base() + 1 );
95
+ }
96
+
97
+ public static function cnb_get_transient_base() {
98
+ return self::cnb__get_transient_base() . self::cnb_get_api_base();
99
+ }
100
+
101
+ public static function cnb_remote_get_args() {
102
+ global $cnb_options;
103
+
104
+ $api_key = isset( $cnb_options['api_key'] ) ? $cnb_options['api_key'] : false;
105
+ if ( ! $api_key ) {
106
+ return new WP_Error( 'CNB_API_NOT_SETUP_YET' );
107
+ }
108
+ $header_name = 'X-CNB-Api-Key';
109
+ $header_value = $api_key;
110
+
111
+ return array(
112
+ 'headers' => array(
113
+ $header_name => $header_value,
114
+ 'Content-Type' => 'application/json'
115
+ ),
116
+ );
117
+ }
118
+
119
+ public static function cnb_remote_handle_response( $response ) {
120
+ if ( $response instanceof WP_Error ) {
121
+ $error = new WP_Error( 'CNB_UNKNOWN_REMOTE_ERROR' );
122
+ $error->merge_from( $response );
123
+
124
+ return $error;
125
+ }
126
+ if ( $response['response']['code'] == 403 ) {
127
+ return new WP_Error( 'CNB_API_KEY_INVALID', $response['response']['message'] );
128
+ }
129
+ if ( $response['response']['code'] == 404 ) {
130
+ return new WP_Error( 'CNB_ENTITY_NOT_FOUND', $response['response']['message'] );
131
+ }
132
+ // 402 == Payment required
133
+ if ( $response['response']['code'] == 402 ) {
134
+ $body = json_decode( $response['body'] );
135
+
136
+ return new WP_Error( 'CNB_PAYMENT_REQUIRED', $response['response']['message'], $body->message );
137
+ }
138
+ if ( $response['response']['code'] != 200 ) {
139
+ return new WP_Error( 'CNB_ERROR', $response['response']['message'], $response['body'] );
140
+ }
141
+
142
+ return json_decode( $response['body'] );
143
+ }
144
+
145
+ /**
146
+ * DELETE, PATCH support.
147
+ *
148
+ * Includes Trace support
149
+ *
150
+ * @param $url
151
+ * @param $parsed_args
152
+ *
153
+ * @return array|WP_Error
154
+ */
155
+ public static function cnb_wp_request( $url, $parsed_args ) {
156
+ $http = _wp_http_get_object();
157
+
158
+ $timer = new RemoteTrace( $url );
159
+ $response = $http->request( $url, $parsed_args );
160
+ $timer->end();
161
+
162
+ return $response;
163
+ }
164
+
165
+ /**
166
+ * DELETE is missing from WordPress Core.
167
+ *
168
+ * This is inspired by https://developer.wordpress.org/reference/functions/wp_remote_post/
169
+ *
170
+ * @param $url
171
+ * @param array $args
172
+ *
173
+ * @return array|WP_Error
174
+ */
175
+ public static function wp_remote_delete( $url, $args = array() ) {
176
+ $defaults = array( 'method' => 'DELETE' );
177
+ $parsed_args = wp_parse_args( $args, $defaults );
178
+
179
+ return self::cnb_wp_request( $url, $parsed_args );
180
+ }
181
+
182
+ /**
183
+ * PATCH is missing from WordPress Core.
184
+ *
185
+ * This is inspired by https://developer.wordpress.org/reference/functions/wp_remote_post/
186
+ *
187
+ * @param $url
188
+ * @param array $args
189
+ *
190
+ * @return array|WP_Error
191
+ */
192
+ public static function wp_remote_patch( $url, $args = array() ) {
193
+ $defaults = array( 'method' => 'PATCH' );
194
+ $parsed_args = wp_parse_args( $args, $defaults );
195
+
196
+ return self::cnb_wp_request( $url, $parsed_args );
197
+ }
198
+
199
+ public static function cnb_remote_patch( $rest_endpoint, $body ) {
200
+ $args = self::cnb_remote_get_args();
201
+ if ( $args instanceof WP_Error ) {
202
+ return $args;
203
+ }
204
+
205
+ if ( $body != null ) {
206
+ $args['body'] = json_encode( $body );
207
+ }
208
+
209
+ $url = self::cnb_get_api_base() . $rest_endpoint;
210
+ $response = self::wp_remote_patch( $url, $args );
211
+ self::cnb_incr_transient_base();
212
+
213
+ return self::cnb_remote_handle_response( $response );
214
+ }
215
+
216
+ public static function cnb_remote_delete( $rest_endpoint, $body = null ) {
217
+ $args = self::cnb_remote_get_args();
218
+ if ( $args instanceof WP_Error ) {
219
+ return $args;
220
+ }
221
+
222
+ if ( $body != null ) {
223
+ $args['body'] = json_encode( $body );
224
+ }
225
+
226
+ $url = self::cnb_get_api_base() . $rest_endpoint;
227
+ $response = self::wp_remote_delete( $url, $args );
228
+ self::cnb_incr_transient_base();
229
+
230
+ return self::cnb_remote_handle_response( $response );
231
+ }
232
+
233
+ public static function cnb_remote_post( $rest_endpoint, $body ) {
234
+ $args = self::cnb_remote_get_args();
235
+ if ( $args instanceof WP_Error ) {
236
+ return $args;
237
+ }
238
+
239
+ if ( $body != null ) {
240
+ $args['body'] = json_encode( $body );
241
+ }
242
+
243
+ $url = self::cnb_get_api_base() . $rest_endpoint;
244
+ $timer = new RemoteTrace( $url );
245
+ $response = wp_remote_post( $url, $args );
246
+ self::cnb_incr_transient_base();
247
+ $timer->end();
248
+
249
+ return self::cnb_remote_handle_response( $response );
250
+ }
251
+
252
+ public static function cnb_remote_get( $rest_endpoint ) {
253
+ $cnb_get_cache = new CnbGet();
254
+ $args = self::cnb_remote_get_args();
255
+ if ( $args instanceof WP_Error ) {
256
+ return $args;
257
+ }
258
+
259
+ $url = self::cnb_get_api_base() . $rest_endpoint;
260
+ $timer = new RemoteTrace( $url );
261
+ $response = $cnb_get_cache->get( $url, $args );
262
+ $timer->setCacheHit( $cnb_get_cache->isLastCallCached() );
263
+ $timer->end();
264
+
265
+ return self::cnb_remote_handle_response( $response );
266
+ }
267
+
268
+ public static function cnb_remote_get_user_info() {
269
+ $rest_endpoint = '/v1/user';
270
+
271
+ return self::cnb_remote_get( $rest_endpoint );
272
+ }
273
+
274
+ /**
275
+ * This returns the domain matching the WordPress domain
276
+ * @return mixed|WP_Error
277
+ */
278
+ public static function cnb_remote_get_wp_domain() {
279
+ $rest_endpoint = '/v1/domain/byName/' . self::cnb_clean_site_url();
280
+
281
+ return self::cnb_remote_get( $rest_endpoint );
282
+ }
283
+
284
+ public static function cnb_remote_get_domain( $id ) {
285
+ $rest_endpoint = '/v1/domain/' . $id;
286
+
287
+ return self::cnb_remote_get( $rest_endpoint );
288
+ }
289
+
290
+ public static function cnb_remote_get_domains() {
291
+ $rest_endpoint = '/v1/domain';
292
+
293
+ return self::cnb_remote_get( $rest_endpoint );
294
+ }
295
+
296
+ public static function cnb_remote_get_button( $id ) {
297
+ $rest_endpoint = '/v1/button/' . $id;
298
+
299
+ return self::cnb_remote_get( $rest_endpoint );
300
+ }
301
+
302
+ public static function cnb_remote_get_button_full( $id ) {
303
+ $rest_endpoint = '/v1/button/' . $id . '/full';
304
+
305
+ return self::cnb_remote_get( $rest_endpoint );
306
+ }
307
+
308
+ public static function cnb_remote_get_buttons() {
309
+ $rest_endpoint = '/v1/button';
310
+
311
+ return self::cnb_remote_get( $rest_endpoint );
312
+ }
313
+
314
+ public static function cnb_remote_get_buttons_full() {
315
+ $rest_endpoint = '/v1/button/full';
316
+
317
+ return self::cnb_remote_get( $rest_endpoint );
318
+ }
319
+
320
+ public static function cnb_remote_get_action( $id ) {
321
+ $rest_endpoint = '/v1/action/' . $id;
322
+
323
+ return self::cnb_remote_get( $rest_endpoint );
324
+ }
325
+
326
+ public static function cnb_remote_get_actions() {
327
+ $rest_endpoint = '/v1/action';
328
+
329
+ return self::cnb_remote_get( $rest_endpoint );
330
+ }
331
+
332
+ public static function cnb_remote_get_conditions() {
333
+ $rest_endpoint = '/v1/condition/';
334
+
335
+ return self::cnb_remote_get( $rest_endpoint );
336
+ }
337
+
338
+ public static function cnb_remote_get_condition( $id ) {
339
+ $rest_endpoint = '/v1/condition/' . $id;
340
+
341
+ return self::cnb_remote_get( $rest_endpoint );
342
+ }
343
+
344
+ public static function cnb_remote_get_apikeys() {
345
+ $rest_endpoint = '/v1/apikey';
346
+
347
+ return self::cnb_remote_get( $rest_endpoint );
348
+ }
349
+
350
+ public static function cnb_remote_update_button( $button ) {
351
+ // Find the ID in the options
352
+ $buttonId = $button['id'];
353
+
354
+ if ( ! $buttonId ) {
355
+ return new WP_Error( 'CNB_BUTTON_ID_MISSING', 'buttonId expected, but not found' );
356
+ }
357
+
358
+ $rest_endpoint = '/v1/button/' . $buttonId;
359
+
360
+ return self::cnb_remote_patch( $rest_endpoint, $button );
361
+ }
362
+
363
+ public static function cnb_remote_update_domain( $domain ) {
364
+ // Find the ID in the options
365
+ $domainId = $domain['id'];
366
+
367
+ if ( ! $domainId ) {
368
+ return new WP_Error( 'CNB_DOMAIN_ID_MISSING', 'domainId expected, but not found' );
369
+ }
370
+
371
+ $rest_endpoint = '/v1/domain/' . $domainId;
372
+
373
+ return self::cnb_remote_patch( $rest_endpoint, $domain );
374
+ }
375
+
376
+ public static function cnb_remote_delete_button( $button ) {
377
+ // Find the ID in the options
378
+ $buttonId = $button['id'];
379
+
380
+ if ( ! $buttonId ) {
381
+ return new WP_Error( 'CNB_BUTTON_ID_MISSING', 'buttonId expected, but not found' );
382
+ }
383
+
384
+ $rest_endpoint = '/v1/button/' . $buttonId;
385
+
386
+ return self::cnb_remote_delete( $rest_endpoint, $button );
387
+ }
388
+
389
+ public static function cnb_remote_delete_domain( $domain ) {
390
+ // Find the ID in the options
391
+ $domainId = $domain['id'];
392
+
393
+ if ( ! $domainId ) {
394
+ return new WP_Error( 'CNB_DOMAIN_ID_MISSING', 'domainId expected, but not found' );
395
+ }
396
+
397
+ $rest_endpoint = '/v1/domain/' . $domainId;
398
+
399
+ return self::cnb_remote_delete( $rest_endpoint );
400
+ }
401
+
402
+ public static function cnb_remote_delete_condition( $condition ) {
403
+ // Find the ID in the options
404
+ $entityId = $condition['id'];
405
+
406
+ if ( ! $entityId ) {
407
+ return new WP_Error( 'CNB_CONDITION_ID_MISSING', 'conditionId expected, but not found' );
408
+ }
409
+
410
+ $rest_endpoint = '/v1/condition/' . $entityId;
411
+
412
+ return self::cnb_remote_delete( $rest_endpoint );
413
+ }
414
+
415
+ public static function cnb_remote_delete_action( $action ) {
416
+ // Find the ID in the options
417
+ $entityId = $action['id'];
418
+
419
+ if ( ! $entityId ) {
420
+ return new WP_Error( 'CNB_ACTION_ID_MISSING', 'actionId expected, but not found' );
421
+ }
422
+
423
+ $rest_endpoint = '/v1/action/' . $entityId;
424
+
425
+ return self::cnb_remote_delete( $rest_endpoint );
426
+ }
427
+
428
+ public static function cnb_remote_delete_apikey( $apikey ) {
429
+ // Find the ID in the options
430
+ $apikeyId = $apikey['id'];
431
+
432
+ if ( ! $apikeyId ) {
433
+ return new WP_Error( 'CNB_APIKEY_ID_MISSING', 'apikeyId expected, but not found' );
434
+ }
435
+
436
+ $rest_endpoint = '/v1/apikey/' . $apikeyId;
437
+
438
+ return self::cnb_remote_delete( $rest_endpoint );
439
+ }
440
+
441
+ public static function cnb_remote_update_action( $action ) {
442
+ // Find the action ID in the options
443
+ $actionId = $action['id'];
444
+
445
+ if ( ! $actionId ) {
446
+ return new WP_Error( 'CNB_ACTION_ID_MISSING', 'actionId expected, but not found' );
447
+ }
448
+
449
+ $schedule = null;
450
+ if (isset($action['schedule'])) {
451
+ $schedule = array(
452
+ 'showAlways' => $action['schedule']['showAlways'],
453
+ 'daysOfWeek' => isset( $action['schedule']['daysOfWeek'] ) ? $action['schedule']['daysOfWeek'] : array(),
454
+ 'start' => $action['schedule']['start'],
455
+ 'stop' => $action['schedule']['stop'],
456
+ 'timezone' => $action['schedule']['timezone'],
457
+ 'outsideHours' => isset( $action['schedule']['outsideHours'] ) ? $action['schedule']['outsideHours'] : false,
458
+ );
459
+ }
460
+ $body = array(
461
+ 'actionType' => $action['actionType'],
462
+ 'actionValue' => $action['actionValue'],
463
+ 'schedule' => $schedule,
464
+ 'backgroundColor' => isset( $action['color'] ) ? $action['color'] : '#009900',
465
+ 'iconColor' => $action['iconColor'],
466
+ // phpcs:ignore
467
+ 'iconEnabled' => isset( $action['iconEnabled'] ) ? boolval( $action['iconEnabled'] ) : false,
468
+ 'labelText' => $action['labelText'],
469
+ // 'iconText' => !empty($action['iconText']) ? $action['iconText'] : cnb_actiontype_to_icontext($action['actionType']),
470
+ 'iconText' => cnb_actiontype_to_icontext($action['actionType']),
471
+ 'properties' => ! empty( $action['properties'] ) ? $action['properties'] : null,
472
+ );
473
+
474
+ $rest_endpoint = '/v1/action/' . $actionId;
475
+
476
+ return self::cnb_remote_patch( $rest_endpoint, $body );
477
+ }
478
+
479
+ /**
480
+ * TODO See if we can make this cleaner (without actions/conditions passed in?)
481
+ *
482
+ * @param $button array Single Button object
483
+ * @param $actions array Action objects
484
+ * @param $conditions array Conditions objects
485
+ *
486
+ * @return mixed|WP_Error
487
+ */
488
+ public static function cnb_remote_update_wp_button( $button, $actions, $conditions ) {
489
+ $buttonId = $button['id'];
490
+
491
+ if ( ! $buttonId ) {
492
+ return new WP_Error( 'CNB_BUTTON_ID_MISSING', 'buttonId expected, but not found' );
493
+ }
494
+
495
+ $body = array(
496
+ 'id' => $button['id'],
497
+ // phpcs:ignore
498
+ 'active' => boolval( $button['active'] ),
499
+ 'name' => ! empty( $button['name'] ) ? $button['name'] : 'Button created via Wordpress plugin',
500
+ 'domain' => $button['domain'],
501
+ 'actions' => is_array( $actions ) ? cnb_array_column( $actions, 'id' ) : array(),
502
+ 'conditions' => is_array( $conditions ) ? cnb_array_column( $conditions, 'id' ) : array(),
503
+ 'type' => $button['type'],
504
+ 'options' => $button['options']
505
+ );
506
+
507
+ return self::cnb_remote_update_button( $body );
508
+ }
509
+
510
+ public static function cnb_remote_cleanup_properties( $array ) {
511
+ return empty( $array['properties'] ) || ! is_array( $array['properties'] ) ? null : $array['properties'];
512
+ }
513
+
514
+ public static function cnb_remote_create_domain( $domain ) {
515
+ $domainId = isset( $domain['id'] ) && $domain['id'];
516
+
517
+ if ( $domainId ) {
518
+ return new WP_Error( 'CNB_DOMAIN_ID_FOUND', 'no domainId expected, but one was given' );
519
+ }
520
+ $body = array(
521
+ 'name' => $domain['name'],
522
+ 'timezone' => $domain['timezone'],
523
+ 'trackGA' => $domain['trackGA'],
524
+ 'trackConversion' => $domain['trackConversion'],
525
+ 'renew' => $domain['renew'],
526
+ 'properties' => self::cnb_remote_cleanup_properties( $domain )
527
+ );
528
+
529
+ $rest_endpoint = '/v1/domain';
530
+
531
+ return self::cnb_remote_post( $rest_endpoint, $body );
532
+ }
533
+
534
+ /**
535
+ * @param $button array Single Button object
536
+ *
537
+ * @return mixed|WP_Error
538
+ */
539
+ public static function cnb_remote_create_button( $button ) {
540
+ $buttonId = isset($button['id']) && $button['id'];
541
+
542
+ if ( $buttonId ) {
543
+ return new WP_Error( 'CNB_BUTTON_ID_FOUND', 'no buttonId expected, but one was given' );
544
+ }
545
+
546
+ $body = array(
547
+ 'name' => ! empty( $button['name'] ) ? $button['name'] : 'Button created via Wordpress plugin',
548
+ 'domain' => $button['domain'],
549
+ // phpcs:ignore
550
+ 'active' => isset( $button['active'] ) ? boolval( $button['active'] ) : false,
551
+ 'actions' => isset( $button['actions'] ) && is_array( $button['actions'] ) ? cnb_array_column( $button['actions'], 'id' ) : array(),
552
+ 'conditions' => isset( $button['conditions'] ) && is_array( $button['conditions'] ) ? cnb_array_column( $button['conditions'], 'id' ) : array(),
553
+ 'type' => $button['type'],
554
+ 'options' => $button['options']
555
+ );
556
+
557
+ $rest_endpoint = '/v1/button';
558
+
559
+ return self::cnb_remote_post( $rest_endpoint, $body );
560
+ }
561
+
562
+ public static function cnb_remote_create_action( $action ) {
563
+ $actionId = isset($action['id']) && $action['id'];
564
+
565
+ if ( $actionId ) {
566
+ return new WP_Error( 'CNB_ACTION_ID_FOUND', 'no actionId expected, but one was given' );
567
+ }
568
+
569
+ $body = array(
570
+ 'actionType' => ! empty( $action['actionType'] ) ? $action['actionType'] : 'PHONE',
571
+ 'actionValue' => $action['actionValue'],
572
+ 'schedule' => array(
573
+ 'showAlways' => $action['schedule']['showAlways'],
574
+ 'daysOfWeek' => isset( $action['schedule']['daysOfWeek'] ) ? $action['schedule']['daysOfWeek'] : null,
575
+ 'start' => isset($action['schedule']['start']) ? $action['schedule']['start'] : null,
576
+ 'stop' => isset($action['schedule']['stop']) ? $action['schedule']['stop'] : null,
577
+ 'timezone' => isset( $action['schedule']['timezone'] ) ? $action['schedule']['timezone'] : null,
578
+ 'outsideHours' => isset( $action['schedule']['outsideHours'] ) ? $action['schedule']['outsideHours'] : null,
579
+ ),
580
+ 'backgroundColor' => ! empty( $action['backgroundColor'] ) ? $action['backgroundColor'] : '#009900',
581
+ 'iconColor' => ! empty( $action['iconcolor'] ) ? $action['iconcolor'] : '#ffffff',
582
+ // phpcs:ignore
583
+ 'iconEnabled' => isset( $action['iconEnabled'] ) ? boolval( $action['iconEnabled'] ) : false,
584
+ 'labelText' => isset($action['labelText']) ? $action['labelText'] : null,
585
+ // 'iconText' => isset($action['iconText']) ? $action['iconText'] : null,
586
+ 'iconText' => cnb_actiontype_to_icontext($action['actionType']),
587
+ 'properties' => ! empty( $action['properties'] ) ? $action['properties'] : null,
588
+ );
589
+
590
+ $rest_endpoint = '/v1/action';
591
+
592
+ return self::cnb_remote_post( $rest_endpoint, $body );
593
+ }
594
+
595
+ public static function cnb_remote_create_condition( $condition ) {
596
+ $body = array(
597
+ 'conditionType' => $condition['conditionType'],
598
+ 'filterType' => $condition['filterType'],
599
+ 'matchType' => $condition['matchType'],
600
+ 'matchValue' => $condition['matchValue'],
601
+ );
602
+
603
+ $rest_endpoint = '/v1/condition';
604
+
605
+ return self::cnb_remote_post( $rest_endpoint, $body );
606
+ }
607
+
608
+ public static function cnb_remote_update_condition( $condition ) {
609
+ if ( ! $condition['id'] ) {
610
+ return new WP_Error( 'CNB_CONDITION_ID_MISSING', 'conditionId expected, but not found' );
611
+ }
612
+
613
+ $body = array(
614
+ 'id' => $condition['id'],
615
+ 'conditionType' => $condition['conditionType'],
616
+ 'filterType' => $condition['filterType'],
617
+ 'matchType' => $condition['matchType'],
618
+ 'matchValue' => $condition['matchValue'],
619
+ );
620
+
621
+ $rest_endpoint = '/v1/condition/' . $condition['id'];
622
+
623
+ return self::cnb_remote_patch( $rest_endpoint, $body );
624
+ }
625
+
626
+ public static function cnb_remote_create_apikey( $apikey ) {
627
+ $body = array(
628
+ 'name' => $apikey['name'],
629
+ );
630
+
631
+ $rest_endpoint = '/v1/apikey';
632
+
633
+ return self::cnb_remote_post( $rest_endpoint, $body);
634
+ }}
src/admin/api/CnbAppRemotePayment.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname( __FILE__ ) . '/CnbAppRemote.php';
4
+
5
+ class CnbAppRemotePayment {
6
+
7
+ public static function cnb_remote_get_plans() {
8
+ $rest_endpoint = '/v1/stripe/plans';
9
+
10
+ return CnbAppRemote::cnb_remote_get( $rest_endpoint );
11
+ }
12
+
13
+ public static function cnb_remote_get_stripe_key() {
14
+ $rest_endpoint = '/v1/stripe/key';
15
+
16
+ return CnbAppRemote::cnb_remote_get( $rest_endpoint );
17
+ }
18
+
19
+ public static function cnb_remote_post_subscription( $planId, $domainId, $callbackUri = null ) {
20
+ $callbackUri = $callbackUri === null
21
+ ? get_site_url()
22
+ : $callbackUri;
23
+
24
+ $body = array(
25
+ 'plan' => $planId,
26
+ 'domain' => $domainId,
27
+ 'callbackUri' => $callbackUri
28
+ );
29
+
30
+ $rest_endpoint = '/v1/subscription';
31
+
32
+ return CnbAppRemote::cnb_remote_post( $rest_endpoint, $body );
33
+ }
34
+
35
+ public static function cnb_remote_get_subscription_session( $subscriptionSessionId ) {
36
+ $rest_endpoint = '/v1/subscription/session/' . $subscriptionSessionId;
37
+
38
+ return CnbAppRemote::cnb_remote_get( $rest_endpoint );
39
+ }
40
+
41
+ public static function cnb_remote_get_subscription( $subscriptionId ) {
42
+ $rest_endpoint = '/v1/subscription/' . $subscriptionId;
43
+
44
+ return CnbAppRemote::cnb_remote_get( $rest_endpoint );
45
+ }
46
+ }
src/admin/api/RemoteTrace.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once dirname( __FILE__ ) . '/RemoteTracer.php';
3
+
4
+ class RemoteTrace {
5
+ /**
6
+ * @var string
7
+ */
8
+ protected $endpoint;
9
+ /**
10
+ * @var float
11
+ */
12
+ protected $start;
13
+ /**
14
+ * @var float
15
+ */
16
+ protected $end;
17
+
18
+ protected $cacheHit = false;
19
+
20
+ public function __construct($endpoint) {
21
+ $cnb_remoted_traces = RemoteTracer::getInstance();
22
+
23
+ $this->endpoint = $endpoint;
24
+ $cnb_remoted_traces->addTrace($this);
25
+ $this->start();
26
+ }
27
+
28
+ /**
29
+ * Optional, since a "start" is also calculated during Class creation.
30
+ */
31
+ public function start() {
32
+ $this->start = microtime(true);
33
+ }
34
+
35
+ public function end() {
36
+ $this->end = microtime(true);
37
+ }
38
+
39
+ /**
40
+ * @return string
41
+ */
42
+ public function getEndpoint() {
43
+ return $this->endpoint;
44
+ }
45
+
46
+ /**
47
+ * @param bool $cacheHit
48
+ */
49
+ public function setCacheHit( $cacheHit ) {
50
+ // phpcs:ignore
51
+ $this->cacheHit = boolval($cacheHit);
52
+ }
53
+
54
+ public function isCacheHit() {
55
+ return $this->cacheHit;
56
+ }
57
+
58
+ /**
59
+ * @return string A formatted version of number.
60
+ */
61
+ public function getTime($precision=4) {
62
+ $diff = $this->end - $this->start;
63
+ return number_format($diff, $precision);
64
+ }
65
+ }
src/admin/api/RemoteTracer.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Used to keep track of all traces
5
+ */
6
+ class RemoteTracer {
7
+
8
+ /**
9
+ * @var RemoteTracer Hold the class instance.
10
+ */
11
+ private static $instance;
12
+
13
+ /**
14
+ * The constructor is private to prevent initiation with outer code.
15
+ */
16
+ private function __construct() { }
17
+
18
+ /**
19
+ * The object is created from within the class itself only if the class has no instance.
20
+ *
21
+ * @return RemoteTracer
22
+ */
23
+ public static function getInstance() {
24
+ if (self::$instance == null) {
25
+ self::$instance = new RemoteTracer();
26
+ }
27
+
28
+ return self::$instance;
29
+ }
30
+
31
+ /**
32
+ * @var RemoteTrace[]
33
+ */
34
+ protected $traces = array();
35
+
36
+ /**
37
+ * @param RemoteTrace $trace
38
+ */
39
+ public function addTrace($trace) {
40
+ self::$instance->traces[] = $trace;
41
+ }
42
+
43
+ /**
44
+ * @return RemoteTrace[]
45
+ */
46
+ public function getTraces() {
47
+ return self::$instance->traces;
48
+ }
49
+
50
+ /**
51
+ * Remove all traces (used by tests to reset the internal state
52
+ */
53
+ public function clearTraces() {
54
+ self::$instance->traces = array();
55
+ }
56
+ }
src/admin/apikey-overview.php ADDED
@@ -0,0 +1,365 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname( __FILE__ ) . '/api/CnbAppRemote.php';
4
+ require_once dirname( __FILE__ ) . '/api/CnbAdminCloud.php';
5
+ require_once dirname( __FILE__ ) . '/partials/admin-functions.php';
6
+ require_once dirname( __FILE__ ) . '/partials/admin-header.php';
7
+ require_once dirname( __FILE__ ) . '/partials/admin-footer.php';
8
+
9
+ function cnb_add_header_apikey_overview() {
10
+ echo 'API keys ';
11
+ }
12
+
13
+ function cnb_add_new_modal_action() {
14
+ $url = admin_url('admin.php');
15
+ $new_link =
16
+ add_query_arg(
17
+ array(
18
+ 'TB_inline' => 'true',
19
+ 'inlineId' => 'cnb-add-new-apikey-modal',
20
+ 'height' => '150',
21
+ 'page' => 'call-now-button-apikeys',
22
+ 'action' => 'new',
23
+ 'id' => 'new' ),
24
+ $url );
25
+ printf(
26
+ '<a href="%s" title="%s" class="thickbox open-plugin-details-modal page-title-action" data-title="%s">%s</a>',
27
+ $new_link,
28
+ __('Create new API key', CNB_NAME),
29
+ __('Create new API key', CNB_NAME),
30
+ __('Add New', CNB_NAME)
31
+ );
32
+ }
33
+
34
+ /**
35
+ * This is called to create the Domain
36
+ */
37
+ function cnb_admin_page_apikey_create_process() {
38
+ global $cnb_slug_base;
39
+ $nonce = filter_input( INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING );
40
+ if( isset( $_REQUEST['_wpnonce'] ) && wp_verify_nonce( $nonce, 'cnb_create_apikey') ) {
41
+
42
+ // sanitize the input
43
+ $apikey_data = filter_input(
44
+ INPUT_POST,
45
+ 'apikey',
46
+ FILTER_SANITIZE_STRING,
47
+ FILTER_REQUIRE_ARRAY);
48
+
49
+ $apikey = array();
50
+ $apikey['name'] = sanitize_text_field( $apikey_data['name'] );
51
+
52
+ // do the processing
53
+ $cnb_cloud_notifications = array();
54
+ $new_apikey = CnbAdminCloud::cnb_create_apikey( $cnb_cloud_notifications, $apikey );
55
+
56
+ // redirect the user to the appropriate page
57
+ $transient_id = 'cnb-' . wp_generate_uuid4();
58
+ set_transient($transient_id, $cnb_cloud_notifications, HOUR_IN_SECONDS);
59
+
60
+ // Create link
61
+ $url = admin_url('admin.php');
62
+ $redirect_link =
63
+ add_query_arg(
64
+ array(
65
+ 'page' => 'call-now-button-apikeys',
66
+ 'id' => $new_apikey->id,
67
+ 'tid' => $transient_id),
68
+ $url );
69
+ $redirect_url = esc_url_raw( $redirect_link );
70
+ wp_safe_redirect($redirect_url);
71
+ exit;
72
+ }
73
+ else {
74
+ wp_die( __( 'Invalid nonce specified', CNB_NAME), __( 'Error', CNB_NAME), array(
75
+ 'response' => 403,
76
+ 'back_link' => 'admin.php?page=' . $cnb_slug_base,
77
+ ) );
78
+ }
79
+ }
80
+
81
+ if(!class_exists('WP_List_Table')) {
82
+ require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
83
+ }
84
+
85
+ class Cnb_Apikey_List_Table extends WP_List_Table {
86
+
87
+ /**
88
+ * Constructor, we override the parent to pass our own arguments
89
+ * We usually focus on three parameters: singular and plural labels, as well as whether the class supports AJAX.
90
+ */
91
+ function __construct() {
92
+ parent::__construct(array(
93
+ 'singular' => 'cnb_list_apikey', //Singular label
94
+ 'plural' => 'cnb_list_apikeys', //plural label, also this well be one of the table css class
95
+ 'ajax' => false //We don't support Ajax for this table
96
+ ));
97
+ }
98
+
99
+ /**
100
+ * Define the columns that are going to be used in the table
101
+ * @return array $columns, the array of columns to use with the table
102
+ */
103
+ function get_columns() {
104
+ return array(
105
+ 'cb' => '<input type="checkbox">',
106
+ 'id' => __('ID'),
107
+ 'name' => __('Name'),
108
+ 'created' => __('Created'),
109
+ 'lastUsed' => __('Last used'),
110
+ );
111
+ }
112
+
113
+ function get_sortable_columns() {
114
+ return array(
115
+ 'name' => array('name', false),
116
+ 'created' => array('created', false),
117
+ 'lastUsed' => array('lastUsed', false),
118
+ );
119
+ }
120
+
121
+ function get_hidden_columns() {
122
+ return array('id');
123
+ }
124
+
125
+ function prepare_items() {
126
+ // Process any Bulk actions before gathering data
127
+ $this->process_bulk_action();
128
+
129
+ /* -- Preparing your query -- */
130
+ $data = $this->get_data();
131
+
132
+ if ($data instanceof WP_Error) {
133
+ return $data;
134
+ }
135
+
136
+ /* -- Ordering parameters -- */
137
+ //Parameters that are going to be used to order the result
138
+ usort( $data, array( &$this, 'sort_data' ) );
139
+
140
+ /* -- Pagination parameters -- */
141
+ //Number of elements in your table?
142
+ $totalitems = count($data); //return the total number of affected rows
143
+ $per_page = 20; //How many to display per page?
144
+ //Which page is this?
145
+ $current_page = !empty($_GET['paged']) ? (int)sanitize_text_field($_GET['paged']) : 1;
146
+
147
+ //Page Number
148
+ if (empty($current_page) || !is_numeric($current_page) || $current_page <= 0) {
149
+ $current_page = 1;
150
+ }
151
+
152
+ //How many pages do we have in total?
153
+ $totalpages = ceil($totalitems / $per_page); //adjust the query to take pagination into account
154
+ if (!empty($current_page) && !empty($per_page)) {
155
+ $offset = ($current_page - 1) * $per_page;
156
+
157
+ /* -- Register the pagination -- */
158
+ $this->set_pagination_args(array(
159
+ 'total_items' => $totalitems,
160
+ 'total_pages' => $totalpages,
161
+ 'per_page' => $per_page,
162
+ ));
163
+ //The pagination links are automatically built according to those parameters
164
+
165
+ /* -- Register the Columns -- */
166
+ $columns = $this->get_columns();
167
+ $hidden_columns = $this->get_hidden_columns();
168
+ $sortable_columns = $this->get_sortable_columns();
169
+ $this->_column_headers = array($columns, $hidden_columns, $sortable_columns, 'name');
170
+
171
+ /* -- Register the items -- */
172
+ $data = array_slice($data,$offset,$per_page);
173
+ $this->items = $data;
174
+ }
175
+ return null;
176
+ }
177
+
178
+ function column_default( $item, $column_name )
179
+ {
180
+ switch( $column_name ) {
181
+ case 'id':
182
+ return '<code>' . esc_html($item[ $column_name ]) . '</code>';
183
+ case 'name':
184
+ case 'created':
185
+ case 'lastUsed':
186
+ return esc_html($item[ $column_name ]);
187
+ default:
188
+ return '<em>Unknown column ' .esc_html($column_name) . '</em>';
189
+ }
190
+ }
191
+
192
+ private function get_data() {
193
+ $apikeys = CnbAppRemote::cnb_remote_get_apikeys();
194
+
195
+ if ($apikeys instanceof WP_Error) {
196
+ return $apikeys;
197
+ }
198
+
199
+ $data = array();
200
+ foreach ($apikeys as $apikey) {
201
+ $data[] = array(
202
+ 'id' => $apikey->id,
203
+ 'name' => $apikey->name,
204
+ 'created' => $apikey->created,
205
+ 'lastUsed' => $apikey->lastUsed,
206
+ );
207
+ }
208
+ return $data;
209
+ }
210
+
211
+ /**
212
+ * Allows you to sort the data by the variables set in the $_GET
213
+ *
214
+ * @return Mixed
215
+ */
216
+ private function sort_data( $a, $b ) {
217
+ // If orderby is set, use this as the sort column
218
+ $orderby = !empty($_GET['orderby']) ? sanitize_text_field($_GET['orderby']) : 'name';
219
+ // If order is set use this as the order
220
+ $order = !empty($_GET['order']) ? sanitize_text_field($_GET['order']) : 'asc';
221
+
222
+ $result = strcmp( $a[$orderby], $b[$orderby] );
223
+
224
+ if($order === 'asc') {
225
+ return $result;
226
+ }
227
+ return -$result;
228
+ }
229
+
230
+ /**
231
+ * Custom action for `cb` columns (checkboxes)
232
+ *
233
+ * @param array|object $item
234
+ * @return string|void
235
+ */
236
+ function column_cb($item) {
237
+ return sprintf(
238
+ '<input type="checkbox" name="%1$s[]" value="%2$s" />',
239
+ $this->_args['singular'],
240
+ esc_html($item['id'])
241
+ );
242
+ }
243
+
244
+ function get_bulk_actions() {
245
+ return array(
246
+ 'delete' => 'Delete',
247
+ );
248
+ }
249
+
250
+ function process_bulk_action() {
251
+ if ( isset( $_REQUEST['_wpnonce'] ) && ! empty( $_REQUEST['_wpnonce'] ) ) {
252
+ $nonce = filter_input( INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING );
253
+ $action = 'bulk-' . $this->_args['plural'];
254
+
255
+ if ( wp_verify_nonce( $nonce, $action ) ) {
256
+ $apikeyIds = filter_input(INPUT_POST, 'cnb_list_apikey', FILTER_SANITIZE_STRING, FILTER_REQUIRE_ARRAY);
257
+ switch ($this->current_action()) {
258
+ case 'delete':
259
+ foreach ($apikeyIds as $apikeyId) {
260
+ $apikey = array('id' => $apikeyId);
261
+ CnbAppRemote::cnb_remote_delete_apikey( $apikey );
262
+ }
263
+ echo '<div class="notice-success notice"><p>' . count($apikeyIds) . ' Api key(s) deleted</p></div>';
264
+ break;
265
+ }
266
+ }
267
+ }
268
+ }
269
+ }
270
+
271
+ function cnb_after_header() {
272
+ // Only add the "Add new" action in the overview part
273
+ $id = !empty($_GET['id']) ? sanitize_text_field($_GET['id']) : null;
274
+ $action = !empty($_GET['action']) ? sanitize_text_field($_GET['action']) : null;
275
+ if ($id === null || ($action != 'new' && $action != 'edit')) {
276
+ // Create link
277
+ $url = admin_url('admin.php');
278
+ $new_link =
279
+ add_query_arg(
280
+ array(
281
+ 'page' => 'call-now-button-apikeys',
282
+ 'action' => 'new',
283
+ 'id' => 'new'),
284
+ $url);
285
+ $new_url = esc_url($new_link);
286
+
287
+ echo '<a href="' . $new_url . '" class="page-title-action">Add New</a>';
288
+ }
289
+ }
290
+
291
+ /**
292
+ * Main entrypoint, used by `call-now-button.php`.
293
+ */
294
+ function cnb_admin_page_apikey_overview_render() {
295
+ $id = !empty($_GET['id']) ? sanitize_text_field($_GET['id']) : null;
296
+ $action = !empty($_GET['action']) ? sanitize_text_field($_GET['action']) : null;
297
+ if ($id === null || $action === null) {
298
+ cnb_admin_page_apikey_overview_render_list();
299
+ } else {
300
+ switch ($action) {
301
+ case 'delete':
302
+ if (isset($_REQUEST['_wpnonce']) && !empty($_REQUEST['_wpnonce'])) {
303
+ $nonce = filter_input(INPUT_GET, '_wpnonce', FILTER_SANITIZE_STRING);
304
+ $action = 'cnb_delete_apikey';
305
+
306
+ if (wp_verify_nonce($nonce, $action)) {
307
+ $cnb_cloud_notifications = array();
308
+ CnbAdminCloud::cnb_delete_apikey( $cnb_cloud_notifications, $id );
309
+ foreach ($cnb_cloud_notifications as $cnb_cloud_notification) {
310
+ echo $cnb_cloud_notification;
311
+ }
312
+ cnb_admin_page_apikey_overview_render_list();
313
+ }
314
+ }
315
+ break;
316
+ }
317
+ }
318
+ }
319
+
320
+ function cnb_admin_page_apikey_overview_render_list() {
321
+ global $cnb_options, $cnb_settings;
322
+
323
+ add_action('cnb_header', 'cnb_add_header_apikey_overview');
324
+
325
+ $cnb_notices = cnb_get_notices();
326
+ $cnb_changelog = cnb_get_changelog();
327
+
328
+ $wp_list_table = new Cnb_Apikey_List_Table();
329
+ $data = $wp_list_table->prepare_items();
330
+
331
+ if ($data instanceof WP_Error) {
332
+ $cnb_notices[] = cnb_admin_header_get_cloud_error($data);
333
+ } else {
334
+ add_action('cnb_after_header', 'cnb_add_new_modal_action');
335
+ }
336
+
337
+ echo '<div class="wrap">';
338
+ cnb_admin_header($cnb_options, $cnb_settings, $cnb_notices, $cnb_changelog);
339
+
340
+ echo '<form id="wp_list_event" method="post">';
341
+ //Prepare Table of elements
342
+
343
+ //Table of elements
344
+ $wp_list_table->display();
345
+ echo '</form>';
346
+ cnb_admin_footer();
347
+ echo '</div>';
348
+ cnb_admin_page_render_thickbox();
349
+ }
350
+
351
+ function cnb_admin_page_render_thickbox() {
352
+ add_thickbox();
353
+ ?>
354
+ <div id="cnb-add-new-apikey-modal" style="display:none;"><div>
355
+ <form action="<?php echo esc_url( admin_url('admin-post.php') ); ?>" method="post">
356
+ <input type="hidden" name="page" value="call-now-button-apikeys" />
357
+ <input type="hidden" name="action" value="cnb_create_apikey" />
358
+ <input type="hidden" name="_wpnonce" value="<?php echo wp_create_nonce('cnb_create_apikey')?>" />
359
+
360
+ <label>Name
361
+ <input type="text" name="apikey[name]" />
362
+ </label>
363
+ <?php submit_button(); ?>
364
+ </div></div>
365
+ <?php }
src/admin/button-edit.php ADDED
@@ -0,0 +1,569 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname( __FILE__ ) . '/api/CnbAppRemote.php';
4
+ require_once dirname( __FILE__ ) . '/api/CnbAdminCloud.php';
5
+ require_once dirname( __FILE__ ) . '/partials/admin-functions.php';
6
+ require_once dirname( __FILE__ ) . '/partials/admin-header.php';
7
+ require_once dirname( __FILE__ ) . '/partials/admin-footer.php';
8
+ require_once dirname( __FILE__ ) . '/../utils/utils.php';
9
+ require_once dirname( __FILE__ ) . '/action-overview.php';
10
+ require_once dirname( __FILE__ ) . '/action-edit.php';
11
+
12
+ /**
13
+ * Renders the "Edit <type>" header
14
+ *
15
+ * @param object $button (optional) Used to determine type if available
16
+ */
17
+ function cnb_add_header_button_edit($button = null) {
18
+ $type = strtoupper(filter_input(INPUT_GET, 'type', FILTER_SANITIZE_STRING));
19
+ $name = 'New Button';
20
+ if ($button) {
21
+ $type = $button->type;
22
+ $name = $button->name;
23
+ }
24
+ $buttonTypes = cnb_get_button_types();
25
+ $typeName = $buttonTypes[$type];
26
+ echo 'Edit ' . esc_html($typeName) . ': "' . esc_html($name) . '"';
27
+ }
28
+
29
+ function cnb_create_tab_url($button, $tab) {
30
+ $url = admin_url('admin.php');
31
+ $tab_link =
32
+ add_query_arg(
33
+ array(
34
+ 'page' => 'call-now-button',
35
+ 'action' => 'edit',
36
+ 'type' => strtolower($button->type),
37
+ 'id' => $button->id,
38
+ 'tab' => $tab),
39
+ $url );
40
+ return esc_url( $tab_link );
41
+ }
42
+
43
+ /**
44
+ * This is called to update the button
45
+ * via `call-now-button.php#cnb_create_<type>_button`
46
+ */
47
+ function cnb_admin_create_button() {
48
+ global $cnb_slug_base;
49
+ $nonce = filter_input( INPUT_POST, '_wpnonce_button', FILTER_SANITIZE_STRING );
50
+ if( isset( $_REQUEST['_wpnonce_button'] ) && wp_verify_nonce( $nonce, 'cnb-button-edit') ) {
51
+
52
+ // sanitize the input
53
+ $button = filter_input(
54
+ INPUT_POST,
55
+ 'cnb',
56
+ FILTER_SANITIZE_STRING,
57
+ FILTER_REQUIRE_ARRAY | FILTER_FLAG_NO_ENCODE_QUOTES);
58
+
59
+
60
+ // ensure the position is valid for FULL
61
+ if (strtoupper($button['type']) === 'FULL') {
62
+ $placement = $button['options']['placement'];
63
+ if ($placement !== 'BOTTOM_CENTER' || $placement !== 'TOP_CENTER') {
64
+ $button['options']['placement'] = 'BOTTOM_CENTER';
65
+ }
66
+ }
67
+
68
+ // Do the processing
69
+ $cnb_cloud_notifications = array();
70
+ $new_button = CnbAdminCloud::cnb_create_button( $cnb_cloud_notifications, $button );
71
+
72
+ // redirect the user to the appropriate page
73
+ $tab = filter_input( INPUT_POST, 'tab', FILTER_SANITIZE_STRING );
74
+ $transient_id = 'cnb-' . wp_generate_uuid4();
75
+ set_transient($transient_id, $cnb_cloud_notifications, HOUR_IN_SECONDS);
76
+
77
+ // Create link
78
+ $url = admin_url('admin.php');
79
+ $redirect_link =
80
+ add_query_arg(
81
+ array(
82
+ 'page' => 'call-now-button',
83
+ 'action' => 'edit',
84
+ 'type' => strtolower($new_button->type),
85
+ 'id' => $new_button->id,
86
+ 'tid' => $transient_id,
87
+ 'tab' => $tab),
88
+ $url );
89
+ $redirect_url = esc_url_raw( $redirect_link );
90
+ wp_safe_redirect($redirect_url);
91
+ exit;
92
+ }
93
+ else {
94
+ wp_die( __( 'Invalid nonce specified', CNB_NAME), __( 'Error', CNB_NAME), array(
95
+ 'response' => 403,
96
+ 'back_link' => 'admin.php?page=' . $cnb_slug_base,
97
+ ) );
98
+ }
99
+ }
100
+
101
+ /**
102
+ * This is called to update the button
103
+ * via `call-now-button.php#cnb_update_<type>_button`
104
+ */
105
+ function cnb_admin_update_button() {
106
+ global $cnb_slug_base;
107
+ $nonce = filter_input( INPUT_POST, '_wpnonce_button', FILTER_SANITIZE_STRING );
108
+ if( isset( $_REQUEST['_wpnonce_button'] ) && wp_verify_nonce( $nonce, 'cnb-button-edit') ) {
109
+
110
+ // sanitize the input
111
+ $button = filter_input(
112
+ INPUT_POST,
113
+ 'cnb',
114
+ FILTER_SANITIZE_STRING,
115
+ FILTER_REQUIRE_ARRAY | FILTER_FLAG_NO_ENCODE_QUOTES);
116
+ $actions = filter_input(
117
+ INPUT_POST,
118
+ 'actions',
119
+ FILTER_SANITIZE_STRING,
120
+ FILTER_REQUIRE_ARRAY | FILTER_FLAG_NO_ENCODE_QUOTES);
121
+ $conditions = filter_input(
122
+ INPUT_POST,
123
+ 'condition',
124
+ FILTER_SANITIZE_STRING,
125
+ FILTER_REQUIRE_ARRAY | FILTER_FLAG_NO_ENCODE_QUOTES);
126
+
127
+ if ($conditions === null) {
128
+ $conditions = array();
129
+ }
130
+
131
+ // ensure the position is valid for FULL
132
+ if (strtoupper($button['type']) === 'FULL') {
133
+ $placement = $button['options']['placement'];
134
+ if ($placement !== 'BOTTOM_CENTER' || $placement !== 'TOP_CENTER') {
135
+ $button['options']['placement'] = 'BOTTOM_CENTER';
136
+ }
137
+ }
138
+
139
+ // do the processing
140
+ $processed_actions = array();
141
+ foreach($actions as $action) {
142
+ $processed_actions[] = cnb_admin_process_action( $action );
143
+ }
144
+ $result = CnbAdminCloud::cnb_update_button_and_conditions( $button, $processed_actions, $conditions );
145
+
146
+ // redirect the user to the appropriate page
147
+ $tab = filter_input( INPUT_POST, 'tab', FILTER_SANITIZE_STRING );
148
+ $transient_id = 'cnb-' . wp_generate_uuid4();
149
+ set_transient($transient_id, $result, HOUR_IN_SECONDS);
150
+
151
+ // Create link
152
+ $url = admin_url('admin.php');
153
+ $redirect_link =
154
+ add_query_arg(
155
+ array(
156
+ 'page' => 'call-now-button',
157
+ 'action' => 'edit',
158
+ 'type' => strtolower($button['type']),
159
+ 'id' => $button['id'],
160
+ 'tid' => $transient_id,
161
+ 'tab' => $tab),
162
+ $url );
163
+ $redirect_url = esc_url_raw( $redirect_link );
164
+ wp_safe_redirect($redirect_url);
165
+ exit;
166
+ }
167
+ else {
168
+ wp_die( __( 'Invalid nonce specified', CNB_NAME), __( 'Error', CNB_NAME), array(
169
+ 'response' => 403,
170
+ 'back_link' => 'admin.php?page=' . $cnb_slug_base,
171
+ ) );
172
+ }
173
+ }
174
+
175
+ function cnb_button_edit_form($button_id, $button, $default_domain, $options=array()) {
176
+ $domains = CnbAppRemote::cnb_remote_get_domains();
177
+
178
+ $cnb_single_image = esc_url(plugins_url( '../../resources/images/button-new-single.png', __FILE__ ));
179
+ $cnb_multi_image = esc_url(plugins_url( '../../resources/images/button-new-multi.png', __FILE__ ));
180
+ $cnb_full_image = esc_url(plugins_url( '../../resources/images/button-new-full.png', __FILE__ ));
181
+
182
+ $show_submit_button = true;
183
+ $submit_button_text = array_key_exists('submit_button_text', $options) ? $options['submit_button_text'] : '';
184
+ $hide_on_modal = array_key_exists('modal_view', $options) && $options['modal_view'] === true;
185
+ $show_advanced_view_only = array_key_exists('advanced_view', $options) && $options['advanced_view'] === 1;
186
+ if($hide_on_modal) {
187
+ echo '<script type="text/javascript">cnb_hide_on_modal_set=1</script>';
188
+ }
189
+ if ($show_advanced_view_only) {
190
+ echo '<script type="text/javascript">show_advanced_view_only_set=1</script>';
191
+ }
192
+
193
+ // Create "add Action" link WITH Button association
194
+ $url = admin_url('admin.php');
195
+ $new_action_link =
196
+ add_query_arg(
197
+ array(
198
+ 'page' => 'call-now-button-actions',
199
+ 'action' => 'new',
200
+ 'id' => 'new',
201
+ 'tab' => 'actions',
202
+ 'bid' => $button->id),
203
+ $url);
204
+ $new_action_url = esc_url($new_action_link);
205
+
206
+ // In case the API isn't working properly
207
+ if ($default_domain instanceof WP_Error) {
208
+ $default_domain = array();
209
+ $default_domain['id'] = 0;
210
+ }
211
+
212
+ ?>
213
+ <form action="<?php echo esc_url( admin_url('admin-post.php') ); ?>" method="post" class="cnb-container">
214
+ <input type="hidden" name="page" value="call-now-button" />
215
+ <input type="hidden" name="action" value="<?php echo $button_id === 'new' ? 'cnb_create_'.strtolower($button->type).'_button' :'cnb_update_'.esc_attr(strtolower($button->type)).'_button' ?>" />
216
+ <input type="hidden" name="_wpnonce_button" value="<?php echo wp_create_nonce('cnb-button-edit')?>" />
217
+ <input type="hidden" name="tab" value="<?php esc_attr_e(cnb_get_active_tab_name()) ?>" />
218
+
219
+ <input type="hidden" name="cnb[id]" value="<?php esc_attr_e($button->id) ?>" />
220
+ <input type="hidden" name="cnb[type]" value="<?php esc_attr_e($button->type) ?>" id="cnb_type" />
221
+ <input type="hidden" name="cnb[active]" value="<?php esc_attr_e($button->active) ?>" />
222
+ <input type="hidden" name="cnb[domain]" value="<?php esc_attr_e($default_domain->id) ?>" />
223
+ <?php
224
+ // Show all the current actions (needed to submit the form)
225
+ foreach($button->actions as $action) { ?>
226
+ <input type="hidden" name="actions[<?php esc_attr_e($action->id) ?>][id]" value="<?php esc_attr_e($action->id) ?>" />
227
+ <?php } ?>
228
+
229
+ <div class="cnb-button-name-field <?php if(!$hide_on_modal) { echo cnb_is_active_tab('basic_options'); } else { echo 'nav-tab-only'; } ?>">
230
+ <label for="cnb[name]"><input type="text" name="cnb[name]" id="cnb[name]" class="large-text" placeholder="Name your button" required="required" value="<?php esc_attr_e($button->name); ?>" /></label>
231
+ </div>
232
+
233
+ <table class="form-table <?php if(!$hide_on_modal) { echo cnb_is_active_tab('basic_options'); } else { echo 'nav-tab-only'; } ?>">
234
+ <tr class="cnb_hide_on_modal">
235
+ <th scope="row">Button status:</th>
236
+
237
+ <td class="activated">
238
+ <div class="cnb-radio-item">
239
+ <input id="cnb-disable" type="radio" name="cnb[active]" value="0" <?php checked(false, $button->active); ?> />
240
+ <label for="cnb-disable">Disabled</label>
241
+ </div>
242
+ <div class="cnb-radio-item">
243
+ <input id="cnb-enable" type="radio" name="cnb[active]" value="1" <?php checked(true, $button->active); ?> />
244
+ <label for="cnb-enable">Enabled</label>
245
+ </div>
246
+ </td>
247
+ </tr>
248
+ <tr class="cnb_hide_on_modal cnb_advanced_view">
249
+ <th scope="row"><label for="cnb[domain]">Domain:</label></th>
250
+ <td>
251
+ <select name="cnb[domain]" id="cnb[domain]">
252
+ <?php
253
+ foreach ($domains as $domain) { ?>
254
+ <option value="<?php esc_attr_e($domain->id) ?>"<?php selected($domain->id, $button->domain->id) ?>>
255
+ <?php esc_html_e($domain->name) ?>
256
+ <?php if ($domain->id == $default_domain->id) { echo ' (current Wordpress domain)'; } ?>
257
+ </option>
258
+ <?php } ?>
259
+ </select>
260
+ </td>
261
+ </tr>
262
+ <?php if ($button->type !== 'SINGLE') { ?>
263
+ <tr class="cnb_hide_on_modal">
264
+ <th colspan="2" class="cnb_padding_0">
265
+ <h2 >Actions <?php echo '<a href="' . $new_action_url . '" class="page-title-action">Add Action</a>'; ?>
266
+ <a href="https://help.callnowbutton.com/portal/en/kb/articles/adding-actions-to-a-multi-button-or-buttonbar" target="_blank" class="cnb-nounderscore">
267
+ <span class="dashicons dashicons-editor-help"></span>
268
+ </a></h2>
269
+ </th>
270
+ </tr>
271
+ <?php } ?>
272
+ <?php
273
+ $is_active_tab_basic_options = cnb_is_active_tab('basic_options');
274
+ if ($button->type === 'SINGLE') {
275
+ if (!empty($is_active_tab_basic_options)) {
276
+ // Create a dummy Action
277
+ $action = new stdClass();
278
+ $action->id = 'new';
279
+ $action->actionType = '';
280
+ $action->actionValue = '';
281
+ $action->labelText = '';
282
+ $action->properties = new stdClass();
283
+
284
+ if (sizeof($button->actions) > 0) {
285
+ $action = $button->actions[0];
286
+ }
287
+ cnb_admin_page_action_edit_render_main($action, $button, false);
288
+ }
289
+ } else {
290
+ // Only render the Actions table if that is the active tab (otherwise it's a pretty expensive operation)
291
+ if (!empty($is_active_tab_basic_options)) {
292
+ ?></table>
293
+ <div class="cnb-button-edit-action-table <?php if(!$hide_on_modal) { echo cnb_is_active_tab('basic_options'); } else { echo 'nav-tab-only'; } ?>">
294
+ <?php cnb_admin_page_action_overview_render_form(array('button' => $button));
295
+ ?></div>
296
+ <table class="form-table <?php if(!$hide_on_modal) { echo cnb_is_active_tab('basic_options'); } else { echo 'nav-tab-only'; } ?>"><?php
297
+ }
298
+ } ?>
299
+ <?php if ($button_id === 'new') { ?>
300
+ <tr>
301
+ <th scope="row">Type</th>
302
+ <td></td>
303
+ </tr>
304
+ <tr>
305
+ <th scope="row" colspan="2" class="cnb_type_selector">
306
+ <div style="width:33.3%;float:left;text-align: center;<?php if ($button->type !== 'SINGLE') { ?>opacity: 0.25;<?php } ?>" class="cnb_type_selector_single"
307
+ onclick="
308
+ jQuery('#cnb_type').val('single');
309
+ jQuery('.cnb_type_selector_multi').animate({opacity: 0.25});
310
+ jQuery('.cnb_type_selector_full').animate({opacity: 0.25});
311
+ jQuery(this).animate({opacity: 1});"
312
+ >
313
+ <img style="max-width:100%;" alt="Choose a Single button type" src="<?php echo $cnb_single_image ?>">
314
+ </div>
315
+ <div style="width:33.3%;float:left;text-align: center;<?php if ($button->type !== 'MULTI') { ?>opacity: 0.25;<?php } ?>" class="cnb_type_selector_multi"
316
+ onclick="
317
+ jQuery('#cnb_type').val('multi');
318
+ jQuery('.cnb_type_selector_single').animate({opacity: 0.25});
319
+ jQuery('.cnb_type_selector_full').animate({opacity: 0.25});
320
+ jQuery(this).animate({opacity: 1});"
321
+ >
322
+ <img style="max-width:100%;" alt="Choose a Multi button type" src="<?php echo $cnb_multi_image ?>">
323
+ </div>
324
+ <div style="width:33.3%;float:left;text-align: center;<?php if ($button->type !== 'FULL') { ?>opacity: 0.25;<?php } ?>" class="cnb_type_selector_full"
325
+ onclick="
326
+ jQuery('#cnb_type').val('full');
327
+ jQuery('.cnb_type_selector_single').animate({opacity: 0.25});
328
+ jQuery('.cnb_type_selector_multi').animate({opacity: 0.25});
329
+ jQuery(this).animate({opacity: 1});"
330
+ >
331
+ <img style="max-width:100%;" alt="Choose a Full button type" src="<?php echo $cnb_full_image ?>">
332
+ </div>
333
+ </th>
334
+ </tr>
335
+ <?php } ?>
336
+ </table>
337
+ <table class="form-table <?php echo cnb_is_active_tab('extra_options') ?>">
338
+ <?php if ($button->type === 'FULL') { ?>
339
+ <tr>
340
+ <th colspan="2">
341
+ <h2>Colors for the Button bar are defined via the Actions.</h2>
342
+ <input name="cnb[options][iconBackgroundColor]" type="hidden" value="<?php esc_attr_e($button->options->iconBackgroundColor); ?>" />
343
+ <input name="cnb[options][iconColor]" type="hidden" value="<?php esc_attr_e($button->options->iconColor); ?>" />
344
+ </th>
345
+ </tr>
346
+ <?php } else { ?>
347
+ <tr class="cnb_hide_on_modal">
348
+ <th></th>
349
+ <td></td>
350
+ </tr>
351
+ <tr>
352
+ <th scope="row"><label for="cnb[options][iconBackgroundColor]">Background color:</label></th>
353
+ <td>
354
+ <input name="cnb[options][iconBackgroundColor]" id="cnb[options][iconBackgroundColor]" type="text" value="<?php esc_attr_e($button->options->iconBackgroundColor); ?>"
355
+ class="cnb-iconcolor-field" data-default-color="#009900"/>
356
+ <?php if ($button->type === 'MULTI') { ?>
357
+ <p class="description"><span class="dashicons dashicons-info"></span>This color applies to the collapsable button only.</p>
358
+ <?php } ?>
359
+ </td>
360
+ </tr>
361
+ <tr>
362
+ <th scope="row"><label for="cnb[options][iconColor]">Icon color:</label></th>
363
+ <td>
364
+ <input name="cnb[options][iconColor]" id="cnb[options][iconColor]" type="text" value="<?php esc_attr_e($button->options->iconColor); ?>"
365
+ class="cnb-iconcolor-field" data-default-color="#FFFFFF"/>
366
+ <?php if ($button->type === 'MULTI') { ?>
367
+ <p class="description"><span class="dashicons dashicons-info"></span>This color applies to the collapsable button only.</p>
368
+ <?php } ?>
369
+ </td>
370
+ </tr>
371
+ <?php } ?>
372
+ <tr>
373
+ <th scope="row">Position: <a
374
+ href="<?php echo CNB_SUPPORT; ?>button-position/<?php cnb_utm_params("question-mark", "button-position"); ?>"
375
+ target="_blank" class="cnb-nounderscore">
376
+ <span class="dashicons dashicons-editor-help"></span>
377
+ </a></th>
378
+ <td class="appearance">
379
+ <div class="appearance-options">
380
+ <?php if ($button->type === 'FULL') { ?>
381
+ <div class="cnb-radio-item">
382
+ <input type="radio" id="appearance1" name="cnb[options][placement]"
383
+ value="TOP_CENTER" <?php checked('TOP_CENTER', $button->options->placement); ?>>
384
+ <label title="top-center" for="appearance1">Top</label>
385
+ </div>
386
+ <div class="cnb-radio-item">
387
+ <input type="radio" id="appearance2" name="cnb[options][placement]"
388
+ value="BOTTOM_CENTER" <?php checked('BOTTOM_CENTER', $button->options->placement); ?>>
389
+ <label title="bottom-center" for="appearance2">Bottom</label>
390
+ </div>
391
+ <?php } else { ?>
392
+ <div class="cnb-radio-item">
393
+ <input type="radio" id="appearance1" name="cnb[options][placement]"
394
+ value="BOTTOM_RIGHT" <?php checked('BOTTOM_RIGHT', $button->options->placement); ?>>
395
+ <label title="bottom-right" for="appearance1">Right corner</label>
396
+ </div>
397
+ <div class="cnb-radio-item">
398
+ <input type="radio" id="appearance2" name="cnb[options][placement]"
399
+ value="BOTTOM_LEFT" <?php checked('BOTTOM_LEFT', $button->options->placement); ?>>
400
+ <label title="bottom-left" for="appearance2">Left corner</label>
401
+ </div>
402
+ <div class="cnb-radio-item">
403
+ <input type="radio" id="appearance3" name="cnb[options][placement]"
404
+ value="BOTTOM_CENTER" <?php checked('BOTTOM_CENTER', $button->options->placement); ?>>
405
+ <label title="bottom-center" for="appearance3">Center bottom</label>
406
+ </div>
407
+
408
+ <!-- Extra placement options -->
409
+ <br class="cnb-extra-placement">
410
+ <div class="cnb-radio-item cnb-extra-placement <?php echo $button->options->placement == "MIDDLE_RIGHT" ? "cnb-extra-active" : ""; ?>">
411
+ <input type="radio" id="appearance5" name="cnb[options][placement]"
412
+ value="MIDDLE_RIGHT" <?php checked('MIDDLE_RIGHT', $button->options->placement); ?>>
413
+ <label title="middle-right" for="appearance5">Middle right</label>
414
+ </div>
415
+ <div class="cnb-radio-item cnb-extra-placement <?php echo $button->options->placement == "MIDDLE_LEFT" ? "cnb-extra-active" : ""; ?>">
416
+ <input type="radio" id="appearance6" name="cnb[options][placement]"
417
+ value="MIDDLE_LEFT" <?php checked('MIDDLE_LEFT', $button->options->placement); ?>>
418
+ <label title="middle-left" for="appearance6">Middle left </label>
419
+ </div>
420
+ <br class="cnb-extra-placement">
421
+ <div class="cnb-radio-item cnb-extra-placement <?php echo $button->options->placement == "TOP_RIGHT" ? "cnb-extra-active" : ""; ?>">
422
+ <input type="radio" id="appearance7" name="cnb[options][placement]"
423
+ value="TOP_RIGHT" <?php checked('TOP_RIGHT', $button->options->placement); ?>>
424
+ <label title="top-right" for="appearance7">Top right corner</label>
425
+ </div>
426
+ <div class="cnb-radio-item cnb-extra-placement <?php echo $button->options->placement == "TOP_LEFT" ? "cnb-extra-active" : ""; ?>">
427
+ <input type="radio" id="appearance8" name="cnb[options][placement]"
428
+ value="TOP_LEFT" <?php checked('TOP_LEFT', $button->options->placement); ?>>
429
+ <label title="top-left" for="appearance8">Top left corner</label>
430
+ </div>
431
+ <div class="cnb-radio-item cnb-extra-placement <?php echo $button->options->placement == "TOP_CENTER" ? "cnb-extra-active" : ""; ?>">
432
+ <input type="radio" id="appearance9" name="cnb[options][placement]"
433
+ value="TOP_CENTER" <?php checked('TOP_CENTER', $button->options->placement); ?>>
434
+ <label title="top-center" for="appearance9">Center top</label>
435
+ </div>
436
+ <a href="#" id="cnb-more-placements">More placement options...</a>
437
+ <!-- END extra placement options -->
438
+ <?php } ?>
439
+ </div>
440
+ </td>
441
+ </tr>
442
+ </table>
443
+ <table class="form-table <?php echo cnb_is_active_tab('visibility') ?>">
444
+ <tbody id="cnb_form_table_visibility">
445
+ <tr>
446
+ <th scope="row"><h2>Conditions <input type="button" onclick="return cnb_add_condition();" value="Add New" class="button button-secondary page-title-action"></h2>
447
+ </tr>
448
+ <?php if (empty($button->conditions)) { ?>
449
+ <tr>
450
+ <td colspan="2">
451
+ <p>You have no Conditions yet.</p>
452
+ <p>Conditions allow you to show/hide the Button on page URLs you specify.</p>
453
+ <p>Click <code>Add Condition</code> below to create your first Condition.</p>
454
+ </td>
455
+ </tr>
456
+ <?php } else { ?>
457
+ <?php foreach ($button->conditions as $condition) { ?>
458
+ <tr class="appearance" id="cnb_condition_<?php esc_attr_e($condition->id) ?>">
459
+ <th scope="row"><label for="condition[<?php esc_attr_e($condition->id) ?>][filterType]">Condition:<div class="cnb_font_normal cnb_font_90">ID: <code class="cnb_font_90"><?php esc_html_e($condition->id) ?></code></div></label></th>
460
+ <td>
461
+ <input type="hidden" name="condition[<?php esc_attr_e($condition->id) ?>][id]" value="<?php esc_attr_e($condition->id) ?>" />
462
+ <input type="hidden" name="condition[<?php esc_attr_e($condition->id) ?>][conditionType]" value="<?php esc_attr_e($condition->conditionType) ?>" />
463
+ <input type="hidden" name="condition[<?php esc_attr_e($condition->id) ?>][delete]" id="cnb_condition_<?php esc_attr_e($condition->id) ?>_delete" value="" />
464
+ <select name="condition[<?php esc_attr_e($condition->id) ?>][filterType]" id="condition[<?php esc_attr_e($condition->id) ?>][filterType]">
465
+ <option value="INCLUDE"<?php selected('INCLUDE', $condition->filterType) ?>>Include</option>
466
+ <option value="EXCLUDE"<?php selected('EXCLUDE', $condition->filterType) ?>>Exclude</option>
467
+ </select><br />
468
+
469
+ <label>
470
+ <select name="condition[<?php esc_attr_e($condition->id) ?>][matchType]">
471
+ <?php foreach (cnb_get_condition_match_types() as $condition_match_type_key => $condition_match_type_value) { ?>
472
+ <option value="<?php esc_attr_e($condition_match_type_key) ?>"<?php selected($condition_match_type_key, $condition->matchType) ?>>
473
+ <?php esc_html_e($condition_match_type_value) ?>
474
+ </option>
475
+ <?php } ?>
476
+ </select>
477
+ </label><br />
478
+
479
+ <label>
480
+ <input type="text" name="condition[<?php esc_attr_e($condition->id) ?>][matchValue]" value="<?php esc_attr_e($condition->matchValue); ?>"/>
481
+ </label>
482
+
483
+ <?php // Match old "Hide button on front page"
484
+ if ($condition->conditionType === 'URL' && $condition->filterType === 'EXCLUDE' && $condition->matchType === 'EXACT' && $condition->matchValue === get_home_url()) { ?>
485
+ <p class="description"><span class="dashicons dashicons-info"></span> This condition matches the legacy "<strong>Hide button on front page</strong>" checkbox.</p>
486
+ <?php } else { ?>
487
+ <br />
488
+ <?php }?>
489
+
490
+ <input type="button" onclick="return cnb_remove_condition('<?php echo esc_js($condition->id) ?>');" value="Remove Condition" class="button-link button-link-delete">
491
+ </td>
492
+ </tr>
493
+ <?php } } ?>
494
+ <tr id="cnb_form_table_add_condition">
495
+ <th></th>
496
+ <td>
497
+
498
+ </td>
499
+ </tr>
500
+ </tbody>
501
+ </table>
502
+
503
+ <input type="hidden" name="cnb[version]" value="<?php echo CNB_VERSION; ?>"/>
504
+ <?php if ($show_submit_button) {submit_button($submit_button_text);} ?>
505
+ </form>
506
+ <?php
507
+ }
508
+
509
+ function cnb_admin_page_edit_render() {
510
+ global $cnb_options, $cnb_settings;
511
+
512
+ $cnb_notices = cnb_get_notices();
513
+ $cnb_changelog = cnb_get_changelog();
514
+
515
+ $button_id = cnb_get_button_id();
516
+ $button = new stdClass();
517
+
518
+ // Get the various supported domains
519
+ $default_domain = CnbAppRemote::cnb_remote_get_wp_domain();
520
+
521
+ if (strlen($button_id) > 0 && $button_id !== 'new') {
522
+ $button = CnbAppRemote::cnb_remote_get_button_full( $button_id );
523
+ } elseif ($button_id === 'new') {
524
+ $button->type = strtoupper(filter_input(INPUT_GET, 'type', FILTER_SANITIZE_STRING));
525
+ $button->domain = $default_domain;
526
+ }
527
+ if ($button->actions === null) {
528
+ $button->actions = array();
529
+ }
530
+
531
+ // Set some sane defaults
532
+ if (!isset($button->options)) $button->options = new stdClass();
533
+ $button->options->iconBackgroundColor = !empty($button->options->iconBackgroundColor)
534
+ ? $button->options->iconBackgroundColor
535
+ : '#009900';
536
+ $button->options->iconColor = !empty($button->options->iconColor)
537
+ ? $button->options->iconColor
538
+ : '#FFFFFF';
539
+ $button->options->placement = !empty($button->options->placement)
540
+ ? $button->options->placement
541
+ : ($button->type === 'FULL' ? 'BOTTOM_CENTER' : 'BOTTOM_RIGHT');
542
+
543
+ // Create options
544
+ $options = array();
545
+ $options['advanced_view'] = $cnb_options['advanced_view'];
546
+
547
+ add_action('cnb_header', function() use($button) {
548
+ cnb_add_header_button_edit($button);
549
+ });
550
+ ?>
551
+
552
+ <div class="wrap">
553
+ <?php cnb_admin_header($cnb_options, $cnb_settings, $cnb_notices, $cnb_changelog) ?>
554
+ <h2 class="nav-tab-wrapper">
555
+ <a href="<?php echo cnb_create_tab_url($button, 'basic_options') ?>"
556
+ class="nav-tab <?php echo cnb_is_active_tab('basic_options') ?>">Basics</a>
557
+ <?php if ($button_id !== 'new') { ?>
558
+ <a href="<?php echo cnb_create_tab_url($button, 'extra_options') ?>"
559
+ class="nav-tab <?php echo cnb_is_active_tab('extra_options') ?>">Presentation</a>
560
+ <a href="<?php echo cnb_create_tab_url($button, 'visibility') ?>"
561
+ class="nav-tab <?php echo cnb_is_active_tab('visibility') ?>">Visibility</a>
562
+ <?php } else { ?>
563
+ <a class="nav-tab"><i>Additional options available after saving</i></a>
564
+ <?php } ?>
565
+ </h2>
566
+ <?php cnb_button_edit_form($button_id, $button, $default_domain, $options) ?>
567
+ <?php cnb_admin_footer() ?>
568
+ </div>
569
+ <?php }
src/admin/button-overview.php ADDED
@@ -0,0 +1,589 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname( __FILE__ ) . '/api/CnbAppRemote.php';
4
+ require_once dirname( __FILE__ ) . '/api/CnbAdminCloud.php';
5
+ require_once dirname( __FILE__ ) . '/partials/admin-functions.php';
6
+ require_once dirname( __FILE__ ) . '/partials/admin-header.php';
7
+ require_once dirname( __FILE__ ) . '/partials/admin-footer.php';
8
+ require_once dirname( __FILE__ ) . '/../utils/utils.php';
9
+ require_once dirname( __FILE__ ) . '/button-edit.php';
10
+
11
+ function cnb_add_header_button_overview() {
12
+ echo 'Buttons ';
13
+ }
14
+
15
+ function cnb_add_new_modal_action() {
16
+ $url = admin_url('admin.php');
17
+ $new_link =
18
+ add_query_arg(
19
+ array(
20
+ 'TB_inline' => 'true',
21
+ 'inlineId' => 'cnb-add-new-modal',
22
+ 'height' => '440', // 405 is ideal -> To hide the scrollbar
23
+ 'page' => 'call-now-button',
24
+ 'action' => 'new',
25
+ 'type' => 'single',
26
+ 'id' => 'new' ),
27
+ $url );
28
+ printf(
29
+ '<a href="%s" title="%s" class="thickbox open-plugin-details-modal page-title-action" data-title="%s">%s</a>',
30
+ $new_link,
31
+ __('Create new button', CNB_NAME),
32
+ __('Choose a Button type', CNB_NAME),
33
+ __('Add New', CNB_NAME)
34
+ );
35
+ }
36
+
37
+ if(!class_exists('WP_List_Table')) {
38
+ require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
39
+ }
40
+
41
+ class Cnb_Button_List_Table extends WP_List_Table {
42
+
43
+ private $data;
44
+ /**
45
+ * Constructor, we override the parent to pass our own arguments
46
+ * We usually focus on three parameters: singular and plural labels, as well as whether the class supports AJAX.
47
+ */
48
+ function __construct() {
49
+ parent::__construct(array(
50
+ 'singular' => 'cnb_list_button', //Singular label
51
+ 'plural' => 'cnb_list_buttons', //plural label, also this well be one of the table css class
52
+ 'ajax' => false //We won't support Ajax for this table
53
+ ));
54
+ }
55
+
56
+ /**
57
+ * Define the columns that are going to be used in the table
58
+ * @return array $columns, the array of columns to use with the table
59
+ */
60
+ function get_columns() {
61
+ return array(
62
+ 'cb' => '<input type="checkbox">',
63
+ 'id' => __('ID'),
64
+ 'name' => __('Name'),
65
+ 'type' => __('Type'),
66
+ 'actions' => __('Actions'),
67
+ 'status' => __('Status')
68
+ );
69
+ }
70
+
71
+ function get_sortable_columns() {
72
+ return array(
73
+ 'name' => array('name', false),
74
+ 'type' => array('type', false),
75
+ 'title' => array('title', false),
76
+ 'status' => array('status', false),
77
+ );
78
+ }
79
+
80
+ function get_hidden_columns() {
81
+ return array('id');
82
+ }
83
+
84
+ function get_views() {
85
+ // Let's count
86
+ $data = $this->get_data();
87
+
88
+ // In case of error (CNB not setup yet), return an empty view
89
+ if ($data instanceof WP_Error) return array();
90
+ $all_count = count($data);
91
+ $all_count_str = '<span class="count">('.$all_count.')</span>';
92
+ $active_count = count(array_filter($data, function($el) { return $el['status'] === true; }));
93
+ $active_count_str = '<span class="count">('.$active_count.')</span>';
94
+
95
+ // Let's build a link
96
+ $url = admin_url('admin.php');
97
+
98
+ // Which one is current?
99
+ $current_view_is_active = isset( $_REQUEST['view'] ) && $_REQUEST['view'] === 'active';
100
+ $all_link =
101
+ add_query_arg(
102
+ array( 'page' => 'call-now-button'),
103
+ $url );
104
+ $all_url = esc_url( $all_link );
105
+
106
+ $active_link =
107
+ add_query_arg(
108
+ array( 'page' => 'call-now-button', 'view' => 'active'),
109
+ $url );
110
+ $active_url = esc_url( $active_link );
111
+
112
+ return array(
113
+ 'all' => __("<a href='" . $all_url . "' " . (!$current_view_is_active ? "class='current'" : '') . ">All ".$all_count_str."</a>", CNB_NAME),
114
+ 'active' => __("<a href='" . $active_url . "' " . ($current_view_is_active ? "class='current'" : '') . "'>Active ".$active_count_str."</a>", CNB_NAME)
115
+ );
116
+ }
117
+ function prepare_items() {
118
+ // Process any Bulk actions before gathering data
119
+ $this->process_bulk_action();
120
+
121
+ /* -- Preparing your query -- */
122
+ $data = $this->get_data();
123
+
124
+ if ($data instanceof WP_Error) {
125
+ return $data;
126
+ }
127
+
128
+ /* -- Filtering parameters -- */
129
+ $current_view_is_active = isset( $_REQUEST['view'] ) && $_REQUEST['view'] === 'active';
130
+ if ($current_view_is_active) {
131
+ $data = array_filter($data, function($el) { return $el['status'] === true; });
132
+ }
133
+
134
+ /* -- Ordering parameters -- */
135
+ //Parameters that are going to be used to order the result
136
+ usort( $data, array( &$this, 'sort_data' ) );
137
+
138
+ /* -- Pagination parameters -- */
139
+ //Number of elements in your table?
140
+ $totalitems = count($data); //return the total number of affected rows
141
+ $per_page = 20; //How many to display per page?
142
+ //Which page is this?
143
+ $current_page = !empty($_GET['paged']) ? (int)sanitize_text_field($_GET['paged']) : 1;
144
+
145
+ //Page Number
146
+ if (empty($current_page) || !is_numeric($current_page) || $current_page <= 0) {
147
+ $current_page = 1;
148
+ }
149
+
150
+ //How many pages do we have in total?
151
+ $totalpages = ceil($totalitems / $per_page); //adjust the query to take pagination into account
152
+ if (!empty($current_page) && !empty($per_page)) {
153
+ $offset = ($current_page - 1) * $per_page;
154
+
155
+ /* -- Register the pagination -- */
156
+ $this->set_pagination_args(array(
157
+ 'total_items' => $totalitems,
158
+ 'total_pages' => $totalpages,
159
+ 'per_page' => $per_page,
160
+ ));
161
+ //The pagination links are automatically built according to those parameters
162
+
163
+ /* -- Register the Columns -- */
164
+ $columns = $this->get_columns();
165
+ $hidden_columns = $this->get_hidden_columns();
166
+ $sortable_columns = $this->get_sortable_columns();
167
+ $this->_column_headers = array($columns, $hidden_columns, $sortable_columns, 'name');
168
+
169
+ /* -- Register the items -- */
170
+ $data = array_slice($data,$offset,$per_page);
171
+ $this->items = $data;
172
+ }
173
+ return null;
174
+ }
175
+
176
+ function column_default( $item, $column_name )
177
+ {
178
+ switch( $column_name ) {
179
+ case 'id':
180
+ return '<code>' . esc_html($item[ $column_name ]) . '</code>';
181
+ case 'name':
182
+ case 'type':
183
+ switch ($item[ $column_name ]) {
184
+ case 'SINGLE':
185
+ case 'FULL':
186
+ case 'MULTI':
187
+ $button_types = cnb_get_button_types();
188
+ return $button_types[$item[ $column_name ]];
189
+ default:
190
+ return esc_html($item[$column_name]);
191
+ }
192
+ case 'actions':
193
+ // Moved to column_actions
194
+ case 'status':
195
+ return $item[ $column_name ] ? 'Active' : 'Inactive';
196
+
197
+ default:
198
+ return '<em>Unknown column ' .esc_html($column_name) . '</em>';
199
+ }
200
+ }
201
+
202
+ function column_actions( $item ) {
203
+ global $cnb_options;
204
+
205
+ $items = '';
206
+ $domain = '';
207
+ $actionMsg = '';
208
+ $count = 0;
209
+
210
+ // Action info
211
+ if ($item['actions']) {
212
+ $count = count( $item['actions'] );
213
+ }
214
+
215
+ if ($count === 0) {
216
+ $items .= '<em>No action yet</em>';
217
+ }
218
+
219
+ // Action detail
220
+ $actions = CnbAdminCloud::cnb_wp_get_actions_for_button( $item );
221
+ foreach ($actions as $action) {
222
+ $actionValue = !empty($action->actionValue) ? esc_html($action->actionValue) : '<em>No value</em>';
223
+ $actionTypes = cnb_get_action_types();
224
+ $actionType = $actionTypes[$action->actionType];
225
+ $actionMsg .= "$actionType ($actionValue)<br />";
226
+ }
227
+ $diff = $count - count($actions);
228
+ if ($diff > 0) {
229
+ $actionMsg .= "<em>Plus $diff more...</em><br />";
230
+ }
231
+
232
+ // Domain info
233
+ if ($cnb_options['advanced_view'] === 1) {
234
+ $domain = '<br />Domain: <code>' . esc_html($item['domain']->name) . '</code>';
235
+ }
236
+
237
+ return "$items$actionMsg$domain";
238
+ }
239
+
240
+ private function get_data() {
241
+ if ($this->data != null) return $this->data;
242
+ $buttons = CnbAppRemote::cnb_remote_get_buttons_full();
243
+
244
+ if ($buttons instanceof WP_Error) {
245
+ return $buttons;
246
+ }
247
+
248
+ $data = array();
249
+ foreach ($buttons as $button) {
250
+ $data[] = array(
251
+ 'id' => $button->id,
252
+ 'name' => $button->name,
253
+ 'type' => $button->type,
254
+ 'actions' => $button->actions,
255
+ 'status' => $button->active,
256
+ 'domain' => $button->domain
257
+ );
258
+ }
259
+ $this->data = $data;
260
+ return $data;
261
+ }
262
+
263
+
264
+ /**
265
+ * Allows you to sort the data by the variables set in the $_GET
266
+ *
267
+ * @return Mixed
268
+ */
269
+ private function sort_data( $a, $b ) {
270
+ // If orderby is set, use this as the sort column
271
+ $orderby = !empty($_GET['orderby']) ? sanitize_text_field($_GET['orderby']) : 'name';
272
+ // If order is set use this as the order
273
+ $order = !empty($_GET['order']) ? sanitize_text_field($_GET['order']) : 'asc';
274
+
275
+ $result = strcmp( $a[$orderby], $b[$orderby] );
276
+
277
+ // Flip Enabled/Disabled, since we actually consider "Enabled" to be before "Disabled"
278
+ if ($orderby === 'status') {
279
+ $result = -$result;
280
+ }
281
+
282
+ if($order === 'asc') {
283
+ return $result;
284
+ }
285
+ return -$result;
286
+ }
287
+
288
+ /**
289
+ * Custom action for `cb` columns (checkboxes)
290
+ *
291
+ * @param array|object $item
292
+ * @return string|void
293
+ */
294
+ function column_cb($item) {
295
+ return sprintf(
296
+ '<input type="checkbox" name="%1$s[]" value="%2$s" />',
297
+ $this->_args['singular'],
298
+ $item['id']
299
+ );
300
+ }
301
+
302
+ function column_name($item) {
303
+ // Let's build a link
304
+ $url = admin_url('admin.php');
305
+ $edit_link =
306
+ add_query_arg(
307
+ array( 'page' => 'call-now-button', 'action' => 'edit', 'type' => strtolower($item['type']), 'id' => $item['id'] ),
308
+ $url );
309
+ $edit_url = esc_url( $edit_link );
310
+
311
+ $actions = array(
312
+ 'edit' => '<a href="'.$edit_url.'">Edit</a>',
313
+ );
314
+ $enable_disable_link = wp_nonce_url(
315
+ add_query_arg(
316
+ array(
317
+ 'page' => 'call-now-button',
318
+ 'action' => $item['status'] == true ? 'cnb_disable_button' : 'cnb_enable_button',
319
+ 'id' => $item['id'] ),
320
+ $url ),
321
+ 'cnb_enable_disable_button' );
322
+ $enable_disable_url = esc_url( $enable_disable_link );
323
+
324
+ if ($item['status'] == true) {
325
+ $actions['disable'] = '<a href="'.$enable_disable_url.'">Disable</a>';
326
+ } else {
327
+ $actions['enable'] = '<a href="'.$enable_disable_url.'">Enable</a>';
328
+ }
329
+ $delete_link = wp_nonce_url(
330
+ add_query_arg( array(
331
+ 'page' => 'call-now-button',
332
+ 'action' => 'cnb_delete_button',
333
+ 'id' => $item['id'] ),
334
+ $url ),
335
+ 'cnb_delete_button' );
336
+ $delete_url = esc_url( $delete_link );
337
+ $actions['delete'] = '<a href="'.$delete_url.'">Delete</a>';
338
+
339
+ return sprintf(
340
+ '%1$s %2$s',
341
+ '<a href="'.$edit_url.'">'.esc_html($item['name']) . '</a>',
342
+ $this->row_actions($actions)
343
+ );
344
+ }
345
+
346
+ function get_bulk_actions() {
347
+ return array(
348
+ 'enable' => 'Enable',
349
+ 'disable' => 'Disable',
350
+ 'delete' => 'Delete',
351
+ );
352
+ }
353
+
354
+ function process_bulk_action() {
355
+ if ( isset( $_REQUEST['_wpnonce'] ) && ! empty( $_REQUEST['_wpnonce'] ) ) {
356
+ $nonce = filter_input(INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING);
357
+ $action = 'bulk-' . $this->_args['plural'];
358
+
359
+ if (wp_verify_nonce($nonce, $action)) {
360
+ $buttonIds = filter_input(INPUT_POST, 'cnb_list_button', FILTER_SANITIZE_STRING, FILTER_REQUIRE_ARRAY);
361
+ switch ($this->current_action()) {
362
+ case 'enable':
363
+ case 'disable':
364
+ foreach ($buttonIds as $buttonId) {
365
+ $button = array('id' => $buttonId, 'active' => $this->current_action() === 'enable');
366
+ CnbAppRemote::cnb_remote_update_button( $button );
367
+ }
368
+ echo '<div class="notice-success notice"><p>' . count($buttonIds) . ' Buttons updated</p></div>';
369
+ break;
370
+ case 'delete':
371
+ foreach ($buttonIds as $buttonId) {
372
+ $button = array('id' => $buttonId);
373
+ CnbAppRemote::cnb_remote_delete_button( $button );
374
+ }
375
+ echo '<div class="notice-success notice"><p>' . count($buttonIds) . ' Button(s) deleted</p></div>';
376
+ break;
377
+ }
378
+ }
379
+ }
380
+ }
381
+ }
382
+
383
+ /**
384
+ * Main entrypoint, used by `call-now-button.php`.
385
+ */
386
+ function cnb_admin_button_overview_render() {
387
+ $id = !empty($_GET['id']) ? sanitize_text_field($_GET['id']) : null;
388
+ $action = !empty($_GET['action']) ? sanitize_text_field($_GET['action']) : null;
389
+
390
+ if ($id === null) {
391
+ cnb_admin_page_overview_render_list();
392
+ } else {
393
+ switch ($action) {
394
+ case 'new':
395
+ case 'edit':
396
+ require_once dirname( __FILE__ ) . "/button-edit.php";
397
+ cnb_admin_page_edit_render();
398
+ break;
399
+ case 'cnb_enable_button':
400
+ case 'cnb_disable_button':
401
+ if ( isset( $_REQUEST['_wpnonce'] ) && ! empty( $_REQUEST['_wpnonce'] ) ) {
402
+ $nonce = filter_input( INPUT_GET, '_wpnonce', FILTER_SANITIZE_STRING );
403
+
404
+ if ( wp_verify_nonce( $nonce, 'cnb_enable_disable_button' ) ) {
405
+ $buttonId = filter_input( INPUT_GET, 'id', FILTER_SANITIZE_STRING );
406
+ $button = array( 'id' => $buttonId, 'active' => $action === 'cnb_enable_button' );
407
+ CnbAppRemote::cnb_remote_update_button( $button );
408
+ echo '<div class="notice-success notice is-dismissible"><p>Button <code>' . esc_html($buttonId) . '</code> updated</p></div>';
409
+ } else {
410
+ echo '<div class="notice-error notice"><p>Something went wrong</p></div>';
411
+ }
412
+ }
413
+ cnb_admin_page_overview_render_list();
414
+ break;
415
+ case 'cnb_delete_button':
416
+ if ( isset( $_REQUEST['_wpnonce'] ) && ! empty( $_REQUEST['_wpnonce'] ) ) {
417
+ $nonce = filter_input( INPUT_GET, '_wpnonce', FILTER_SANITIZE_STRING );
418
+
419
+ if ( wp_verify_nonce( $nonce, $action ) ) {
420
+ $cnb_cloud_notifications = array();
421
+ CnbAdminCloud::cnb_delete_button( $cnb_cloud_notifications, $id );
422
+ foreach ($cnb_cloud_notifications as $cnb_cloud_notification) {
423
+ echo $cnb_cloud_notification;
424
+ }
425
+ }
426
+ }
427
+ cnb_admin_page_overview_render_list();
428
+ break;
429
+ }
430
+ }
431
+ }
432
+
433
+ function cnb_admin_page_overview_render_list() {
434
+ global $cnb_options, $cnb_settings;
435
+
436
+ $cnb_notices = cnb_get_notices();
437
+ $cnb_changelog = cnb_get_changelog();
438
+
439
+ //Prepare Table of elements
440
+ $wp_list_table = new Cnb_Button_List_Table();
441
+ $data = $wp_list_table->prepare_items();
442
+
443
+ if ($data instanceof WP_Error) {
444
+ $cnb_notices[] = cnb_admin_header_get_cloud_error($data);
445
+ } else {
446
+ add_action('cnb_header', 'cnb_add_header_button_overview');
447
+ add_action('cnb_after_header', 'cnb_add_new_modal_action');
448
+ }
449
+
450
+ $cnb_cloud_domain = CnbAppRemote::cnb_remote_get_wp_domain();
451
+ if (!($cnb_cloud_domain instanceof WP_Error)) {
452
+ $url = admin_url('admin.php');
453
+ $upgrade_link =
454
+ add_query_arg(array(
455
+ 'page' => 'call-now-button-domains',
456
+ 'action' => 'upgrade',
457
+ 'id' => $cnb_cloud_domain->id
458
+ ),
459
+ $url);
460
+ $upgrade_url = esc_url($upgrade_link);
461
+ }
462
+ echo '<div class="wrap">';
463
+ cnb_admin_header($cnb_options, $cnb_settings, $cnb_notices, $cnb_changelog);
464
+
465
+ echo '<div id="poststuff">
466
+
467
+ <div id="post-body" class="metabox-holder columns-2">
468
+ <div id="post-body-content" style="position: relative;">';
469
+
470
+ // Check if we should warn about inactive buttons
471
+ $views = $wp_list_table->get_views();
472
+ $active_views = $views['active'];
473
+ if (false !== strpos($active_views, '(0)')) {
474
+ echo '<div class="notice-warning notice"><p><strong>You have no active buttons.</strong> Create or enable one (or more) buttons to use the Call Now Button.</p></div>';
475
+ }
476
+ $wp_list_table->views();
477
+
478
+ echo '<form id="wp_list_event" method="post">';
479
+
480
+ //Table of elements
481
+ $wp_list_table->display();
482
+ echo '</form></div>' ?>
483
+
484
+ <?php if (isset($upgrade_url)) { ?>
485
+ <div id="postbox-container-1" class="postbox-container"> <!-- Sidebar promo boxes -->
486
+ <?php if (!($cnb_cloud_domain instanceof WP_Error) && $cnb_cloud_domain->type === 'FREE') { ?>
487
+ <!-- Sidebar messages -->
488
+ <div id="cnb_upgrade_box" class="postbox "> <!-- Upgrade promobox -->
489
+ <div class="postbox-header">
490
+ <h2 class="hndle">Need more power?</h2>
491
+ </div>
492
+ <div class="inside">
493
+ <div class="submitbox" id="submitpost">
494
+ <div id="minor-publishing">
495
+ <div id="misc-publishing-actions">
496
+ <div class="cnb_promobox_item">Upgrade to add the following functionality:</div>
497
+ <div class="cnb_promobox_item cnb-side-icon cnb-side-checkbox">Unlimited buttons</div>
498
+ <div class="cnb_promobox_item cnb-side-icon cnb-side-checkbox">Email, Maps, URL, WhatsApp</div>
499
+ <div class="cnb_promobox_item cnb-side-icon cnb-side-checkbox">Button bar with multiple actions</div>
500
+ <div class="cnb_promobox_item cnb-side-icon cnb-side-checkbox">Expandable multi button</div>
501
+ <div class="cnb_promobox_item cnb-side-icon cnb-side-checkbox">Advanced page selection per button</div>
502
+ <div class="cnb_promobox_item cnb-side-icon cnb-side-checkbox">Button scheduling</div>
503
+ <div class="cnb_promobox_item cnb-side-icon cnb-side-checkbox">And much more!</div>
504
+ </div>
505
+ <div class="clear"></div>
506
+ </div>
507
+ <div id="major-publishing-actions">
508
+ <div id="publishing-action">
509
+ <a class="button button-primary button-large" href="<?php echo $upgrade_url ?>">Upgrade</a>
510
+ </div>
511
+ <div class="clear"></div>
512
+ </div>
513
+ </div>
514
+ </div>
515
+ </div>
516
+ <?php } ?>
517
+ <div id="cnb_support_box" class="postbox "> <!-- support promobox -->
518
+ <div class="postbox-header">
519
+ <h2 class="hndle">Need help?</h2>
520
+ </div>
521
+ <div class="inside">
522
+ <div class="submitbox" id="submitpost">
523
+ <div id="minor-publishing">
524
+ <div id="misc-publishing-actions">
525
+ <div class="cnb_promobox_item">
526
+ Please head over to our <a href="https://help.callnowbutton.com/" target="_blank">Help Center</a> for all your questions.
527
+ </div>
528
+ <div class="cnb_promobox_item cnb-side-icon cnb-side-checkbox"><a href="https://help.callnowbutton.com/" target="_blank">Help Center</a></div>
529
+ <div class="cnb_promobox_item cnb-side-icon cnb-side-checkbox"><a href="https://help.callnowbutton.com/" target="_blank">Support forum</a></div>
530
+ <div class="cnb_promobox_item cnb-side-icon cnb-side-checkbox"><a href="https://help.callnowbutton.com/" target="_blank">FAQ</a></div>
531
+ </div>
532
+ <div class="clear"></div>
533
+ </div>
534
+ </div>
535
+ </div>
536
+ </div>
537
+ </div><!-- end #postbox-container-1 -->
538
+ <?php } ?>
539
+
540
+
541
+
542
+
543
+ <br class="clear">
544
+ <?php
545
+ echo '</div><!--End #poststuff -->';
546
+ cnb_admin_footer();
547
+ echo '</div>';
548
+ cnb_admin_page_render_thickbox($cnb_cloud_domain);
549
+ }
550
+
551
+ function cnb_admin_page_render_thickbox($default_domain = null) {
552
+ add_thickbox();
553
+ echo '<div id="cnb-add-new-modal" style="display:none;"><div>';
554
+
555
+ if (!$default_domain) {
556
+ // Get the various supported domains
557
+ $default_domain = CnbAppRemote::cnb_remote_get_wp_domain();
558
+ }
559
+
560
+ $button_id = 'new';
561
+
562
+ // Create a dummy button
563
+ $button = new stdClass();
564
+ $button->id = '';
565
+ $button->active = false;
566
+ $button->name = '';
567
+ $button->type = 'SINGLE';
568
+ $button->domain = $default_domain;
569
+ $button->actions = array();
570
+
571
+ // Set some sane defaults
572
+ if (!isset($button->options)) $button->options = new stdClass();
573
+ $button->options->iconBackgroundColor = !empty($button->options->iconBackgroundColor)
574
+ ? $button->options->iconBackgroundColor
575
+ : '#009900';
576
+ $button->options->iconColor = !empty($button->options->iconColor)
577
+ ? $button->options->iconColor
578
+ : '#FFFFFF';
579
+ $button->options->placement = !empty($button->options->placement)
580
+ ? $button->options->placement
581
+ : ($button->type === 'FULL' ? 'BOTTOM_CENTER' : 'BOTTOM_RIGHT');
582
+ $button->options->scale = !empty($button->options->scale)
583
+ ? $button->options->scale
584
+ : '1';
585
+
586
+ $options = array('modal_view' => true, 'submit_button_text' => 'Next');
587
+ cnb_button_edit_form($button_id, $button, $default_domain, $options);
588
+ echo '</div></div>';
589
+ }
src/admin/condition-edit.php ADDED
@@ -0,0 +1,233 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname( __FILE__ ) . '/api/CnbAppRemote.php';
4
+ require_once dirname( __FILE__ ) . '/api/CnbAdminCloud.php';
5
+ require_once dirname( __FILE__ ) . '/partials/admin-functions.php';
6
+ require_once dirname( __FILE__ ) . '/partials/admin-header.php';
7
+ require_once dirname( __FILE__ ) . '/partials/admin-footer.php';
8
+ require_once dirname( __FILE__ ) . '/../utils/utils.php';
9
+
10
+ function cnb_add_header_condition_edit($condition) {
11
+ $id = filter_input( INPUT_GET, 'id', FILTER_SANITIZE_STRING );
12
+ $name = 'New Condition';
13
+ if ($condition && $condition->id !== 'new') {
14
+ $name = $condition->filterType;
15
+ if ($condition->matchValue) {
16
+ $name = $condition->matchValue;
17
+ }
18
+ }
19
+ if (strlen($id) > 0 && $id === 'new') {
20
+ echo 'Add condition';
21
+ } else {
22
+ echo 'Edit condition: "' . esc_html($name) . '"';
23
+ }
24
+ }
25
+
26
+ /**
27
+ * This is called to update the condition
28
+ * via `call-now-button.php#cnb_update_condition`
29
+ */
30
+ function cnb_admin_page_condition_edit_process() {
31
+ global $cnb_slug_base;
32
+ $nonce = filter_input( INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING );
33
+ if( isset( $_REQUEST['_wpnonce'] ) && wp_verify_nonce( $nonce, 'cnb-condition-edit') ) {
34
+
35
+ // sanitize the input
36
+ $conditions = filter_input(
37
+ INPUT_POST,
38
+ 'conditions',
39
+ FILTER_SANITIZE_STRING,
40
+ FILTER_REQUIRE_ARRAY | FILTER_FLAG_NO_ENCODE_QUOTES);
41
+ $result = '';
42
+ $cnb_cloud_notifications = array();
43
+ foreach($conditions as $condition) {
44
+ // do the processing
45
+ $result = CnbAdminCloud::cnb_update_condition( $cnb_cloud_notifications, $condition );
46
+ }
47
+
48
+ // redirect the user to the appropriate page
49
+ $transient_id = 'cnb-' . wp_generate_uuid4();
50
+ set_transient($transient_id, $cnb_cloud_notifications, HOUR_IN_SECONDS);
51
+
52
+ // Create link
53
+ $bid = !empty($_GET['bid']) ? sanitize_text_field($_GET['bid']) : null;
54
+ $url = admin_url('admin.php');
55
+ if (!empty($bid)) {
56
+ $redirect_link =
57
+ add_query_arg(
58
+ array(
59
+ 'page' => 'call-now-button',
60
+ 'action' => 'edit',
61
+ 'id' => $bid,
62
+ 'tid' => $transient_id,
63
+ 'tab' => 'visibility',
64
+ ),
65
+ $url);
66
+ $redirect_url = esc_url_raw($redirect_link);
67
+ wp_safe_redirect($redirect_url);
68
+ exit;
69
+ } else {
70
+ $redirect_link =
71
+ add_query_arg(
72
+ array(
73
+ 'page' => 'call-now-button-conditions',
74
+ 'action' => 'edit',
75
+ 'id' => $result->id,
76
+ 'tid' => $transient_id,
77
+ 'bid' => $bid),
78
+ $url);
79
+ $redirect_url = esc_url_raw($redirect_link);
80
+ wp_safe_redirect($redirect_url);
81
+ exit;
82
+ }
83
+ }
84
+ else {
85
+ wp_die( __( 'Invalid nonce specified', CNB_NAME), __( 'Error', CNB_NAME), array(
86
+ 'response' => 403,
87
+ 'back_link' => 'admin.php?page=' . $cnb_slug_base,
88
+ ) );
89
+ }
90
+ }
91
+
92
+ function cnb_create_tab_url($button, $tab) {
93
+ $url = admin_url('admin.php');
94
+ $tab_link =
95
+ add_query_arg(
96
+ array(
97
+ 'page' => 'call-now-button',
98
+ 'action' => 'edit',
99
+ 'type' => strtolower($button->type),
100
+ 'id' => $button->id,
101
+ 'tab' => $tab),
102
+ $url );
103
+ return esc_url( $tab_link );
104
+ }
105
+
106
+ /**
107
+ * @param $condition
108
+ * @param object? $button
109
+ */
110
+ function cnb_render_form($condition, $button=null) { ?>
111
+ <table class="form-table nav-tab-active">
112
+ <tr>
113
+ <th colspan="2"><h2>Basic Settings</h2>
114
+ <input type="hidden" name="conditions[<?php esc_attr_e($condition->id) ?>][id]" value="<?php if ($condition->id !== null && $condition->id !== 'new') { esc_attr_e($condition->id); } ?>" />
115
+ <input type="hidden" name="conditions[<?php esc_attr_e($condition->id) ?>][delete]" id="cnb_condition_<?php esc_attr_e($condition->id) ?>_delete" value="" />
116
+ <input type="hidden" name="conditions[<?php esc_attr_e($condition->id) ?>][conditionType]" value="<?php esc_attr_e($condition->conditionType) ?>" />
117
+ </th>
118
+ </tr>
119
+ <tr>
120
+ <th scope="row"><label for="cnb_condition_filter_type">Filter Type</label></th>
121
+ <td>
122
+ <select id="cnb_condition_filter_type" name="conditions[<?php esc_attr_e($condition->id) ?>][filterType]">
123
+ <?php foreach (cnb_get_condition_filter_types() as $condition_filter_type_key => $condition_filter_type_value) { ?>
124
+ <option value="<?php esc_attr_e($condition_filter_type_key) ?>"<?php selected($condition_filter_type_key, $condition->filterType) ?>>
125
+ <?php esc_html_e($condition_filter_type_value) ?>
126
+ </option>
127
+ <?php } ?>
128
+ </select>
129
+ </td>
130
+ </tr>
131
+ <tr>
132
+ <th scope="row"><label for="cnb_condition_match_type">Match Type</label></th>
133
+ <td>
134
+ <select id="cnb_condition_match_type" name="conditions[<?php esc_attr_e($condition->id) ?>][matchType]">
135
+ <?php foreach (cnb_get_condition_match_types() as $condition_match_type_key => $condition_match_type_value) { ?>
136
+ <option value="<?php esc_attr_e($condition_match_type_key) ?>"<?php selected($condition_match_type_key, $condition->matchType) ?>>
137
+ <?php esc_html_e($condition_match_type_value) ?>
138
+ </option>
139
+ <?php } ?>
140
+ </select>
141
+ </td>
142
+ </tr>
143
+ <tr>
144
+ <th scope="row"><label for="cnb_condition_match_value">Match Value</label></th>
145
+ <td>
146
+ <input type="text" id="cnb_condition_match_value" class="regular-text" name="conditions[<?php esc_attr_e($condition->id) ?>][matchValue]" value="<?php esc_attr_e($condition->matchValue) ?>" />
147
+ </td>
148
+ </tr>
149
+
150
+ </table>
151
+ <?php
152
+ }
153
+
154
+ function cnb_admin_page_condition_edit_render() {
155
+ global $cnb_options, $cnb_settings;
156
+
157
+ $cnb_notices = cnb_get_notices();
158
+ $cnb_changelog = cnb_get_changelog();
159
+
160
+ $condition_id = cnb_get_button_id();
161
+ $condition = new stdClass();
162
+ $button = null;
163
+ if (strlen($condition_id) > 0 && $condition_id !== 'new') {
164
+ $condition = CnbAppRemote::cnb_remote_get_condition( $condition_id );
165
+ } elseif ($condition_id === 'new') {
166
+ $condition->id = 'new';
167
+ }
168
+
169
+ add_action('cnb_header', function() use($condition) {
170
+ cnb_add_header_condition_edit($condition);
171
+ });
172
+
173
+ echo '<div class="wrap">';
174
+ cnb_admin_header($cnb_options, $cnb_settings, $cnb_notices, $cnb_changelog);
175
+
176
+ $bid = !empty($_GET['bid']) ? sanitize_text_field($_GET['bid']) : null;
177
+ if ($bid !== null) {
178
+ $button = CnbAppRemote::cnb_remote_get_button( $bid );
179
+ // Create back link
180
+ $url = admin_url('admin.php');
181
+ $redirect_link = esc_url(
182
+ add_query_arg(
183
+ array(
184
+ 'page' => 'call-now-button',
185
+ 'action' => 'edit',
186
+ 'tab' => 'visibility',
187
+ 'id' => $bid),
188
+ $url ));
189
+
190
+ $action_verb = $condition->id === 'new' ? 'adding' : 'editing';
191
+ echo '<div class="notice notice-info">
192
+ <p>You are '.$action_verb.' a Condition for Button ID <code>'.esc_html($button->id).'</code> (<strong>'.esc_html($button->name).'</strong>),
193
+ click <a href="'.$redirect_link.'">here</a> to go back to continue configuring the Button.</p></div>';
194
+ }
195
+
196
+ $url = admin_url('admin-post.php');
197
+
198
+ $form_action = esc_url( $url );
199
+ $redirect_link = add_query_arg(
200
+ array(
201
+ 'bid' => $bid
202
+ ),
203
+ $form_action
204
+ );
205
+
206
+ if ($bid !== null) { ?>
207
+ <h2 class="nav-tab-wrapper">
208
+ <a href="<?php echo cnb_create_tab_url($button, 'basic_options') ?>"
209
+ class="nav-tab <?php echo cnb_is_active_tab('basic_options') ?>">Basics</a>
210
+ <a href="<?php echo cnb_create_tab_url($button, 'actions') ?>"
211
+ class="nav-tab <?php echo cnb_is_active_tab('actions') ?>">Actions</a>
212
+ <a href="<?php echo cnb_create_tab_url($button, 'extra_options') ?>"
213
+ class="nav-tab <?php echo cnb_is_active_tab('extra_options') ?>">Presentation</a>
214
+ <a href="<?php echo cnb_create_tab_url($button, 'visibility') ?>"
215
+ class="nav-tab <?php echo cnb_is_active_tab('visibility') ?>">Visibility</a>
216
+ <a href="<?php echo cnb_create_tab_url($button, 'advanced_options') ?>"
217
+ class="nav-tab <?php echo cnb_is_active_tab('advanced_options') ?>">Advanced</a>
218
+ </h2>
219
+ <?php } ?>
220
+ <form action="<?php echo $redirect_link; ?>" method="post">
221
+ <input type="hidden" name="page" value="call-now-button-conditions" />
222
+ <input type="hidden" name="bid" value="<?php esc_attr_e($bid) ?>" />
223
+ <input type="hidden" name="condition_id" value="<?php esc_attr_e($condition->id) ?>" />
224
+ <input type="hidden" name="action" value="<?php echo $condition_id === 'new' ? 'cnb_create_condition' :'cnb_update_condition' ?>" />
225
+ <input type="hidden" name="_wpnonce" value="<?php echo wp_create_nonce('cnb-condition-edit')?>" />
226
+ <?php
227
+ cnb_render_form($condition, $button);
228
+ submit_button();
229
+ ?>
230
+ </form>
231
+ <?php cnb_admin_footer();
232
+ echo '</div>';
233
+ }
src/admin/condition-overview.php ADDED
@@ -0,0 +1,408 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname( __FILE__ ) . '/api/CnbAppRemote.php';
4
+ require_once dirname( __FILE__ ) . '/api/CnbAdminCloud.php';
5
+ require_once dirname( __FILE__ ) . '/partials/admin-functions.php';
6
+ require_once dirname( __FILE__ ) . '/partials/admin-header.php';
7
+ require_once dirname( __FILE__ ) . '/partials/admin-footer.php';
8
+
9
+ if(!class_exists('WP_List_Table')) {
10
+ require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
11
+ }
12
+
13
+ function cnb_add_header_condition_overview() {
14
+ echo 'Conditions ';
15
+ }
16
+
17
+ class Cnb_Condition_List_Table extends WP_List_Table {
18
+
19
+ /**
20
+ * CallNowButton Condition object
21
+ *
22
+ * @since v0.5.5
23
+ * @var object
24
+ */
25
+ public $button;
26
+
27
+ /**
28
+ * Constructor, we override the parent to pass our own arguments
29
+ * We usually focus on three parameters: singular and plural labels, as well as whether the class supports AJAX.
30
+ * @param array $args
31
+ */
32
+ function __construct( $args = array() ) {
33
+ if (isset($args['button'])) {
34
+ $this->button = $args['button'];
35
+ }
36
+
37
+ parent::__construct(array(
38
+ 'singular' => 'cnb_list_condition', //Singular label
39
+ 'plural' => 'cnb_list_conditions', //plural label, also this well be one of the table css class
40
+ 'ajax' => false //We won't support Ajax for this table
41
+ ));
42
+ }
43
+
44
+ /**
45
+ * Define the columns that are going to be used in the table
46
+ * @return array $columns, the array of columns to use with the table
47
+ */
48
+ function get_columns() {
49
+ $columns = array(
50
+ 'cb' => '<input type="checkbox">',
51
+ 'id' => __('ID'),
52
+ 'conditionType' => __('Type'),
53
+ 'filterType' => __('Filter type'),
54
+ 'matchType' => __('Match type'),
55
+ 'matchValue' => __('Match value'),
56
+ );
57
+ if ($this->button) { unset($columns['cb']); }
58
+ return $columns;
59
+ }
60
+
61
+ function get_sortable_columns() {
62
+ return array(
63
+ 'conditionType' => array('conditionType', false),
64
+ 'filterType' => array('filterType', false),
65
+ 'matchType' => array('matchType', false),
66
+ 'matchValue' => array('matchValue', false),
67
+ );
68
+ }
69
+
70
+ function get_hidden_columns() {
71
+ return array('id');
72
+ }
73
+
74
+ function prepare_items() {
75
+ // Process any Bulk actions before gathering data
76
+ $this->process_bulk_action();
77
+
78
+ /* -- Preparing your query -- */
79
+ $data = $this->get_data();
80
+
81
+ if ($data instanceof WP_Error) {
82
+ return $data;
83
+ }
84
+
85
+ /* -- Ordering parameters -- */
86
+ //Parameters that are going to be used to order the result
87
+ usort( $data, array( &$this, 'sort_data' ) );
88
+
89
+ /* -- Pagination parameters -- */
90
+ //Number of elements in your table?
91
+ $totalitems = count($data); //return the total number of affected rows
92
+ $per_page = 20; //How many to display per page?
93
+ //Which page is this?
94
+ $current_page = !empty($_GET['paged']) ? (int)sanitize_text_field($_GET['paged']) : 1;
95
+
96
+ //Page Number
97
+ if (empty($current_page) || !is_numeric($current_page) || $current_page <= 0) {
98
+ $current_page = 1;
99
+ }
100
+
101
+ //How many pages do we have in total?
102
+ $totalpages = ceil($totalitems / $per_page); //adjust the query to take pagination into account
103
+ if (!empty($current_page) && !empty($per_page)) {
104
+ $offset = ($current_page - 1) * $per_page;
105
+
106
+ /* -- Register the pagination -- */
107
+ $this->set_pagination_args(array(
108
+ 'total_items' => $totalitems,
109
+ 'total_pages' => $totalpages,
110
+ 'per_page' => $per_page,
111
+ ));
112
+ //The pagination links are automatically built according to those parameters
113
+
114
+ /* -- Register the Columns -- */
115
+ $columns = $this->get_columns();
116
+ $hidden_columns = $this->get_hidden_columns();
117
+ $sortable_columns = $this->get_sortable_columns();
118
+ $this->_column_headers = array($columns, $hidden_columns, $sortable_columns, 'conditionType');
119
+
120
+ /* -- Register the items -- */
121
+ $data = array_slice($data,$offset,$per_page);
122
+ $this->items = $data;
123
+ }
124
+ return null;
125
+ }
126
+
127
+ function column_default( $item, $column_name )
128
+ {
129
+ switch( $column_name ) {
130
+ case 'id':
131
+ case 'conditionType':
132
+ case 'filterType':
133
+ case 'matchType':
134
+ return '<code>' . esc_html($item[ $column_name ]) . '</code>';
135
+ case 'matchValue':
136
+ return !empty($item[$column_name]) ? esc_html($item[$column_name]) : '<em>No value</em>';
137
+ default:
138
+ return '<em>Unknown column ' .esc_html($column_name) . '</em>';
139
+ }
140
+ }
141
+
142
+ private function get_data() {
143
+ $entities = array();
144
+ if ($this->button === null) {
145
+ $entities = CnbAppRemote::cnb_remote_get_conditions();
146
+ } else {
147
+ // Find ConditionIDs for Button
148
+ $button = CnbAppRemote::cnb_remote_get_button( $this->button->id );
149
+ if ($button instanceof WP_Error) {
150
+ return $button;
151
+ }
152
+
153
+ foreach ($button->conditions as $entityId) {
154
+ $entities[] = CnbAppRemote::cnb_remote_get_condition( $entityId );
155
+ }
156
+ }
157
+
158
+ if ($entities instanceof WP_Error) {
159
+ return $entities;
160
+ }
161
+
162
+ $data = array();
163
+ foreach ($entities as $entity) {
164
+ $data[] = array(
165
+ 'id' => $entity->id,
166
+ 'conditionType' => $entity->conditionType,
167
+ 'filterType' => $entity->filterType,
168
+ 'matchType' => $entity->matchType,
169
+ 'matchValue' => $entity->matchValue,
170
+ );
171
+ }
172
+ return $data;
173
+ }
174
+
175
+ /**
176
+ * Allows you to sort the data by the variables set in the $_GET
177
+ *
178
+ * @return Mixed
179
+ */
180
+ private function sort_data( $a, $b ) {
181
+ // If orderby is set, use this as the sort column
182
+ $orderby = !empty($_GET['orderby']) ? sanitize_text_field($_GET['orderby']) : 'conditionType';
183
+ // If order is set use this as the order
184
+ $order = !empty($_GET['order']) ? sanitize_text_field($_GET['order']) : 'asc';
185
+
186
+ $result = strcmp( $a[$orderby], $b[$orderby] );
187
+
188
+ if($order === 'asc') {
189
+ return $result;
190
+ }
191
+ return -$result;
192
+ }
193
+
194
+ /**
195
+ * Custom action for `cb` columns (checkboxes)
196
+ *
197
+ * @param array|object $item
198
+ * @return string|void
199
+ */
200
+ function column_cb($item) {
201
+ return sprintf(
202
+ '<input type="checkbox" name="%1$s[]" value="%2$s" />',
203
+ $this->_args['singular'],
204
+ esc_attr($item['id'])
205
+ );
206
+ }
207
+
208
+ function column_conditionType($item) {
209
+ $bid = $this->button !== null ? $this->button->id : null;
210
+ $tab = $this->button !== null ? 'visibility' : null;
211
+
212
+ // Let's build a link
213
+ $url = admin_url('admin.php');
214
+ $edit_link =
215
+ add_query_arg(
216
+ array(
217
+ 'page' => 'call-now-button-conditions',
218
+ 'action' => 'edit',
219
+ 'id' => $item['id'],
220
+ 'bid' => $bid,
221
+ 'tab' => $tab),
222
+ $url );
223
+ $edit_url = esc_url( $edit_link );
224
+ $actions = array(
225
+ 'edit' => '<a href="'.$edit_url.'">Edit</a>',
226
+ );
227
+
228
+ $delete_link = wp_nonce_url(
229
+ add_query_arg( array(
230
+ 'page' => 'call-now-button-conditions',
231
+ 'action' => 'delete',
232
+ 'id' => $item['id'],
233
+ 'bid' => $bid ),
234
+ $url ),
235
+ 'cnb_delete_condition' );
236
+ $delete_url = esc_url( $delete_link );
237
+ $actions['delete'] = '<a href="'.$delete_url.'">Delete</a>';
238
+
239
+ $value = !empty($item['conditionType']) ? $item['conditionType'] : '<em>No value</em>';
240
+ return sprintf(
241
+ '%1$s %2$s',
242
+ '<a href="'.$edit_url.'">'.esc_html($value) . '</a>',
243
+ $this->row_actions($actions)
244
+ );
245
+ }
246
+
247
+ function get_bulk_actions() {
248
+ // Hide Bulk Actions if we're on the Button edit page
249
+ if ($this->button) { return array(); }
250
+ return array(
251
+ 'delete' => 'Delete',
252
+ );
253
+ }
254
+
255
+ function process_bulk_action() {
256
+ if ( isset( $_REQUEST['_wpnonce'] ) && ! empty( $_REQUEST['_wpnonce'] ) ) {
257
+ $nonce = filter_input( INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING );
258
+ $action = 'bulk-' . $this->_args['plural'];
259
+
260
+ if ( wp_verify_nonce( $nonce, $action ) ) {
261
+ $entityIds = filter_input(INPUT_POST, 'cnb_list_condition', FILTER_SANITIZE_STRING, FILTER_REQUIRE_ARRAY);
262
+ switch ($this->current_action()) {
263
+ case 'delete':
264
+ foreach ($entityIds as $entityId) {
265
+ $entity = array('id' => $entityId);
266
+ CnbAppRemote::cnb_remote_delete_condition( $entity );
267
+ }
268
+ echo '<div class="notice-success notice"><p>' . count($entityIds) . ' Condition(s) deleted</p></div>';
269
+ break;
270
+ }
271
+ }
272
+ }
273
+ }
274
+ }
275
+
276
+ function cnb_action_after_header() {
277
+ // Only add the "Add new" action in the overview part
278
+ $id = !empty($_GET['id']) ? sanitize_text_field($_GET['id']) : null;
279
+ $action = !empty($_GET['action']) ? sanitize_text_field($_GET['action']) : null;
280
+ $bid = !empty($_GET['bid']) ? sanitize_text_field($_GET['bid']) : null;
281
+ if ($id === null || ($action != 'new' && $action != 'edit')) {
282
+ // Create link
283
+ $url = admin_url('admin.php');
284
+ $new_link =
285
+ add_query_arg(
286
+ array(
287
+ 'page' => 'call-now-button-conditions',
288
+ 'action' => 'new',
289
+ 'id' => 'new',
290
+ 'bid' => $bid),
291
+ $url);
292
+ $new_url = esc_url($new_link);
293
+
294
+ echo '<a href="' . $new_url . '" class="page-title-action">Add New</a>';
295
+ }
296
+ }
297
+
298
+ /**
299
+ * Main entrypoint, used by `call-now-button.php`.
300
+ */
301
+ function cnb_admin_page_condition_overview_render() {
302
+ $id = !empty($_GET['id']) ? sanitize_text_field($_GET['id']) : null;
303
+ $action = !empty($_GET['action']) ? sanitize_text_field($_GET['action']) : null;
304
+ if ($id === null) {
305
+ cnb_admin_page_condition_overview_render_list();
306
+ } else {
307
+ switch ($action) {
308
+ case 'new':
309
+ case 'edit':
310
+ require_once dirname( __FILE__ ) . '/condition-edit.php';
311
+ cnb_admin_page_condition_edit_render();
312
+ break;
313
+ case 'delete':
314
+ if (isset($_REQUEST['_wpnonce']) && !empty($_REQUEST['_wpnonce'])) {
315
+ $nonce = filter_input(INPUT_GET, '_wpnonce', FILTER_SANITIZE_STRING);
316
+ $action = 'cnb_delete_condition';
317
+
318
+ if (wp_verify_nonce($nonce, $action)) {
319
+ $cnb_cloud_notifications = array();
320
+ // If a button is set, remove this ID from the conditions array
321
+ $bid = !empty($_GET['bid']) ? sanitize_text_field($_GET['bid']) : null;
322
+ if ($bid !== null) {
323
+ // Get the button
324
+ $button = CnbAppRemote::cnb_remote_get_button( $bid );
325
+
326
+ // Remove the current Condition
327
+ $pos = array_search($id, $button->conditions);
328
+ unset($button->conditions[$pos]);
329
+
330
+ // Convert to array and update
331
+ $button_array = json_decode(json_encode($button), true);
332
+ CnbAdminCloud::cnb_update_button( $cnb_cloud_notifications, $button_array );
333
+ }
334
+
335
+ CnbAdminCloud::cnb_delete_condition( $cnb_cloud_notifications, $id );
336
+
337
+ foreach ($cnb_cloud_notifications as $cnb_cloud_notification) {
338
+ echo $cnb_cloud_notification;
339
+ }
340
+ if ($bid !== null) {
341
+ // Create link
342
+ // Create link
343
+ $url = admin_url('admin.php');
344
+ $new_link =
345
+ add_query_arg(
346
+ array(
347
+ 'page' => 'call-now-button',
348
+ 'action' => 'edit',
349
+ 'id' => $bid,
350
+ 'tab' => 'visibility'),
351
+ $url);
352
+ $new_url = esc_url_raw($new_link);
353
+ echo '<div class="notice-success notice"><p>';
354
+ echo '<p>You will be redirected back to the Button overview in 5 seconds...</p><p>Or click here to go immediately: <a href="'.$new_url.'">'.$new_url.'</a></p>';
355
+ echo '<script type="text/javascript">setTimeout(function(){location.href="' . $new_url .'"} , 5000); </script>';
356
+ echo '</p></div>';
357
+ } else {
358
+ cnb_admin_page_condition_overview_render_list();
359
+ }
360
+ }
361
+ }
362
+ break;
363
+ }
364
+ }
365
+ }
366
+
367
+ function cnb_admin_page_condition_overview_bid(&$cnb_notices) {
368
+ $bid = !empty($_GET['bid']) ? sanitize_text_field($_GET['bid']) : null;
369
+ $args = array();
370
+ $button = null;
371
+ if ($bid !== null) {
372
+ $button = CnbAppRemote::cnb_remote_get_button( $bid );
373
+ }
374
+
375
+ if ($button && !($button instanceof WP_Error)) {
376
+ $args['button'] = $button;
377
+ $cnb_notices[] = '<div class="notice notice-info"><p>Only conditions for Button ID <code>'.esc_html($button->id).'</code> (<strong>'.esc_html($button->name).'</strong>) are shown</p></div>';
378
+ }
379
+ return $args;
380
+ }
381
+ function cnb_admin_page_condition_overview_render_list() {
382
+ global $cnb_options, $cnb_settings;
383
+
384
+ add_action('cnb_header', 'cnb_add_header_condition_overview');
385
+
386
+ $cnb_notices = cnb_get_notices();
387
+ $cnb_changelog = cnb_get_changelog();
388
+
389
+ $args = cnb_admin_page_condition_overview_bid($cnb_notices);
390
+ //Prepare Table of elements
391
+ $wp_list_table = new Cnb_Condition_List_Table($args);
392
+ $data = $wp_list_table->prepare_items();
393
+
394
+ if ($data instanceof WP_Error) {
395
+ $cnb_notices[] = cnb_admin_header_get_cloud_error($data);
396
+ }
397
+
398
+ echo '<div class="wrap">';
399
+ cnb_admin_header($cnb_options, $cnb_settings, $cnb_notices, $cnb_changelog);
400
+
401
+ echo '<form id="wp_list_event" method="post">';
402
+
403
+ //Table of elements
404
+ $wp_list_table->display();
405
+ echo '</form>';
406
+ cnb_admin_footer();
407
+ echo '</div>';
408
+ }
src/admin/domain-edit.php ADDED
@@ -0,0 +1,370 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname( __FILE__ ) . '/api/CnbAppRemote.php';
4
+ require_once dirname( __FILE__ ) . '/api/CnbAdminCloud.php';
5
+ require_once dirname( __FILE__ ) . '/partials/admin-functions.php';
6
+ require_once dirname( __FILE__ ) . '/partials/admin-header.php';
7
+ require_once dirname( __FILE__ ) . '/partials/admin-footer.php';
8
+
9
+ /***
10
+ * To ensure arrays are properly sanitized to WordPress Codex standards,
11
+ * they encourage usage of sanitize_text_field(). That only works with a single
12
+ * variable (string). This function allows for a full blown array to get sanitized
13
+ * properly, while sanitizing each individual value in a key -> value pair.
14
+ *
15
+ * Source: https://wordpress.stackexchange.com/questions/24736/wordpress-sanitize-array
16
+ * Author: Broshi, answered Feb 5 '17 at 9:14
17
+ * Via: https://developer.wordpress.org/reference/functions/sanitize_text_field/
18
+ */
19
+ function cnb_wporg_recursive_sanitize_text_field( $array ) {
20
+ foreach ( $array as $key => &$value ) {
21
+ if ( is_array( $value ) ) {
22
+ $value = cnb_wporg_recursive_sanitize_text_field( $value );
23
+ } else {
24
+ $value = sanitize_text_field( $value );
25
+ }
26
+ }
27
+ return $array;
28
+ }
29
+
30
+ function cnb_add_header_domain_edit($domain=null) {
31
+ $domain_id = filter_input( INPUT_GET, 'id', FILTER_SANITIZE_STRING );
32
+ $name = 'New Domain';
33
+
34
+ if ($domain && !empty($domain->id) && $domain->id !== 'new') {
35
+ $name = $domain->name;
36
+ }
37
+ if (strlen($domain_id) > 0 && $domain_id === 'new') {
38
+ echo 'Add domain';
39
+ } else {
40
+ echo 'Edit domain: "' . esc_html($name) . '"';
41
+ }
42
+ }
43
+
44
+ /**
45
+ * This is called to create the Domain
46
+ */
47
+ function cnb_admin_page_domain_create_process() {
48
+ global $cnb_slug_base;
49
+ $nonce = filter_input( INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING );
50
+ if( isset( $_REQUEST['_wpnonce'] ) && wp_verify_nonce( $nonce, 'cnb_create_domain') ) {
51
+
52
+ // sanitize the input
53
+ $domain_data = filter_input(
54
+ INPUT_POST,
55
+ 'domain',
56
+ FILTER_SANITIZE_STRING,
57
+ FILTER_REQUIRE_ARRAY | FILTER_FLAG_NO_ENCODE_QUOTES);
58
+
59
+ $domain = array();
60
+ $domain['name'] = sanitize_text_field( $domain_data['name'] );
61
+ $domain['timezone'] = sanitize_text_field( $domain_data['timezone'] );
62
+ $domain['trackGA'] = sanitize_text_field( $domain_data['trackGA'] );
63
+ $domain['trackConversion'] = sanitize_text_field( $domain_data['trackConversion'] );
64
+ $domain['properties'] = cnb_wporg_recursive_sanitize_text_field( $domain_data['properties']);
65
+
66
+ // Convert into booleans
67
+ $domain['trackGA'] = !empty($domain['trackGA']) ? $domain['trackGA'] : "false";
68
+ $domain['trackConversion'] = !empty($domain['trackConversion']) ? $domain['trackConversion'] : "false";
69
+ $domain['renew'] = !empty($domain['renew']) ? $domain['renew'] : "false";
70
+
71
+ // Convert the zindex order back to an actual zindex
72
+ $domain['properties']['zindex'] = zindex($domain['properties']['zindex']);
73
+
74
+ // do the processing
75
+ $cnb_cloud_notifications = array();
76
+ $new_domain = CnbAdminCloud::cnb_create_domain( $cnb_cloud_notifications, $domain );
77
+
78
+ // redirect the user to the appropriate page
79
+ $transient_id = 'cnb-' . wp_generate_uuid4();
80
+ set_transient( $transient_id, $cnb_cloud_notifications, HOUR_IN_SECONDS );
81
+
82
+ // Get ID in case of error
83
+ $id = 'new';
84
+ if ((!$new_domain instanceof WP_Error)) {
85
+ $id = $new_domain->id;
86
+ }
87
+
88
+ // Create link
89
+ $url = admin_url( 'admin.php' );
90
+ $redirect_link =
91
+ add_query_arg(
92
+ array(
93
+ 'page' => 'call-now-button-domains',
94
+ 'action' => 'edit',
95
+ 'id' => $id,
96
+ 'tid' => $transient_id
97
+ ),
98
+ $url );
99
+ $redirect_url = esc_url_raw( $redirect_link );
100
+ wp_safe_redirect( $redirect_url );
101
+ exit;
102
+ }
103
+ else {
104
+ wp_die( __( 'Invalid nonce specified', CNB_NAME), __( 'Error', CNB_NAME), array(
105
+ 'response' => 403,
106
+ 'back_link' => 'admin.php?page=' . $cnb_slug_base,
107
+ ) );
108
+ }
109
+ }
110
+
111
+ function cnb_admin_page_domain_edit_process_domain($domain_data, &$transient_id=null) {
112
+ $domain = array();
113
+
114
+ // sanitize the input
115
+ $domain['id'] = sanitize_text_field( $domain_data['id'] );
116
+ if (isset($domain_data['name'])) {
117
+ $domain['name'] = sanitize_text_field($domain_data['name']);
118
+ }
119
+ $domain['renew'] = sanitize_text_field( isset($domain_data['renew']) ? $domain_data['renew'] : 'false' );
120
+ $domain['timezone'] = sanitize_text_field( $domain_data['timezone'] );
121
+ $domain['trackGA'] = sanitize_text_field( isset($domain_data['trackGA']) ? $domain_data['trackGA'] : 'false' );
122
+ $domain['trackConversion'] = sanitize_text_field( isset($domain_data['trackConversion']) ? $domain_data['trackConversion'] : 'false' );
123
+ $domain['properties'] = cnb_wporg_recursive_sanitize_text_field( $domain_data['properties']);
124
+
125
+ // Convert into booleans
126
+ $domain['trackGA'] = !empty($domain['trackGA']) ? $domain['trackGA'] : 'false';
127
+ $domain['trackConversion'] = !empty($domain['trackConversion']) ? $domain['trackConversion'] : 'false';
128
+ $domain['renew'] = !empty($domain['renew']) ? $domain['renew'] : 'false';
129
+
130
+ // Convert the zindex order back to an actual zindex
131
+ $domain['properties']['zindex'] = zindex($domain['properties']['zindex']);
132
+
133
+ // do the processing
134
+ $cnb_cloud_notifications = array();
135
+ CnbAdminCloud::cnb_update_domain( $cnb_cloud_notifications, $domain );
136
+
137
+ $transient_id = 'cnb-' . wp_generate_uuid4();
138
+ set_transient($transient_id, $cnb_cloud_notifications, HOUR_IN_SECONDS);
139
+
140
+ return $domain;
141
+ }
142
+ /**
143
+ * This is called to update the Domain
144
+ */
145
+ function cnb_admin_page_domain_edit_process() {
146
+ global $cnb_slug_base;
147
+ $nonce = filter_input( INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING );
148
+ if( isset( $_REQUEST['_wpnonce'] ) && wp_verify_nonce( $nonce, 'cnb_update_domain') ) {
149
+
150
+ $domain_data = $_POST['domain'];
151
+ $transient_id = null;
152
+ $domain = cnb_admin_page_domain_edit_process_domain($domain_data, $transient_id);
153
+
154
+ // redirect the user to the appropriate page
155
+ $url = admin_url('admin.php');
156
+ $redirect_link =
157
+ add_query_arg(
158
+ array(
159
+ 'page' => 'call-now-button-domains',
160
+ 'action' => 'edit',
161
+ 'id' => $domain['id'],
162
+ 'tid' => $transient_id),
163
+ $url );
164
+ $redirect_url = esc_url_raw( $redirect_link );
165
+ wp_safe_redirect($redirect_url);
166
+ exit;
167
+ }
168
+ else {
169
+ wp_die( __( 'Invalid nonce specified', CNB_NAME), __( 'Error', CNB_NAME), array(
170
+ 'response' => 403,
171
+ 'back_link' => 'admin.php?page=' . $cnb_slug_base,
172
+ ) );
173
+ }
174
+ }
175
+
176
+ function cnb_admin_page_domain_edit_render() {
177
+ global $cnb_options, $cnb_settings;
178
+
179
+ $cnb_notices = cnb_get_notices();
180
+ $cnb_changelog = cnb_get_changelog();
181
+
182
+ $domain_id = filter_input( INPUT_GET, 'id', FILTER_SANITIZE_STRING );
183
+ $domain = new stdClass();
184
+ if (strlen($domain_id) > 0 && $domain_id != 'new') {
185
+ $domain = CnbAppRemote::cnb_remote_get_domain( $domain_id );
186
+ }
187
+ // Domain specific conversions
188
+
189
+ // Set default values in case they are missing
190
+ if (strlen($domain_id) > 0 && $domain_id == 'new' && empty($domain->id)) {
191
+ $domain->id = null;
192
+ }
193
+ if (empty($domain->timezone)) {
194
+ $domain->timezone = wp_timezone_string();
195
+ }
196
+ if (empty($domain->type)) {
197
+ $domain->type = 'FREE';
198
+ }
199
+ if (empty($domain->properties)) {
200
+ $domain->properties = new stdClass();
201
+ $domain->properties->scale = '1';
202
+ $domain->properties->debug = false;
203
+ }
204
+ if (empty($domain->name)) {
205
+ $domain->name = null;
206
+ }
207
+ if (!isset($domain->trackGA)) {
208
+ $domain->trackGA = true;
209
+ }
210
+ if (!isset($domain->trackConversion)) {
211
+ $domain->trackConversion = true;
212
+ }
213
+
214
+ add_action('cnb_header', function() use($domain) {
215
+ cnb_add_header_domain_edit($domain);
216
+ });
217
+
218
+ ?>
219
+
220
+ <div class="wrap">
221
+ <?php cnb_admin_header($cnb_options, $cnb_settings, $cnb_notices, $cnb_changelog) ?>
222
+ <form action="<?php echo esc_url( admin_url('admin-post.php') ); ?>" method="post">
223
+ <input type="hidden" name="page" value="call-now-button" />
224
+ <input type="hidden" name="action" value="<?php echo $domain_id === 'new' ? 'cnb_create_domain' :'cnb_update_domain' ?>" />
225
+ <input type="hidden" name="_wpnonce" value="<?php echo wp_create_nonce($domain_id === 'new' ? 'cnb_create_domain' : 'cnb_update_domain')?>" />
226
+
227
+ <table class="form-table nav-tab-active" role="presentation">
228
+ <?php cnb_admin_page_domain_edit_render_form($domain) ?>
229
+ </table>
230
+
231
+ <?php submit_button(); ?>
232
+ </form>
233
+
234
+ <?php cnb_admin_footer() ?>
235
+ </div>
236
+ <?php }
237
+
238
+ function cnb_admin_page_domain_edit_render_form($domain) {
239
+ $domain_properties_zindex = !empty($domain->properties->zindex) ? $domain->properties->zindex : 2147483647;
240
+ $domain_properties_zindex_order = zindexToOrder($domain_properties_zindex);
241
+
242
+ $url = admin_url( 'admin.php' );
243
+ $upgrade_link =
244
+ add_query_arg( array(
245
+ 'page' => 'call-now-button-domains',
246
+ 'action' => 'upgrade',
247
+ 'id' => $domain->id
248
+ ),
249
+ $url );
250
+ $upgrade_url = esc_url( $upgrade_link );
251
+ ?>
252
+ <tr>
253
+ <th>Plan</th>
254
+ <td>
255
+ <code><?php esc_html_e($domain->type) ?></code>
256
+ <?php
257
+ if ($domain->type !== 'PROPLUS') {
258
+ echo '<a href="' . $upgrade_url . '">Upgrade!</a>';
259
+ }
260
+ ?>
261
+ </td>
262
+ </tr> <?php if ($domain->type != 'FREE') { ?>
263
+ <tr>
264
+ <th scope="row">Renew</th>
265
+ <td><fieldset><legend class="screen-reader-text"><span>Renew</span></legend><label for="renew">
266
+ <input name="domain[renew]" type="checkbox" id="renew" value="true" <?php checked('1', $domain->renew); ?> />
267
+ Renew domain automatically</label>
268
+ </fieldset>
269
+ <?php if (!empty($domain->expires)) { ?>
270
+ <p class="description" id="domain_expires-description">
271
+ Domain expires <code><?php esc_html_e($domain->expires) ?></code>.
272
+ </p>
273
+ <?php } ?>
274
+ </td>
275
+ </tr>
276
+ <?php } ?>
277
+ <tr>
278
+ <th colspan="2"><h2>Tracking</h2></th>
279
+ </tr>
280
+ <tr>
281
+ <th scope="row">Google Analytics</th>
282
+ <td>
283
+ <input type="hidden" name="domain[trackGA]" value="0" />
284
+ <input name="domain[trackGA]" type="checkbox" id="google_analytics" value="true" <?php checked('1', $domain->trackGA); ?> />
285
+ <label for="google_analytics">Enable </label>
286
+ <p class="description">
287
+ Supports Classic, Universal Analytics and Global site tag (v3 and v4).<br>
288
+ Using Google Tag Manager? Set up click tracking in GTM. <a href="https://callnowbutton.com/support/click-tracking/google-tag-manager-event-tracking/?utm_source=wp-plugin&amp;utm_medium=referral&amp;utm_campaign=description_link&amp;utm_term=google-tag-manager-event-tracking" target="_blank">Learn how to do this...</a>
289
+ </p>
290
+ </td>
291
+ </tr>
292
+ <tr>
293
+ <th scope="row">Google Ads conversions</th>
294
+ <td>
295
+ <input type="hidden" name="domain[trackConversion]" value="0" />
296
+ <input name="domain[trackConversion]" type="checkbox" id="conversion_tracking" value="true" <?php checked('1', $domain->trackConversion); ?> />
297
+ <label for="conversion_tracking">Enable</label>
298
+ <p class="description">Select this option if you want to count clicks on the button as Google Ads conversions. This option requires the Event snippet to be present on the page. <a href="https://support.google.com/google-ads/answer/6331304" target="_blank">Learn more...</a></p>
299
+ </td>
300
+ </tr>
301
+ <tr>
302
+ <th colspan="2"><h2>Button display</h2></th>
303
+ </tr>
304
+ <tr class="zoom">
305
+ <th scope="row">Button size <span id="cnb_slider_value"></span></th>
306
+ <td><fieldset>
307
+ <label class="cnb_slider_value" for="cnb_slider" onclick="jQuery('#cnb_slider:enabled')[0].stepDown();cnb_update_sliders()">Smaller&nbsp;&laquo;&nbsp;</label>
308
+ <input type="range" min="0.7" max="1.3" step="0.1" name="domain[properties][scale]"
309
+ value="<?php esc_attr_e($domain->properties->scale) ?>" class="slider" id="cnb_slider">
310
+ <label class="cnb_slider_value" for="cnb_slider" onclick="jQuery('#cnb_slider:enabled')[0].stepUp();cnb_update_sliders()">&nbsp;&raquo;&nbsp;Bigger</label>
311
+ </fieldset></td>
312
+ </tr>
313
+ <tr class="z-index">
314
+ <th scope="row">Order (<span id="cnb_order_value"></span>) <a
315
+ href="https://callnowbutton.com/set-order/" target="_blank" class="cnb-nounderscore">
316
+ <span class="dashicons dashicons-editor-help"></span>
317
+ </a></th>
318
+ <td>
319
+ <label class="cnb_slider_value" for="cnb_order_slider" onclick="jQuery('#cnb_order_slider:enabled')[0].stepDown();cnb_update_sliders()">Backwards&nbsp;&laquo;&nbsp;</label>
320
+ <input type="range" min="1" max="10" name="domain[properties][zindex]"
321
+ value="<?php esc_attr_e($domain_properties_zindex_order) ?>" class="slider2" id="cnb_order_slider"
322
+ step="1">
323
+ <label class="cnb_slider_value" for="cnb_order_slider" onclick="jQuery('#cnb_order_slider:enabled')[0].stepUp();cnb_update_sliders()">&nbsp;&raquo;&nbsp;Front</label>
324
+ <p class="description">The default (and recommended) value is all the way to the front so the
325
+ button sits on top of everything else. In case you have a specific usecase where you want
326
+ something else to sit in front of the Call Now Button (e.g. a chat window or a cookie
327
+ notice) you can move this backwards one step at a time to adapt it to your situation.</p>
328
+ </td>
329
+ </tr>
330
+ <tr>
331
+ <th colspan="2"><h2>Advanced</h2></th>
332
+ </tr>
333
+ <tr>
334
+ <th scope="row"><label for="domain_name">Domain name</label></th>
335
+ <td>
336
+ <input type="hidden" name="domain[id]" value="<?php esc_attr_e($domain->id) ?>" />
337
+ <input type="text" id="domain_name" name="domain[name]" value="<?php esc_attr_e($domain->name) ?>" class="regular-text" disabled="disabled"/>
338
+ <p class="description">
339
+ <strong>Warning</strong>: Changing your domain name means remapping all existing Buttons for that domain. Please use with caution. <a class="cnb_cursor_pointer" onclick="return jQuery('#domain_name').prop('disabled', false);">Click here to change your domain.</a>
340
+ </p>
341
+ </td>
342
+ </tr>
343
+ <tr>
344
+ <th scope="row"><label for="domain_timezone">Timezone</label></th>
345
+ <td>
346
+ <select name="domain[timezone]" id="domain_timezone">
347
+ <?php echo wp_timezone_choice($domain->timezone) ?>
348
+ </select>
349
+ <p class="description" id="domain_timezone-description">
350
+ <?php if (empty($domain->timezone)) { ?>
351
+ Please select your timezone.
352
+ <?php } else { ?>
353
+ Currently set to <code><?php esc_html_e($domain->timezone) ?></code>.
354
+ <?php } ?>
355
+ </p>
356
+ </td>
357
+ </tr>
358
+ <tr>
359
+ <th scope="row">Debug mode</th>
360
+ <td>
361
+ <input type="hidden" name="domain[properties][debug]" value="false" />
362
+ <input name="domain[properties][debug]" type="checkbox" id="domain_properties_debug" value="true" <?php checked('true', $domain->properties->debug); ?> />
363
+ <label for="domain_properties_debug">Enabled</label>
364
+ <p class="description">
365
+ This enables some additional information in your browser's console, which can help during troubleshooting.
366
+ </p>
367
+ </fieldset></td>
368
+ </tr>
369
+ <?php
370
+ }
src/admin/domain-overview.php ADDED
@@ -0,0 +1,352 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname( __FILE__ ) . '/api/CnbAppRemote.php';
4
+ require_once dirname( __FILE__ ) . '/api/CnbAppRemotePayment.php';
5
+ require_once dirname( __FILE__ ) . '/api/CnbAdminCloud.php';
6
+ require_once dirname( __FILE__ ) . '/partials/admin-functions.php';
7
+ require_once dirname( __FILE__ ) . '/partials/admin-header.php';
8
+ require_once dirname( __FILE__ ) . '/partials/admin-footer.php';
9
+
10
+ if(!class_exists('WP_List_Table')) {
11
+ require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
12
+ }
13
+
14
+ function cnb_add_header_domain_overview() {
15
+ echo 'Domains ';
16
+ }
17
+
18
+ class Cnb_Domain_List_Table extends WP_List_Table {
19
+
20
+ /**
21
+ * Constructor, we override the parent to pass our own arguments
22
+ * We usually focus on three parameters: singular and plural labels, as well as whether the class supports AJAX.
23
+ */
24
+ function __construct() {
25
+ parent::__construct(array(
26
+ 'singular' => 'cnb_list_domain', //Singular label
27
+ 'plural' => 'cnb_list_domains', //plural label, also this well be one of the table css class
28
+ 'ajax' => false //We won't support Ajax for this table
29
+ ));
30
+ }
31
+
32
+ /**
33
+ * Define the columns that are going to be used in the table
34
+ * @return array $columns, the array of columns to use with the table
35
+ */
36
+ function get_columns() {
37
+ return array(
38
+ 'cb' => '<input type="checkbox">',
39
+ 'id' => __('ID'),
40
+ 'name' => __('Name'),
41
+ 'type' => __('Type'),
42
+ 'expires' => __('Expires'),
43
+ 'renew' => __('Renew automatically'),
44
+ 'timezone' => __('Timezone'),
45
+ 'trackGA' => __('Google Analytics'),
46
+ 'trackConversion' => __('Google Ads')
47
+ );
48
+ }
49
+
50
+ function get_sortable_columns() {
51
+ return array(
52
+ 'name' => array('name', false),
53
+ 'type' => array('type', false),
54
+ );
55
+ }
56
+
57
+ function get_hidden_columns() {
58
+ return array('id');
59
+ }
60
+
61
+ function prepare_items() {
62
+ // Process any Bulk actions before gathering data
63
+ $this->process_bulk_action();
64
+
65
+ /* -- Preparing your query -- */
66
+ $data = $this->get_data();
67
+
68
+ if ($data instanceof WP_Error) {
69
+ return $data;
70
+ }
71
+
72
+ /* -- Ordering parameters -- */
73
+ //Parameters that are going to be used to order the result
74
+ usort( $data, array( &$this, 'sort_data' ) );
75
+
76
+ /* -- Pagination parameters -- */
77
+ //Number of elements in your table?
78
+ $totalitems = count($data); //return the total number of affected rows
79
+ $per_page = 20; //How many to display per page?
80
+ //Which page is this?
81
+ $current_page = !empty($_GET['paged']) ? (int)sanitize_text_field($_GET['paged']) : 1;
82
+
83
+ //Page Number
84
+ if (empty($current_page) || !is_numeric($current_page) || $current_page <= 0) {
85
+ $current_page = 1;
86
+ }
87
+
88
+ //How many pages do we have in total?
89
+ $totalpages = ceil($totalitems / $per_page); //adjust the query to take pagination into account
90
+ if (!empty($current_page) && !empty($per_page)) {
91
+ $offset = ($current_page - 1) * $per_page;
92
+
93
+ /* -- Register the pagination -- */
94
+ $this->set_pagination_args(array(
95
+ 'total_items' => $totalitems,
96
+ 'total_pages' => $totalpages,
97
+ 'per_page' => $per_page,
98
+ ));
99
+ //The pagination links are automatically built according to those parameters
100
+
101
+ /* -- Register the Columns -- */
102
+ $columns = $this->get_columns();
103
+ $hidden_columns = $this->get_hidden_columns();
104
+ $sortable_columns = $this->get_sortable_columns();
105
+ $this->_column_headers = array($columns, $hidden_columns, $sortable_columns, 'name');
106
+
107
+ /* -- Register the items -- */
108
+ $data = array_slice($data,$offset,$per_page);
109
+ $this->items = $data;
110
+ }
111
+ return null;
112
+ }
113
+
114
+ function column_default( $item, $column_name )
115
+ {
116
+ switch( $column_name ) {
117
+ case 'id':
118
+ return '<code>' . esc_html($item[ $column_name ]) . '</code>';
119
+ case 'type':
120
+ switch ($item[ $column_name ]) {
121
+ case 'FREE':
122
+ return 'Free';
123
+ case 'PRO':
124
+ return 'Pro';
125
+ case 'PROPLUS':
126
+ return 'Pro+';
127
+ default:
128
+ return esc_html($item[$column_name]);
129
+ }
130
+ case 'renew':
131
+ case 'trackGA':
132
+ case 'trackConversion':
133
+ return $item[ $column_name ] ? 'Enabled' : 'Disabled';
134
+ case 'timezone':
135
+ case 'expires':
136
+ return esc_html($item[ $column_name ]);
137
+
138
+ default:
139
+ return '<em>Unknown column ' .esc_html($column_name) . '</em>';
140
+ }
141
+ }
142
+
143
+ private function get_data() {
144
+ $domains = CnbAppRemote::cnb_remote_get_domains();
145
+
146
+ if ($domains instanceof WP_Error) {
147
+ return $domains;
148
+ }
149
+
150
+ $data = array();
151
+ foreach ($domains as $domain) {
152
+ $data[] = array(
153
+ 'id' => $domain->id,
154
+ 'name' => $domain->name,
155
+ 'type' => $domain->type,
156
+ 'expires' => $domain->expires,
157
+ 'renew' => $domain->renew,
158
+ 'timezone' => $domain->timezone,
159
+ 'trackGA' => $domain->trackGA,
160
+ 'trackConversion' => $domain->trackConversion,
161
+ );
162
+ }
163
+ return $data;
164
+ }
165
+
166
+ /**
167
+ * Allows you to sort the data by the variables set in the $_GET
168
+ *
169
+ * @return Mixed
170
+ */
171
+ private function sort_data( $a, $b ) {
172
+ // If orderby is set, use this as the sort column
173
+ $orderby = !empty($_GET['orderby']) ? sanitize_text_field($_GET['orderby']) : 'name';
174
+ // If order is set use this as the order
175
+ $order = !empty($_GET['order']) ? sanitize_text_field($_GET['order']) : 'asc';
176
+
177
+ $result = strcmp( $a[$orderby], $b[$orderby] );
178
+
179
+ if($order === 'asc') {
180
+ return $result;
181
+ }
182
+ return -$result;
183
+ }
184
+
185
+ /**
186
+ * Custom action for `cb` columns (checkboxes)
187
+ *
188
+ * @param array|object $item
189
+ * @return string|void
190
+ */
191
+ function column_cb($item) {
192
+ return sprintf(
193
+ '<input type="checkbox" name="%1$s[]" value="%2$s" />',
194
+ $this->_args['singular'],
195
+ esc_attr($item['id'])
196
+ );
197
+ }
198
+
199
+ function column_name($item) {
200
+ $actions = array();
201
+ // Let's build a link
202
+ $url = admin_url('admin.php');
203
+ $edit_link =
204
+ add_query_arg(
205
+ array( 'page' => 'call-now-button-domains', 'action' => 'edit', 'id' => $item['id'] ),
206
+ $url );
207
+ $edit_url = esc_url( $edit_link );
208
+ $actions['edit'] = '<a href="'.$edit_url.'">Edit</a>';
209
+
210
+ $delete_link = wp_nonce_url(
211
+ add_query_arg( array(
212
+ 'page' => 'call-now-button-domains',
213
+ 'action' => 'delete',
214
+ 'id' => $item['id'] ),
215
+ $url ),
216
+ 'cnb_delete_domain' );
217
+ $delete_url = esc_url( $delete_link );
218
+ $actions['delete'] = '<a href="'.$delete_url.'">Delete</a>';
219
+
220
+ // If the type is not PRO+, offer an upgrade
221
+ if ($item['type'] !== 'PROPLUS') {
222
+ $upgrade_link =
223
+ add_query_arg( array(
224
+ 'page' => 'call-now-button-domains',
225
+ 'action' => 'upgrade',
226
+ 'id' => $item['id']
227
+ ),
228
+ $url );
229
+ $upgrade_url = esc_url( $upgrade_link );
230
+ $actions['upgrade'] = '<a href="' . $upgrade_url . '" style="color: orange">Upgrade!</a>';
231
+ }
232
+
233
+ return sprintf(
234
+ '%1$s %2$s',
235
+ '<a href="'.$edit_url.'">'.esc_html($item['name']) . '</a>',
236
+ $this->row_actions($actions)
237
+ );
238
+ }
239
+
240
+ function get_bulk_actions() {
241
+ return array(
242
+ 'delete' => 'Delete',
243
+ );
244
+ }
245
+
246
+ function process_bulk_action() {
247
+ if ( isset( $_REQUEST['_wpnonce'] ) && ! empty( $_REQUEST['_wpnonce'] ) ) {
248
+ $nonce = filter_input( INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING );
249
+ $action = 'bulk-' . $this->_args['plural'];
250
+
251
+ if ( wp_verify_nonce( $nonce, $action ) ) {
252
+ $domainIds = filter_input(INPUT_POST, 'cnb_list_domain', FILTER_SANITIZE_STRING, FILTER_REQUIRE_ARRAY);
253
+ switch ($this->current_action()) {
254
+ case 'delete':
255
+ foreach ($domainIds as $domainId) {
256
+ $domain = array('id' => $domainId);
257
+ CnbAppRemote::cnb_remote_delete_domain( $domain );
258
+ }
259
+ echo '<div class="notice-success notice"><p>' . count($domainIds) . ' Domain(s) deleted</p></div>';
260
+ break;
261
+ }
262
+ }
263
+ }
264
+ }
265
+ }
266
+
267
+ function cnb_after_header() {
268
+ // Only add the "Add new" action in the overview part
269
+ $id = !empty($_GET['id']) ? sanitize_text_field($_GET['id']) : null;
270
+ $action = !empty($_GET['action']) ? sanitize_text_field($_GET['action']) : null;
271
+ if ($id === null || ($action != 'new' && $action != 'edit')) {
272
+ // Create link
273
+ $url = admin_url('admin.php');
274
+ $new_link =
275
+ add_query_arg(
276
+ array(
277
+ 'page' => 'call-now-button-domains',
278
+ 'action' => 'new',
279
+ 'id' => 'new'),
280
+ $url);
281
+ $new_url = esc_url($new_link);
282
+
283
+ echo '<a href="' . $new_url . '" class="page-title-action">Add New</a>';
284
+ }
285
+ }
286
+
287
+ /**
288
+ * Main entrypoint, used by `call-now-button.php`.
289
+ */
290
+ function cnb_admin_page_domain_overview_render() {
291
+ $id = !empty($_GET['id']) ? sanitize_text_field($_GET['id']) : null;
292
+ $action = !empty($_GET['action']) ? sanitize_text_field($_GET['action']) : null;
293
+ if ($id === null) {
294
+ cnb_admin_page_domain_overview_render_list();
295
+ } else {
296
+ switch ($action) {
297
+ case 'new':
298
+ case 'edit':
299
+ require_once dirname( __FILE__ ) . "/domain-edit.php";
300
+ cnb_admin_page_domain_edit_render();
301
+ break;
302
+ case 'upgrade':
303
+ require_once dirname( __FILE__ ) . "/domain-upgrade.php";
304
+ cnb_admin_page_domain_upgrade_render();
305
+ break;
306
+ case 'delete':
307
+ if (isset($_REQUEST['_wpnonce']) && !empty($_REQUEST['_wpnonce'])) {
308
+ $nonce = filter_input(INPUT_GET, '_wpnonce', FILTER_SANITIZE_STRING);
309
+ $action = 'cnb_delete_domain';
310
+
311
+ if (wp_verify_nonce($nonce, $action)) {
312
+ $cnb_cloud_notifications = array();
313
+ CnbAdminCloud::cnb_delete_domain( $cnb_cloud_notifications, $id );
314
+ foreach ($cnb_cloud_notifications as $cnb_cloud_notification) {
315
+ echo $cnb_cloud_notification;
316
+ }
317
+ cnb_admin_page_domain_overview_render_list();
318
+ }
319
+ }
320
+ break;
321
+ }
322
+ }
323
+ }
324
+
325
+ function cnb_admin_page_domain_overview_render_list() {
326
+ global $cnb_options, $cnb_settings;
327
+
328
+ $cnb_notices = cnb_get_notices();
329
+ $cnb_changelog = cnb_get_changelog();
330
+
331
+ $wp_list_table = new Cnb_Domain_List_Table();
332
+ $data = $wp_list_table->prepare_items();
333
+
334
+ if ($data instanceof WP_Error) {
335
+ $cnb_notices[] = cnb_admin_header_get_cloud_error($data);
336
+ } else {
337
+ add_action('cnb_header', 'cnb_add_header_domain_overview');
338
+ add_action('cnb_after_header', 'cnb_after_header');
339
+ }
340
+
341
+ echo '<div class="wrap">';
342
+ cnb_admin_header($cnb_options, $cnb_settings, $cnb_notices, $cnb_changelog);
343
+
344
+ echo '<form id="wp_list_event" method="post">';
345
+ //Prepare Table of elements
346
+
347
+ //Table of elements
348
+ $wp_list_table->display();
349
+ echo '</form>';
350
+ cnb_admin_footer();
351
+ echo '</div>';
352
+ }
src/admin/domain-upgrade.php ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname( __FILE__ ) . '/api/CnbAppRemote.php';
4
+ require_once dirname( __FILE__ ) . '/api/CnbAppRemotePayment.php';
5
+ require_once dirname( __FILE__ ) . '/api/CnbAdminCloud.php';
6
+ require_once dirname( __FILE__ ) . '/partials/admin-functions.php';
7
+ require_once dirname( __FILE__ ) . '/partials/admin-header.php';
8
+ require_once dirname( __FILE__ ) . '/partials/admin-footer.php';
9
+
10
+ function cnb_add_header_domain_upgrade() {
11
+ echo 'Upgrade domain';
12
+ }
13
+
14
+ /**
15
+ * Sort currency first, then type (so EUR->Pro, EUR->Pro+, USD->Pro, USD->Pro+)
16
+ */
17
+ function cnb_plan_sort($a, $b) {
18
+ $currency = strcmp($a->currency, $b->currency);
19
+ if ($currency === 0) {
20
+ return strcmp($a->domainType, $b->domainType);
21
+ }
22
+ return $currency;
23
+ }
24
+
25
+ function cnb_get_domain() {
26
+ $domain_id = filter_input( INPUT_GET, 'id', FILTER_SANITIZE_STRING );
27
+ $domain = new stdClass();
28
+ if (strlen($domain_id) > 0 && $domain_id != 'new') {
29
+ $domain = CnbAppRemote::cnb_remote_get_domain( $domain_id );
30
+ }
31
+ return $domain;
32
+ }
33
+
34
+ function cnb_domain_type_is_current_or_better($plan, $domain) {
35
+ if ($domain->type === 'FREE') {
36
+ return false;
37
+ }
38
+ switch ($domain->type) {
39
+ case 'PRO':
40
+ return $plan->domainType === 'PRO';
41
+ case 'PROPLUS':
42
+ return $plan->domainType === 'PRO' || $plan->domainType === 'PROPLUS';
43
+ default: // Same as 'FREE'
44
+ return false;
45
+ }
46
+ }
47
+
48
+ function cnb_print_domain_upgrade_notice_cache_flush() {
49
+ $upgradeStatus = filter_input( INPUT_GET, 'upgrade', FILTER_SANITIZE_STRING );
50
+ $checkoutSesionId = filter_input( INPUT_GET, 'checkout_session_id', FILTER_SANITIZE_STRING );
51
+ if ($upgradeStatus || $checkoutSesionId) {
52
+ // Increase the cache ID
53
+ CnbAppRemote::cnb_incr_transient_base();
54
+ }
55
+ }
56
+
57
+
58
+ function cnb_print_domain_upgrade_notice($domain, &$cnb_notices) {
59
+ $upgradeStatus = filter_input( INPUT_GET, 'upgrade', FILTER_SANITIZE_STRING );
60
+ $checkoutSesionId = filter_input( INPUT_GET, 'checkout_session_id', FILTER_SANITIZE_STRING );
61
+ if ($upgradeStatus === 'success?payment=success') {
62
+ // Get checkout Session Details
63
+ $session = CnbAppRemotePayment::cnb_remote_get_subscription_session( $checkoutSesionId );
64
+ // This results in a subscription (via ->subscriptionId), get that for ->type
65
+ $subscription = CnbAppRemotePayment::cnb_remote_get_subscription( $session->subscriptionId );
66
+ //
67
+ $message = '<div class="notice-success notice"><p>Your domain <strong>'.esc_html($domain->name).'</strong> has been upgraded to <strong>'.esc_html($subscription->type).'</strong>!</p></div>';
68
+ $cnb_notices[] = $message;
69
+ return true;
70
+ }
71
+ return false;
72
+ }
73
+
74
+ function cnb_admin_page_domain_upgrade_render() {
75
+ global $cnb_options, $cnb_settings;
76
+
77
+ add_action('cnb_header', 'cnb_add_header_domain_upgrade');
78
+
79
+ $cnb_notices = cnb_get_notices();
80
+ $cnb_changelog = cnb_get_changelog();
81
+
82
+ $domain = cnb_get_domain();
83
+ $updated = cnb_print_domain_upgrade_notice($domain, $cnb_notices);
84
+
85
+ if ($updated) {
86
+ cnb_print_domain_upgrade_notice_cache_flush();
87
+ $domain = cnb_get_domain();
88
+ }
89
+
90
+
91
+ echo '<div class="wrap">';
92
+ cnb_admin_header($cnb_options, $cnb_settings, $cnb_notices, $cnb_changelog);
93
+
94
+ $plans = CnbAppRemotePayment::cnb_remote_get_plans();
95
+ usort($plans, 'cnb_plan_sort');
96
+
97
+ $planCount = count($plans);
98
+
99
+ // If the type is missing, we assume FREE
100
+ if (empty($domain->type)) {
101
+ $domain->type = 'FREE';
102
+ }
103
+
104
+ // Stripe integration
105
+ ?>
106
+ <script src="https://js.stripe.com/v3/"></script>
107
+ <script>
108
+ function cnb_get_checkout(planId) {
109
+ jQuery('.spinner');
110
+ const data = {
111
+ 'action': 'cnb_get_checkout',
112
+ 'planId': planId,
113
+ 'domainId': jQuery('#cnb_domain_id').val()
114
+ };
115
+
116
+ jQuery.post(ajaxurl, data, function(response) {
117
+ cnb_goto_checkout(response)
118
+ });
119
+ }
120
+
121
+ function cnb_goto_checkout(checkoutSessionId) {
122
+ stripe.redirectToCheckout({ sessionId: checkoutSessionId });
123
+ }
124
+
125
+ const stripe = Stripe("<?php echo esc_js( CnbAppRemotePayment::cnb_remote_get_stripe_key()->key) ?>");
126
+ </script>
127
+
128
+ <h2><?php esc_html_e($domain->name) ?></h2>
129
+ <form id="wp_domain_upgrade" method="post">
130
+ <input type="hidden" name="cnb_domain_id" id="cnb_domain_id" value="<?php esc_attr_e($domain->id) ?>">
131
+ <p>Your current plan: <code><?php esc_html_e($domain->type) ?></code></p>
132
+ <h2>Select Plan</h2>
133
+ <table class="cnb-pricing">
134
+ <tr>
135
+ <?php foreach ($plans as $plan) { ?>
136
+ <td>
137
+ <h1><?php echo $plan->domainType === 'PROPLUS' ? 'PRO+' : 'PRO'; ?></h1>
138
+ <div class="cnb_font_120 cnb_font_bold"><?php echo $plan->currency === 'EUR' ? '€' : '$' ?><?php echo round($plan->price / 12.0, 2) ?>/month</div>
139
+ billed annually<br />
140
+ <?php if ($plan->domainType === $domain->type) { ?>
141
+ <strong>CURRENT PLAN</strong>
142
+ <?php } ?>
143
+ </td>
144
+ <?php } ?>
145
+ </tr>
146
+ <tr><?php echo str_repeat('<td>Unlimited buttons</td>', $planCount); ?></tr>
147
+ <tr><?php echo str_repeat('<td>Scheduling</td>', $planCount); ?></tr>
148
+ <tr><?php echo str_repeat('<td>Multi button</td>', $planCount); ?></tr>
149
+ <tr><?php echo str_repeat('<td>Button bar</td>', $planCount); ?></tr>
150
+ <tr>
151
+ <?php foreach ($plans as $plan) {
152
+ if ($plan->domainType === 'PRO') { ?>
153
+ <td>
154
+ <svg class="MuiSvgIcon-root" focusable="false" viewBox="0 0 24 24" aria-hidden="true">
155
+ <path d="M6.54 5c.06.89.21 1.76.45 2.59l-1.2 1.2c-.41-1.2-.67-2.47-.76-3.79h1.51m9.86 12.02c.85.24 1.72.39 2.6.45v1.49c-1.32-.09-2.59-.35-3.8-.75l1.2-1.19M7.5 3H4c-.55 0-1 .45-1 1 0 9.39 7.61 17 17 17 .55 0 1-.45 1-1v-3.49c0-.55-.45-1-1-1-1.24 0-2.45-.2-3.57-.57-.1-.04-.21-.05-.31-.05-.26 0-.51.1-.71.29l-2.2 2.2c-2.83-1.45-5.15-3.76-6.59-6.59l2.2-2.2c.28-.28.36-.67.25-1.02C8.7 6.45 8.5 5.25 8.5 4c0-.55-.45-1-1-1z"></path>
156
+ </svg>
157
+ </td>
158
+ <?php } else { ?>
159
+ <td>
160
+ <svg class="MuiSvgIcon-root" focusable="false" viewBox="0 0 24 24" aria-hidden="true">
161
+ <path d="M6.54 5c.06.89.21 1.76.45 2.59l-1.2 1.2c-.41-1.2-.67-2.47-.76-3.79h1.51m9.86 12.02c.85.24 1.72.39 2.6.45v1.49c-1.32-.09-2.59-.35-3.8-.75l1.2-1.19M7.5 3H4c-.55 0-1 .45-1 1 0 9.39 7.61 17 17 17 .55 0 1-.45 1-1v-3.49c0-.55-.45-1-1-1-1.24 0-2.45-.2-3.57-.57-.1-.04-.21-.05-.31-.05-.26 0-.51.1-.71.29l-2.2 2.2c-2.83-1.45-5.15-3.76-6.59-6.59l2.2-2.2c.28-.28.36-.67.25-1.02C8.7 6.45 8.5 5.25 8.5 4c0-.55-.45-1-1-1z"></path>
162
+ </svg>
163
+ <svg class="MuiSvgIcon-root" focusable="false" viewBox="0 0 24 24" aria-hidden="true">
164
+ <path d="M22 6c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6zm-2 0l-8 5-8-5h16zm0 12H4V8l8 5 8-5v10z"></path>
165
+ </svg>
166
+ <svg class="MuiSvgIcon-root" focusable="false" viewBox="0 0 24 24" aria-hidden="true">
167
+ <path d="M16.75,13.96C17,14.09 17.16,14.16 17.21,14.26C17.27,14.37 17.25,14.87 17,15.44C16.8,16 15.76,16.54 15.3,16.56C14.84,16.58 14.83,16.92 12.34,15.83C9.85,14.74 8.35,12.08 8.23,11.91C8.11,11.74 7.27,10.53 7.31,9.3C7.36,8.08 8,7.5 8.26,7.26C8.5,7 8.77,6.97 8.94,7H9.41C9.56,7 9.77,6.94 9.96,7.45L10.65,9.32C10.71,9.45 10.75,9.6 10.66,9.76L10.39,10.17L10,10.59C9.88,10.71 9.74,10.84 9.88,11.09C10,11.35 10.5,12.18 11.2,12.87C12.11,13.75 12.91,14.04 13.15,14.17C13.39,14.31 13.54,14.29 13.69,14.13L14.5,13.19C14.69,12.94 14.85,13 15.08,13.08L16.75,13.96M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22C10.03,22 8.2,21.43 6.65,20.45L2,22L3.55,17.35C2.57,15.8 2,13.97 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12C4,13.72 4.54,15.31 5.46,16.61L4.5,19.5L7.39,18.54C8.69,19.46 10.28,20 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z"></path>
168
+ </svg>
169
+ <svg class="MuiSvgIcon-root" focusable="false" viewBox="0 0 24 24" aria-hidden="true">
170
+ <path d="M12 12c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm6-1.8C18 6.57 15.35 4 12 4s-6 2.57-6 6.2c0 2.34 1.95 5.44 6 9.14 4.05-3.7 6-6.8 6-9.14zM12 2c4.2 0 8 3.22 8 8.2 0 3.32-2.67 7.25-8 11.8-5.33-4.55-8-8.48-8-11.8C4 5.22 7.8 2 12 2z"></path>
171
+ </svg>
172
+ <svg class="MuiSvgIcon-root" focusable="false" viewBox="0 0 24 24" aria-hidden="true">
173
+ <path d="M17 7h-4v2h4c1.65 0 3 1.35 3 3s-1.35 3-3 3h-4v2h4c2.76 0 5-2.24 5-5s-2.24-5-5-5zm-6 8H7c-1.65 0-3-1.35-3-3s1.35-3 3-3h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-2zm-3-4h8v2H8z"></path>
174
+ </svg>
175
+ <svg class="MuiSvgIcon-root" focusable="false" viewBox="0 0 24 24" aria-hidden="true">
176
+ <path d="M12,2A3,3 0 0,0 9,5C9,6.27 9.8,7.4 11,7.83V10H8V12H11V18.92C9.16,18.63 7.53,17.57 6.53,16H8V14H3V19H5V17.3C6.58,19.61 9.2,21 12,21C14.8,21 17.42,19.61 19,17.31V19H21V14H16V16H17.46C16.46,17.56 14.83,18.63 13,18.92V12H16V10H13V7.82C14.2,7.4 15,6.27 15,5A3,3 0 0,0 12,2M12,4A1,1 0 0,1 13,5A1,1 0 0,1 12,6A1,1 0 0,1 11,5A1,1 0 0,1 12,4Z"></path>
177
+ </svg>
178
+ </td>
179
+ <?php } } ?>
180
+ </tr>
181
+ <tr><?php
182
+ // Get link based on Stripe checkoutSessionId
183
+ foreach ( $plans as $plan ) {
184
+ echo '<td><button class="button button-secondary" type="button" ';
185
+ if ( cnb_domain_type_is_current_or_better($plan, $domain) ) {
186
+ echo 'disabled="disabled"';
187
+ } else {
188
+ echo 'onclick="cnb_get_checkout(\'' . esc_js($plan->id) . '\')"';
189
+ }
190
+ echo '>Upgrade</button></td>';
191
+ }
192
+ ?></tr>
193
+ </table>
194
+ </form>
195
+ <?php
196
+ cnb_admin_footer();
197
+ echo '</div>';
198
+ }
src/admin/legacy-edit.php ADDED
@@ -0,0 +1,246 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once dirname( __FILE__ ) . '/partials/admin-functions.php';
3
+ require_once dirname( __FILE__ ) . '/partials/admin-header.php';
4
+ require_once dirname( __FILE__ ) . '/partials/admin-footer.php';
5
+
6
+ function cnb_add_header_legacy_edit() {
7
+ echo CNB_NAME . ' <span class="cnb-version">v' . CNB_VERSION . '</span>';
8
+ }
9
+
10
+ function cnb_create_tab_url($tab) {
11
+ $url = admin_url('admin.php');
12
+ $tab_link =
13
+ add_query_arg(
14
+ array(
15
+ 'page' => 'call-now-button',
16
+ 'action' => 'edit',
17
+ 'tab' => $tab),
18
+ $url );
19
+ return esc_url( $tab_link );
20
+ }
21
+
22
+ function cnb_admin_page_legacy_edit_render() {
23
+ global $cnb_options, $cnb_settings;
24
+
25
+ add_action('cnb_header', 'cnb_add_header_legacy_edit');
26
+
27
+ $cnb_notices = cnb_get_notices();
28
+ $cnb_changelog = cnb_get_changelog();
29
+
30
+ echo '<div class="wrap">';
31
+ cnb_admin_header($cnb_options, $cnb_settings, $cnb_notices, $cnb_changelog);
32
+ ?>
33
+
34
+ <h2 class="nav-tab-wrapper">
35
+ <a href="<?php echo cnb_create_tab_url('basic_options') ?>"
36
+ class="nav-tab <?php echo cnb_is_active_tab('basic_options') ?>">Basics</a>
37
+ <a href="<?php echo cnb_create_tab_url('extra_options') ?>"
38
+ class="nav-tab <?php echo cnb_is_active_tab('extra_options') ?>">Presentation</a>
39
+ </h2>
40
+ <form method="post" action="<?php echo esc_url( admin_url('options.php') ); ?>" class="cnb-container">
41
+ <?php settings_fields('cnb_options'); ?>
42
+ <table class="form-table <?php echo cnb_is_active_tab('basic_options') ?>">
43
+ <tr>
44
+ <th colspan="2"><h2>Basic Settings</h2></th>
45
+ </tr>
46
+ <tr>
47
+ <th scope="row">Phone number:<a href="<?php echo CNB_SUPPORT; ?>phone-number/<?php cnb_utm_params("question-mark", "phone-number"); ?>" target="_blank" class="cnb-nounderscore">
48
+ <span class="dashicons dashicons-editor-help"></span>
49
+ </a></th>
50
+ <td><input type="text" name="cnb[number]" value="<?php esc_attr_e($cnb_options['number']) ?>" /></td>
51
+ </tr>
52
+ <tr class="button-text">
53
+ <th scope="row">Button text <small style="font-weight: 400">(optional)</small>:<a href="<?php echo CNB_SUPPORT; ?>using-text-buttons/<?php cnb_utm_params("question-mark", "using-text-buttons"); ?>" target="_blank" class="cnb-nounderscore">
54
+ <span class="dashicons dashicons-editor-help"></span>
55
+ </a></th>
56
+ <td>
57
+ <input id="buttonTextField" type="text" name="cnb[text]" value="<?php esc_attr_e($cnb_options['text']) ?>" maxlength="30" />
58
+ <p class="description">Leave this field empty to only show an icon.</p>
59
+ </td>
60
+ </tr>
61
+ </table>
62
+
63
+ <table class="form-table <?php echo cnb_is_active_tab('extra_options') ?>">
64
+ <tr>
65
+ <th colspan="2"><h2>Presentation Settings</h2></th>
66
+ </tr>
67
+
68
+ <tr>
69
+ <th scope="row">Button color:</th>
70
+ <td><input name="cnb[color]" type="text" value="<?php esc_attr_e($cnb_options['color']) ?>" class="cnb-color-field" data-default-color="#009900" /></td>
71
+ </tr>
72
+ <tr>
73
+ <th scope="row">Icon color:</th>
74
+ <td><input name="cnb[iconcolor]" type="text" value="<?php esc_attr_e($cnb_options['iconcolor']) ?>" class="cnb-iconcolor-field" data-default-color="#ffffff" /></td>
75
+ </tr>
76
+ <tr>
77
+ <th scope="row">Position: <a href="<?php echo CNB_SUPPORT; ?>button-position/<?php cnb_utm_params("question-mark", "button-position"); ?>" target="_blank" class="cnb-nounderscore">
78
+ <span class="dashicons dashicons-editor-help"></span>
79
+ </a></th>
80
+ <td class="appearance">
81
+ <div class="appearance-options">
82
+ <div class="cnb-radio-item">
83
+ <input type="radio" id="appearance1" name="cnb[appearance]" value="right" <?php checked('right', $cnb_options['appearance']); ?>>
84
+ <label title="right" for="appearance1">Right corner</label>
85
+ </div>
86
+ <div class="cnb-radio-item">
87
+ <input type="radio" id="appearance2" name="cnb[appearance]" value="left" <?php checked('left', $cnb_options['appearance']); ?>>
88
+ <label title="left" for="appearance2">Left corner</label>
89
+ </div>
90
+ <div class="cnb-radio-item">
91
+ <input type="radio" id="appearance3" name="cnb[appearance]" value="middle" <?php checked('middle', $cnb_options['appearance']); ?>>
92
+ <label title="middle" for="appearance3">Center bottom</label>
93
+ </div>
94
+ <div class="cnb-radio-item">
95
+ <input type="radio" id="appearance4" name="cnb[appearance]" value="full" <?php checked('full', $cnb_options['appearance']); ?>>
96
+ <label title="full" for="appearance4">Full bottom</label>
97
+ </div>
98
+
99
+ <!-- Extra placement options -->
100
+ <br class="cnb-extra-placement">
101
+ <div class="cnb-radio-item cnb-extra-placement <?php echo $cnb_options['appearance'] == "mright" ? "cnb-extra-active" : ""; ?>">
102
+ <input type="radio" id="appearance5" name="cnb[appearance]" value="mright" <?php checked('mright', $cnb_options['appearance']); ?>>
103
+ <label title="mright" for="appearance5">Middle right</label>
104
+ </div>
105
+ <div class="cnb-radio-item cnb-extra-placement <?php echo $cnb_options['appearance'] == "mleft" ? "cnb-extra-active" : ""; ?>">
106
+ <input type="radio" id="appearance6" name="cnb[appearance]" value="mleft" <?php checked('mleft', $cnb_options['appearance']); ?>>
107
+ <label title="mleft" for="appearance6">Middle left </label>
108
+ </div>
109
+ <br class="cnb-extra-placement">
110
+ <div class="cnb-radio-item cnb-extra-placement <?php echo $cnb_options['appearance'] == "tright" ? "cnb-extra-active" : ""; ?>">
111
+ <input type="radio" id="appearance7" name="cnb[appearance]" value="tright" <?php checked('tright', $cnb_options['appearance']); ?>>
112
+ <label title="tright" for="appearance7">Top right corner</label>
113
+ </div>
114
+ <div class="cnb-radio-item cnb-extra-placement <?php echo $cnb_options['appearance'] == "tleft" ? "cnb-extra-active" : ""; ?>">
115
+ <input type="radio" id="appearance8" name="cnb[appearance]" value="tleft" <?php checked('tleft', $cnb_options['appearance']); ?>>
116
+ <label title="tleft" for="appearance8">Top left corner</label>
117
+ </div>
118
+ <div class="cnb-radio-item cnb-extra-placement <?php echo $cnb_options['appearance'] == "tmiddle" ? "cnb-extra-active" : ""; ?>">
119
+ <input type="radio" id="appearance9" name="cnb[appearance]" value="tmiddle" <?php checked('tmiddle', $cnb_options['appearance']); ?>>
120
+ <label title="tmiddle" for="appearance9">Center top</label>
121
+ </div>
122
+ <div class="cnb-radio-item cnb-extra-placement <?php echo $cnb_options['appearance'] == "tfull" ? "cnb-extra-active" : ""; ?>">
123
+ <input type="radio" id="appearance10" name="cnb[appearance]" value="tfull" <?php checked('tfull', $cnb_options['appearance']); ?>>
124
+ <label title="tfull" for="appearance10">Full top</label>
125
+ </div>
126
+ <a href="#" id="cnb-more-placements">More placement options...</a>
127
+ <!-- END extra placement options -->
128
+ </div>
129
+
130
+ <div id="hideIconTR">
131
+ <br>
132
+ <input type="hidden" name="cnb[hideIcon]" value="0" />
133
+ <input id="hide_icon" type="checkbox" name="cnb[hideIcon]" value="1" <?php checked('1', $cnb_options['hideIcon']); ?>>
134
+ <label title="right" for="hide_icon">Remove icon</label>
135
+ </div>
136
+ </td>
137
+ </tr>
138
+ <tr class="appearance">
139
+ <th scope="row">Limit appearance: <a href="<?php echo CNB_SUPPORT; ?>limit-appearance/<?php cnb_utm_params("question-mark", "limit-appearance"); ?>" target="_blank" class="cnb-nounderscore">
140
+ <span class="dashicons dashicons-editor-help"></span>
141
+ </a></th>
142
+ <td>
143
+ <input type="text" name="cnb[show]" value="<?php esc_attr_e($cnb_options['show']) ?>" placeholder="E.g. 14, 345" />
144
+ <p class="description">Enter IDs of the posts &amp; pages, separated by commas (leave blank for all). <a href="<?php echo CNB_SUPPORT; ?>limit-appearance/<?php cnb_utm_params("question-mark", "limit-appearance"); ?>">Learn more...</a></p>
145
+ <div class="cnb-radio-item">
146
+ <input id="limit1" type="radio" name="cnb[limit]" value="include" <?php checked('include', $cnb_options['limit']);?> />
147
+ <label for="limit1">Limit to these posts and pages.</label>
148
+ </div>
149
+ <div class="cnb-radio-item">
150
+ <input id="limit2" type="radio" name="cnb[limit]" value="exclude" <?php checked('exclude', $cnb_options['limit']);?> />
151
+ <label for="limit2">Exclude these posts and pages.</label>
152
+ </div>
153
+ <br>
154
+ <div>
155
+ <input type="hidden" name="cnb[frontpage]" value="0" />
156
+ <input id="frontpage" type="checkbox" name="cnb[frontpage]" value="1" <?php checked('1', $cnb_options['frontpage']); ?>>
157
+ <label title="right" for="frontpage">Hide button on front page</label>
158
+ </div>
159
+ </td>
160
+ </tr>
161
+ </table>
162
+ <table class="form-table <?php echo cnb_is_active_tab('advanced_options') ?>">
163
+ <tr>
164
+ <th colspan="2"><h2>Advanced Settings</h2></th>
165
+ </tr>
166
+ <tr>
167
+ <th scope="row">Click tracking: <a href="<?php echo CNB_SUPPORT; ?>click-tracking/<?php cnb_utm_params("question-mark", "click-tracking"); ?>" target="_blank" class="cnb-nounderscore">
168
+ <span class="dashicons dashicons-editor-help"></span>
169
+ </a></th>
170
+ <td>
171
+ <div class="cnb-radio-item">
172
+ <input id="tracking3" type="radio" name="cnb[tracking]" value="0" <?php checked('0', $cnb_options['tracking']); ?> />
173
+ <label for="tracking3">Disabled</label>
174
+ </div>
175
+ <div class="cnb-radio-item">
176
+ <input id="tracking4" type="radio" name="cnb[tracking]" value="3" <?php checked('3', $cnb_options['tracking']); ?> />
177
+ <label for="tracking4">Latest Google Analytics (gtag.js)</label>
178
+ </div>
179
+ <div class="cnb-radio-item">
180
+ <input id="tracking1" type="radio" name="cnb[tracking]" value="2" <?php checked('2', $cnb_options['tracking']); ?> />
181
+ <label for="tracking1">Google Universal Analytics (analytics.js)</label>
182
+ </div>
183
+ <div class="cnb-radio-item">
184
+ <input id="tracking2" type="radio" name="cnb[tracking]" value="1" <?php checked('1', $cnb_options['tracking']); ?> />
185
+ <label for="tracking2">Classic Google Analytics (ga.js)</label>
186
+ </div>
187
+ <p class="description">Using Google Tag Manager? Set up click tracking in GTM. <a href="<?php echo CNB_SUPPORT; ?>click-tracking/google-tag-manager-event-tracking/<?php cnb_utm_params("description_link", "google-tag-manager-event-tracking"); ?>" target="_blank">Learn how to do this...</a></p>
188
+ </td>
189
+ </tr>
190
+ <tr>
191
+ <th scope="row">Google Ads: <a href="<?php echo CNB_SUPPORT; ?>google-ads/<?php cnb_utm_params("question-mark", "google-ads"); ?>" target="_blank" class="cnb-nounderscore">
192
+ <span class="dashicons dashicons-editor-help"></span>
193
+ </a></th>
194
+ <td class="conversions">
195
+ <div class="cnb-radio-item">
196
+ <input name="cnb[conversions]" type="radio" value="0" <?php checked('0', $cnb_options['conversions']); ?> /> <label for="conversions">Off </label>
197
+ </div>
198
+ <div class="cnb-radio-item">
199
+ <input name="cnb[conversions]" type="radio" value="1" <?php checked('1', $cnb_options['conversions']); ?> /> <label for="conversions">Conversion Tracking using Google's global site tag </label>
200
+ </div>
201
+ <div class="cnb-radio-item">
202
+ <input name="cnb[conversions]" type="radio" value="2" <?php checked('2', $cnb_options['conversions']); ?> /> <label for="conversions">Conversion Tracking using JavaScript</label>
203
+ </div>
204
+ <p class="description">Select this option if you want to track clicks on the button as Google Ads conversions. This option requires the Event snippet to be present on the page. <a href="https">Learn more...</a></p>
205
+ </td>
206
+ </tr>
207
+ <tr class="zoom">
208
+ <th scope="row">Button size <span id="cnb_slider_value"></span>:</th>
209
+ <td>
210
+ <label class="cnb_slider_value">Smaller&nbsp;&laquo;&nbsp;</label>
211
+ <input type="range" min="0.7" max="1.3" name="cnb[zoom]" value="<?php esc_attr_e($cnb_options['zoom']) ?>" class="slider" id="cnb_slider" step="0.1">
212
+ <label class="cnb_slider_value">&nbsp;&raquo;&nbsp;Bigger</label>
213
+ </td>
214
+ </tr>
215
+ <tr class="z-index">
216
+ <th scope="row">Order (<span id="cnb_order_value"></span>): <a href="https://callnowbutton.com/set-order/" target="_blank" class="cnb-nounderscore">
217
+ <span class="dashicons dashicons-editor-help"></span>
218
+ </a></th>
219
+ <td>
220
+ <label class="cnb_slider_value">Backwards&nbsp;&laquo;&nbsp;</label>
221
+ <input type="range" min="1" max="10" name="cnb[z-index]" value="<?php esc_attr_e($cnb_options['z-index']) ?>" class="slider2" id="cnb_order_slider" step="1">
222
+ <label class="cnb_slider_value">&nbsp;&raquo;&nbsp;Front</label>
223
+ <p class="description">The default (and recommended) value is all the way to the front so the button sits on top of everything else. In case you have a specific usecase where you want something else to sit in front of the Call Now Button (e.g. a chat window or a cookie notice) you can move this backwards one step at a time to adapt it to your situation.</p>
224
+ </td>
225
+ </tr>
226
+ <?php if($cnb_options['classic'] == 1) { ?>
227
+ <tr class="classic">
228
+ <th scope="row">Classic button: <a href="https://callnowbutton.com/new-button-design/<?php cnb_utm_params("question-mark", "new-button-design"); ?>" target="_blank" class="cnb-nounderscore">
229
+ <span class="dashicons dashicons-editor-help"></span>
230
+ </a></th>
231
+ <td>
232
+ <input type="hidden" name="cnb[classic]" value="0" />
233
+ <input id="classic" name="cnb[classic]" type="checkbox" value="1" <?php checked('1', $cnb_options['classic']); ?> /> <label title="Enable" for="classic">Active</label>
234
+ </td>
235
+ </tr>
236
+ <?php } ?>
237
+ </table>
238
+
239
+ <input type="hidden" name="cnb[version]" value="<?php echo CNB_VERSION; ?>" />
240
+ <p class="submit"><input type="submit" class="button-primary" value="<?php _e('Save Changes') ?>" /></p>
241
+ </form>
242
+
243
+ <?php
244
+ cnb_admin_footer();
245
+ echo '</div>';
246
+ }
src/admin/partials/admin-footer.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ function cnb_admin_footer() {
4
+ global $cnb_options;
5
+
6
+ $beta = '';
7
+ if (isset($cnb_options['cloud_beta_enabled']) && $cnb_options['cloud_beta_enabled']) {
8
+ $beta = '<span class="cnb_footer_beta">beta mode</span>';
9
+ }
10
+ ?>
11
+ <div class="feedback-collection">
12
+ <div class="cnb-clear"></div>
13
+ <p class="cnb-url cnb-center"><a href="<?php echo CNB_WEBSITE; ?>" target="_blank">Call Now Button</a><?php echo $beta ?></p>
14
+ <p class="cnb-center">Version <?php echo CNB_VERSION; ?></p>
15
+ <p class="cnb-center cnb-spacing">
16
+ <a href="<?php echo CNB_SUPPORT;
17
+ cnb_utm_params("footer-links", "support"); ?>" target="_blank" title="Support">Support</a> &middot;
18
+ <a href="<?php echo CNB_WEBSITE; ?>feature-request/<?php cnb_utm_params("footer-links", "suggestions"); ?>"
19
+ target="_blank" title="Feature Requests">Suggestions</a> &middot;
20
+ <strong><a href="https://www.paypal.com/paypalme/jgrietveld/" target="_blank" title="Thanks or your support">Donate</a></strong>
21
+ </p>
22
+ <?php
23
+ global $cnb_remoted_traces, $cnb_options;
24
+ if (isset($cnb_options['footer_show_traces']) && $cnb_options['footer_show_traces'] == 1 &&
25
+ isset($cnb_options['advanced_view']) && $cnb_options['advanced_view'] == 1 &&
26
+ isset($cnb_remoted_traces)) {
27
+ echo '<p>';
28
+ $traces = $cnb_remoted_traces->getTraces();
29
+ echo '<strong>'.count($traces) . '</strong> remote calls executed';
30
+ $totaltime = 0.0;
31
+ foreach ($traces as $trace) {
32
+ $totaltime += (float) $trace->getTime();
33
+ }
34
+ echo ' in <strong>' . $totaltime . '</strong>sec:<br />';
35
+
36
+ echo '<ul>';
37
+ foreach ($traces as $trace) {
38
+ echo '<li>';
39
+ echo '<code>' . $trace->getEndpoint() . '</code> in <strong>' . $trace->getTime() . '</strong>sec';
40
+ if ($trace->isCacheHit()) { echo ' (from cache)'; }
41
+ echo '.</li>';
42
+ }
43
+ echo '</ul>';
44
+
45
+ echo '</p>';
46
+ }
47
+ ?>
48
+ </div>
49
+ <?php }
src/admin/partials/admin-functions.php ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ function cnb_get_active_tab_name() {
4
+ return isset($_GET['tab']) ? filter_input( INPUT_GET, 'tab', FILTER_SANITIZE_STRING ) : 'basic_options';
5
+ }
6
+ function cnb_is_active_tab($tab_name) {
7
+ $active_tab = cnb_get_active_tab_name();
8
+ return $active_tab === $tab_name ? 'nav-tab-active' : '';
9
+ }
10
+
11
+ function cnb_get_changelog() {
12
+ return array(
13
+ '0.4.4' => 'Small UI improvements',
14
+ '0.4.3' => 'Critical fix',
15
+ '0.4.2' => 'Button styling adjustments, security improvements',
16
+ '0.4.0' => array(
17
+ 'Text bubbles for standard buttons',
18
+ 'Set the icon color',
19
+ 'Google Ads conversion tracking',
20
+ 'Tabbed admin interface',
21
+ '6 additional button locations, small button design changes',
22
+ 'Added support articles for (nearly) all settings',
23
+ 'Control visibility on front page',
24
+ 'Plus a bunch of smaller fixes. Enjoy!'),
25
+ '0.3.6' => 'Small validation fixes and zoom now controls icon size in full width buttons.',
26
+ '0.3.5' => 'Small JS fix',
27
+ '0.3.4' => 'Option to resize your button and change where it sits in the stack order (z-index).',
28
+ '0.3.3' => 'Some small improvements.',
29
+ '0.3.2' => 'Option to hide icon in text button, small bug fixes.',
30
+ '0.3.1' => 'You can now add text to your button and it\'s possible to switch between including and excluding specific pages.',
31
+ '0.3.0' => 'Option to add text to your button.',
32
+ '0.2.1' => 'Some small fixes',
33
+ '0.2.0' => 'The Call Now Button has a new look!'
34
+ );
35
+ }
36
+
37
+ /**
38
+ * Return an array of all ButtonTypes
39
+ *
40
+ * @return string[] array of ButtonTypes to their nice names
41
+ */
42
+ function cnb_get_button_types() {
43
+ return array(
44
+ 'SINGLE' => 'Single button',
45
+ 'FULL' => 'Button bar',
46
+ 'MULTI' => 'Multi button',
47
+ );
48
+ }
49
+
50
+ /**
51
+ * Return an array of all ActionTypes
52
+ *
53
+ * @return string[] array of ActionType to their nice names
54
+ */
55
+ function cnb_get_action_types() {
56
+ return array(
57
+ 'PHONE' => 'Phone',
58
+ 'EMAIL' => 'Email',
59
+ 'ANCHOR' => 'Anchor',
60
+ 'LINK' => 'Link',
61
+ 'MAP' => 'Google Maps',
62
+ 'WHATSAPP' => 'Whatsapp',
63
+ );
64
+ }
65
+
66
+ function cnb_get_condition_filter_types() {
67
+ return array(
68
+ 'INCLUDE' => 'Include',
69
+ 'EXCLUDE' => 'Exclude',
70
+ );
71
+ }
72
+
73
+ function cnb_get_condition_match_types() {
74
+ return array(
75
+ 'SIMPLE' => 'Page path is:',
76
+ 'EXACT' => 'Page URL is:',
77
+ 'SUBSTRING' => 'Page URL contains:',
78
+ 'REGEX' => 'Page URL matches RegEx:',
79
+ );
80
+ }
81
+
82
+ /**
83
+ * @param $original
84
+ *
85
+ * @return array
86
+ */
87
+ function cnb_create_days_of_week_array($original) {
88
+ // If original does not exist, leave it as it is
89
+ if ($original === null || !is_array($original)) {
90
+ return $original;
91
+ }
92
+ $result = array(false, false, false, false, false, false, false);
93
+ foreach ($result as $day_of_week_index => $day_of_week) {
94
+ $result[$day_of_week_index] =
95
+ isset($original[$day_of_week_index]) && $original[$day_of_week_index] === "true" ?
96
+ true :
97
+ $day_of_week;
98
+ }
99
+ return $result;
100
+ }
src/admin/partials/admin-header.php ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ function cnb_admin_header_get_cloud_error($error) {
4
+ $url = admin_url('admin.php');
5
+ $redirect_link =
6
+ add_query_arg(
7
+ array(
8
+ 'page' => 'call-now-button-settings',
9
+ ),
10
+ $url );
11
+ $redirect_url = esc_url( $redirect_link );
12
+ $errorNotice = '<div class="notice notice-error">
13
+ <h2 class="title">API Key invalid</h2>
14
+ <p>You have enabled Cloud Hosting, but you need a valid API key from CallNowButtom</p>
15
+ <p>Go to <a href="'.$redirect_url.'">Settings</a> for instructions.</p>';
16
+ $errorNotice .= CnbAdminCloud::cnb_admin_get_error_message_details( $error );
17
+ $errorNotice .= '</div>';
18
+ return $errorNotice;
19
+ }
20
+
21
+ function cnb_admin_header( $cnb_options, $cnb_settings, $cnb_cloud_notifications = array(), $cnb_changelog = array() ) {
22
+ echo '<h1 class="wp-heading-inline">';
23
+ do_action( 'cnb_header' );
24
+ do_action( 'cnb_after_header' );
25
+ echo '</h1>';
26
+
27
+ echo '<!--## NOTIFICATION BARS ## -->';
28
+
29
+ // Display notification that the button is active or inactive
30
+ if ( $cnb_options['active'] != 1 ) {
31
+ echo '<div class="notice-error notice"><p>The Call Now Button is currently <b>disabled</b>. Change the <i>Button status</i> under <a href="'.admin_url('admin.php?page=call-now-button-settings').'">Settings</a> to enable.</p></div>';
32
+ }
33
+
34
+ // Display notification that there's a caching plugin active
35
+ if ( isset( $_GET['settings-updated'] ) ) {
36
+ $cnb_caching_check = cnb_check_for_caching();
37
+ if ( $cnb_caching_check[0] == true ) {
38
+ echo '<div class="notice-error notice"><p><span class="dashicons dashicons-warning"></span> Your website is using a <strong><i>Caching Plugin</i></strong> (' . $cnb_caching_check[1] . '). If you\'re not seeing your button or your changes, make sure you empty your cache first.</p></div>';
39
+ }
40
+ }
41
+
42
+ // Show the notifications after updating the cloud
43
+ if ( is_array( $cnb_cloud_notifications ) ) {
44
+ foreach ( $cnb_cloud_notifications as $cnb_cloud_notification ) {
45
+ echo $cnb_cloud_notification;
46
+ }
47
+ }
48
+
49
+ // inform existing users about updates to the button
50
+ if ( $cnb_settings['updated'][0] ) {
51
+ echo '<div class="notice-warning notice is-dismissible">';
52
+ $cnb_old_version = $cnb_settings['updated'][1];
53
+ echo '<h3>' . CNB_NAME . ' has been updated!</h3><h4>What\'s new?</h4>';
54
+ // Only on first run after update show list of changes since last update
55
+ foreach ( $cnb_changelog as $key => $value ) {
56
+ if ( $key > $cnb_old_version ) {
57
+ echo '<h3>' . esc_html($key) . '</h3>';
58
+ if ( is_array( $value ) ) {
59
+ foreach ( $value as $item ) {
60
+ echo '<p><span class="dashicons dashicons-yes"></span> ' . esc_html($item) . '</p>';
61
+ }
62
+ } else {
63
+ echo '<p><span class="dashicons dashicons-yes"></span> ' . esc_html($value) . '</p>';
64
+ }
65
+ }
66
+ }
67
+ echo '</div>';
68
+ }
69
+ }
src/admin/settings.php ADDED
@@ -0,0 +1,471 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname( __FILE__ ) . '/api/CnbAppRemote.php';
4
+ require_once dirname( __FILE__ ) . '/api/CnbAdminCloud.php';
5
+ require_once dirname( __FILE__ ) . '/domain-edit.php';
6
+ require_once dirname( __FILE__ ) . '/partials/admin-functions.php';
7
+ require_once dirname( __FILE__ ) . '/partials/admin-header.php';
8
+ require_once dirname( __FILE__ ) . '/partials/admin-footer.php';
9
+
10
+ function cnb_add_header_settings() {
11
+ echo 'Settings';
12
+ }
13
+
14
+ function cnb_settings_create_tab_url($tab) {
15
+ $url = admin_url('admin.php');
16
+ $tab_link =
17
+ add_query_arg(
18
+ array(
19
+ 'page' => 'call-now-button-settings',
20
+ 'tab' => $tab),
21
+ $url );
22
+ return esc_url( $tab_link );
23
+ }
24
+
25
+ function cnb_settings_options_validate($input) {
26
+ $messages = array();
27
+ $messages[] = '<div class="notice-success notice is-dismissible"><p>Your settings have been updated!</p></div>';
28
+
29
+ // When beta mode is disabled, disable the cloud as well
30
+ if (array_key_exists('cloud_beta_enabled', $input) && $input['cloud_beta_enabled'] == 0) {
31
+ $input['cloud_enabled'] = 0;
32
+ $input['status'] = 'enabled';
33
+ }
34
+
35
+ // Since "active" and "cloud_enabled" have been merged into "status", we have to deal with that
36
+ if (array_key_exists('status', $input)) {
37
+ switch ($input['status']) {
38
+ case 'disabled':
39
+ $input['active'] = 0;
40
+ $input['cloud_enabled'] = 0;
41
+ break;
42
+ case 'enabled':
43
+ $input['active'] = 1;
44
+ $input['cloud_enabled'] = 0;
45
+ break;
46
+ case 'cloud':
47
+ $input['active'] = 1;
48
+ $input['cloud_enabled'] = 1;
49
+ break;
50
+ }
51
+ }
52
+
53
+ // Cloud Domain settings can be set here as well
54
+ if(array_key_exists('domain', $_POST) &&
55
+ array_key_exists('cloud_enabled', $input) && $input['cloud_enabled'] === 1) {
56
+ $domain = $_POST['domain'];
57
+ $transient_id = null;
58
+ cnb_admin_page_domain_edit_process_domain($domain, $transient_id);
59
+ $messages = array_merge($messages, get_transient($transient_id));
60
+
61
+ // Remove from settings
62
+ unset($input['domain']);
63
+ }
64
+
65
+ $updated_options = array_merge(get_option('cnb'), $input);
66
+ if (isset($updated_options['cloud_enabled']) && $updated_options['cloud_enabled'] === '1') {
67
+ $cloud_id = CnbAdminCloud::cnb_set_default_option_for_cloud( $updated_options );
68
+ if ($cloud_id != null) {
69
+ $updated_options['cloud_use_id'] = $cloud_id;
70
+ }
71
+ }
72
+
73
+ $transient_id = 'cnb-options';
74
+ $_GET['tid'] = $transient_id;
75
+ set_transient($transient_id, $messages, HOUR_IN_SECONDS);
76
+
77
+ return $updated_options;
78
+ }
79
+
80
+ /**
81
+ * Only true if beta is in the URL or beta mode is enabled via the options
82
+ * @return bool
83
+ */
84
+ function cnb_is_beta_user_via_url() {
85
+ return filter_input( INPUT_GET, 'beta', FILTER_SANITIZE_STRING ) !== null;
86
+ }
87
+
88
+ /**
89
+ * @param $cnb_options array Regular 'cnb' options
90
+ *
91
+ * @return bool
92
+ */
93
+ function cnb_is_beta_user_via_options($cnb_options) {
94
+ return is_array($cnb_options) && array_key_exists('cloud_beta_enabled', $cnb_options) && $cnb_options['cloud_beta_enabled'] == 1;
95
+ }
96
+
97
+ /**
98
+ * @param $cnb_options array Regular 'cnb' options
99
+ *
100
+ * @return bool
101
+ */
102
+ function cnb_is_beta_user_via_url_only($cnb_options) {
103
+ $beta_url = cnb_is_beta_user_via_url();
104
+ $beta_options = cnb_is_beta_user_via_options($cnb_options);
105
+ return $beta_url && !$beta_options;
106
+ }
107
+ /**
108
+ * Only true if beta is in the URL or beta mode is enabled via the options
109
+ * @param $cnb_options array Regular 'cnb' options
110
+ * @return bool
111
+ */
112
+ function cnb_is_beta_user($cnb_options) {
113
+ // $beta_url = cnb_is_beta_user_via_url();
114
+ // $beta_options = cnb_is_beta_user_via_options($cnb_options);
115
+ // return $beta_url || $beta_options;
116
+ return cnb_is_beta_user_via_options($cnb_options);
117
+ }
118
+
119
+ function cnb_admin_settings_create_cloud_domain($cnb_user) {
120
+ $nonce = filter_input( INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING );
121
+ if( isset( $_REQUEST['_wpnonce'] ) && wp_verify_nonce( $nonce, 'cnb_create_cloud_domain') ) {
122
+ return CnbAdminCloud::cnb_wp_create_domain( $cnb_user );
123
+ }
124
+ }
125
+
126
+ function cnb_admin_settings_migrate_legacy_to_cloud() {
127
+ $nonce = filter_input( INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING );
128
+ if( isset( $_REQUEST['_wpnonce'] ) && wp_verify_nonce( $nonce, 'cnb_migrate_legacy_button') ) {
129
+ return CnbAdminCloud::cnb_wp_migrate_button();
130
+ }
131
+ }
132
+
133
+ function cnb_admin_setting_migrate() {
134
+ // Update the cloud if requested
135
+ $cnb_cloud_notifications = array();
136
+
137
+ $action = !empty($_POST['action']) ? sanitize_text_field($_POST['action']) : null;
138
+ switch ($action) {
139
+ case 'cnb_create_cloud_domain':
140
+ $cnb_user = CnbAppRemote::cnb_remote_get_user_info();
141
+ $cnb_cloud_notifications = cnb_admin_settings_create_cloud_domain($cnb_user);
142
+ break;
143
+ case 'cnb_migrate_legacy_button':
144
+ $cnb_cloud_notifications = cnb_admin_settings_migrate_legacy_to_cloud();
145
+ break;
146
+ }
147
+
148
+ // redirect the user to the appropriate page
149
+ $transient_id = 'cnb-' . wp_generate_uuid4();
150
+ set_transient($transient_id, $cnb_cloud_notifications, HOUR_IN_SECONDS);
151
+
152
+ // Create link
153
+ $url = admin_url('admin.php');
154
+ $redirect_link =
155
+ add_query_arg(
156
+ array(
157
+ 'page' => 'call-now-button-settings',
158
+ 'tid' => $transient_id,
159
+ ),
160
+ $url );
161
+ $redirect_url = esc_url_raw( $redirect_link );
162
+ wp_safe_redirect($redirect_url);
163
+ exit;
164
+ }
165
+
166
+ function cnb_admin_settings_page() {
167
+ global $cnb_options, $cnb_settings;
168
+
169
+ add_action('cnb_header', 'cnb_add_header_settings');
170
+
171
+ $cnb_notices = cnb_get_notices();
172
+ $cnb_changelog = cnb_get_changelog();
173
+
174
+ $beta = cnb_is_beta_user($cnb_options);
175
+ $beta_tmp = cnb_is_beta_user_via_url_only($cnb_options);
176
+ if ($beta_tmp) {
177
+ $notice = '<form method="post" action="' . esc_url( admin_url('options.php') ) . '" class="cnb-container">';
178
+ ob_start();
179
+ settings_fields('cnb_options');
180
+ $notice .= ob_get_clean();
181
+ $notice .= '<input type="hidden" name="page" value="call-now-button-settings" />
182
+ <table>
183
+ <tr>
184
+ <th scope="row"</th>
185
+ <td>
186
+ <input type="hidden" name="cnb[cloud_beta_enabled]" value="1" />
187
+ <input type="submit" class="button-primary" value="' . __('Activate beta mode') . '"/> For testing new unreleased functionality.
188
+ </td>
189
+ </table>
190
+ </form>';
191
+ $cnb_notices[] = '<div class="notice-warning notice"><p>'.$notice.'</p></div>';
192
+ }
193
+ $cnb_user = CnbAppRemote::cnb_remote_get_user_info();
194
+
195
+ if (!($cnb_user instanceof WP_Error)) {
196
+ // Let's check if the domain already exists
197
+ $cnb_cloud_domain = CnbAppRemote::cnb_remote_get_wp_domain();
198
+ $cnb_cloud_domains = CnbAppRemote::cnb_remote_get_domains();
199
+ $cnb_clean_site_url = CnbAppRemote::cnb_clean_site_url();
200
+
201
+ if (empty($cnb_cloud_domain->properties)) {
202
+ $cnb_cloud_domain->properties = array();
203
+ }
204
+ }
205
+ ?>
206
+
207
+ <div class="wrap">
208
+ <?php
209
+ $show_advanced_view_only = array_key_exists('advanced_view', $cnb_options) && $cnb_options['advanced_view'] === 1;
210
+ if ($show_advanced_view_only) {
211
+ echo '<script type="text/javascript">show_advanced_view_only_set=1</script>';
212
+ }
213
+
214
+ cnb_admin_header($cnb_options, $cnb_settings, $cnb_notices, $cnb_changelog) ?>
215
+
216
+ <h2 class="nav-tab-wrapper">
217
+ <a href="<?php echo cnb_settings_create_tab_url('basic_options') ?>"
218
+ class="nav-tab <?php echo cnb_is_active_tab('basic_options') ?>">General</a>
219
+ <?php if ($cnb_options['status'] === 'cloud') { ?>
220
+ <a href="<?php echo cnb_settings_create_tab_url('account_options') ?>"
221
+ class="nav-tab <?php echo cnb_is_active_tab('account_options') ?>">Account</a>
222
+ <?php if ($show_advanced_view_only) { ?>
223
+ <a href="<?php echo cnb_settings_create_tab_url('advanced_options') ?>"
224
+ class="nav-tab <?php echo cnb_is_active_tab('advanced_options') ?>">Advanced</a>
225
+ <?php } ?>
226
+ <?php } ?>
227
+ </h2>
228
+ <form method="post" action="<?php echo esc_url( admin_url('options.php') ); ?>" class="cnb-container">
229
+ <?php settings_fields('cnb_options'); ?>
230
+ <table class="form-table <?php echo cnb_is_active_tab('basic_options'); ?>">
231
+ <tr>
232
+ <th colspan="2"></th>
233
+ </tr>
234
+ <tr>
235
+ <th scope="row">Plugin status:</th>
236
+ <td>
237
+ <div class="cnb-radio-item">
238
+ <input type="radio" name="cnb[status]" id="cnb_status_disabled" value="disabled" <?php checked('disabled', $cnb_options['status']); ?> />
239
+ <label for="cnb_status_disabled">Disabled</label>
240
+ </div>
241
+ <div class="cnb-radio-item">
242
+ <input type="radio" name="cnb[status]" id="cnb_status_enabled" value="enabled" <?php checked('enabled', $cnb_options['status']); ?> />
243
+ <label for="cnb_status_enabled">Enabled <?php if (isset($cnb_options['cloud_beta_enabled']) && $cnb_options['cloud_beta_enabled']) { ?>(local)<?php } ?></label>
244
+ </div>
245
+ <?php if ($beta) { ?>
246
+ <div class="cnb-radio-item">
247
+ <input type="radio" id="cloud_enabled" name="cnb[status]" value="cloud" <?php checked('cloud', $cnb_options['status']) ?> />
248
+ <label for="cloud_enabled">Enabled (cloud)
249
+ <a href="<?php echo CNB_SUPPORT; ?>click-tracking/<?php cnb_utm_params("question-mark", "click-tracking"); ?>" target="_blank" class="cnb-nounderscore">
250
+ <span class="dashicons dashicons-editor-help"></span>
251
+ </a>
252
+ </label>
253
+ </div>
254
+ <?php } ?>
255
+ </td>
256
+ </tr>
257
+ <?php if ($cnb_options['status'] === 'enabled') { ?>
258
+ <tr>
259
+ <th colspan="2"><h2>Tracking</h2></th>
260
+ </tr>
261
+ <tr>
262
+ <th scope="row">Click tracking: <a href="<?php echo CNB_SUPPORT; ?>click-tracking/<?php cnb_utm_params("question-mark", "click-tracking"); ?>" target="_blank" class="cnb-nounderscore">
263
+ <span class="dashicons dashicons-editor-help"></span>
264
+ </a></th>
265
+ <td>
266
+ <div class="cnb-radio-item">
267
+ <input id="tracking3" type="radio" name="cnb[tracking]" value="0" <?php checked('0', $cnb_options['tracking']); ?> />
268
+ <label for="tracking3">Disabled</label>
269
+ </div>
270
+ <div class="cnb-radio-item">
271
+ <input id="tracking4" type="radio" name="cnb[tracking]" value="3" <?php checked('3', $cnb_options['tracking']); ?> />
272
+ <label for="tracking4">Latest Google Analytics (gtag.js)</label>
273
+ </div>
274
+ <div class="cnb-radio-item">
275
+ <input id="tracking1" type="radio" name="cnb[tracking]" value="2" <?php checked('2', $cnb_options['tracking']); ?> />
276
+ <label for="tracking1">Google Universal Analytics (analytics.js)</label>
277
+ </div>
278
+ <div class="cnb-radio-item">
279
+ <input id="tracking2" type="radio" name="cnb[tracking]" value="1" <?php checked('1', $cnb_options['tracking']); ?> />
280
+ <label for="tracking2">Classic Google Analytics (ga.js)</label>
281
+ </div>
282
+ <p class="description">Using Google Tag Manager? Set up click tracking in GTM. <a href="<?php echo CNB_SUPPORT; ?>click-tracking/google-tag-manager-event-tracking/<?php cnb_utm_params("description_link", "google-tag-manager-event-tracking"); ?>" target="_blank">Learn how to do this...</a></p>
283
+ </td>
284
+ </tr>
285
+ <tr>
286
+ <th scope="row">Google Ads: <a href="<?php echo CNB_SUPPORT; ?>google-ads/<?php cnb_utm_params("question-mark", "google-ads"); ?>" target="_blank" class="cnb-nounderscore">
287
+ <span class="dashicons dashicons-editor-help"></span>
288
+ </a></th>
289
+ <td class="conversions">
290
+ <div class="cnb-radio-item">
291
+ <input name="cnb[conversions]" id="conversions_0" type="radio" value="0" <?php checked('0', $cnb_options['conversions']); ?> /> <label for="conversions_0">Off </label>
292
+ </div>
293
+ <div class="cnb-radio-item">
294
+ <input name="cnb[conversions]" id="conversions_1" type="radio" value="1" <?php checked('1', $cnb_options['conversions']); ?> /> <label for="conversions_1">Conversion Tracking using Google's global site tag </label>
295
+ </div>
296
+ <div class="cnb-radio-item">
297
+ <input name="cnb[conversions]" id="conversions_2" type="radio" value="2" <?php checked('2', $cnb_options['conversions']); ?> /> <label for="conversions_2">Conversion Tracking using JavaScript</label>
298
+ </div>
299
+ <p class="description">Select this option if you want to track clicks on the button as Google Ads conversions. This option requires the Event snippet to be present on the page. <a href="https">Learn more...</a></p>
300
+ </td>
301
+ </tr>
302
+ <tr>
303
+ <th colspan="2"><h2>Button display</h2></th>
304
+ </tr>
305
+ <tr class="zoom">
306
+ <th scope="row">Button size <span id="cnb_slider_value"></span>:</th>
307
+ <td>
308
+ <label class="cnb_slider_value">Smaller&nbsp;&laquo;&nbsp;</label>
309
+ <input type="range" min="0.7" max="1.3" name="cnb[zoom]" value="<?php esc_attr_e($cnb_options['zoom']) ?>" class="slider" id="cnb_slider" step="0.1">
310
+ <label class="cnb_slider_value">&nbsp;&raquo;&nbsp;Bigger</label>
311
+ </td>
312
+ </tr>
313
+ <tr class="z-index">
314
+ <th scope="row">Order (<span id="cnb_order_value"></span>): <a href="https://callnowbutton.com/set-order/" target="_blank" class="cnb-nounderscore">
315
+ <span class="dashicons dashicons-editor-help"></span>
316
+ </a></th>
317
+ <td>
318
+ <label class="cnb_slider_value">Backwards&nbsp;&laquo;&nbsp;</label>
319
+ <input type="range" min="1" max="10" name="cnb[z-index]" value="<?php esc_attr_e($cnb_options['z-index']) ?>" class="slider2" id="cnb_order_slider" step="1">
320
+ <label class="cnb_slider_value">&nbsp;&raquo;&nbsp;Front</label>
321
+ <p class="description">The default (and recommended) value is all the way to the front so the button sits on top of everything else. In case you have a specific usecase where you want something else to sit in front of the Call Now Button (e.g. a chat window or a cookie notice) you can move this backwards one step at a time to adapt it to your situation.</p>
322
+ </td>
323
+ </tr>
324
+ <?php if($cnb_options['classic'] == 1) { ?>
325
+ <tr class="classic">
326
+ <th scope="row">Classic button: <a href="https://callnowbutton.com/new-button-design/<?php cnb_utm_params("question-mark", "new-button-design"); ?>" target="_blank" class="cnb-nounderscore">
327
+ <span class="dashicons dashicons-editor-help"></span>
328
+ </a></th>
329
+ <td>
330
+ <input type="hidden" name="cnb[classic]" value="0" />
331
+ <input id="classic" name="cnb[classic]" type="checkbox" value="1" <?php checked('1', $cnb_options['classic']); ?> /> <label title="Enable" for="classic">Active</label>
332
+ </td>
333
+ </tr>
334
+ <?php
335
+ }
336
+ }
337
+ if($cnb_options['status'] === 'cloud' && isset($cnb_cloud_domain) && !($cnb_cloud_domain instanceof WP_Error)) {
338
+ cnb_admin_page_domain_edit_render_form($cnb_cloud_domain);
339
+ } ?>
340
+ <?php if ($beta) { ?>
341
+ <?php if ($cnb_options['status'] !== 'cloud') { ?>
342
+ <tr>
343
+ <th colspan="2"><h2>Advanced</h2></th>
344
+ </tr>
345
+ <?php } ?>
346
+ <tr>
347
+ <th>Beta mode:</th>
348
+ <td><label>
349
+ <input type="hidden" name="cnb[cloud_beta_enabled]" value="0" />
350
+ <input id="beta" type="checkbox" name="cnb[cloud_beta_enabled]" value="1" <?php checked('1', $beta); ?> />
351
+ <label title="Enabled" for="beta">Enabled</label>
352
+ </label>
353
+ <p class="description">You can stop being a beta user and return to the original button at any time</p>
354
+ </td>
355
+ </tr>
356
+ <?php } ?>
357
+ </table>
358
+ <?php if ($cnb_options['status'] === 'cloud') { ?>
359
+ <table class="form-table <?php echo cnb_is_active_tab('account_options'); ?>">
360
+ <tr>
361
+ <th scope="row">API key</th>
362
+ <td>
363
+ <input type="text" class="regular-text" name="cnb[api_key]"
364
+ value="<?php if (isset($cnb_options['api_key'])) { esc_attr_e($cnb_options['api_key']); } ?>"
365
+ placeholder="e.g. b52c3f83-38dc-4493-bc90-642da5be7e39"/>
366
+ <?php if ($cnb_user instanceof WP_Error) { ?>
367
+ <p class="description">Get you key at <a href="<?php echo CNB_WEBSITE?>"><?php echo CNB_WEBSITE?></a></p>
368
+ <?php } ?>
369
+ </td>
370
+ </tr>
371
+ <?php if (!$cnb_user instanceof WP_Error) { ?>
372
+ <tr>
373
+ <th scope="row">Account owner</th>
374
+ <td>
375
+ <?php esc_html_e($cnb_user->name) ?>
376
+ <?php
377
+ if ($cnb_user->email !== $cnb_user->name) { esc_html_e(' (' . $cnb_user->email . ')');
378
+ } ?>
379
+ </td>
380
+ </tr>
381
+ <tr>
382
+ <th scope="row">Account ID</th>
383
+ <td><?php esc_html_e($cnb_user->id) ?></td>
384
+ </tr>
385
+ <?php } ?>
386
+ </table>
387
+ <table class="form-table <?php echo cnb_is_active_tab('advanced_options'); ?>">
388
+ <tr>
389
+ <th>Advanced view</th>
390
+ <td><label>
391
+ <input type="hidden" name="cnb[advanced_view]" value="0" />
392
+ <input type="checkbox" name="cnb[advanced_view]" value="1" <?php checked('1', $cnb_options['advanced_view']); ?> />
393
+ <p class="description">Show all the bells and whistles (for power users)</p>
394
+ </label>
395
+ </td>
396
+ </tr>
397
+ <?php if ($beta) { ?>
398
+ <tr class="cnb_advanced_view">
399
+ <th>Show traces</th>
400
+ <td><label>
401
+ <input type="hidden" name="cnb[footer_show_traces]" value="0" />
402
+ <input type="checkbox" name="cnb[footer_show_traces]" value="1" <?php checked('1', $cnb_options['footer_show_traces']); ?> />
403
+ <p class="description">Show the API calls and their timing in the footer.</p>
404
+ </label>
405
+ </td>
406
+ </tr>
407
+ <?php if (!($cnb_user instanceof WP_Error) && isset($cnb_cloud_domain)) { ?>
408
+ <tr class="when-cloud-enabled cnb_advanced_view">
409
+ <th scope="row">Domain</th>
410
+ <td>
411
+ <div>
412
+ <p>Your domain: <strong><?php esc_html_e($cnb_clean_site_url) ?></strong></p>
413
+ <?php if ($cnb_cloud_domain instanceof WP_Error) { ?>
414
+ <p class="description notice notice-warning">Almost there! Create your domain using the button at the top of this page.</p>
415
+ <?php } else { ?>
416
+ <p class="description">Your domain <strong><?php esc_html_e($cnb_cloud_domain->name) ?></strong> is connected with ID <strong><?php esc_html_e($cnb_cloud_domain->id) ?></strong></p>
417
+ <?php }?>
418
+ <select name="cnb[cloud_use_id]">
419
+
420
+ <optgroup label="Account-wide">
421
+ <option
422
+ value="<?php esc_attr_e($cnb_user->id) ?>"
423
+ <?php selected($cnb_user->id, $cnb_options['cloud_use_id']) ?>
424
+ >
425
+ Let the button decide
426
+ </option>
427
+ </optgroup>
428
+ <optgroup label="Specific domain">
429
+ <?php foreach ($cnb_cloud_domains as $domain) { ?>
430
+ <option
431
+ value="<?php esc_attr_e($domain->id) ?>"
432
+ <?php selected($domain->id, $cnb_options['cloud_use_id']) ?>
433
+ >
434
+ <?php esc_html_e($domain->name) ?>
435
+ </option>
436
+ <?php } ?>
437
+ </optgroup>
438
+ </select>
439
+ </div>
440
+ </td>
441
+ </tr>
442
+ <?php } ?>
443
+ <?php } // end of beta check ?>
444
+ <tr class="when-cloud-enabled cnb_advanced_view">
445
+ <th>API endpoint</th>
446
+ <td><label>
447
+ <input type="text" name="cnb[api_base]" class="regular-text"
448
+ value="<?php echo CnbAppRemote::cnb_get_api_base() ?>" />
449
+ <p class="description">The API endpoint to use to communicate with the CallNowButton Cloud service. <br />
450
+ <strong>Do not change this unless you know what you're doing!</strong>
451
+ </p>
452
+ </label>
453
+ </td>
454
+ </tr>
455
+ <tr class="cnb_advanced_view">
456
+ <th>API caching</th>
457
+ <td><label>
458
+ <input type="hidden" name="cnb[api_caching]" value="0" />
459
+ <input type="checkbox" name="cnb[api_caching]" value="1" <?php checked('1', $cnb_options['api_caching']); ?> />
460
+ <p class="description">Cache API requests (using Wordpress transients)</p>
461
+ </label></td>
462
+ </tr>
463
+ </table>
464
+ <?php } ?>
465
+ <input type="hidden" name="cnb[version]" value="<?php echo CNB_VERSION; ?>"/>
466
+ <p class="submit"><input type="submit" class="button-primary" value="<?php _e('Save Changes') ?>"/></p>
467
+ </form>
468
+
469
+ <?php cnb_admin_footer() ?>
470
+ </div>
471
+ <?php }
src/autoload.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // @codingStandardsIgnoreFile
3
+ // @codeCoverageIgnoreStart
4
+ // this is an autogenerated file - do not edit
5
+ spl_autoload_register(
6
+ function($class) {
7
+ static $classes = null;
8
+ if ($classes === null) {
9
+ $classes = array(
10
+ 'cnb_action_list_table' => '/admin/action-overview.php',
11
+ 'cnb_apikey_list_table' => '/admin/apikey-overview.php',
12
+ 'cnb_button_list_table' => '/admin/button-overview.php',
13
+ 'cnb_condition_list_table' => '/admin/condition-overview.php',
14
+ 'cnb_domain_list_table' => '/admin/domain-overview.php',
15
+ 'cnbadmincloud' => '/admin/api/CnbAdminCloud.php',
16
+ 'cnbappremote' => '/admin/api/CnbAppRemote.php',
17
+ 'cnbappremotepayment' => '/admin/api/CnbAppRemotePayment.php',
18
+ 'cnbget' => '/admin/api/CnbAppRemote.php',
19
+ 'remotetrace' => '/admin/api/RemoteTrace.php',
20
+ 'remotetracer' => '/admin/api/RemoteTracer.php'
21
+ );
22
+ }
23
+ $cn = strtolower($class);
24
+ if (isset($classes[$cn])) {
25
+ require __DIR__ . $classes[$cn];
26
+ }
27
+ },
28
+ true,
29
+ false
30
+ );
31
+ // @codeCoverageIgnoreEnd
src/call-now-button.php ADDED
@@ -0,0 +1,299 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname( __FILE__ ) . '/utils/utils.php';
4
+ require_once dirname( __FILE__ ) . '/admin/admin-ajax.php';
5
+
6
+ // Grabbing the settings and checking for latest version
7
+ // OR creating the options file for first time installations
8
+ $cnb_settings = cnb_get_options();
9
+ $cnb_options = $cnb_settings['options'];
10
+ $cnb_slug_base = 'call-now-button';
11
+
12
+ $cnb_options['active'] = isset($cnb_options['active']) && $cnb_options['active'] == 1 ? 1 : 0;
13
+ $cnb_options['classic'] = isset($cnb_options['classic']) && $cnb_options['classic'] == 1 ? 1 : 0;
14
+ $cnb_options['hideIcon'] = isset($cnb_options['hideIcon']) && $cnb_options['hideIcon'] == 1 ? 1 : 0;
15
+ $cnb_options['frontpage'] = isset($cnb_options['frontpage']) && $cnb_options['frontpage'] == 1 ? 1 : 0;
16
+ $cnb_options['advanced_view'] = isset($cnb_options['advanced_view']) && $cnb_options['advanced_view'] == 1 ? 1 : 0;
17
+ $cnb_options['footer_show_traces'] = isset($cnb_options['footer_show_traces']) && $cnb_options['footer_show_traces'] == 1 ? 1 : 0;
18
+ $cnb_options['api_caching'] = isset($cnb_options['api_caching']) && $cnb_options['api_caching'] == 1 ? 1 : 0;
19
+
20
+ $plugin_title = apply_filters('cnb_plugin_title', CNB_NAME);
21
+ $cnb_cloud_hosting = isset($cnb_options['cloud_enabled']) && $cnb_options['cloud_enabled'] == 1;
22
+
23
+ // Used by settings
24
+ $cnb_options['status'] = $cnb_cloud_hosting ? 'cloud' : ($cnb_options['active'] ? 'enabled' : 'disabled');
25
+
26
+ /**
27
+ * Used by cnb_register_admin_page
28
+ */
29
+ function cnb_admin_styling() {
30
+ wp_enqueue_style('cnb_styling');
31
+ }
32
+
33
+ function cnb_admin_button_overview() {
34
+ require_once dirname( __FILE__ ) . '/admin/button-overview.php';
35
+ cnb_admin_button_overview_render();
36
+ }
37
+
38
+ function cnb_admin_page_domain_overview() {
39
+ require_once dirname( __FILE__ ) . '/admin/domain-overview.php';
40
+ cnb_admin_page_domain_overview_render();
41
+ }
42
+
43
+ function cnb_admin_page_action_overview() {
44
+ require_once dirname( __FILE__ ) . '/admin/action-overview.php';
45
+ cnb_admin_page_action_overview_render();
46
+ }
47
+
48
+ function cnb_admin_page_condition_overview() {
49
+ require_once dirname( __FILE__ ) . '/admin/condition-overview.php';
50
+ cnb_admin_page_condition_overview_render();
51
+ }
52
+
53
+ function cnb_admin_page_apikey_overview() {
54
+ require_once dirname( __FILE__ ) . '/admin/apikey-overview.php';
55
+ cnb_admin_page_apikey_overview_render();
56
+ }
57
+
58
+ function cnb_admin_settings() {
59
+ require_once dirname( __FILE__ ) . '/admin/settings.php';
60
+ cnb_admin_settings_page();
61
+ }
62
+
63
+ function cnb_admin_page_legacy_edit() {
64
+ require_once dirname( __FILE__ ) . '/admin/legacy-edit.php';
65
+ cnb_admin_page_legacy_edit_render();
66
+ }
67
+
68
+ /**
69
+ * Adds the plugin to the options menu
70
+ */
71
+ function cnb_register_admin_pages() {
72
+ global $plugin_title, $cnb_slug_base, $cnb_cloud_hosting, $cnb_options, $wp_version;
73
+
74
+ $menu_page_function = $cnb_cloud_hosting ? 'cnb_admin_button_overview' : 'cnb_admin_page_legacy_edit';
75
+
76
+ // Oldest wordpress only has "smartphone", no "phone" (this is added in a later version)
77
+ $icon_url = version_compare($wp_version, '5.5.0', '<') ? 'dashicons-smartphone' : 'dashicons-phone';
78
+ $menu_page = add_menu_page(
79
+ __( 'Call Now Button - Overview', CNB_NAME ),
80
+ 'Call Now Button',
81
+ 'manage_options',
82
+ $cnb_slug_base,
83
+ $menu_page_function,
84
+ $icon_url,
85
+ 61
86
+ );
87
+ add_action('admin_print_styles-' . $menu_page, 'cnb_admin_styling');
88
+
89
+ if ($cnb_cloud_hosting) {
90
+ // Button overview
91
+ $button_overview = add_submenu_page( $cnb_slug_base, $plugin_title, 'Buttons', 'manage_options', $cnb_slug_base, 'cnb_admin_button_overview' );
92
+ add_action( 'admin_print_styles-' . $button_overview, 'cnb_admin_styling' );
93
+
94
+ if ($cnb_options['advanced_view'] === 1) {
95
+ // Domain overview
96
+ $domain_overview = add_submenu_page($cnb_slug_base, $plugin_title, 'Domains', 'manage_options', $cnb_slug_base . '-domains', 'cnb_admin_page_domain_overview');
97
+ add_action('admin_print_styles-' . $domain_overview, 'cnb_admin_styling');
98
+
99
+ // Action overview
100
+ $action_overview = add_submenu_page($cnb_slug_base, $plugin_title, 'Actions', 'manage_options', $cnb_slug_base . '-actions', 'cnb_admin_page_action_overview');
101
+ add_action('admin_print_styles-' . $action_overview, 'cnb_admin_styling');
102
+
103
+ // Condition overview
104
+ $condition_overview = add_submenu_page($cnb_slug_base, $plugin_title, 'Conditions', 'manage_options', $cnb_slug_base . '-conditions', 'cnb_admin_page_condition_overview');
105
+ add_action('admin_print_styles-' . $condition_overview, 'cnb_admin_styling');
106
+
107
+ // Apikey overview
108
+ $apikey_overview = add_submenu_page($cnb_slug_base, $plugin_title, 'API Keys', 'manage_options', $cnb_slug_base . '-apikeys', 'cnb_admin_page_apikey_overview');
109
+ add_action('admin_print_styles-' . $apikey_overview, 'cnb_admin_styling');
110
+ } else {
111
+ // Fake out Action overview
112
+ if (isset($_GET['page']) && $_GET['page'] === 'call-now-button-actions' && $_GET['action']) {
113
+ $action_overview = add_submenu_page($cnb_slug_base, $plugin_title, 'Edit action', 'manage_options', $cnb_slug_base . '-actions', 'cnb_admin_page_action_overview');
114
+ add_action('admin_print_styles-' . $action_overview, 'cnb_admin_styling');
115
+ }
116
+ // Fake out Domain upgrade page
117
+ if (isset($_GET['page']) && $_GET['page'] === 'call-now-button-domains' && $_GET['action'] === 'upgrade') {
118
+ $domain_overview = add_submenu_page($cnb_slug_base, $plugin_title, 'Upgrade domain', 'manage_options', $cnb_slug_base . '-domains', 'cnb_admin_page_domain_overview');
119
+ add_action('admin_print_styles-' . $domain_overview, 'cnb_admin_styling');
120
+ }
121
+ }
122
+ } else {
123
+ // Legacy edit
124
+ $legacy_edit = add_submenu_page( $cnb_slug_base, $plugin_title, 'Button', 'manage_options', $cnb_slug_base, 'cnb_admin_page_legacy_edit' );
125
+ add_action( 'admin_print_styles-' . $legacy_edit, 'cnb_admin_styling' );
126
+ }
127
+
128
+ // Settings pages
129
+ $settings = add_submenu_page($cnb_slug_base, $plugin_title, 'Settings', 'manage_options', $cnb_slug_base.'-settings', 'cnb_admin_settings');
130
+ add_action('admin_print_styles-' . $settings, 'cnb_admin_styling');
131
+ }
132
+
133
+ add_action('admin_menu', 'cnb_register_admin_pages');
134
+
135
+ function cnb_enqueue_color_picker() {
136
+ wp_enqueue_style('wp-color-picker');
137
+ wp_enqueue_script('cnb-script-handle', plugins_url('../resources/js/call-now-button.js', __FILE__), array('wp-color-picker'), CNB_VERSION, true);
138
+ }
139
+
140
+ add_action('admin_enqueue_scripts', 'cnb_enqueue_color_picker'); // add the color picker
141
+
142
+ /**
143
+ * Used for the modal in Button edit -> Actions edit
144
+ */
145
+ function cnb_enqueue_script_dialog() {
146
+ wp_enqueue_script('jquery-ui-core');
147
+ wp_enqueue_script( 'jquery-ui-dialog' );
148
+ wp_enqueue_style( 'wp-jquery-ui-dialog' );
149
+ }
150
+
151
+ add_action('admin_enqueue_scripts', 'cnb_enqueue_script_dialog');
152
+
153
+ function cnb_plugin_meta($links, $file) {
154
+ if ($file == CNB_BASENAME) {
155
+ $url = admin_url('admin.php');
156
+ $settings_link =
157
+ add_query_arg(
158
+ array(
159
+ 'page' => 'call-now-button-settings'),
160
+ $url);
161
+ $settings_url = esc_url($settings_link);
162
+
163
+ $cnb_new_links = array(
164
+ sprintf('<a href="%s">%s</a>', $settings_url, __('Settings')),
165
+ '<a href="' . CNB_SUPPORT . '">Support</a>');
166
+ array_push(
167
+ $links,
168
+ $cnb_new_links[0],
169
+ $cnb_new_links[1]
170
+ );
171
+ }
172
+ return $links;
173
+ }
174
+
175
+ add_filter('plugin_row_meta', 'cnb_plugin_meta', 10, 2);
176
+
177
+ function cnb_plugin_add_settings_link($links) {
178
+ array_unshift($links, sprintf('<a href="admin.php?page=%s-settings">%s</a>', CNB_BASEFOLDER, __('Settings')));
179
+ return $links;
180
+ }
181
+
182
+ add_filter('plugin_action_links_' . CNB_BASENAME, 'cnb_plugin_add_settings_link');
183
+
184
+
185
+ function cnb_options_validate($input) {
186
+ require_once dirname( __FILE__ ) . '/admin/settings.php';
187
+ return cnb_settings_options_validate($input);
188
+ }
189
+
190
+ function cnb_options_init() {
191
+ register_setting( 'cnb_options', 'cnb', 'cnb_options_validate' );
192
+ wp_register_style('cnb_styling', plugins_url('../resources/style/call-now-button.css', __FILE__), false, CNB_VERSION, 'all');
193
+ }
194
+
195
+ add_action('admin_init', 'cnb_options_init');
196
+
197
+ /**
198
+ * Called when a Single/Multi/ButtonBar Button is created via POST
199
+ */
200
+ function cnb_admin_post_create_button() {
201
+ require_once dirname( __FILE__ ) . '/admin/button-edit.php';
202
+ cnb_admin_create_button();
203
+ }
204
+ add_action( 'admin_post_cnb_create_single_button', 'cnb_admin_post_create_button' );
205
+ add_action( 'admin_post_cnb_create_multi_button', 'cnb_admin_post_create_button' );
206
+ add_action( 'admin_post_cnb_create_full_button', 'cnb_admin_post_create_button' );
207
+
208
+ /**
209
+ * Called when a Single Button is saved via POST
210
+ */
211
+ function cnb_admin_post_update_button() {
212
+ require_once dirname( __FILE__ ) . '/admin/button-edit.php';
213
+ cnb_admin_update_button();
214
+ }
215
+ add_action( 'admin_post_cnb_update_single_button', 'cnb_admin_post_update_button' );
216
+ add_action( 'admin_post_cnb_update_multi_button', 'cnb_admin_post_update_button' );
217
+ add_action( 'admin_post_cnb_update_full_button', 'cnb_admin_post_update_button' );
218
+
219
+ /**
220
+ * Called when a Domain is created via POST
221
+ */
222
+ function cnb_admin_create_domain() {
223
+ require_once dirname( __FILE__ ) . '/admin/domain-edit.php';
224
+ cnb_admin_page_domain_create_process();
225
+ }
226
+
227
+ add_action( 'admin_post_cnb_create_domain', 'cnb_admin_create_domain' );
228
+
229
+ /**
230
+ * Called when a Domain is saved via POST
231
+ */
232
+ function cnb_admin_update_domain() {
233
+ require_once dirname( __FILE__ ) . '/admin/domain-edit.php';
234
+ cnb_admin_page_domain_edit_process();
235
+ }
236
+
237
+ add_action( 'admin_post_cnb_update_domain', 'cnb_admin_update_domain' );
238
+
239
+ /**
240
+ * Called when an Action is created via POST
241
+ */
242
+ function cnb_admin_create_action() {
243
+ require_once dirname( __FILE__ ) . '/admin/action-edit.php';
244
+ cnb_admin_page_action_create_process();
245
+ }
246
+
247
+ add_action( 'admin_post_cnb_create_action', 'cnb_admin_create_action' );
248
+
249
+ /**
250
+ * Called when an Action is saved via POST
251
+ */
252
+ function cnb_admin_update_action() {
253
+ require_once dirname( __FILE__ ) . '/admin/action-edit.php';
254
+ cnb_admin_page_action_edit_process();
255
+ }
256
+
257
+ add_action( 'admin_post_cnb_update_action', 'cnb_admin_update_action' );
258
+
259
+ /**
260
+ * Called when a condition is saved via POST
261
+ */
262
+ function cnb_admin_update_condition() {
263
+ require_once dirname( __FILE__ ) . '/admin/condition-edit.php';
264
+ cnb_admin_page_condition_edit_process();
265
+ }
266
+
267
+ add_action( 'admin_post_cnb_update_condition', 'cnb_admin_update_condition' );
268
+
269
+ /**
270
+ * Called when an API key is created via POST
271
+ */
272
+ function cnb_admin_create_apikey() {
273
+ require_once dirname( __FILE__ ) . '/admin/apikey-overview.php';
274
+ cnb_admin_page_apikey_create_process();
275
+ }
276
+
277
+ add_action( 'admin_post_cnb_create_apikey', 'cnb_admin_create_apikey' );
278
+
279
+ /**
280
+ * Called when the Settings page is migrating from Legacy to the cloud
281
+ */
282
+ function cnb_admin_migrate_to_cloud() {
283
+ require_once dirname( __FILE__ ) . '/admin/settings.php';
284
+ cnb_admin_setting_migrate();
285
+ }
286
+
287
+ add_action( 'admin_post_cnb_create_cloud_domain', 'cnb_admin_migrate_to_cloud' );
288
+ add_action( 'admin_post_cnb_migrate_legacy_button', 'cnb_admin_migrate_to_cloud' );
289
+
290
+ // Render the Frontend
291
+ if (!is_admin() && isButtonActive($cnb_options)) {
292
+ $cnb_has_text = ($cnb_options['text'] == '') ? false : true;
293
+ $cnb_is_classic = isset($cnb_options['classic']) && $cnb_options['classic'] == 1 && !$cnb_has_text;
294
+
295
+ $renderer = $cnb_cloud_hosting ? 'cloud' : ($cnb_is_classic ? 'classic' : 'modern');
296
+
297
+ require_once dirname( __FILE__ ) . "/renderers/$renderer/wp_head.php";
298
+ require_once dirname( __FILE__ ) . "/renderers/$renderer/wp_foot.php";
299
+ }
src/renderers/classic/wp_foot.php ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ function cnb_footer() {
3
+ global $cnb_options;
4
+
5
+ $cnb_tracking_id = (int) $cnb_options['tracking'];
6
+ $cnb_conversion_id = (int) $cnb_options['conversions'];
7
+ $cnb_tracking_id = ($cnb_tracking_id >= 0 && $cnb_tracking_id <= 3) ? $cnb_tracking_id : 0;
8
+ $cnb_conversion_id = ($cnb_conversion_id >= 0 && $cnb_conversion_id <= 2) ? $cnb_conversion_id : 0;
9
+ $cnb_hide_icon = isset($cnb_options['hideIcon']) && $cnb_options['hideIcon'] == 1;
10
+ $cnb_has_text = ($cnb_options['text'] == '') ? false : true;
11
+ $cnb_show_limited = isset($cnb_options['show']) && $cnb_options['show'] != '';
12
+ $cnb_show_included = $cnb_options['limit'] == 'include';
13
+ $cnb_click_tracking = $cnb_tracking_id > 0;
14
+ $cnb_is_full_width = $cnb_options['appearance'] == 'full' || $cnb_options['appearance'] == 'tfull';
15
+ $cnb_hide_frontpage = isset($cnb_options['frontpage']) && $cnb_options['frontpage'] == 1;
16
+ $cnb_conversion_tracking = $cnb_conversion_id > 0;
17
+
18
+ if ($cnb_show_limited) {
19
+ $cnb_show_ids = explode(',', str_replace(' ', '', $cnb_options['show']));
20
+ }
21
+
22
+ if ($cnb_click_tracking) {
23
+ $cnb_tracking_code[1] = '_gaq.push(["_trackEvent", "Contact", "Call Now Button", "Phone"]);';
24
+ $cnb_tracking_code[2] = 'ga("send", "event", "Contact", "Call Now Button", "Phone");';
25
+ $gtag_props = wp_json_encode(array(
26
+ 'event_category' => 'contact',
27
+ 'event_label' => 'phone',
28
+ 'category' => 'Call Now Button',
29
+ 'action_type' => 'PHONE',
30
+ 'button_type' => 'Single',
31
+ 'action_value' => esc_js($cnb_options['number']),
32
+ 'action_label' => esc_js($cnb_options['text']),
33
+ 'cnb_version' => CNB_VERSION));
34
+ $cnb_tracking_code[3] = 'gtag("event", "Call Now Button", '.$gtag_props.');';
35
+ $cnb_tracking_code = $cnb_tracking_code[$cnb_tracking_id];
36
+ } else {
37
+ $cnb_tracking_code = '';
38
+ }
39
+
40
+ if ($cnb_conversion_tracking) {
41
+ $cnb_conversion_code[1] = 'return gtag_report_conversion("tel:' . esc_js($cnb_options['number']) . '");';
42
+ $cnb_conversion_code[2] = 'goog_report_conversion("tel:' . esc_js($cnb_options['number']) . '");';
43
+ $cnb_conversion_code = $cnb_conversion_code[$cnb_conversion_id];
44
+ } else {
45
+ $cnb_conversion_code = '';
46
+ }
47
+
48
+ $cnb_onclick_events = $cnb_click_tracking || $cnb_conversion_tracking ? "onclick='" .$cnb_tracking_code . $cnb_conversion_code . "'" : '';
49
+
50
+ if (!$cnb_has_text && !$cnb_is_full_width) {
51
+ $cnb_button_text = '<span>Call Now Button</span>';
52
+ } elseif (!$cnb_has_text && $cnb_is_full_width) {
53
+ $cnb_button_text = '<img alt="Call Now Button" src="data:image/svg+xml;base64,' . svg(changeColor($cnb_options['color'], 'darker'), $cnb_options['iconcolor']) . '" width="40">';
54
+ } elseif ($cnb_hide_icon && $cnb_is_full_width) {
55
+ $cnb_button_text = '<span style="color:' . esc_attr($cnb_options['iconcolor']) . '">' . esc_html($cnb_options['text']) . '</span>';
56
+ } elseif ($cnb_is_full_width) {
57
+ $cnb_button_text = '<img alt="Call Now Button" src="data:image/svg+xml;base64,' . svg(changeColor($cnb_options['color'], 'darker'), $cnb_options['iconcolor']) . '" width="40"><span style="color:' . esc_attr($cnb_options['iconcolor']) . '">' . esc_html($cnb_options['text']) . '</span>';
58
+ } else {
59
+ $cnb_button_text = '<span>' . str_replace(' ', '&nbsp;', esc_html($cnb_options['text'])) . '</span>';
60
+ }
61
+ $cnb_button_text .= '<span class="cnb-number cnb-hidden">'.esc_html($cnb_options['number']).'</span>';
62
+
63
+
64
+ $cnb_call_link = '<a href="tel:' . esc_attr($cnb_options['number']) . '" id="callnowbutton" ' . $cnb_onclick_events . '>' . $cnb_button_text . '</a>';
65
+
66
+ if (is_front_page()) {
67
+ if (!$cnb_hide_frontpage) {
68
+ echo $cnb_call_link;
69
+ }
70
+ } elseif ($cnb_show_limited) {
71
+ if ($cnb_show_included) {
72
+ if (is_single($cnb_show_ids) || is_page($cnb_show_ids)) {
73
+ echo $cnb_call_link;
74
+ }
75
+ } else {
76
+ if (!is_single($cnb_show_ids) && !is_page($cnb_show_ids)) {
77
+ echo $cnb_call_link;
78
+ }
79
+ }
80
+ } else {
81
+ echo $cnb_call_link;
82
+ }
83
+ }
84
+
85
+ add_action('wp_footer', 'cnb_footer');
src/renderers/classic/wp_head.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ function cnb_head() {
3
+ global $cnb_options;
4
+ $cnb_has_text = ($cnb_options['text'] == '') ? false : true;
5
+ $cnb_button_css = "\n<!-- Call Now Button " . CNB_VERSION . " by Jerry Rietveld (callnowbutton.com) [renderer:classic]-->\n";
6
+
7
+ $ButtonExtra = "";
8
+ // OLD BUTTON DESIGN
9
+ if ($cnb_options['appearance'] == 'full' || $cnb_options['appearance'] == 'middle' || $cnb_has_text) {
10
+ $cnb_button_appearance = "width:100%;left:0;";
11
+ $ButtonExtra = "body {padding-bottom:60px;}";
12
+ } elseif ($cnb_options['appearance'] == 'left') {
13
+ $cnb_button_appearance = "width:100px;left:0;border-bottom-right-radius:40px; border-top-right-radius:40px;";
14
+ } else {
15
+ $cnb_button_appearance = "width:100px;right:0;border-bottom-left-radius:40px; border-top-left-radius:40px;";
16
+ }
17
+
18
+ $cnb_button_css .= "<style>.cnb-hidden { display:none;} #callnowbutton, #callnowbutton span {display:none;} @media screen and (max-width:650px){#callnowbutton .NoButtonText{display:none;}#callnowbutton {display:block; " .
19
+ $cnb_button_appearance .
20
+ " height:80px; position:fixed; bottom:-20px; border-top:2px solid " .
21
+ changeColor(esc_html($cnb_options['color']), 'lighter') .
22
+ "; background:url(data:image/svg+xml;base64," .
23
+ svg(changeColor($cnb_options['color'], 'darker'), $cnb_options['iconcolor']) .
24
+ ") center 2px no-repeat " .
25
+ esc_html($cnb_options['color']) .
26
+ "; text-decoration:none; box-shadow:0 0 5px #888; z-index:" .
27
+ zindex($cnb_options['z-index']) .
28
+ ";background-size:58px 58px}" .
29
+ $ButtonExtra .
30
+ "}</style>\n";
31
+
32
+ echo $cnb_button_css;
33
+ }
34
+
35
+ add_action('wp_head', 'cnb_head');
src/renderers/cloud/wp_foot.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php
src/renderers/cloud/wp_head.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ function cnb_head() {
3
+ global $cnb_options;
4
+ $id = $cnb_options['cloud_use_id'];
5
+ $cnb_button_js = "\n<!-- Call Now Button " . CNB_VERSION . " by Jerry Rietveld (callnowbutton.com) [renderer:cloud, id:".esc_attr($id)."]-->\n";
6
+ if ($id) {
7
+ $cnb_button_js .= '<script type="text/javascript" src="'.CnbAppRemote::cnb_get_user_base().'/' . esc_attr($id) . '.js"></script>';
8
+ }
9
+ echo $cnb_button_js;
10
+ }
11
+
12
+ add_action('wp_head', 'cnb_head');
src/renderers/modern/wp_foot.php ADDED
@@ -0,0 +1,2 @@
 
 
1
+ <?php
2
+ require_once dirname( __FILE__ ) . '/../classic/wp_foot.php';
src/renderers/modern/wp_head.php ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ function cnb_head() {
3
+ global $cnb_options;
4
+ $cnb_has_text = ($cnb_options['text'] == '') ? false : true;
5
+ $cnb_is_full_width = $cnb_options['appearance'] == "full" || $cnb_options['appearance'] == "tfull";
6
+ $cnb_button_css = "\n<!-- Call Now Button " . CNB_VERSION . " by Jerry Rietveld (callnowbutton.com) [renderer:modern]-->\n";
7
+
8
+ $ButtonExtra = "";
9
+ // NEW BUTTON DESIGN
10
+ $cnb_button_shape = "width:55px; height:55px; border-radius:50%; box-shadow: 0 3px 6px rgba(0, 0, 0, 0.3);transform: scale(" . esc_html($cnb_options['zoom']) . ");";
11
+ $cnb_button_positions = array(
12
+ 'middle' => 'bottom:15px; left:50%; margin-left:-28px;',
13
+ 'left' => 'bottom:15px; left:20px;',
14
+ 'right' => 'bottom:15px; right:20px;',
15
+ 'mleft' => 'top:50%; margin-top:-28px; left:20px;',
16
+ 'mright' => 'top:50%; margin-top:-28px; right:20px;',
17
+ 'tleft' => 'top:15px; left:20px;',
18
+ 'tmiddle' => 'top:15px; left:50%; margin-left:-28px;',
19
+ 'tright' => 'top:15px; right:20px;',
20
+ );
21
+
22
+ if ($cnb_options['appearance'] == 'full' || $cnb_options['appearance'] == 'tfull') {
23
+ $cnb_top_or_bottom = ($cnb_options['appearance']) == 'full' ? "bottom" : "top";
24
+
25
+ $cnb_button_appearance = "width:100%;left:0;" . $cnb_top_or_bottom . ":0;height:60px;";
26
+
27
+ $ButtonExtra = "body {padding-" . $cnb_top_or_bottom . ":60px;}#callnowbutton img {transform: scale(" . esc_html($cnb_options['zoom']) . ");}";
28
+ if ($cnb_has_text) {
29
+ $cnb_button_appearance .= "text-align:center;color:#fff; font-weight:600; font-size:120%; overflow: hidden;";
30
+ if (isset($cnb_options['hideIcon']) && $cnb_options['hideIcon'] == 1) {
31
+ $cnb_button_appearance .= 'padding-right:20px;';
32
+ }
33
+ }
34
+ } else {
35
+ $cnb_button_appearance = $cnb_button_shape . $cnb_button_positions[$cnb_options['appearance']];
36
+ }
37
+
38
+ $cnb_label_side = ltrim(ltrim($cnb_options['appearance'], "m"), "t");
39
+
40
+ if ($cnb_has_text && ($cnb_options['appearance'] == 'middle' || $cnb_options['appearance'] == 'tmiddle')) { // don't show the label in this situation
41
+ $circularButtonTextCSS = "#callnowbutton span{display: none;";
42
+ } elseif ($cnb_has_text && !$cnb_is_full_width) {
43
+ $circularButtonTextCSS = "\n#callnowbutton span {-moz-osx-font-smoothing: grayscale; -webkit-user-select: none; -ms-user-select: none; user-select: none; display: block; width: auto; background-color: rgba(70,70,70,.9); position: absolute; ".$cnb_label_side.": 68px; border-radius: 2px; font-family: Helvetica,Arial,sans-serif; padding: 6px 8px; font-size: 13px; font-weight:700; color: #ececec; top: 15px; box-shadow: 0 1px 2px rgba(0,0,0,.15); word-break: keep-all; line-height: 1em; text-overflow: ellipsis; vertical-align: middle; }";
44
+ } elseif (!$cnb_is_full_width) {
45
+ $circularButtonTextCSS = "#callnowbutton span{display:none;}";
46
+ } else {
47
+ $circularButtonTextCSS = "";
48
+ }
49
+
50
+ $cnb_button_css = $cnb_button_css . "<style>";
51
+ $cnb_button_css .= ".cnb-hidden { display:none;} #callnowbutton {display:none;} @media screen and (max-width:650px){#callnowbutton {display:block; position:fixed; text-decoration:none; z-index:" . zindex($cnb_options['z-index']) . ";";
52
+ $cnb_button_css .= $cnb_button_appearance;
53
+ if ($cnb_is_full_width) {
54
+ $cnb_button_css .= "background:" . esc_html($cnb_options['color']) . ";display: flex; justify-content: center; align-items: center;text-shadow: 0 1px 0px rgba(0, 0, 0, 0.18);";
55
+ } else {
56
+ $cnb_button_css .= "background:url(data:image/svg+xml;base64," . svg(changeColor($cnb_options['color'], 'darker'), $cnb_options['iconcolor']) . ") center/35px 35px no-repeat " . esc_html($cnb_options['color']) . ";";
57
+ }
58
+ $cnb_button_css .= "}" . $ButtonExtra . "}" . $circularButtonTextCSS;
59
+ $cnb_button_css .= "</style>\n";
60
+
61
+ echo $cnb_button_css;
62
+ }
63
+
64
+ add_action('wp_head', 'cnb_head');
src/utils/notices.php ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ function cnb_settings_get_account_missing_notice($user) {
3
+ global $cnb_options;
4
+
5
+ $notice = '<div class="notice notice-warning">
6
+ <h2 class="title">You\'re almost there!</h2>
7
+ <p>You have enabled Cloud Hosting, but you need an account and API key from
8
+ <a href="https://app.callnowbutton.com?utm_source=wp-plugin&utm_medium=referral&utm_campaign=beta_tester&utm_term=sign-up-for-api">https://app.callnowbutton.com</a>.</p>
9
+ <p><ul>
10
+ <li>Create an account on <a href="https://app.callnowbutton.com?utm_source=wp-plugin&utm_medium=referral&utm_campaign=beta_tester&utm_term=sign-up-for-api">https://app.callnowbutton.com</a></li>
11
+ <li>Go to your profile page and click <strong>Create new API key</strong>.</li>
12
+ <li>Copy the API key into the "API key" field below</li>
13
+ <li>Click "Save API key"</li>
14
+ </ul></p>
15
+ <form method="post" action="' . esc_url( admin_url('options.php') ) . '" class="cnb-container">';
16
+ ob_start();
17
+ settings_fields('cnb_options');
18
+ $notice .= ob_get_clean();
19
+ $notice .= '<input type="hidden" name="page" value="call-now-button-settings" />
20
+ <table class="form-table">
21
+ <tr class="when-cloud-enabled">
22
+ <th scope="row">Enter API key:</th>
23
+ <td>
24
+ <input type="text" class="regular-text" name="cnb[api_key]"
25
+ value="' . ((isset($cnb_options['api_key'])) ? esc_attr($cnb_options['api_key']) : '') . '"
26
+ placeholder="e.g. b52c3f83-38dc-4493-bc90-642da5be7e39"/>
27
+ <input type="submit" class="button-primary" value="' . __('Save API key') . '"/>
28
+ </td>
29
+ </table>
30
+ </form>
31
+ <p style="font-family: monospace; margin-top: 30px;">
32
+ Technical details: <br />
33
+ Error code: <code>'. esc_html($user->get_error_code()) . '.</code>';
34
+
35
+ if ($user->get_error_message() != '') {
36
+ $notice .= 'Message: <code>'. esc_html($user->get_error_message()) .'</code>';
37
+ }
38
+ $notice .= '</p></div>';
39
+ return $notice;
40
+ }
41
+
42
+ function cnb_settings_get_domain_missing_notice($domain) {
43
+ $notice = '<div class="notice notice-warning">
44
+ <h2 class="title">Domain not found yet</h2>
45
+ <p>You have enabled Cloud Hosting and are logged in,
46
+ but we need to create the domain remotely.</p>
47
+ <p>
48
+ <form action="' . esc_url( admin_url('admin-post.php') ) . '" method="post">
49
+ <input type="hidden" name="page" value="call-now-button-settings" />
50
+ <input type="hidden" name="action" value="cnb_create_cloud_domain" />
51
+ <input type="hidden" name="_wpnonce" value="' . wp_create_nonce('cnb_create_cloud_domain') .'" />
52
+ <input type="submit" value="Create domain" class="button button-secondary" />
53
+ </form>
54
+ </p>
55
+ <p style="font-family: monospace; margin-top: 30px;">
56
+ Technical details: <br />
57
+ Error code: <code>' . esc_html($domain->get_error_code()) . '</code>';
58
+ if ($domain->get_error_message() != '') {
59
+ $notice .= 'Message: <code>' . esc_html($domain->get_error_message()) . '</code>';
60
+ }
61
+ $notice .= '</p></div>';
62
+ return $notice;
63
+ }
64
+
65
+ function cnb_settings_get_button_missing_notice() {
66
+ return '<div class="notice notice-warning">
67
+ <h2 class="title">Creating your first button</h2>
68
+ <p>You have enabled Cloud Hosting and have your domain setup,
69
+ so now it\'s time to create your first button.</p>
70
+ <p>To make it easy, we can migrate your existing button to the Cloud.</p>
71
+ <p>
72
+ <form action="'. esc_url( admin_url('admin-post.php') ) .'" method="post">
73
+ <input type="hidden" name="page" value="call-now-button-settings" />
74
+ <input type="hidden" name="action" value="cnb_migrate_legacy_button" />
75
+ <input type="hidden" name="_wpnonce" value="'. wp_create_nonce('cnb_migrate_legacy_button') .'" />
76
+ <input type="submit" value="Migrate button" class="button button-secondary" />
77
+ </form>
78
+ </p>
79
+ </div>';
80
+ }
81
+
82
+ function cnb_settings_get_buttons_missing_notice($error) {
83
+ $notice = '<div class="notice notice-warning">
84
+ <h2 class="title">Could not retrieve Buttons</h2>
85
+ <p>Something unexpected went wrong retrieving the Buttons for this API key</p>';
86
+ $notice .= '<p style="font-family: monospace; margin-top: 30px;">
87
+ Technical details: <br />
88
+ Error code: <code>' . esc_html($error->get_error_code()) . '</code>';
89
+ if ($error->get_error_message() != '') {
90
+ $notice .= 'Message: <code>' . esc_html($error->get_error_message()) . '</code>';
91
+ }
92
+ $notice .= '</p></div>';
93
+ return $notice;
94
+ }
src/utils/utils.php ADDED
@@ -0,0 +1,371 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once dirname( __FILE__ ) . '/notices.php';
3
+
4
+ /**
5
+ * Returns the default values for a (legacy) Button
6
+ * @return array
7
+ */
8
+ function cnb_get_defaults() {
9
+ return array(
10
+ 'active' => 0,
11
+ 'number' => '',
12
+ 'text' => '',
13
+ 'color' => '#00bb00',
14
+ 'iconcolor' => '#ffffff',
15
+ 'appearance' => 'right',
16
+ 'hideIcon' => 0,
17
+ 'limit' => 'include',
18
+ 'frontpage' => 0,
19
+ 'conversions' => 0,
20
+ 'zoom' => 1,
21
+ 'z-index' => 10,
22
+ 'tracking' => 0,
23
+ 'show' => '',
24
+ 'version' => CNB_VERSION
25
+ );
26
+ }
27
+
28
+ /**
29
+ * Returns the most current set of options
30
+ * Will update the Wordpress Options if they are missing or outdated
31
+ *
32
+ * @return array
33
+ */
34
+ function cnb_get_options() { // Grabbing existing settings and creating them if it's a first time installation
35
+ if(!get_option('cnb')) { // Doesn't exist -> set defaults
36
+ add_option('cnb', cnb_get_defaults());
37
+ } else { // Does exist -> see if update is needed
38
+ $updated = cnb_update_options();
39
+ }
40
+ $cnb_options['options'] = get_option('cnb');
41
+ // Format = options['updated'] = array(true/false [true for "there is an update", false for "CNB is up-to-date" , 0.5.5)
42
+ // Debug example: $cnb_options['updated'] = array(true, '0.0.0');
43
+ $cnb_options['updated'] = isset($updated) ? $updated : array(false, substr(CNB_VERSION, 0, 3));
44
+ return $cnb_options;
45
+ }
46
+
47
+ /**
48
+ * Compares version numbers
49
+ *
50
+ * @param $cnb_options array The result of a basic `get_option('cnb')`
51
+ * @return bool
52
+ */
53
+ function cnb_update_needed($cnb_options) {
54
+ $pluginVersion = CNB_VERSION;
55
+ $setupVersion = array_key_exists('version', $cnb_options) ? $cnb_options['version'] : 0.1;
56
+ if($pluginVersion == $setupVersion) {
57
+ return false;
58
+ } elseif(substr($pluginVersion,0,3) > substr($setupVersion,0,3)) {
59
+ return true;
60
+ } elseif(substr($pluginVersion,0,3) == substr($setupVersion,0,3)) {
61
+ return substr($pluginVersion,-1) > substr($setupVersion,-1);
62
+ } else {
63
+ return false;
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Gets the result of the DB settings combined with the default settings if any setting is missing
69
+ * @return array
70
+ */
71
+ function cnb_update_options() {
72
+ $cnb_options = get_option('cnb');
73
+ if(cnb_update_needed($cnb_options)) { // Check current version and if it needs an update
74
+ $cnb_defaults = cnb_get_defaults();
75
+ $cnb_options['active'] = isset($cnb_options['active'])
76
+ ? $cnb_options['active'] == 1 ? 1 : 0
77
+ : $cnb_defaults['active'];
78
+ $cnb_options['number'] = isset($cnb_options['number']) ? $cnb_options['number'] : $cnb_defaults['number'];
79
+ $cnb_options['text'] = isset($cnb_options['text']) ? $cnb_options['text'] : $cnb_defaults['text'];
80
+ $cnb_options['color'] = !empty($cnb_options['color']) ? $cnb_options['color'] : $cnb_defaults['color'];
81
+ $cnb_options['iconcolor'] = isset($cnb_options['iconcolor']) ? $cnb_options['iconcolor'] : $cnb_defaults['iconcolor'];
82
+ $cnb_options['appearance'] = $cnb_options['text'] != ''
83
+ ? 'full'
84
+ : (!empty($cnb_options['appearance'])
85
+ ? $cnb_options['appearance']
86
+ : $cnb_defaults['appearance']);
87
+ $cnb_options['hideIcon'] = isset($cnb_options['hideIcon'])
88
+ ? $cnb_options['hideIcon'] == 1 ? 1 : 0
89
+ : $cnb_defaults['hideIcon'];
90
+ $cnb_options['limit'] = isset($cnb_options['limit']) ? $cnb_options['limit'] : $cnb_defaults['limit'];
91
+ $cnb_options['frontpage'] = isset($cnb_options['frontpage'])
92
+ ? $cnb_options['frontpage'] == 1 ? 1 : 0
93
+ : $cnb_defaults['frontpage'];
94
+ $cnb_options['conversions'] = isset($cnb_options['conversions'])
95
+ ? $cnb_options['conversions'] == 1 ? 1 : 0
96
+ : $cnb_defaults['conversions'];
97
+ $cnb_options['zoom'] = isset($cnb_options['zoom']) ? $cnb_options['zoom'] : $cnb_defaults['zoom'];
98
+ $cnb_options['z-index'] = isset($cnb_options['z-index']) ? $cnb_options['z-index'] : $cnb_defaults['z-index'];
99
+ $cnb_options['tracking'] = !empty($cnb_options['tracking']) ? $cnb_options['tracking'] : $cnb_defaults['tracking'];
100
+ $cnb_options['show'] = !empty($cnb_options['show']) ? $cnb_options['show'] : $cnb_defaults['show'];
101
+
102
+ $updated_options = array(
103
+ 'active' => $cnb_options['active'],
104
+ 'number' => $cnb_options['number'],
105
+ 'text' => $cnb_options['text'],
106
+ 'color' => $cnb_options['color'],
107
+ 'iconcolor' => $cnb_options['iconcolor'],
108
+ 'appearance' => $cnb_options['appearance'],
109
+ 'hideIcon' => $cnb_options['hideIcon'],
110
+ 'limit' => $cnb_options['limit'],
111
+ 'frontpage' => $cnb_options['frontpage'],
112
+ 'conversions' => $cnb_options['conversions'],
113
+ 'zoom' => $cnb_options['zoom'],
114
+ 'z-index' => $cnb_options['z-index'],
115
+ 'tracking' => $cnb_options['tracking'],
116
+ 'show' => $cnb_options['show'],
117
+ 'version' => CNB_VERSION
118
+ );
119
+
120
+ if(array_key_exists('classic', $cnb_options) && $cnb_options['classic'] == 1 ) {
121
+ $default_options['classic'] = 1;
122
+ }
123
+
124
+ update_option('cnb', $updated_options);
125
+ $updated = array(true, $cnb_options['version']); // Updated and previous version number
126
+ } else {
127
+ $updated = array(false, $cnb_options['version']); // Not updated and current version number
128
+ }
129
+ return $updated;
130
+ }
131
+
132
+ /**
133
+ * Color functions to calculate borders
134
+ *
135
+ * @param $color
136
+ * @param $direction
137
+ * @return string
138
+ */
139
+ function changeColor($color, $direction) {
140
+ preg_match('/^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i', $color, $parts);
141
+ if(!isset($direction) || $direction == "lighter") { $change = 45; } else { $change = -30; }
142
+ for($i = 1; $i <= 3; $i++) {
143
+ $parts[$i] = hexdec($parts[$i]);
144
+ $parts[$i] = round($parts[$i] + $change);
145
+ if($parts[$i] > 255) { $parts[$i] = 255; } elseif($parts[$i] < 0) { $parts[$i] = 0; }
146
+ $parts[$i] = dechex($parts[$i]);
147
+ }
148
+
149
+ return '#' . str_pad($parts[1],2,"0",STR_PAD_LEFT) . str_pad($parts[2],2,"0",STR_PAD_LEFT) . str_pad($parts[3],2,"0",STR_PAD_LEFT);
150
+ }
151
+
152
+ function svg($color, $icon) {
153
+ $phone = '<path d="M7.104 14.032l15.586 1.984c0 0-0.019 0.5 0 0.953c0.029 0.756-0.26 1.534-0.809 2.1 l-4.74 4.742c2.361 3.3 16.5 17.4 19.8 19.8l16.813 1.141c0 0 0 0.4 0 1.1 c-0.002 0.479-0.176 0.953-0.549 1.327l-6.504 6.505c0 0-11.261 0.988-25.925-13.674C6.117 25.3 7.1 14 7.1 14" fill="'.esc_attr($color).'"/><path d="M7.104 13.032l6.504-6.505c0.896-0.895 2.334-0.678 3.1 0.35l5.563 7.8 c0.738 1 0.5 2.531-0.36 3.426l-4.74 4.742c2.361 3.3 5.3 6.9 9.1 10.699c3.842 3.8 7.4 6.7 10.7 9.1 l4.74-4.742c0.897-0.895 2.471-1.026 3.498-0.289l7.646 5.455c1.025 0.7 1.3 2.2 0.4 3.105l-6.504 6.5 c0 0-11.262 0.988-25.925-13.674C6.117 24.3 7.1 13 7.1 13" fill="'.esc_attr($icon).'"/>';
154
+ $svg = '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 60 60">' . $phone . '</svg>';
155
+ return base64_encode($svg);
156
+ }
157
+
158
+ /**
159
+ * Returns true if the `active` flag is set and is enabled
160
+ *
161
+ * @param $cnb_options array the current set of Button Options
162
+ * @return bool
163
+ */
164
+ function isButtonActive($cnb_options) {
165
+ return isset($cnb_options['active']) && $cnb_options['active'] == 1;
166
+ }
167
+
168
+ function getZindexMap() {
169
+ return array(
170
+ 10 => 2147483647,
171
+ 9 => 214748365,
172
+ 8 => 21474836,
173
+ 7 => 2147484,
174
+ 6 => 214748,
175
+ 5 => 21475,
176
+ 4 => 2147,
177
+ 3 => 215,
178
+ 2 => 21,
179
+ 1 => 2
180
+ );
181
+ }
182
+
183
+ /**
184
+ * @param $value int 1-10
185
+ * @return int
186
+ */
187
+ function zindex($value) {
188
+ $zindexMap = getZindexMap();
189
+ return $zindexMap[$value];
190
+ }
191
+
192
+ function zindexToOrder($zindex) {
193
+ // This starts at the higher number
194
+ foreach (getZindexMap() as $order => $value) {
195
+ if ($zindex >= $value) return $order;
196
+ }
197
+ return 1;
198
+ }
199
+
200
+ function cnb_actiontype_to_icontext($actionType) {
201
+ switch ($actionType) {
202
+ case 'PHONE': return 'call';
203
+ case 'ANCHOR': return 'anchor';
204
+ case 'WHATSAPP': return 'whatsapp';
205
+ case 'EMAIL': return 'email';
206
+ case 'LINK': return 'link';
207
+ case 'MAP': return 'directions';
208
+ case 'HOURS': return 'access_time';
209
+ }
210
+ }
211
+
212
+ function cnb_check_for_caching() {
213
+ $caching_plugins = array(
214
+ 'autoptimize/autoptimize.php',
215
+ 'breeze/breeze.php',
216
+ 'cache-control/cache-control.php',
217
+ 'cache-enabler/cache-enabler.php',
218
+ 'comet-cache/comet-cache.php',
219
+ 'fast-velocity-minify/fvm.php',
220
+ 'hyper-cache/plugin.php',
221
+ 'litespeed-cache/litespeed-cache.php',
222
+ 'simple-cache/simple-cache.php',
223
+ 'w3-total-cache/w3-total-cache.php',
224
+ 'wp-fastest-cache/wpFastestCache.php',
225
+ 'wp-super-cache/wp-cache.php'
226
+ );
227
+ $active = FALSE; //Default is false
228
+ $name = 'none'; // Default name is none
229
+ foreach ($caching_plugins as $plugin) {
230
+ if ( is_plugin_active( $plugin ) ) {
231
+ $active = TRUE;
232
+ $name = explode('/', $plugin);
233
+ $name = $name[0];
234
+ break;
235
+ }
236
+ }
237
+ return array($active,$name);
238
+ }
239
+ function cnb_utm_params($element, $page) {
240
+ $output = "?utm_source=wp-plugin";
241
+ $output .= "&utm_medium=referral";
242
+ $output .= "&utm_campaign=" . $element;
243
+ $output .= "&utm_term=" . $page;
244
+ echo $output;
245
+ }
246
+
247
+ /**
248
+ * Returns the filtered ID from the GET parameter
249
+ * @return mixed
250
+ */
251
+ function cnb_get_button_id() {
252
+ return filter_input( INPUT_GET, 'id', FILTER_SANITIZE_STRING );
253
+ }
254
+
255
+ function cnb_get_cloud_notices() {
256
+ global $cnb_options;
257
+
258
+ if (isset($cnb_options['cloud_enabled']) && $cnb_options['cloud_enabled'] == 1) {
259
+ // TODO - These 3 REMOTE calls are VERY expensive
260
+ $cnb_user = CnbAppRemote::cnb_remote_get_user_info();
261
+ if ($cnb_user instanceof WP_Error) {
262
+ return array(cnb_settings_get_account_missing_notice($cnb_user));
263
+ }
264
+
265
+ $cnb_cloud_domain = CnbAppRemote::cnb_remote_get_wp_domain();
266
+ if ($cnb_cloud_domain instanceof WP_Error) {
267
+ return array(cnb_settings_get_domain_missing_notice($cnb_cloud_domain));
268
+ }
269
+
270
+ // Check if any buttons are for the current WP domain
271
+ $cnb_cloud_buttons = CnbAppRemote::cnb_remote_get_buttons();
272
+ if ($cnb_cloud_buttons instanceof WP_Error) {
273
+ return array(cnb_settings_get_buttons_missing_notice($cnb_cloud_buttons));
274
+ }
275
+
276
+ $cnb_cloud_buttons_this_domain = array_filter($cnb_cloud_buttons, function ($button) use ($cnb_cloud_domain) {
277
+ return $button->domain === $cnb_cloud_domain->id;
278
+ });
279
+
280
+ if ( ! ( $cnb_cloud_domain instanceof WP_Error ) && count( $cnb_cloud_buttons_this_domain ) === 0 ) {
281
+ return array(cnb_settings_get_button_missing_notice());
282
+ }
283
+ }
284
+ return array();
285
+ }
286
+
287
+ function cnb_get_notices() {
288
+ $transient_id = filter_input( INPUT_GET, 'tid', FILTER_SANITIZE_STRING );
289
+
290
+ $notices = array();
291
+ if ($transient_id) {
292
+ $notices_cloud = get_transient($transient_id);
293
+ if (is_array($notices_cloud)) {
294
+ $notices = array_merge($notices, $notices_cloud);
295
+ }
296
+ delete_transient($transient_id);
297
+ }
298
+
299
+ $options_notice = get_transient('cnb-options');
300
+ if ($options_notice) {
301
+ $notices = array_merge($notices, $options_notice);
302
+ delete_transient('cnb-options');
303
+ }
304
+
305
+ return array_merge($notices, cnb_get_cloud_notices());
306
+ }
307
+
308
+ /**
309
+ * Based on https://www.php.net/manual/en/function.array-column.php#123045
310
+ *
311
+ * For PHP < 5.5.0
312
+ */
313
+ function array_column_ext($array, $column_key, $index_key = null) {
314
+ $result = array();
315
+ foreach ($array as $subarray => $value) {
316
+ if (array_key_exists($column_key,$value)) { $val = $value[$column_key]; }
317
+ else if ($column_key === null) { $val = $value; }
318
+ else { continue; }
319
+
320
+ if ($index_key === null) { $result[] = $val; }
321
+ elseif ($index_key == -1 || array_key_exists($index_key,$value)) {
322
+ $result[($index_key == -1)?$subarray: $value[$index_key]] = $val;
323
+ }
324
+ }
325
+ return $result;
326
+ }
327
+
328
+ /**
329
+ * For PHP < 5.5.0
330
+ */
331
+ if (!function_exists('boolval')) {
332
+ function boolval($val) {
333
+ return (bool) $val;
334
+ }
335
+ }
336
+
337
+ /**
338
+ * For Wordpress < 4.0
339
+ */
340
+ if (!function_exists('wp_generate_uuid4')) {
341
+ function wp_generate_uuid4() {
342
+ return sprintf(
343
+ '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
344
+ mt_rand( 0, 0xffff ),
345
+ mt_rand( 0, 0xffff ),
346
+ mt_rand( 0, 0xffff ),
347
+ mt_rand( 0, 0x0fff ) | 0x4000,
348
+ mt_rand( 0, 0x3fff ) | 0x8000,
349
+ mt_rand( 0, 0xffff ),
350
+ mt_rand( 0, 0xffff ),
351
+ mt_rand( 0, 0xffff )
352
+ );
353
+ }
354
+ }
355
+ function cnb_array_column($array, $column_key, $index_key = null) {
356
+ if (version_compare(PHP_VERSION, '7.0.0', '>=')) {
357
+ // phpcs:ignore
358
+ return array_column($array, $column_key, $index_key);
359
+ } else {
360
+ // Convert objects to array, since PHP < 7 cannot deal with objects as the first argument
361
+ $array_arr = array();
362
+ foreach ($array as $key => $value) {
363
+ $array_arr[$key] = (array) $value;
364
+ }
365
+ if(!function_exists('array_column')) {
366
+ return array_column_ext($array_arr, $column_key, $index_key);
367
+ }
368
+ // phpcs:ignore
369
+ return array_column($array_arr, $column_key, $index_key);
370
+ }
371
+ }
uninstall.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Doc: https://developer.wordpress.org/plugins/plugin-basics/uninstall-methods/
4
+
5
+ // if uninstall.php is not called by WordPress, die
6
+ if (!defined('WP_UNINSTALL_PLUGIN')) {
7
+ die;
8
+ }
9
+
10
+ $option_name = 'cnb';
11
+
12
+ // Delete the standard options
13
+ delete_option($option_name);
14
+
15
+ // Delete site options in Multisite
16
+ delete_site_option($option_name);