Call Now Button - Version 1.0.6

Version Description

= * Adding SMS support to Premium * More intuitive scheduler in Premium * Better timezone checks * Other small fixes and improvements

Download this release

Release Info

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

Code changes from version 1.0.5 to 1.0.6

call-now-button.php CHANGED
@@ -3,7 +3,7 @@
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: 1.0.5
7
  Author: Jerry Rietveld
8
  Author URI: https://www.callnowbutton.com
9
  GitHub Plugin URI: https://github.com/callnowbutton/wp-plugin
@@ -26,7 +26,7 @@ License: GPL2
26
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27
  */
28
 
29
- define('CNB_VERSION', '1.0.5');
30
  define('CNB_NAME', 'Call Now Button');
31
  define('CNB_BASENAME', plugin_basename(__FILE__));
32
  define('CNB_BASEFOLDER', plugin_basename(dirname(__FILE__)));
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: 1.0.6
7
  Author: Jerry Rietveld
8
  Author URI: https://www.callnowbutton.com
9
  GitHub Plugin URI: https://github.com/callnowbutton/wp-plugin
26
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27
  */
28
 
29
+ define('CNB_VERSION', '1.0.6');
30
  define('CNB_NAME', 'Call Now Button');
31
  define('CNB_BASENAME', plugin_basename(__FILE__));
32
  define('CNB_BASEFOLDER', plugin_basename(dirname(__FILE__)));
readme.txt CHANGED
@@ -5,11 +5,11 @@ 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.9
8
- Stable tag: 1.0.5
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
12
- A very simple yet very effective and flexible plugin that adds a Call Now Button to your website for mobile visitors (only for responsive websites).
13
 
14
  == Description ==
15
 
@@ -29,7 +29,7 @@ Under the **Presentation tab** you can change the color of the button, move it t
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
  ###Need even more control?
32
- Easily upgrade the plugin to Premium to add additional features such as multiple buttons, additional actions such as mail, WhatsApp, Maps and links, a button scheduler, more advanced page selection options, Buttonbars and Multibuttons, and much more!
33
 
34
 
35
 
@@ -111,12 +111,17 @@ Yes, you can upgrade to Premium to enable tons of extra features. Checkout [call
111
 
112
 
113
  == Changelog ==
 
 
 
 
 
 
114
  = 1.0.5 =
115
  * Preview improvements
116
  * Back link when editing actions
117
  * Some bug, style and copy fixes
118
 
119
-
120
  = 1.0.4 =
121
  * Live button preview in Premium
122
  * Easy activation of Premium via email
@@ -235,3 +240,7 @@ Yes, you can upgrade to Premium to enable tons of extra features. Checkout [call
235
 
236
  = 0.0.1 =
237
  * First time launch
 
 
 
 
5
  Requires at least: 3.9
6
  Requires PHP: 5.4
7
  Tested up to: 5.9
8
+ Stable tag: 1.0.6
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
12
+ The web's #1 click to call button for your website! A very simple but powerful plugin that adds a Call Now Button to your website for your mobile visitors (only for responsive websites).
13
 
14
  == Description ==
15
 
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
  ###Need even more control?
32
+ Easily upgrade the plugin to Premium to add additional features such as multiple buttons, additional actions such as SMS/Text, Email, WhatsApp, Maps and links, a button scheduler, more advanced page selection options, Buttonbars and Multibuttons, a live preview window and much much more!
33
 
34
 
35
 
111
 
112
 
113
  == Changelog ==
114
+ = 1.0.6 =
115
+ * Adding SMS support to Premium
116
+ * More intuitive scheduler in Premium
117
+ * Better timezone checks
118
+ * Other small fixes and improvements
119
+
120
  = 1.0.5 =
121
  * Preview improvements
122
  * Back link when editing actions
123
  * Some bug, style and copy fixes
124
 
 
125
  = 1.0.4 =
126
  * Live button preview in Premium
127
  * Easy activation of Premium via email
240
 
241
  = 0.0.1 =
242
  * First time launch
243
+
244
+ == Upgrade Notice ==
245
+ = 1.0.6 =
246
+ * Upgrade now to keep your Call Now Button up to date. The update contains some small UI improvements and we've added SMS/Text support to the Premium version. Maybe give Premium Free a go!
resources/js/action-edit-scheduler.js ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ *
3
+ * Range of minutes is 0 - 1439:
4
+ * - 0 == midnight
5
+ * - 24*60-1 (1439) = minute before end of day
6
+ *
7
+ * This is the inverse of fromFormattedToMinutes
8
+ *
9
+ * @param {number} time amount of minutes from midnight
10
+ * @returns {string} hh:mm in 24h time notation, padded with 0 were needed
11
+ */
12
+ function getFormattedTime(time) {
13
+ let hour = Math.floor(time / 60);
14
+ let minutes = time - (hour * 60);
15
+
16
+ if (hour < 10) hour = "0" + hour
17
+ if (hour === 0) hour = "00"
18
+
19
+ if (minutes< 10) minutes = "0" + minutes
20
+ if (minutes === 0) minutes = "00"
21
+ return hour + ':' + minutes
22
+ }
23
+
24
+ /**
25
+ * The inverse of getFormattedTime
26
+ *
27
+ * @param {string} time Formatted time string ("16:39")
28
+ * @returns {number} number between 0 (inclusive) and 1440 (exclusive)
29
+ */
30
+ function fromFormattedToMinutes(time) {
31
+ const splitTime = time.split(":")
32
+ return (splitTime[0] * 60) + (splitTime[1] * 1)
33
+ }
34
+
35
+ /**
36
+ * This sets the actual <input> elements that are submitted as part of the scheduler
37
+ *
38
+ * Note that "stop" should be later than "start".
39
+ *
40
+ * @param {string} start Formatted 24h time ("13:00")
41
+ * @param {string} stop Formatted 24h time ("14:50")
42
+ */
43
+ function cnbUpdateTime(start, stop) {
44
+ jQuery('#actions-schedule-start').val(start)
45
+ jQuery('#actions-schedule-stop').val(stop)
46
+ }
47
+
48
+ function cnbUpdateColors() {
49
+ const reverse = jQuery('#actions_schedule_outside_hours').prop('checked')
50
+ const headerColor = reverse ? '#b3afaf' : '#3582c4';
51
+ const widgetColor = headerColor === '#b3afaf' ? '#3582c4' : '#b3afaf';
52
+ jQuery('#cnb-schedule-range .ui-widget-header').css({"background-color": headerColor});
53
+ jQuery('#cnb-schedule-range.ui-widget-content').css({"background": widgetColor});
54
+ }
55
+
56
+ function cnbAjaxTimeFormat(start, stop) {
57
+ const data = {
58
+ 'action': 'cnb_time_format',
59
+ 'start': start,
60
+ 'stop': stop
61
+ };
62
+
63
+ jQuery.post(ajaxurl, data, function(response) {
64
+ cnbUpdateTimeText(response.start, response.stop)
65
+ });
66
+ }
67
+
68
+ function cnbUpdateTimeText(start, stop) {
69
+ const reverse1 = jQuery('#actions_schedule_outside_hours').prop('checked') ? 'Before': 'From';
70
+ const reverse2 = jQuery('#actions_schedule_outside_hours').prop('checked') ? 'and after': 'till';
71
+
72
+ jQuery( "#cnb-schedule-range-text" ).html( reverse1+" <strong>" + start + "</strong> "+reverse2+" <strong>" + stop + "</strong>")
73
+ }
74
+
75
+ function cnbRangeInverter() {
76
+ jQuery('#actions_schedule_outside_hours').on('change', () => {
77
+ cnbUpdateColors();
78
+
79
+ const start = jQuery('#actions-schedule-start').val()
80
+ const stop = jQuery('#actions-schedule-stop').val()
81
+ cnbAjaxTimeFormat(start, stop)
82
+ });
83
+ }
84
+
85
+ function cnbSetupScheduleStartStopSlider(start, stop) {
86
+ jQuery( "#cnb-schedule-range" ).slider({
87
+ range: true,
88
+ min: 0,
89
+ max: 24 * 60, // Do not include midnight, since "min" is inclusive
90
+ values: [ start, stop ],
91
+ step: 15,
92
+ slide: function( event, ui ) {
93
+ const start = getFormattedTime(ui.values[ 0 ])
94
+ let stop = getFormattedTime(ui.values[ 1 ])
95
+ stop = stop !== "24:00" ? stop : "23:59"
96
+
97
+ cnbAjaxTimeFormat(start, stop)
98
+ cnbUpdateTime(start, stop)
99
+ cnbUpdateColors()
100
+ }
101
+ });
102
+ }
103
+
104
+ function cnbSetupActionEditScheduler(start, stop) {
105
+ // Set time
106
+ cnbAjaxTimeFormat(getFormattedTime(start), getFormattedTime(stop))
107
+ cnbUpdateTime(getFormattedTime(start), getFormattedTime(stop))
108
+ cnbSetupScheduleStartStopSlider(start, stop)
109
+
110
+ // First time setup
111
+ cnbRangeInverter();
112
+ cnbUpdateColors();
113
+ }
114
+
115
+ jQuery( () => {
116
+ // Get the current value
117
+ const startVal = jQuery('#actions-schedule-start').val()
118
+ let stopVal = jQuery('#actions-schedule-stop').val()
119
+ stopVal = stopVal !== "24:00" ? stopVal : "23:59"
120
+
121
+ // Set it or use the default
122
+ const start = fromFormattedToMinutes(startVal ? startVal : '08:00');
123
+ const stop = fromFormattedToMinutes(stopVal ? stopVal : '17:00');
124
+
125
+ cnbSetupActionEditScheduler(start, stop)
126
+ });
resources/js/action-type-to-icon-text.js CHANGED
@@ -1,12 +1,22 @@
 
 
 
 
 
 
 
 
 
1
  function cnbActiontypeToIcontext(actionType) {
2
  switch (actionType) {
3
  case 'ANCHOR': return 'anchor';
4
- case 'WHATSAPP': return 'whatsapp';
5
  case 'EMAIL': return 'email';
 
6
  case 'LINK': return 'link';
7
  case 'MAP': return 'directions';
8
- case 'HOURS': return 'access_time';
9
- case 'PHONE':
 
10
  default:
11
  return 'call';
12
  }
1
+ /**
2
+ * Get the default glyph for a particular action type.
3
+ *
4
+ * This should have the same content as the PHP function cnb_actiontype_to_icontext
5
+ *
6
+ * @param {string} actionType
7
+ *
8
+ * @returns {string}
9
+ */
10
  function cnbActiontypeToIcontext(actionType) {
11
  switch (actionType) {
12
  case 'ANCHOR': return 'anchor';
 
13
  case 'EMAIL': return 'email';
14
+ case 'HOURS': return 'access_time';
15
  case 'LINK': return 'link';
16
  case 'MAP': return 'directions';
17
+ case 'PHONE': return 'call';
18
+ case 'SMS': return 'chat';
19
+ case 'WHATSAPP': return 'whatsapp';
20
  default:
21
  return 'call';
22
  }
resources/js/call-now-button.js CHANGED
@@ -329,6 +329,8 @@ function cnb_action_update_appearance(value) {
329
  const emailExtraEle = jQuery('.cnb-action-properties-email-extra');
330
  const whatsappEle = jQuery('.cnb-action-properties-whatsapp');
331
  const whatsappExtraEle = jQuery('.cnb-action-properties-whatsapp-extra');
 
 
332
 
333
  const propertiesEle = jQuery('.cnb-action-properties-map');
334
  const valueEle = jQuery('.cnb-action-value');
@@ -339,9 +341,10 @@ function cnb_action_update_appearance(value) {
339
  emailExtraEle.hide();
340
  whatsappEle.hide();
341
  whatsappExtraEle.hide();
 
 
342
  propertiesEle.hide();
343
 
344
-
345
  valueEle.show();
346
  valueTextEle.prop( 'disabled', false );
347
  whatsappValueEle.prop( 'disabled', true );
@@ -350,22 +353,35 @@ function cnb_action_update_appearance(value) {
350
  whatsappValueEle.removeAttr("required")
351
 
352
  switch (value) {
353
- case 'PHONE':
354
- valuelabelEle.text('Phone number');
355
- valueTextEle.attr("required", "required");
356
- break
357
  case 'ANCHOR':
358
  valuelabelEle.text('On-page anchor');
359
  valueTextEle.attr("required", "required");
360
  break
 
 
 
 
 
 
 
 
361
  case 'LINK':
362
  valuelabelEle.text('Full URL');
363
  valueTextEle.attr("required", "required");
364
  break
365
- case 'EMAIL':
366
- valuelabelEle.text('E-mail address');
367
  valueTextEle.attr("required", "required");
368
- emailEle.show()
 
 
 
 
 
 
 
 
 
369
  break
370
  case 'WHATSAPP':
371
  valuelabelEle.text('Whatsapp number');
@@ -375,11 +391,6 @@ function cnb_action_update_appearance(value) {
375
  whatsappEle.show();
376
  whatsappValueEle.attr("required", "required");
377
  break
378
- case 'MAP':
379
- valuelabelEle.text('Address');
380
- valueTextEle.attr("required", "required");
381
- propertiesEle.show();
382
- break
383
  default:
384
  valuelabelEle.text('Action value');
385
  valueTextEle.attr("required", "required");
329
  const emailExtraEle = jQuery('.cnb-action-properties-email-extra');
330
  const whatsappEle = jQuery('.cnb-action-properties-whatsapp');
331
  const whatsappExtraEle = jQuery('.cnb-action-properties-whatsapp-extra');
332
+ const smsEle = jQuery('.cnb-action-properties-sms');
333
+ const smsExtraEle = jQuery('.cnb-action-properties-sms-extra');
334
 
335
  const propertiesEle = jQuery('.cnb-action-properties-map');
336
  const valueEle = jQuery('.cnb-action-value');
341
  emailExtraEle.hide();
342
  whatsappEle.hide();
343
  whatsappExtraEle.hide();
344
+ smsEle.hide();
345
+ smsExtraEle.hide();
346
  propertiesEle.hide();
347
 
 
348
  valueEle.show();
349
  valueTextEle.prop( 'disabled', false );
350
  whatsappValueEle.prop( 'disabled', true );
353
  whatsappValueEle.removeAttr("required")
354
 
355
  switch (value) {
 
 
 
 
356
  case 'ANCHOR':
357
  valuelabelEle.text('On-page anchor');
358
  valueTextEle.attr("required", "required");
359
  break
360
+ case 'EMAIL':
361
+ valuelabelEle.text('E-mail address');
362
+ valueTextEle.attr("required", "required");
363
+ emailEle.show()
364
+ break
365
+ case 'HOURS':
366
+ // Not implemented yet
367
+ break;
368
  case 'LINK':
369
  valuelabelEle.text('Full URL');
370
  valueTextEle.attr("required", "required");
371
  break
372
+ case 'MAP':
373
+ valuelabelEle.text('Address');
374
  valueTextEle.attr("required", "required");
375
+ propertiesEle.show();
376
+ break
377
+ case 'PHONE':
378
+ valuelabelEle.text('Phone number');
379
+ valueTextEle.attr("required", "required");
380
+ break
381
+ case 'SMS':
382
+ valuelabelEle.text('Phone number');
383
+ valueTextEle.attr("required", "required");
384
+ smsEle.show();
385
  break
386
  case 'WHATSAPP':
387
  valuelabelEle.text('Whatsapp number');
391
  whatsappEle.show();
392
  whatsappValueEle.attr("required", "required");
393
  break
 
 
 
 
 
394
  default:
395
  valuelabelEle.text('Action value');
396
  valueTextEle.attr("required", "required");
resources/js/dismiss.js CHANGED
@@ -39,10 +39,10 @@ function cnb_upgrade_notice_dismiss_listener() {
39
  })
40
  }
41
 
42
- function cnb_welcome_panel_dismiss_listener() {
43
- jQuery('#welcome-panel').on('click', '.notice-dismiss', () => {
44
  cnb_decrease_sidenav_counter();
45
- jQuery('#welcome-panel').remove();
46
  })
47
  }
48
 
@@ -62,5 +62,5 @@ jQuery(() => {
62
  cnb_add_jquery_textnodes();
63
  cnb_dismissables_listener();
64
  cnb_upgrade_notice_dismiss_listener();
65
- cnb_welcome_panel_dismiss_listener();
66
  })
39
  })
40
  }
41
 
42
+ function cnb_welcome_banner_dismiss_listener() {
43
+ jQuery('#welcome-banner').on('click', '.notice-dismiss', () => {
44
  cnb_decrease_sidenav_counter();
45
+ jQuery('#welcome-banner').remove();
46
  })
47
  }
48
 
62
  cnb_add_jquery_textnodes();
63
  cnb_dismissables_listener();
64
  cnb_upgrade_notice_dismiss_listener();
65
+ cnb_welcome_banner_dismiss_listener();
66
  })
resources/js/preview.js CHANGED
@@ -66,6 +66,14 @@ function livePreview() {
66
  // Ensure it is always visible
67
  parsedData.cnb.options.displayMode = 'ALWAYS';
68
 
 
 
 
 
 
 
 
 
69
  // Ensure a Multi button / Buttonbar gets its actions
70
  if (typeof cnb_actions !== 'undefined') {
71
  if (parsedData &&
@@ -79,6 +87,7 @@ function livePreview() {
79
  }
80
  }
81
 
 
82
  // Fix iconenabled (should be true/false instead of 0/1)
83
  if (parsedData.action_id && parsedData.actions &&
84
  parsedData.actions[parsedData.action_id]) {
@@ -150,5 +159,7 @@ function initButtonEdit() {
150
 
151
 
152
  jQuery(() => {
 
 
153
  initButtonEdit()
154
  })
66
  // Ensure it is always visible
67
  parsedData.cnb.options.displayMode = 'ALWAYS';
68
 
69
+ // Ensure all Actions are visible
70
+ if (typeof cnb_actions !== 'undefined' && cnb_ignore_schedule) {
71
+ cnb_actions = cnb_actions.map((item) => {
72
+ item.schedule.showAlways = true;
73
+ return item
74
+ });
75
+ }
76
+
77
  // Ensure a Multi button / Buttonbar gets its actions
78
  if (typeof cnb_actions !== 'undefined') {
79
  if (parsedData &&
87
  }
88
  }
89
 
90
+
91
  // Fix iconenabled (should be true/false instead of 0/1)
92
  if (parsedData.action_id && parsedData.actions &&
93
  parsedData.actions[parsedData.action_id]) {
159
 
160
 
161
  jQuery(() => {
162
+ // Default, we show all actions
163
+ window.cnb_ignore_schedule = true;
164
  initButtonEdit()
165
  })
resources/js/settings.js CHANGED
@@ -1,3 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  /**
2
  * This calls the admin-ajax action called 'cnb_email_activation' (function cnb_admin_cnb_email_activation)
3
  */
@@ -8,22 +34,30 @@ function cnb_email_activation(admin_email) {
8
  'admin_email': admin_email
9
  };
10
 
11
- const errorMessage = '<h3 class="title">Something went wrong!</h3>' +
12
- '<p>Something has gone wrong and we do not know why...</p>' +
13
- '<p>As unlikely as it is, our service might be experiencing issues (check <a href="https://status.callnowbutton.com">our status page</a>).</p>' +
14
- '<p>If you think you\'ve found a bug, please report it at our <a href="https://callnowbutton.com/support/" target="_blank">Help Center</a>.';
15
- const errorDetails = '<p>Technical details:</p><p style="color:red"><span id="cnb_email_activation_details"></span></p>';
 
 
 
 
 
 
16
 
17
  // Send remove request
18
  jQuery.post(ajaxurl, data)
19
  .done((result) => {
20
  if (result && result.email) {
21
- jQuery('#cnb_email_activation').html('Check your inbox for an activation email sent to <strong><span id="cnb_email_activation_email"></span></strong>.')
 
22
  jQuery('#cnb_email_activation_email').text(result.email)
23
  }
24
 
25
  if (result && result.errors) {
26
- jQuery('#cnb_email_activation').html(errorMessage + errorDetails);
 
27
 
28
  const keys = Object.keys(result.errors)
29
  keys.forEach((key) => {
@@ -36,7 +70,8 @@ function cnb_email_activation(admin_email) {
36
  }
37
  })
38
  .fail((result) => {
39
- jQuery('#cnb_email_activation').html(errorMessage + errorDetails);
 
40
 
41
  // Create Text Nodes to ensure escaping of the content
42
  const codeMsg = document.createTextNode(result.status + ' ' + result.statusText)
@@ -53,6 +88,21 @@ function cnb_email_activation_alternate() {
53
  return cnb_email_activation(alternate_admin_email);
54
  }
55
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  function init_settings() {
57
  jQuery("#cnb_email_activation_alternate_form").hide()
58
  }
1
+ function cnb_email_activation_reenable_fields() {
2
+ const errorMessage = '<h3 class="title">Something went wrong!</h3>' +
3
+ '<p>Something has gone wrong and we do not know why...</p>' +
4
+ '<p>As unlikely as it is, our service might be experiencing issues (check <a href="https://status.callnowbutton.com">our status page</a>).</p>' +
5
+ '<p>If you think you\'ve found a bug, please report it at our <a href="https://callnowbutton.com/support/" target="_blank">Help Center</a>.';
6
+ const errorDetails = '<p>Technical details:</p><p style="color:red"><span id="cnb_email_activation_details"></span></p>';
7
+
8
+ jQuery('#cnb_email_activation_alternate_address').removeAttr("disabled")
9
+ jQuery('#cnb_email_activation_alternate').removeAttr("disabled")
10
+ jQuery('#cnb_email_activation_alternate').val("Activate Premium")
11
+ jQuery('#cnb_email_activation').html(errorMessage + errorDetails);
12
+ }
13
+
14
+ function cnb_email_activation_taking_too_long() {
15
+ const errorMessage = '<h3 class="title">Hmm, that\'s taking a while...</h3>' +
16
+ '<p>This call should not take this long. Please try again in a minute or so.</p>' +
17
+ '<p>As unlikely as it is, our service might be experiencing issues (check <a href="https://status.callnowbutton.com">our status page</a>).</p>' +
18
+ '<p>If you think you\'ve found a bug, please report it at our <a href="https://callnowbutton.com/support/" target="_blank">Help Center</a>.';
19
+ const errorDetails = '<p>Technical details:</p><p style="color:red"><span id="cnb_email_activation_details"></span></p>';
20
+
21
+ jQuery('#cnb_email_activation_alternate_address').removeAttr("disabled")
22
+ jQuery('#cnb_email_activation_alternate').removeAttr("disabled")
23
+ jQuery('#cnb_email_activation_alternate').val("Activate Premium")
24
+ jQuery('#cnb_email_activation').html(errorMessage + errorDetails);
25
+ }
26
+
27
  /**
28
  * This calls the admin-ajax action called 'cnb_email_activation' (function cnb_admin_cnb_email_activation)
29
  */
34
  'admin_email': admin_email
35
  };
36
 
37
+ // Disable the Email and Button fields (reactivate in case of errors)
38
+ jQuery('#cnb_email_activation_alternate_address').attr("disabled", "disabled")
39
+ jQuery('#cnb_email_activation_alternate').attr("disabled", "disabled")
40
+ jQuery('#cnb_email_activation_alternate').val("Check your e-mail")
41
+
42
+ // Clear the error fields
43
+ jQuery('#cnb_email_activation').empty()
44
+ jQuery('#cnb_email_activation_email').empty()
45
+
46
+ const statusTimeout = 5000
47
+ const takingTooLongTimer = setTimeout(cnb_email_activation_taking_too_long, statusTimeout)
48
 
49
  // Send remove request
50
  jQuery.post(ajaxurl, data)
51
  .done((result) => {
52
  if (result && result.email) {
53
+ clearTimeout(takingTooLongTimer)
54
+ jQuery('#cnb_email_activation').html('<span class="cnb_check_email_message">Check your inbox for an activation email sent to <strong><span id="cnb_email_activation_email"></span></strong>.</span>')
55
  jQuery('#cnb_email_activation_email').text(result.email)
56
  }
57
 
58
  if (result && result.errors) {
59
+ clearTimeout(takingTooLongTimer)
60
+ cnb_email_activation_reenable_fields()
61
 
62
  const keys = Object.keys(result.errors)
63
  keys.forEach((key) => {
70
  }
71
  })
72
  .fail((result) => {
73
+ clearTimeout(takingTooLongTimer)
74
+ cnb_email_activation_reenable_fields()
75
 
76
  // Create Text Nodes to ensure escaping of the content
77
  const codeMsg = document.createTextNode(result.status + ' ' + result.statusText)
88
  return cnb_email_activation(alternate_admin_email);
89
  }
90
 
91
+ // Note: IDE marks this as unused, but it is used by settings.php ("Delete API key")
92
+ function cnb_delete_apikey() {
93
+ const apiKeyField = jQuery(".call-now-button #cnb_api_key")
94
+ apiKeyField.prop("type", "hidden");
95
+ apiKeyField.prop("value", "delete_me");
96
+ apiKeyField.removeAttr("disabled");
97
+
98
+ // Ensure we use the exact verbiage of the Submit button
99
+ const saveVal = apiKeyField.parents('.cnb-container').find('#submit').val();
100
+ jQuery('.call-now-button #cnb_api_key_delete').replaceWith("<p>Click <strong>"+saveVal+"</strong> to disconnect your account.</p>")
101
+
102
+ // Present the default behavior of this submit button (since it needs to be actioned on by the *actual* submit button
103
+ return false;
104
+ }
105
+
106
  function init_settings() {
107
  jQuery("#cnb_email_activation_alternate_form").hide()
108
  }
resources/style/call-now-button.css CHANGED
@@ -48,6 +48,15 @@ Universal CNB admin styling options
48
  text-decoration: underline;
49
  }
50
 
 
 
 
 
 
 
 
 
 
51
  .cnb_footer_beta {
52
  border: 1px solid;
53
  border-radius: 3px;
@@ -147,6 +156,18 @@ table.form-table.nav-tab-active,
147
  font-size: 14px;
148
  }
149
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  table.form-table.nav-tab-only {
151
  display: table;
152
  }
@@ -366,7 +387,7 @@ input[type='range'] {
366
  border: 1px solid #c3c4c7;
367
  box-shadow: 0 1px 1px rgb(0 0 0 / 4%);
368
  background: #fff;
369
- margin-bottom: 10px;
370
  max-width:1000px;
371
  }
372
 
@@ -701,6 +722,17 @@ input[type=checkbox].cnb_day_selector:checked + label.cnb_day_selector {
701
  /*
702
  Preview phone and button placement
703
  */
 
 
 
 
 
 
 
 
 
 
 
704
  #phone-preview .phone-outside > div {
705
  position: absolute;
706
  }
@@ -745,7 +777,7 @@ Preview phone and button placement
745
  margin-left: -30px;
746
  }
747
  #cnb-button-preview {
748
- position: relative;
749
  height: 100%;
750
  overflow: hidden;
751
  }
@@ -757,7 +789,7 @@ Enforce mobile viewport for previews via overwrites:
757
  #cnb-button-preview .cnb-full,
758
  #cnb-button-preview .cnb-multi,
759
  #cnb-button-preview .cnb-single {
760
- position: absolute;
761
  z-index: 99998 !important;
762
  }
763
  #cnb-button-preview .cnb-full:before {
@@ -784,12 +816,69 @@ Enforce mobile viewport for previews via overwrites:
784
  }
785
 
786
  /*
787
- Welcome panel styling
788
  */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
789
  .welcome-column-box {
790
  padding-right: 10px;
791
  }
 
 
 
 
 
 
 
 
 
792
  @media screen and (max-width: 870px) {
 
 
 
 
793
  .cnb-mobile-inline {
794
  display: inline-block;
795
  margin-bottom:0;
@@ -797,19 +886,15 @@ Welcome panel styling
797
  .only-in-columns {
798
  display: none;
799
  }
800
- .call-now-button .welcome-panel-column .button {
801
  margin-top:22px !important;
802
  }
803
  }
804
  @media screen and (max-width: 600px) {
805
- .call-now-button .welcome-panel .welcome-panel-close {
806
- height: 27px;
807
- width: 27px;
808
  }
809
- .cnb-mobile-inline {
810
- display: block;
811
- margin-bottom:0;
812
- }
813
 
814
  }
815
 
@@ -832,9 +917,72 @@ a.cnb-nav-tab:hover {
832
  background-color: #fff;
833
  color: #3c434a;
834
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
835
 
836
- #cnb_email_activation {
837
- text-align: center;
838
- font-weight: bold;
839
- background: #fff;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
840
  }
48
  text-decoration: underline;
49
  }
50
 
51
+ .top-0 {
52
+ margin-top: 0;
53
+ padding-top: 0;
54
+ }
55
+ .bottom-0 {
56
+ margin-bottom: 0;
57
+ padding-bottom: 0;
58
+ }
59
+
60
  .cnb_footer_beta {
61
  border: 1px solid;
62
  border-radius: 3px;
156
  font-size: 14px;
157
  }
158
 
159
+ #TB_window {
160
+ top:10px !important;
161
+ margin-top:0 !important;
162
+ }
163
+ .call-now-button0_page_call-now-button-domains #TB_window {
164
+ height: 97%;
165
+ }
166
+ .call-now-button0_page_call-now-button-domains #TB_ajaxContent {
167
+ height: calc(100% - 32px) !important;
168
+ padding: 0 15px !important;
169
+ }
170
+
171
  table.form-table.nav-tab-only {
172
  display: table;
173
  }
387
  border: 1px solid #c3c4c7;
388
  box-shadow: 0 1px 1px rgb(0 0 0 / 4%);
389
  background: #fff;
390
+ margin: 5px;
391
  max-width:1000px;
392
  }
393
 
722
  /*
723
  Preview phone and button placement
724
  */
725
+ #phone-preview {
726
+ margin-bottom: 50px;
727
+ }
728
+ #phone-preview .phone-outside:after {
729
+ content: '*The preview ignores the scheduler';
730
+ bottom: -25px;
731
+ position: absolute;
732
+ font-style: italic;
733
+ width: 100%;
734
+ text-align: center;
735
+ }
736
  #phone-preview .phone-outside > div {
737
  position: absolute;
738
  }
777
  margin-left: -30px;
778
  }
779
  #cnb-button-preview {
780
+ position: relative !important;
781
  height: 100%;
782
  overflow: hidden;
783
  }
789
  #cnb-button-preview .cnb-full,
790
  #cnb-button-preview .cnb-multi,
791
  #cnb-button-preview .cnb-single {
792
+ position: absolute !important;
793
  z-index: 99998 !important;
794
  }
795
  #cnb-button-preview .cnb-full:before {
816
  }
817
 
818
  /*
819
+ Welcome banner styling
820
  */
821
+ .welcome-banner {
822
+ position: relative;
823
+ overflow: auto;
824
+ margin: 16px 0;
825
+ padding: 23px 10px 10px;
826
+ border: 1px solid #c3c4c7;
827
+ box-shadow: 0 1px 1px rgb(0 0 0 / 4%);
828
+ background: #fff;
829
+ font-size: 13px;
830
+ line-height: 1.7;
831
+ }
832
+ .welcome-banner h2 {
833
+ margin: 0;
834
+ font-size: 21px;
835
+ font-weight: 400;
836
+ line-height: 1.2;
837
+ }
838
+ .welcome-banner h3 {
839
+ margin-bottom:0.5em;
840
+ }
841
+ .welcome-banner .about-description {
842
+ font-size: 16px;
843
+ margin: 0;
844
+ }
845
+ .welcome-banner-content {
846
+ margin-left: 13px;
847
+ max-width: 1500px;
848
+ }
849
+ .welcome-banner p {
850
+ color: #646970;
851
+ margin:0.5em 0;
852
+ }
853
+ .welcome-banner .welcome-banner-column-container {
854
+ clear: both;
855
+ position: relative;
856
+ }
857
+ .welcome-banner .welcome-banner-column {
858
+ width: 32%;
859
+ min-width: 200px;
860
+ float: left;
861
+ }
862
+ .welcome-banner .welcome-banner-column:first-child {
863
+ width: 36%;
864
+ }
865
  .welcome-column-box {
866
  padding-right: 10px;
867
  }
868
+ .welcome-banner .welcome-banner-close {
869
+ position: absolute;
870
+ top: 10px;
871
+ right: 10px;
872
+ padding: 10px 15px 10px 21px;
873
+ font-size: 13px;
874
+ line-height: 1.23076923;
875
+ text-decoration: none;
876
+ }
877
  @media screen and (max-width: 870px) {
878
+ .welcome-banner .welcome-banner-column,
879
+ .welcome-banner .welcome-banner-column:first-child {
880
+ width: 100%;
881
+ }
882
  .cnb-mobile-inline {
883
  display: inline-block;
884
  margin-bottom:0;
886
  .only-in-columns {
887
  display: none;
888
  }
889
+ .call-now-button .welcome-banner-column .button {
890
  margin-top:22px !important;
891
  }
892
  }
893
  @media screen and (max-width: 600px) {
894
+ .cnb-mobile-inline {
895
+ display: block;
896
+ margin-bottom:0;
897
  }
 
 
 
 
898
 
899
  }
900
 
917
  background-color: #fff;
918
  color: #3c434a;
919
  }
920
+ /* Message when email activation was initiated */
921
+ input.cnb_activation_input_field {
922
+ width:calc(100% - 158px);
923
+ max-width: 25em;
924
+ }
925
+ span.cnb_check_email_message {
926
+ background-color: #fff;
927
+ display: block;
928
+ padding: 5px;
929
+ text-align: center;
930
+ border: 1px solid #2271b1;
931
+ border-radius: 4px;
932
+ }
933
+ .notice-call-now-button span.cnb_check_email_message {
934
+ border-radius: 0;
935
+ border: 0;
936
+ border-top: 1px solid #2271b1;
937
+ border-bottom: 1px solid #2271b1;
938
+ background-color: #f7f7f7;
939
+ padding:15px 5px;
940
+ text-align: left;
941
+ }
942
+ @media screen and (max-width: 450px) {
943
+ input.cnb_activation_input_field {
944
+ width: 100%;
945
+ margin-bottom: 10px;
946
+ }
947
+ input.cnb_activation_input_field ~ input[type=submit] {
948
+ width: 100%;
949
+ }
950
+ .notice-call-now-button span.cnb_check_email_message {
951
+ text-align: center;
952
+ }
953
+ }
954
+ /* There's currently no native class for a delete button. Adding our own */
955
+ .wp-core-ui .button.delete {
956
+ color: #c00;
957
+ border-color: #c00;
958
+ }
959
+ .wp-core-ui .button.delete:focus,
960
+ .wp-core-ui .button.delete:focus-visible {
961
+ outline-color: #C00;
962
+ }
963
 
964
+
965
+ .cnb-scheduler-slider .ui-slider-horizontal {
966
+ border:0 !important;
967
+ }
968
+ .cnb-scheduler-slider .ui-state-active,
969
+ .cnb-scheduler-slider .ui-widget-content .ui-state-active,
970
+ .cnb-scheduler-slider .ui-widget-header .ui-state-active,
971
+ .cnb-scheduler-slider a.ui-button:active,
972
+ .cnb-scheduler-slider .ui-button:active,
973
+ .cnb-scheduler-slider .ui-button.ui-state-active:hover {
974
+ border: 1px solid #2371b1;
975
+ background: #fff;
976
+ font-weight: normal;
977
+ color: #fff
978
+ }
979
+ .cnb-scheduler-slider p:first-child {
980
+ margin-bottom:1em;
981
+ }
982
+ .cnb-promobox-action-left #cnb_email_activation_alternate_formd:before {
983
+ content: '*The Premium Free plan is for websites with up to 20k pageviews per month. More popular websites can try it out but are required to upgrade to a PRO plan once the limit gets hit.';
984
+ display: block;
985
+ padding-left: 12px;
986
+ padding-bottom: 16px;
987
+ color: #646970;
988
  }
src/admin/action-edit.php CHANGED
@@ -278,6 +278,17 @@ function cnb_wp_locale_day_to_daysofweek_array_index($wp_locale_day) {
278
  return $wp_locale_day - 1;
279
  }
280
 
 
 
 
 
 
 
 
 
 
 
 
281
  /**
282
  * @param $action CnbAction
283
  * @param $button CnbButton
@@ -285,21 +296,15 @@ function cnb_wp_locale_day_to_daysofweek_array_index($wp_locale_day) {
285
  * @param $show_table boolean
286
  */
287
  function cnb_render_form_action($action, $button=null, $domain=null, $show_table=true) {
288
- // In case a domain is not passed, we take it from the button
289
- $domain = isset($domain) ? $domain : (isset($button) ? $button->domain : null);
290
  /**
291
  * @global WP_Locale $wp_locale WordPress date and time locale object.
292
  */
293
  global $wp_locale;
294
- //
295
- /**
296
- * CNB week starts on Monday (0), WP_Local starts on Sunday (0)
297
- * See cnb_wp_locale_day_to_daysofweek_array_index()
298
- *
299
- * This array only signifies the order to DISPLAY the days in the UI according to WP_Locale
300
- * So, in this case, we make the UI render the week starting on Monday (1) and end on Sunday (0).
301
- */
302
- $cnb_days_of_week_order = array(1,2,3,4,5,6,0);
303
 
304
  if (empty($action->actionType)) {
305
  $action->actionType = 'PHONE';
@@ -313,11 +318,13 @@ function cnb_render_form_action($action, $button=null, $domain=null, $show_table
313
  wp_enqueue_script('jquery');
314
  wp_enqueue_script('jquery-ui-core');
315
  wp_enqueue_script('jquery-ui-slider');
 
316
 
317
  // Uses domain timezone if no timezone can be found
318
  $timezone = (isset($action->schedule) && !empty($action->schedule->timezone)) ? $action->schedule->timezone : (isset($domain) ? $domain->timezone : null);
319
  $action_tz_different_from_domain = isset($domain) && !empty($domain->timezone) && $domain->timezone !== $timezone;
320
  cnb_domain_timezone_check( $domain );
 
321
  ?>
322
  <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); } ?>" />
323
  <input type="hidden" name="actions[<?php esc_attr_e($action->id) ?>][delete]" id="cnb_action_<?php esc_attr_e($action->id) ?>_delete" value="" />
@@ -401,20 +408,25 @@ function cnb_render_form_action($action, $button=null, $domain=null, $show_table
401
  <th colspan="2"><hr /></th>
402
  </tr>
403
 
 
 
 
 
 
404
  <tr class="cnb-action-properties-whatsapp">
405
  <th></th>
406
  <td><a class="cnb_cursor_pointer" onclick="jQuery('.cnb-action-properties-whatsapp-extra').show();jQuery(this).parent().parent().hide()">Extra Whatsapp settings...</a></td>
407
  </tr>
408
- <tr class="cnb-action-properties-whatsapp-extra">
409
  <th colspan="2"><hr /></th>
410
  </tr>
411
- <tr class="cnb-action-properties-whatsapp-extra">
412
  <th scope="row"><label for="action-properties-message">Default message</label></th>
413
  <td>
414
  <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>
415
  </td>
416
  </tr>
417
- <tr class="cnb-action-properties-whatsapp-extra">
418
  <th colspan="2"><hr /></th>
419
  </tr>
420
 
@@ -464,14 +476,21 @@ function cnb_render_form_action($action, $button=null, $domain=null, $show_table
464
  <?php } ?>
465
 
466
  <tr class="cnb_hide_on_modal">
467
- <th scope="row">Always on</th>
468
  <td>
 
469
  <input name="actions[<?php esc_attr_e($action->id) ?>][schedule][showAlways]" type="hidden" value="false" />
470
  <input id="actions_schedule_show_always" class="cnb_toggle_checkbox" onchange="return cnb_hide_on_show_always();" name="actions[<?php esc_attr_e($action->id) ?>][schedule][showAlways]" type="checkbox"
471
- value="true" <?php checked(true, $action->id === 'new' || $action->schedule->showAlways); ?> />
 
472
  <label for="actions_schedule_show_always" class="cnb_toggle_label">Toggle</label>
473
- <span data-cnb_toggle_state_label="actions_schedule_show_always" class="cnb_toggle_state cnb_toggle_false">(No)</span>
474
- <span data-cnb_toggle_state_label="actions_schedule_show_always" class="cnb_toggle_state cnb_toggle_true">Yes</span>
 
 
 
 
 
475
  </td>
476
  </tr>
477
  <tr>
@@ -480,7 +499,7 @@ function cnb_render_form_action($action, $button=null, $domain=null, $show_table
480
  </td>
481
  </tr>
482
  <tr class="cnb_hide_on_show_always">
483
- <th>Show on these days</th>
484
  <td>
485
  <?php
486
  foreach ($cnb_days_of_week_order as $cnb_day_of_week) {
@@ -499,12 +518,27 @@ function cnb_render_form_action($action, $button=null, $domain=null, $show_table
499
  </td>
500
  </tr>
501
  <tr class="cnb_hide_on_show_always">
502
- <th><label for="actions[<?php esc_attr_e($action->id) ?>][schedule][start]">Start time</label></th>
503
- <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>
 
 
 
 
504
  </tr>
505
  <tr class="cnb_hide_on_show_always">
506
- <th><label for="actions[<?php esc_attr_e($action->id) ?>][schedule][stop]">End time</label></th>
507
- <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>
 
 
 
 
 
 
 
 
 
 
 
508
  </tr>
509
  <tr class="cnb_hide_on_show_always<?php if (!$action_tz_different_from_domain) { ?> cnb_advanced_view<?php } ?>">
510
  <th><label for="actions[<?php esc_attr_e($action->id) ?>][schedule][timezone]">Timezone</label></th>
@@ -528,17 +562,6 @@ function cnb_render_form_action($action, $button=null, $domain=null, $show_table
528
  <?php } ?>
529
  </td>
530
  </tr>
531
- <tr class="cnb_hide_on_show_always">
532
- <th><label for="actions_schedule_outside_hours">After hours</label></th>
533
- <td>
534
- <input id="actions_schedule_outside_hours" class="cnb_toggle_checkbox" name="actions[<?php esc_attr_e($action->id) ?>][schedule][outsideHours]" type="checkbox"
535
- value="true" <?php checked(true, isset($action->schedule) && $action->schedule->outsideHours); ?> />
536
- <label for="actions_schedule_outside_hours" class="cnb_toggle_label">Toggle</label>
537
- <span data-cnb_toggle_state_label="actions_schedule_outside_hours" class="cnb_toggle_state cnb_toggle_false">Button shows between set hours</span>
538
- <span data-cnb_toggle_state_label="actions_schedule_outside_hours" class="cnb_toggle_state cnb_toggle_true">Button shows outside set hours</span>
539
-
540
- </td>
541
- </tr>
542
  <?php if ($show_table) { ?>
543
  </table>
544
  <?php } ?>
@@ -567,8 +590,7 @@ function cnb_admin_page_action_edit_render_main($action, $button, $domain=null,
567
  ? boolval($action->iconEnabled)
568
  : true;
569
  ?>
570
- <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>
571
- <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" />
572
  <input type="hidden" name="bid" value="<?php echo $bid ?>" />
573
  <input type="hidden" name="action_id" value="<?php echo $action->id ?>" />
574
  <input type="hidden" name="_wpnonce" value="<?php echo wp_create_nonce('cnb-action-edit')?>" />
@@ -628,6 +650,7 @@ function cnb_admin_page_action_edit_render() {
628
  wp_enqueue_script(CNB_SLUG . '-client');
629
 
630
  do_action('cnb_header');
 
631
  ?>
632
  <div class="cnb-two-column-section-preview">
633
  <div class="cnb-body-column">
@@ -645,7 +668,6 @@ function cnb_admin_page_action_edit_render() {
645
  let cnb_button = <?php echo json_encode($button); ?>;
646
  let cnb_actions = <?php echo json_encode($button->actions); ?>;
647
  let cnb_domain = <?php echo json_encode($button->domain) ?>;
648
-
649
  </script>
650
 
651
  <form class="cnb-container" action="<?php echo $redirect_link; ?>" method="post">
278
  return $wp_locale_day - 1;
279
  }
280
 
281
+ /**
282
+ * CNB week starts on Monday (0), WP_Local starts on Sunday (0)
283
+ * See cnb_wp_locale_day_to_daysofweek_array_index()
284
+ *
285
+ * This array only signifies the order to DISPLAY the days in the UI according to WP_Locale
286
+ * So, in this case, we make the UI render the week starting on Monday (1) and end on Sunday (0).
287
+ */
288
+ function cnb_wp_get_order_of_days() {
289
+ return array(1,2,3,4,5,6,0);
290
+ }
291
+
292
  /**
293
  * @param $action CnbAction
294
  * @param $button CnbButton
296
  * @param $show_table boolean
297
  */
298
  function cnb_render_form_action($action, $button=null, $domain=null, $show_table=true) {
 
 
299
  /**
300
  * @global WP_Locale $wp_locale WordPress date and time locale object.
301
  */
302
  global $wp_locale;
303
+
304
+ // In case a domain is not passed, we take it from the button
305
+ $domain = isset($domain) ? $domain : (isset($button) ? $button->domain : null);
306
+
307
+ $cnb_days_of_week_order = cnb_wp_get_order_of_days();
 
 
 
 
308
 
309
  if (empty($action->actionType)) {
310
  $action->actionType = 'PHONE';
318
  wp_enqueue_script('jquery');
319
  wp_enqueue_script('jquery-ui-core');
320
  wp_enqueue_script('jquery-ui-slider');
321
+ wp_enqueue_script(CNB_SLUG . '-action-edit-scheduler');
322
 
323
  // Uses domain timezone if no timezone can be found
324
  $timezone = (isset($action->schedule) && !empty($action->schedule->timezone)) ? $action->schedule->timezone : (isset($domain) ? $domain->timezone : null);
325
  $action_tz_different_from_domain = isset($domain) && !empty($domain->timezone) && $domain->timezone !== $timezone;
326
  cnb_domain_timezone_check( $domain );
327
+ $timezone_set_correctly = cnb_warn_about_timezone($domain);
328
  ?>
329
  <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); } ?>" />
330
  <input type="hidden" name="actions[<?php esc_attr_e($action->id) ?>][delete]" id="cnb_action_<?php esc_attr_e($action->id) ?>_delete" value="" />
408
  <th colspan="2"><hr /></th>
409
  </tr>
410
 
411
+ <tr class="cnb-action-properties-sms">
412
+ <th></th>
413
+ <td><a class="cnb_cursor_pointer" onclick="jQuery('.cnb-action-properties-sms-extra').show();jQuery(this).parent().parent().hide()">Extra SMS settings...</a></td>
414
+ </tr>
415
+
416
  <tr class="cnb-action-properties-whatsapp">
417
  <th></th>
418
  <td><a class="cnb_cursor_pointer" onclick="jQuery('.cnb-action-properties-whatsapp-extra').show();jQuery(this).parent().parent().hide()">Extra Whatsapp settings...</a></td>
419
  </tr>
420
+ <tr class="cnb-action-properties-whatsapp-extra cnb-action-properties-sms-extra">
421
  <th colspan="2"><hr /></th>
422
  </tr>
423
+ <tr class="cnb-action-properties-whatsapp-extra cnb-action-properties-sms-extra">
424
  <th scope="row"><label for="action-properties-message">Default message</label></th>
425
  <td>
426
  <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>
427
  </td>
428
  </tr>
429
+ <tr class="cnb-action-properties-whatsapp-extra cnb-action-properties-sms-extra">
430
  <th colspan="2"><hr /></th>
431
  </tr>
432
 
476
  <?php } ?>
477
 
478
  <tr class="cnb_hide_on_modal">
479
+ <th scope="row">Show at all times</th>
480
  <td>
481
+ <?php $showAlwaysValue = $action->id === 'new' || $action->schedule->showAlways; ?>
482
  <input name="actions[<?php esc_attr_e($action->id) ?>][schedule][showAlways]" type="hidden" value="false" />
483
  <input id="actions_schedule_show_always" class="cnb_toggle_checkbox" onchange="return cnb_hide_on_show_always();" name="actions[<?php esc_attr_e($action->id) ?>][schedule][showAlways]" type="checkbox"
484
+ value="true" <?php checked(true, $showAlwaysValue); ?>
485
+ <?php if (!$timezone_set_correctly) { ?>disabled="disabled" <?php } ?>/>
486
  <label for="actions_schedule_show_always" class="cnb_toggle_label">Toggle</label>
487
+ <span data-cnb_toggle_state_label="actions_schedule_show_always" class="cnb_toggle_state cnb_toggle_true">Yes <?php if (!$timezone_set_correctly) { ?>(disabled)<?php } ?></span>
488
+ <?php if (!$timezone_set_correctly && $showAlwaysValue){ ?>
489
+ <p class="description"><span class="dashicons dashicons-warning"></span>The scheduler is disabled because your timezone is not set correctly yet.</p>
490
+ <?php } ?>
491
+ <?php if (!$timezone_set_correctly && !$showAlwaysValue){ ?>
492
+ <p class="description"><span class="dashicons dashicons-warning"></span>Please set your timezone before making any more changes. See the notice at the top of the page for more information.</p>
493
+ <?php } ?>
494
  </td>
495
  </tr>
496
  <tr>
499
  </td>
500
  </tr>
501
  <tr class="cnb_hide_on_show_always">
502
+ <th>Set days</th>
503
  <td>
504
  <?php
505
  foreach ($cnb_days_of_week_order as $cnb_day_of_week) {
518
  </td>
519
  </tr>
520
  <tr class="cnb_hide_on_show_always">
521
+ <th><label for="actions_schedule_outside_hours">After hours</label></th>
522
+ <td>
523
+ <input id="actions_schedule_outside_hours" class="cnb_toggle_checkbox" name="actions[<?php esc_attr_e($action->id) ?>][schedule][outsideHours]" type="checkbox"
524
+ value="true" <?php checked(true, isset($action->schedule) && $action->schedule->outsideHours); ?> />
525
+ <label for="actions_schedule_outside_hours" class="cnb_toggle_label">Toggle</label>
526
+ </td>
527
  </tr>
528
  <tr class="cnb_hide_on_show_always">
529
+ <th>Set times</th>
530
+ <td class="cnb-scheduler-slider">
531
+ <p id="cnb-schedule-range-text"></p>
532
+ <div id="cnb-schedule-range" style="max-width: 300px"></div>
533
+ </td>
534
+ </tr>
535
+ <tr class="cnb_hide_on_show_always cnb_advanced_view">
536
+ <th><label for="actions-schedule-start">Start time</label></th>
537
+ <td><input type="time" name="actions[<?php esc_attr_e($action->id) ?>][schedule][start]" id="actions-schedule-start" value="<?php if (isset($action->schedule)) { esc_attr_e($action->schedule->start); } ?>"></td>
538
+ </tr>
539
+ <tr class="cnb_hide_on_show_always cnb_advanced_view">
540
+ <th><label for="actions-schedule-stop">End time</label></th>
541
+ <td><input type="time" name="actions[<?php esc_attr_e($action->id) ?>][schedule][stop]" id="actions-schedule-stop" value="<?php if (isset($action->schedule)) { esc_attr_e($action->schedule->stop); } ?>"></td>
542
  </tr>
543
  <tr class="cnb_hide_on_show_always<?php if (!$action_tz_different_from_domain) { ?> cnb_advanced_view<?php } ?>">
544
  <th><label for="actions[<?php esc_attr_e($action->id) ?>][schedule][timezone]">Timezone</label></th>
562
  <?php } ?>
563
  </td>
564
  </tr>
 
 
 
 
 
 
 
 
 
 
 
565
  <?php if ($show_table) { ?>
566
  </table>
567
  <?php } ?>
590
  ? boolval($action->iconEnabled)
591
  : true;
592
  ?>
593
+
 
594
  <input type="hidden" name="bid" value="<?php echo $bid ?>" />
595
  <input type="hidden" name="action_id" value="<?php echo $action->id ?>" />
596
  <input type="hidden" name="_wpnonce" value="<?php echo wp_create_nonce('cnb-action-edit')?>" />
650
  wp_enqueue_script(CNB_SLUG . '-client');
651
 
652
  do_action('cnb_header');
653
+
654
  ?>
655
  <div class="cnb-two-column-section-preview">
656
  <div class="cnb-body-column">
668
  let cnb_button = <?php echo json_encode($button); ?>;
669
  let cnb_actions = <?php echo json_encode($button->actions); ?>;
670
  let cnb_domain = <?php echo json_encode($button->domain) ?>;
 
671
  </script>
672
 
673
  <form class="cnb-container" action="<?php echo $redirect_link; ?>" method="post">
src/admin/action-overview.php CHANGED
@@ -75,7 +75,8 @@ class Cnb_Action_List_Table extends WP_List_Table {
75
  'id' => __('ID'),
76
  'actionType' => __('Type'),
77
  'actionValue' => __('Value'),
78
- 'labelText' => __('Label')
 
79
  );
80
  if ($this->button) { unset($columns['cb']); }
81
 
@@ -156,7 +157,8 @@ class Cnb_Action_List_Table extends WP_List_Table {
156
  case 'actionValue':
157
  case 'labelText':
158
  return !empty($item[$column_name]) ? esc_html($item[$column_name]) : '<em>No value</em>';
159
- // Handled by column_actionType
 
160
  case 'actionType':
161
  return null;
162
  case 'actionButton':
@@ -218,7 +220,8 @@ class Cnb_Action_List_Table extends WP_List_Table {
218
  'actionButton' => $button,
219
  'actionType' => $action->actionType,
220
  'actionValue' => $action->actionValue,
221
- 'labelText' => $action->labelText
 
222
  );
223
  }
224
  return $data;
@@ -258,6 +261,89 @@ class Cnb_Action_List_Table extends WP_List_Table {
258
  );
259
  }
260
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
261
  function column_actionType($item) {
262
  $column_name = 'actionType';
263
  $bid = $this->button !== null ? $this->button->id : null;
75
  'id' => __('ID'),
76
  'actionType' => __('Type'),
77
  'actionValue' => __('Value'),
78
+ 'labelText' => __('Label'),
79
+ 'schedule' => __('Schedule'),
80
  );
81
  if ($this->button) { unset($columns['cb']); }
82
 
157
  case 'actionValue':
158
  case 'labelText':
159
  return !empty($item[$column_name]) ? esc_html($item[$column_name]) : '<em>No value</em>';
160
+ // Handled by column_actionType & column_schedule
161
+ case 'schedule':
162
  case 'actionType':
163
  return null;
164
  case 'actionButton':
220
  'actionButton' => $button,
221
  'actionType' => $action->actionType,
222
  'actionValue' => $action->actionValue,
223
+ 'labelText' => $action->labelText,
224
+ 'schedule' => $action->schedule,
225
  );
226
  }
227
  return $data;
261
  );
262
  }
263
 
264
+ function column_schedule($item) {
265
+ /**
266
+ * @global WP_Locale $wp_locale WordPress date and time locale object.
267
+ */
268
+ global $wp_locale;
269
+
270
+ if (!$item || !$item['schedule'] || $item['schedule']->showAlways) {
271
+ return __('Always visible');
272
+ }
273
+
274
+ $cnb_days_of_week_order = cnb_wp_get_order_of_days();
275
+
276
+ $schedule = $item['schedule'];
277
+
278
+ $days = '';
279
+ $daysOfWeek = array_unique($schedule->daysOfWeek);
280
+ if (count($daysOfWeek) === 1) {
281
+ // All disabled or enabled
282
+ if ($daysOfWeek[0] === true) {
283
+ $days = __( 'All days');
284
+ } else {
285
+ // Alligator alert: normally this is no possible, but adding for completeness
286
+ $days = __('No days');
287
+ }
288
+ } else {
289
+ // Special case: weekdays (and weekends?)
290
+ // First 5 are true, last 2 are false == weekdays
291
+ // Get first 5, filter for false/null, count
292
+ $weekdays = (count(array_filter(array_slice($schedule->daysOfWeek, 0, 5))) === 5 &&
293
+ count(array_filter(array_slice($schedule->daysOfWeek, 5, 2))) === 0);
294
+ $weekend = (count(array_filter(array_slice($schedule->daysOfWeek, 0, 5))) === 0 &&
295
+ count(array_filter(array_slice($schedule->daysOfWeek, 5, 2))) === 2);
296
+ if ($weekdays) {
297
+ $days = __('Week days only');
298
+ } else if ($weekend) {
299
+ $days = __('Weekend only');
300
+ } else {
301
+ // Print days it's enabled
302
+ $split = '';
303
+ foreach ( $cnb_days_of_week_order as $cnb_day_of_week ) {
304
+ $api_server_index = cnb_wp_locale_day_to_daysofweek_array_index( $cnb_day_of_week );
305
+ $day = $wp_locale->get_weekday( $cnb_day_of_week );
306
+ if ( $schedule->daysOfWeek[ $api_server_index ] ) {
307
+ $days .= $split . $day;
308
+ $split = ', ';
309
+ }
310
+ }
311
+ }
312
+ }
313
+ $wp_time_format = get_option('time_format');
314
+
315
+ $start = $schedule->start;
316
+ $stop = $schedule->stop;
317
+
318
+ $date = new DateTime();
319
+ $hour = explode(':', $start)[0];
320
+ $min = explode(':', $start)[1];
321
+ $date->setTime($hour, $min);
322
+ $wp_start = $date->format($wp_time_format);
323
+
324
+ $hour = explode(':', $stop)[0];
325
+ $min = explode(':', $stop)[1];
326
+ $date->setTime($hour, $min);
327
+ $wp_stop = $date->format($wp_time_format);
328
+
329
+ // print time
330
+ $time = '';
331
+ if ($start == "00:00" && $stop === "23:59") {
332
+ $time = '<em>'.__('All day').'</em>';
333
+ } else {
334
+ $time = esc_html($wp_start) . ' - ' . esc_html($wp_stop);
335
+ }
336
+
337
+ if ($schedule->outsideHours) {
338
+ $time = 'Before ' . esc_html($wp_start) . ', after ' . esc_html($wp_stop);
339
+ }
340
+ return sprintf(
341
+ '%1$s %2$s',
342
+ '<div class="cnb-scheduler-days">'.$days.'</div>',
343
+ '<div class="time">'.$time.'</div>'
344
+ );
345
+ }
346
+
347
  function column_actionType($item) {
348
  $column_name = 'actionType';
349
  $bid = $this->button !== null ? $this->button->id : null;
src/admin/admin-ajax.php CHANGED
@@ -93,3 +93,21 @@ function cnb_admin_cnb_email_activation() {
93
  }
94
 
95
  add_action('wp_ajax_cnb_email_activation', 'cnb_admin_cnb_email_activation');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  }
94
 
95
  add_action('wp_ajax_cnb_email_activation', 'cnb_admin_cnb_email_activation');
96
+
97
+ function cnb_time_format_($time) {
98
+ $time_format = get_option('time_format');
99
+ $time_formatted = strtotime($time);
100
+ return date_i18n( $time_format, $time_formatted );
101
+ }
102
+
103
+ function cnb_time_format() {
104
+ $start = trim(filter_input( INPUT_POST, 'start', FILTER_SANITIZE_STRING ));
105
+ $stop = trim(filter_input( INPUT_POST, 'stop', FILTER_SANITIZE_STRING ));
106
+ wp_send_json(array(
107
+ 'start' => cnb_time_format_($start),
108
+ 'stop' => cnb_time_format_($stop),
109
+ )
110
+ );
111
+ }
112
+
113
+ add_action('wp_ajax_cnb_time_format', 'cnb_time_format');
src/admin/api/CnbAdminCloud.php CHANGED
@@ -4,6 +4,19 @@ require_once dirname( __FILE__ ) . '/../../utils/CnbAdminNotices.class.php';
4
 
5
  class CnbAdminCloud {
6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  /**
8
  * Called when Cloud Hosting is enabled via settings
9
  *
@@ -13,14 +26,15 @@ class CnbAdminCloud {
13
  *
14
  * @return string The ID to use for the Cloud Button
15
  */
16
- public static function cnb_set_default_option_for_cloud( $options ) {
17
  global $cnb_options;
18
 
19
  // Check if an ID has already been set. If so, use if and continue
20
  if ( isset( $options['cloud_use_id'] ) && ! empty( $options['cloud_use_id'] ) ) {
21
  return null;
22
  }
23
- // Check if an ID has already been set. If so, use if and continue
 
24
  if ( empty( $options['api_key'] ) ) {
25
  return null;
26
  }
@@ -30,12 +44,33 @@ class CnbAdminCloud {
30
  $cnb_options['api_key'] = $options['api_key'];
31
  // Check if we can talk to the API via a key. If so, use the current user to be safe
32
  $user_info = CnbAppRemote::cnb_remote_get_user_info();
 
33
  // Reset key
34
  $cnb_options['api_key'] = $original_key;
35
  if ( !is_wp_error($user_info)) {
36
  return $user_info->id;
37
  }
38
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  return null;
40
  }
41
 
@@ -451,8 +486,8 @@ class CnbAdminCloud {
451
  $button_overview_url = esc_url( $button_overview_link );
452
 
453
  $notice = new CnbNotice('success', '<p><span class="dashicons dashicons-cloud-saved"></span>
454
- Congratulations, you have successfully activated the cloud version!
455
- Click <a href="' . $button_overview_url . '">here</a> to create your first button.</p>');
456
  array_push( $cnb_cloud_notifications, $notice);
457
 
458
  return $cnb_cloud_notifications;
@@ -500,8 +535,8 @@ class CnbAdminCloud {
500
  $button_edit_url = esc_url( $button_edit_link );
501
 
502
  $notice = new CnbNotice('success', '<p><span class="dashicons dashicons-cloud-saved"></span>
503
- Congratulations! You are now using the Call Now Button cloud version!
504
- Click <a href="' . $button_edit_url . '">here</a> to edit your button.</p>');
505
  array_push( $cnb_cloud_notifications, $notice);
506
  }
507
 
4
 
5
  class CnbAdminCloud {
6
 
7
+ /**
8
+ * @param $api_key
9
+ *
10
+ * @return boolean true is the key is valid, false if not
11
+ */
12
+ public function is_api_key_valid($api_key) {
13
+ global $cnb_options;
14
+ $old_value = $cnb_options['api_key'];
15
+ $cnb_options['api_key'] = $api_key;
16
+ $user_info = CnbAppRemote::cnb_remote_get_user_info();
17
+ $cnb_options['api_key'] = $old_value;
18
+ return !is_wp_error($user_info);
19
+ }
20
  /**
21
  * Called when Cloud Hosting is enabled via settings
22
  *
26
  *
27
  * @return string The ID to use for the Cloud Button
28
  */
29
+ public static function cnb_set_default_option_for_cloud( &$options ) {
30
  global $cnb_options;
31
 
32
  // Check if an ID has already been set. If so, use if and continue
33
  if ( isset( $options['cloud_use_id'] ) && ! empty( $options['cloud_use_id'] ) ) {
34
  return null;
35
  }
36
+
37
+ // If no API key is passed, there is nothing to do here
38
  if ( empty( $options['api_key'] ) ) {
39
  return null;
40
  }
44
  $cnb_options['api_key'] = $options['api_key'];
45
  // Check if we can talk to the API via a key. If so, use the current user to be safe
46
  $user_info = CnbAppRemote::cnb_remote_get_user_info();
47
+
48
  // Reset key
49
  $cnb_options['api_key'] = $original_key;
50
  if ( !is_wp_error($user_info)) {
51
  return $user_info->id;
52
  }
53
 
54
+ // See if the passed in API key is REALLY an OTT
55
+ $api_key_from_ott = cnb_try_api_key_ott($options['api_key'], false);
56
+
57
+ if ($api_key_from_ott != null) {
58
+ $original_key = !empty($cnb_options['api_key']) ? $cnb_options['api_key'] : null;
59
+ $cnb_options['api_key'] = $api_key_from_ott;
60
+
61
+ // Check if we can talk to the API via a key. If so, use the current user to be safe
62
+ $user_info = CnbAppRemote::cnb_remote_get_user_info();
63
+
64
+ if ( !is_wp_error($user_info)) {
65
+ $options['api_key'] = $api_key_from_ott;
66
+ return $user_info->id;
67
+ }
68
+
69
+ // Reset key(s)
70
+ $options['api_key'] = $original_key;
71
+ $cnb_options['api_key'] = $original_key;
72
+ }
73
+
74
  return null;
75
  }
76
 
486
  $button_overview_url = esc_url( $button_overview_link );
487
 
488
  $notice = new CnbNotice('success', '<p><span class="dashicons dashicons-cloud-saved"></span>
489
+ Congratulations! You have successfully activated Call Now Button <strong>Premium</strong>!
490
+ Click <a href="' . $button_overview_url . '">here</a> to get stared.</p>');
491
  array_push( $cnb_cloud_notifications, $notice);
492
 
493
  return $cnb_cloud_notifications;
535
  $button_edit_url = esc_url( $button_edit_link );
536
 
537
  $notice = new CnbNotice('success', '<p><span class="dashicons dashicons-cloud-saved"></span>
538
+ Congratulations! You are now using the Premium version of the Call Now Button plugin!
539
+ Click <a href="' . $button_edit_url . '">here</a> to get started.</p>');
540
  array_push( $cnb_cloud_notifications, $notice);
541
  }
542
 
src/admin/api/CnbAppRemote.php CHANGED
@@ -1,5 +1,7 @@
1
  <?php
2
 
 
 
3
  require_once dirname( __FILE__ ) . '/RemoteTrace.php';
4
 
5
  /**
@@ -396,6 +398,9 @@ class CnbAppRemote {
396
  return self::cnb_remote_handle_response( $response );
397
  }
398
 
 
 
 
399
  public static function cnb_remote_get_user_info() {
400
  $rest_endpoint = '/v1/user';
401
 
@@ -443,6 +448,11 @@ class CnbAppRemote {
443
  return self::cnb_remote_get( $rest_endpoint );
444
  }
445
 
 
 
 
 
 
446
  public static function cnb_remote_get_buttons() {
447
  $rest_endpoint = '/v1/button';
448
 
1
  <?php
2
 
3
+ use cnb\admin\models\Cnb_User;
4
+
5
  require_once dirname( __FILE__ ) . '/RemoteTrace.php';
6
 
7
  /**
398
  return self::cnb_remote_handle_response( $response );
399
  }
400
 
401
+ /**
402
+ * @return Cnb_User|WP_Error
403
+ */
404
  public static function cnb_remote_get_user_info() {
405
  $rest_endpoint = '/v1/user';
406
 
448
  return self::cnb_remote_get( $rest_endpoint );
449
  }
450
 
451
+ /**
452
+ * This does not (yet) actually return CnbButton, but a stdclass that resembles it.
453
+ *
454
+ * @return CnbButton[]|WP_Error
455
+ */
456
  public static function cnb_remote_get_buttons() {
457
  $rest_endpoint = '/v1/button';
458
 
src/admin/button-edit.php CHANGED
@@ -574,6 +574,7 @@ function cnb_admin_page_edit_render() {
574
  });
575
 
576
  do_action('cnb_header');
 
577
  ?>
578
 
579
  <div class="cnb-two-column-section-preview">
574
  });
575
 
576
  do_action('cnb_header');
577
+ cnb_warn_about_timezone($default_domain);
578
  ?>
579
 
580
  <div class="cnb-two-column-section-preview">
src/admin/button-overview.php CHANGED
@@ -24,7 +24,7 @@ function cnb_get_new_button_link() {
24
  array(
25
  'TB_inline' => 'true',
26
  'inlineId' => 'cnb-add-new-modal',
27
- 'height' => '440', // 405 is ideal -> To hide the scrollbar
28
  'page' => 'call-now-button',
29
  'action' => 'new',
30
  'type' => 'single',
@@ -501,6 +501,8 @@ function cnb_admin_page_overview_render_list() {
501
  $wp_list_table->setOption( 'filter_buttons_for_domain', $cnb_cloud_domain->id);
502
  }
503
 
 
 
504
  add_action('cnb_header_name', 'cnb_add_header_button_overview');
505
 
506
  $upgrade_url = get_cnb_domain_upgrade($cnb_cloud_domain);
24
  array(
25
  'TB_inline' => 'true',
26
  'inlineId' => 'cnb-add-new-modal',
27
+ 'height' => '500', // 405 is ideal -> To hide the scrollbar
28
  'page' => 'call-now-button',
29
  'action' => 'new',
30
  'type' => 'single',
501
  $wp_list_table->setOption( 'filter_buttons_for_domain', $cnb_cloud_domain->id);
502
  }
503
 
504
+ // If users come to this page before activating, we need the -settings JS for the activation notice
505
+ wp_enqueue_script(CNB_SLUG . '-settings');
506
  add_action('cnb_header_name', 'cnb_add_header_button_overview');
507
 
508
  $upgrade_url = get_cnb_domain_upgrade($cnb_cloud_domain);
src/admin/domain-edit.php CHANGED
@@ -155,7 +155,7 @@ function cnb_admin_page_domain_edit_process() {
155
  /**
156
  * @param CnbDomain $domain
157
  *
158
- * @return void
159
  */
160
  function cnb_domain_timezone_check($domain) {
161
  if ($domain && !is_wp_error($domain) && !empty($domain->timezone)) {
@@ -175,8 +175,10 @@ function cnb_domain_timezone_check($domain) {
175
  $message = "<p id='cnb-notice-domain-timezone-unsupported'>Please set your timezone in the <a href=\"". $redirect_url . "\">Advanced settings</a> tab to avoid unpredictable behavior when using the scheduler.</p>";
176
  $notice = new CnbNotice('warning', $message, false);
177
  CnbAdminNotices::get_instance()->renderNotice($notice);
 
178
  }
179
  }
 
180
  }
181
  /**
182
  * Main entrypoint, used by `domain-overview.php`.
155
  /**
156
  * @param CnbDomain $domain
157
  *
158
+ * @return boolean true if everything is already
159
  */
160
  function cnb_domain_timezone_check($domain) {
161
  if ($domain && !is_wp_error($domain) && !empty($domain->timezone)) {
175
  $message = "<p id='cnb-notice-domain-timezone-unsupported'>Please set your timezone in the <a href=\"". $redirect_url . "\">Advanced settings</a> tab to avoid unpredictable behavior when using the scheduler.</p>";
176
  $notice = new CnbNotice('warning', $message, false);
177
  CnbAdminNotices::get_instance()->renderNotice($notice);
178
+ return false;
179
  }
180
  }
181
+ return true;
182
  }
183
  /**
184
  * Main entrypoint, used by `domain-overview.php`.
src/admin/legacy-edit.php CHANGED
@@ -111,7 +111,7 @@ function cnb_admin_page_legacy_edit_render() {
111
 
112
  do_action('cnb_header');
113
  ?>
114
- <?php cnb_get_welcome_panel() ?>
115
  <div class="cnb-two-column-section">
116
  <div class="cnb-body-column">
117
  <div class="cnb-body-content">
@@ -291,23 +291,23 @@ function cnb_admin_page_legacy_edit_render() {
291
  'Unlock extra power',
292
  '<p>Need more powerful features such as:</p>
293
  <p>&check; More buttons<br>
294
- &check; Email, WhatsApp, Maps<br>
295
  &check; Scheduling<br>
296
- &check; Buttonbar (multi action full width)<br>
297
- &check; Multibutton (expandable single button)<br>
298
  &check; And more!</p>',
299
  'unlock',
300
  '',
301
- 'Find out more',
302
  cnb_legacy_upgrade_page()
303
  );
304
  ?>
305
  <?php
306
  cnb_promobox(
307
  'blue',
308
- 'Enable more actions',
309
  '<p>Looking for more than just a call button? You can now add the following actions:</p>
310
- <p>&check; Email<br>
 
311
  &check; WhatsApp<br>
312
  &check; Directions<br>
313
  &check; Links</p>',
111
 
112
  do_action('cnb_header');
113
  ?>
114
+ <?php cnb_get_welcome_banner() ?>
115
  <div class="cnb-two-column-section">
116
  <div class="cnb-body-column">
117
  <div class="cnb-body-content">
291
  'Unlock extra power',
292
  '<p>Need more powerful features such as:</p>
293
  <p>&check; More buttons<br>
294
+ &check; Text/SMS, Email, WhatsApp, Maps<br>
295
  &check; Scheduling<br>
296
+ &check; Multi action buttons<br>
 
297
  &check; And more!</p>',
298
  'unlock',
299
  '',
300
+ 'Try Premium',
301
  cnb_legacy_upgrade_page()
302
  );
303
  ?>
304
  <?php
305
  cnb_promobox(
306
  'blue',
307
+ 'Add more actions!',
308
  '<p>Looking for more than just a call button? You can now add the following actions:</p>
309
+ <p>&check; SMS/Text<br>
310
+ &check; Email<br>
311
  &check; WhatsApp<br>
312
  &check; Directions<br>
313
  &check; Links</p>',
src/admin/legacy-upgrade.php CHANGED
@@ -15,9 +15,8 @@ function cnb_standard_plugin_promobox() { ?>
15
  'Standard plugin',
16
  '<p>&check; One button<br>
17
  &check; Phone<br>
18
- &check; Circular<br>
19
- &check; Full width<br>
20
- <span class="cnb_description">(single action Buttonbar&#8482;)</span>
21
  &check; Action label<br>
22
  </p>
23
  <hr>
@@ -25,6 +24,7 @@ function cnb_standard_plugin_promobox() { ?>
25
  &check; Placement options<br>
26
  &check; For mobile devices<br>
27
  &check; Include or exclude pages<br>
 
28
  </p>
29
  <hr>
30
  <p>
@@ -34,7 +34,8 @@ function cnb_standard_plugin_promobox() { ?>
34
  <hr>
35
  <p>
36
  &check; Adjust the button size<br>
37
- &check; Flexible z-index
 
38
  </p>',
39
  'admin-plugins',
40
  '<strong>Free</strong>',
@@ -51,24 +52,19 @@ function cnb_premium_plugin_promobox() { ?>
51
  cnb_promobox(
52
  'blue',
53
  'Premium',
54
- '<p class="cnb_align_center">From <strong>&euro;2.49/$2.99</strong> per month or <strong style="text-decoration:underline">FREE</strong> with subtle "<em>powered by</em>" branding.</p>
55
- <hr>
56
  <p><strong>&check; Lots of buttons!</strong><br>
57
- &check; Phone, Email, WhatsApp, Maps, URLs<br>
58
- &check; Circular button<br>
59
- &check; Buttonbar&#8482;
60
- <span class="cnb_description">(multi action full width button)</span>
61
- &check; Multibutton&#8482;
62
- <span class="cnb_description">(single button unfolding into multiple)</span>
63
  &check; Actions labels<br>
64
  </p>
65
  <hr>
66
  <p>
67
  &check; Placement options<br>
68
  &check; For mobile and desktop/laptop<br>
69
- &check; Scheduling<br>
70
- &check; Advanced page targeting
71
- <span class="cnb_description">(for deciding where a button should appear. E.g. only show to Google Ads visitors)</span>
72
  </p>
73
  <hr>
74
  <p>
@@ -79,9 +75,9 @@ function cnb_premium_plugin_promobox() { ?>
79
  <p>
80
  &check; Adjust the button size<br>
81
  &check; Flexible z-index<br>
82
- &check; And much more!</p>
83
  <hr>
84
- <p class="cnb_align_center">From <strong>&euro;2.49/$2.99</strong> per month or <strong style="text-decoration:underline">FREE</strong> with subtle "<em>powered by</em>" branding.</p>',
85
  'cloud',
86
  cnb_settings_email_activation_input(),
87
  'none'
@@ -94,7 +90,9 @@ function cnb_upgrade_faq() { ?>
94
  <div style="max-width:600px;margin:0 auto">
95
  <h1 class="cnb-center">FAQ</h1>
96
  <h3>Can I really get Premium for Free?</h3>
97
- <p>Yes. It's possible to access all premium features of the Call Now Button for free. No credit card is required. You only need an account for that. The difference with the paid Premium plans is that a small "Powered by Call Now Button" notice is added to your buttons.</p>
 
 
98
  <h3>Does the Premium plan require an account?</h3>
99
  <p>Yes. We want the Call Now Button to be accessible to all website owners. Even those that do not have a WordPress powered website. The Premium version of the Call Now Button can be used by everyone. You can continue to manage your buttons from your WordPress instance, but you could also do this via our web app. And should you ever move to a different CMS, your button(s) will just move with you.</p>
100
  <h3>What is the "powered by" notice on the Free Premium plan?</h3>
15
  'Standard plugin',
16
  '<p>&check; One button<br>
17
  &check; Phone<br>
18
+ &check; Circular (single action)<br>
19
+ &check; Buttonbar (single action)<br>
 
20
  &check; Action label<br>
21
  </p>
22
  <hr>
24
  &check; Placement options<br>
25
  &check; For mobile devices<br>
26
  &check; Include or exclude pages<br>
27
+ &nbsp;
28
  </p>
29
  <hr>
30
  <p>
34
  <hr>
35
  <p>
36
  &check; Adjust the button size<br>
37
+ &check; Flexible z-index<br>
38
+ &nbsp;
39
  </p>',
40
  'admin-plugins',
41
  '<strong>Free</strong>',
52
  cnb_promobox(
53
  'blue',
54
  'Premium',
55
+ '
 
56
  <p><strong>&check; Lots of buttons!</strong><br>
57
+ &check; Phone, SMS/Text, Email, WhatsApp, Maps, URLs<br>
58
+ &check; Circular button (multi action)<br>
59
+ &check; Buttonbar (multi action)<br>
 
 
 
60
  &check; Actions labels<br>
61
  </p>
62
  <hr>
63
  <p>
64
  &check; Placement options<br>
65
  &check; For mobile and desktop/laptop<br>
66
+ &check; Advanced page targeting<br>
67
+ &check; Scheduling
 
68
  </p>
69
  <hr>
70
  <p>
75
  <p>
76
  &check; Adjust the button size<br>
77
  &check; Flexible z-index<br>
78
+ &check; Live button preview</p>
79
  <hr>
80
+ <p class="cnb_align_center">From <strong>&euro;2.49/$2.99</strong> per month or <strong style="text-decoration:underline">FREE*</strong> with subtle "<em>powered by</em>" branding.</p>',
81
  'cloud',
82
  cnb_settings_email_activation_input(),
83
  'none'
90
  <div style="max-width:600px;margin:0 auto">
91
  <h1 class="cnb-center">FAQ</h1>
92
  <h3>Can I really get Premium for Free?</h3>
93
+ <p>Yes. It's possible to access all premium features of the Call Now Button for free. No credit card is required. You only need an account for that. The difference with the paid Premium plans is that a small "Powered by Call Now Button" notice is added to your buttons and there's a monthly pageviews limit of 20k.</p>
94
+ <h3>My website has more than 20k monthly pageviews. Can I still use Premium for Free?</h3>
95
+ <p>The Free plan can be used by all websites. But once you hit the 20k pageview limit you will need to upgrade to PRO to keep using the plugin. PRO starts at &euro;2.49/$2.99 per month.</p>
96
  <h3>Does the Premium plan require an account?</h3>
97
  <p>Yes. We want the Call Now Button to be accessible to all website owners. Even those that do not have a WordPress powered website. The Premium version of the Call Now Button can be used by everyone. You can continue to manage your buttons from your WordPress instance, but you could also do this via our web app. And should you ever move to a different CMS, your button(s) will just move with you.</p>
98
  <h3>What is the "powered by" notice on the Free Premium plan?</h3>
src/admin/models/CnbButton.class.php CHANGED
@@ -30,6 +30,11 @@ class CnbButton {
30
 
31
  public $conditions;
32
 
 
 
 
 
 
33
  public static function setSaneDefault($button) {
34
  // Set some sane defaults
35
  if (!isset($button->options)) $button->options = new CnbButtonOptions();
@@ -55,14 +60,78 @@ class CnbButton {
55
  $button->type = 'SINGLE';
56
  $button->domain = $domain;
57
  $button->actions = array();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
  return $button;
60
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  }
62
 
63
  class CnbButtonOptions {
64
  public $iconBackgroundColor;
65
  public $iconColor;
 
 
 
66
  public $placement;
67
  public $scale;
68
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
  public $conditions;
32
 
33
+ /**
34
+ * @var CnbButtonOptions
35
+ */
36
+ public $options;
37
+
38
  public static function setSaneDefault($button) {
39
  // Set some sane defaults
40
  if (!isset($button->options)) $button->options = new CnbButtonOptions();
60
  $button->type = 'SINGLE';
61
  $button->domain = $domain;
62
  $button->actions = array();
63
+ $button->conditions = array();
64
+
65
+ return $button;
66
+ }
67
+
68
+ public function toArray() {
69
+ return array(
70
+ 'id' => $this->id,
71
+ 'active' => $this->active,
72
+ 'name' => $this->name,
73
+ 'type' => $this->type,
74
+ 'domain' => $this->domain,
75
+ 'actions' => $this->actions,
76
+ 'conditions' => $this->conditions,
77
+ 'options' => isset($this->options) ? $this->options->toArray() : array()
78
+ );
79
+ }
80
+
81
+ public static function fromObject($object) {
82
+ $button = new CnbButton();
83
+ $button->id = $object->id;
84
+ $button->active = $object->active;
85
+ $button->name = $object->name;
86
+ $button->type = $object->type;
87
+ $button->domain = $object->domain;
88
+ $button->actions = $object->actions;
89
+ $button->conditions = $object->conditions;
90
+ $button->options = CnbButtonOptions::fromObject($object->options);
91
 
92
  return $button;
93
  }
94
+
95
+ /**
96
+ * @param $buttons CnbButton[]
97
+ *
98
+ * @return array
99
+ */
100
+ public static function convert($buttons) {
101
+ return array_map(
102
+ function($button) {
103
+ $button = $button instanceof CnbButton ? $button : self::fromObject($button);
104
+ return ($button instanceof CnbButton) ? $button->toArray() : array();
105
+ }, $buttons
106
+ );
107
+ }
108
  }
109
 
110
  class CnbButtonOptions {
111
  public $iconBackgroundColor;
112
  public $iconColor;
113
+ /**
114
+ * @var string
115
+ */
116
  public $placement;
117
  public $scale;
118
+
119
+ public function toArray() {
120
+ return array(
121
+ 'iconBackgroundColor' => $this->iconBackgroundColor,
122
+ 'iconColor' => $this->iconColor,
123
+ 'placement' => $this->placement,
124
+ 'scale' => $this->scale
125
+ );
126
+ }
127
+
128
+ public static function fromObject( $object ) {
129
+ $options = new CnbButtonOptions();
130
+ $options->iconBackgroundColor = $object->iconBackgroundColor;
131
+ $options->iconColor = $object->iconColor;
132
+ $options->placement = $object->placement;
133
+ $options->scale = $object->scale;
134
+
135
+ return $options;
136
+ }
137
+ }
src/admin/models/CnbUser.class.php ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace cnb\admin\models;
4
+
5
+ class Cnb_User {
6
+ /**
7
+ * @var string UUID of the User
8
+ */
9
+ public $id;
10
+
11
+ /**
12
+ * @var string Name of the User
13
+ */
14
+ public $name;
15
+
16
+ /**
17
+ * Usually the same as admin_email
18
+ *
19
+ * @var string email address of the User
20
+ */
21
+ public $email;
22
+
23
+ /**
24
+ * @var string
25
+ */
26
+ public $companyName;
27
+
28
+ /**
29
+ * @var Cnb_User_Address
30
+ */
31
+ public $address;
32
+
33
+ /**
34
+ * @var array{Cnb_user_TaxId}
35
+ */
36
+ public $taxIds;
37
+ }
38
+
39
+ class Cnb_user_TaxId {
40
+ public $value;
41
+ /**
42
+ * @var Cnb_user_TaxId_Verification
43
+ */
44
+ public $verification;
45
+ }
46
+
47
+ class Cnb_user_TaxId_Verification {
48
+ /**
49
+ * @var string either "verified" or "pending"
50
+ */
51
+ public $status;
52
+ }
53
+
54
+ class Cnb_User_Address {
55
+ public $line1;
56
+ public $line2;
57
+ public $postalCode;
58
+ public $city;
59
+ public $state;
60
+ public $country;
61
+
62
+ }
src/admin/partials/admin-footer.php CHANGED
@@ -17,8 +17,10 @@ function cnb_show_feedback_collection() {
17
  $upgrade_url = esc_url( $upgrade_link );?>
18
  <div class="feedback-collection">
19
  <div class="cnb-clear"></div>
20
- <p class="cnb-url cnb-center"><a href="<?php echo CNB_WEBSITE; ?><?php cnb_utm_params("footer-links", "branding"); ?>" target="_blank">Call Now Button</a></p>
21
- <p class="cnb-center">Version <?php echo CNB_VERSION; ?></p>
 
 
22
  <p class="cnb-center cnb-spacing">
23
  <a href="<?php echo CNB_SUPPORT;
24
  cnb_utm_params("footer-links", "support"); ?>" target="_blank" title="Support">Support</a> &middot;
17
  $upgrade_url = esc_url( $upgrade_link );?>
18
  <div class="feedback-collection">
19
  <div class="cnb-clear"></div>
20
+ <p class="cnb-url cnb-center"><a href="<?php echo CNB_WEBSITE; ?><?php cnb_utm_params("footer-links", "branding"); ?>" target="_blank">Call Now Button<?php if ($cnb_cloud_hosting) {
21
+ echo '<span class="cnb_footer_beta">PREMIUM</span>';
22
+ } ?></a></p>
23
+ <p class="cnb-center">Version <?php echo CNB_VERSION; ?>
24
  <p class="cnb-center cnb-spacing">
25
  <a href="<?php echo CNB_SUPPORT;
26
  cnb_utm_params("footer-links", "support"); ?>" target="_blank" title="Support">Support</a> &middot;
src/admin/partials/admin-functions.php CHANGED
@@ -52,6 +52,10 @@ function has_changelog($cnb_options) {
52
 
53
  function cnb_get_changelog() {
54
  return array(
 
 
 
 
55
  '1.0.4' => array(
56
  'Live button preview for Premium',
57
  'Easy premium activation via email',
@@ -98,6 +102,11 @@ function cnb_get_button_types() {
98
  /**
99
  * Return an array of all ActionTypes
100
  *
 
 
 
 
 
101
  * @return string[] array of ActionType to their nice names
102
  */
103
  function cnb_get_action_types() {
@@ -108,6 +117,7 @@ function cnb_get_action_types() {
108
  'LINK' => 'Link',
109
  'MAP' => 'Google Maps',
110
  'WHATSAPP' => 'Whatsapp',
 
111
  );
112
  }
113
 
52
 
53
  function cnb_get_changelog() {
54
  return array(
55
+ '1.0.6' => array(
56
+ '💬 SMS/Text support in Premium',
57
+ '⏱️ More intuitive button scheduler in Premium',
58
+ ),
59
  '1.0.4' => array(
60
  'Live button preview for Premium',
61
  'Easy premium activation via email',
102
  /**
103
  * Return an array of all ActionTypes
104
  *
105
+ * Note(s):
106
+ * - This is NOT in alphabetical order, but rather in order of
107
+ * what feels more likely to be choosen
108
+ * - HOURS is missing, since that is not implemented yet
109
+ *
110
  * @return string[] array of ActionType to their nice names
111
  */
112
  function cnb_get_action_types() {
117
  'LINK' => 'Link',
118
  'MAP' => 'Google Maps',
119
  'WHATSAPP' => 'Whatsapp',
120
+ 'SMS' => 'SMS',
121
  );
122
  }
123
 
src/admin/partials/domain-upgrade/overview.php CHANGED
@@ -8,14 +8,14 @@ function cnb_get_plan($plans, $name) {
8
  return null;
9
  }
10
 
11
- function getProfileEditModal($additional_classes=null, $link_text='Verify your information', $modal_header=null, $data_title='') {
12
  if (!$modal_header) {$modal_header=$link_text;}
13
  $url = admin_url('admin.php');
14
  $full_url = add_query_arg(
15
  array(
16
  'TB_inline' => 'true',
17
  'inlineId' => 'cnb_admin_page_domain_upgrade_profile',
18
- 'height' => '800'),
19
  $url );
20
  printf(
21
  '<a href="%1$s" title="%2$s" class="thickbox open-profile-details-modal %4$s" onclick="cnb_btn=\'%5$s\'">%3$s</a>',
@@ -77,7 +77,7 @@ function cnb_domain_upgrade_overview($domain, $user) {
77
  <div class="currency-box currency-box-eur cnb-flexbox<?php if($active_currency !== 'usd') {?> currency-box-active<?php }?>">
78
  <?php if($domain->type === 'FREE') { ?>
79
  <div class="pricebox cnb-premium-free">
80
- <h3 class="free">Premium Free</h3>
81
  <div class="benefit">Shows "Powered by" branding<br>Up to 20k monthly pageviews</div>
82
  <div class="plan-amount"><span class="currency"></span><span class="euros">&nbsp;</span><span class="cents"></span><span class="timeframe"></span></div>
83
  <div class="billingprice">&nbsp;</div>
@@ -86,32 +86,32 @@ function cnb_domain_upgrade_overview($domain, $user) {
86
  <?php } ?>
87
  <?php $plan = cnb_get_plan($plans, 'powered-by-eur-yearly'); ?>
88
  <div class="pricebox">
89
- <h3 class="yearly"><span class="cnb-premium-label">Premium </span>Yearly</h3>
90
  <div class="benefit">No "Powered by" branding<br>Up to 50k monthly pageviews</div>
91
  <div class="plan-amount"><span class="currency">€</span><span class="euros">2</span><span class="cents">.49</span><span class="timeframe">/month</span></div>
92
  <div class="billingprice">
93
  Billed at €29.88 annually
94
  </div>
95
- <?php getProfileEditModal('button button-primary', 'Upgrade', 'Verify your information', 'powered-by-eur-yearly'); ?>
96
  <a class="button button-primary button-upgrade powered-by-eur-yearly" href="#" onclick="cnb_get_checkout('<?php _e($plan->id) ?>')">Upgrade</a>
97
  </div>
98
 
99
  <?php $plan = cnb_get_plan($plans, 'powered-by-eur-monthly'); ?>
100
  <div class="pricebox">
101
- <h3 class="monthly"><span class="cnb-premium-label">Premium </span>Monthly</h3>
102
  <div class="benefit">No "Powered by" branding<br>Up to 50k monthly pageviews</div>
103
  <div class="plan-amount"><span class="currency">€</span><span class="euros">4</span><span class="cents">.98</span><span class="timeframe">/month</span></div>
104
  <div class="billingprice">
105
  Billed monthly
106
  </div>
107
- <?php getProfileEditModal('button button-secondary', 'Upgrade', 'Verify your information', 'powered-by-eur-monthly'); ?>
108
  <a class="button button-secondary button-upgrade powered-by-eur-monthly" href="#" onclick="cnb_get_checkout('<?php _e($plan->id) ?>')">Upgrade</a>
109
  </div>
110
  </div>
111
  <div class="currency-box currency-box-usd cnb-flexbox<?php if($active_currency === 'usd') {?> currency-box-active<?php }?>">
112
  <?php if($domain->type === 'FREE') { ?>
113
  <div class="pricebox cnb-premium-free">
114
- <h3 class="free">Premium Free</h3>
115
  <div class="benefit">Shows "Powered by" branding<br>Up to 20k monthly pageviews</div>
116
  <div class="plan-amount"><span class="currency"></span><span class="euros">&nbsp;</span><span class="cents"></span><span class="timeframe"></span></div>
117
  <div class="billingprice">&nbsp;</div>
@@ -120,24 +120,24 @@ function cnb_domain_upgrade_overview($domain, $user) {
120
  <?php } ?>
121
  <?php $plan = cnb_get_plan($plans, 'powered-by-usd-yearly'); ?>
122
  <div class="pricebox">
123
- <h3 class="yearly"><span class="cnb-premium-label">Premium </span>Yearly</h3>
124
  <div class="benefit">No "Powered by" branding<br>Up to 50k monthly pageviews</div>
125
  <div class="plan-amount"><span class="currency">$</span><span class="euros">2</span><span class="cents">.99</span><span class="timeframe">/month</span></div>
126
  <div class="billingprice">
127
  Billed at $34.88 annually
128
  </div>
129
- <?php getProfileEditModal('button button-primary', 'Upgrade', 'Verify your information', 'powered-by-usd-yearly'); ?>
130
  <a class="button button-primary button-upgrade powered-by-usd-yearly" href="#" onclick="cnb_get_checkout('<?php _e($plan->id) ?>')">Upgrade</a>
131
  </div>
132
  <?php $plan = cnb_get_plan($plans, 'powered-by-usd-monthly'); ?>
133
  <div class="pricebox">
134
- <h3 class="monthly"><span class="cnb-premium-label">Premium </span>Monthly</h3>
135
  <div class="benefit">No "Powered by" branding<br>Up to 50k monthly pageviews</div>
136
  <div class="plan-amount"><span class="currency">$</span><span class="euros">5</span><span class="cents">.98</span><span class="timeframe">/month</span></div>
137
  <div class="billingprice">
138
  Billed monthly
139
  </div>
140
- <?php getProfileEditModal('button button-secondary', 'Upgrade', 'Verify your information', 'powered-by-usd-monthly'); ?>
141
  <a class="button button-secondary button-upgrade powered-by-usd-monthly" href="#" onclick="cnb_get_checkout('<?php _e($plan->id) ?>')">Upgrade</a>
142
  </div>
143
  </div>
8
  return null;
9
  }
10
 
11
+ function getProfileEditModal($additional_classes=null, $link_text='Enter or verify your information', $modal_header=null, $data_title='') {
12
  if (!$modal_header) {$modal_header=$link_text;}
13
  $url = admin_url('admin.php');
14
  $full_url = add_query_arg(
15
  array(
16
  'TB_inline' => 'true',
17
  'inlineId' => 'cnb_admin_page_domain_upgrade_profile',
18
+ 'height' => '525'),
19
  $url );
20
  printf(
21
  '<a href="%1$s" title="%2$s" class="thickbox open-profile-details-modal %4$s" onclick="cnb_btn=\'%5$s\'">%3$s</a>',
77
  <div class="currency-box currency-box-eur cnb-flexbox<?php if($active_currency !== 'usd') {?> currency-box-active<?php }?>">
78
  <?php if($domain->type === 'FREE') { ?>
79
  <div class="pricebox cnb-premium-free">
80
+ <h3 class="free">Premium (free)</h3>
81
  <div class="benefit">Shows "Powered by" branding<br>Up to 20k monthly pageviews</div>
82
  <div class="plan-amount"><span class="currency"></span><span class="euros">&nbsp;</span><span class="cents"></span><span class="timeframe"></span></div>
83
  <div class="billingprice">&nbsp;</div>
86
  <?php } ?>
87
  <?php $plan = cnb_get_plan($plans, 'powered-by-eur-yearly'); ?>
88
  <div class="pricebox">
89
+ <h3 class="yearly"><span class="cnb-premium-label">PRO </span>Yearly</h3>
90
  <div class="benefit">No "Powered by" branding<br>Up to 50k monthly pageviews</div>
91
  <div class="plan-amount"><span class="currency">€</span><span class="euros">2</span><span class="cents">.49</span><span class="timeframe">/month</span></div>
92
  <div class="billingprice">
93
  Billed at €29.88 annually
94
  </div>
95
+ <?php getProfileEditModal('button button-primary', 'Upgrade', 'Enter or verify your information', 'powered-by-eur-yearly'); ?>
96
  <a class="button button-primary button-upgrade powered-by-eur-yearly" href="#" onclick="cnb_get_checkout('<?php _e($plan->id) ?>')">Upgrade</a>
97
  </div>
98
 
99
  <?php $plan = cnb_get_plan($plans, 'powered-by-eur-monthly'); ?>
100
  <div class="pricebox">
101
+ <h3 class="monthly"><span class="cnb-premium-label">PRO </span>Monthly</h3>
102
  <div class="benefit">No "Powered by" branding<br>Up to 50k monthly pageviews</div>
103
  <div class="plan-amount"><span class="currency">€</span><span class="euros">4</span><span class="cents">.98</span><span class="timeframe">/month</span></div>
104
  <div class="billingprice">
105
  Billed monthly
106
  </div>
107
+ <?php getProfileEditModal('button button-secondary', 'Upgrade', 'Enter or verify your information', 'powered-by-eur-monthly'); ?>
108
  <a class="button button-secondary button-upgrade powered-by-eur-monthly" href="#" onclick="cnb_get_checkout('<?php _e($plan->id) ?>')">Upgrade</a>
109
  </div>
110
  </div>
111
  <div class="currency-box currency-box-usd cnb-flexbox<?php if($active_currency === 'usd') {?> currency-box-active<?php }?>">
112
  <?php if($domain->type === 'FREE') { ?>
113
  <div class="pricebox cnb-premium-free">
114
+ <h3 class="free">Premium (free)</h3>
115
  <div class="benefit">Shows "Powered by" branding<br>Up to 20k monthly pageviews</div>
116
  <div class="plan-amount"><span class="currency"></span><span class="euros">&nbsp;</span><span class="cents"></span><span class="timeframe"></span></div>
117
  <div class="billingprice">&nbsp;</div>
120
  <?php } ?>
121
  <?php $plan = cnb_get_plan($plans, 'powered-by-usd-yearly'); ?>
122
  <div class="pricebox">
123
+ <h3 class="yearly"><span class="cnb-premium-label">PRO </span>Yearly</h3>
124
  <div class="benefit">No "Powered by" branding<br>Up to 50k monthly pageviews</div>
125
  <div class="plan-amount"><span class="currency">$</span><span class="euros">2</span><span class="cents">.99</span><span class="timeframe">/month</span></div>
126
  <div class="billingprice">
127
  Billed at $34.88 annually
128
  </div>
129
+ <?php getProfileEditModal('button button-primary', 'Upgrade', 'Enter or verify your information', 'powered-by-usd-yearly'); ?>
130
  <a class="button button-primary button-upgrade powered-by-usd-yearly" href="#" onclick="cnb_get_checkout('<?php _e($plan->id) ?>')">Upgrade</a>
131
  </div>
132
  <?php $plan = cnb_get_plan($plans, 'powered-by-usd-monthly'); ?>
133
  <div class="pricebox">
134
+ <h3 class="monthly"><span class="cnb-premium-label">PRO </span>Monthly</h3>
135
  <div class="benefit">No "Powered by" branding<br>Up to 50k monthly pageviews</div>
136
  <div class="plan-amount"><span class="currency">$</span><span class="euros">5</span><span class="cents">.98</span><span class="timeframe">/month</span></div>
137
  <div class="billingprice">
138
  Billed monthly
139
  </div>
140
+ <?php getProfileEditModal('button button-secondary', 'Upgrade', 'Enter or verify your information', 'powered-by-usd-monthly'); ?>
141
  <a class="button button-secondary button-upgrade powered-by-usd-monthly" href="#" onclick="cnb_get_checkout('<?php _e($plan->id) ?>')">Upgrade</a>
142
  </div>
143
  </div>
src/admin/settings-profile.php CHANGED
@@ -1,4 +1,7 @@
1
  <?php
 
 
 
2
  function cnb_add_header_profile_edit() {
3
  echo 'Profile';
4
  }
@@ -306,7 +309,7 @@ function cnb_admin_profile_edit_process() {
306
  /**
307
  * @param false $modal
308
  *
309
- * @return CNB_User|WP_Error
310
  */
311
  function cnb_admin_page_profile_edit_render_form($modal = false) {
312
  $cnb_user = CnbAppRemote::cnb_remote_get_user_info();
@@ -362,6 +365,37 @@ function cnb_admin_page_profile_edit_render_form($modal = false) {
362
  class="regular-text ltr cnb_vat_companies_required">
363
  </td>
364
  </tr>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
365
 
366
  <tr class="cnb_vat_companies_show" style="display:none">
367
  <th scope="row"><label for="user[address][line1]">Address<span class="cnb_required">*</span></label></th>
@@ -378,8 +412,8 @@ function cnb_admin_page_profile_edit_render_form($modal = false) {
378
  </td>
379
  </tr>
380
 
381
- <tr class="cnb_vat_companies_show cnb_us_show" style="display:none">
382
- <th scope="row"><label for="user[address][postalCode]"><span class="cnb_ie_only" style="display:none">Eircode/</span>Zip/Postal code<span class="cnb_required cnb_us_show cnb_vat_companies_show" style="display:none">*</span></label></th>
383
  <td>
384
  <input type="text" id="user[address][postalCode]" name="user[address][postalCode]"
385
  value="<?php esc_html_e( isset($cnb_user->address) ? $cnb_user->address->postalCode : '' ) ?>"
@@ -403,40 +437,10 @@ function cnb_admin_page_profile_edit_render_form($modal = false) {
403
  </td>
404
  </tr>
405
 
406
- <tr>
407
- <th scope="row"><label for="cnb_profile_country">Country<span class="cnb_required">*</span></label></th>
408
- <td>
409
- <label>
410
- <select id="cnb_profile_country" class="select-menu" name="user[address][country]"
411
- required="required">
412
- <option value=""></option>
413
- <?php
414
- foreach ( cnb_stripe_get_countries() as $country ) {
415
- $selected = '';
416
- if (isset($cnb_user->address)) {
417
- $selected = $country['code'] === $cnb_user->address->country ? ' selected="selected"' : '';
418
- }
419
- echo '<option value="' . $country['code'] . '"' . $selected . '>' . $country['country'] . '</option>' . "\n";
420
- }
421
- ?>
422
- </select>
423
- </label>
424
- </td>
425
- </tr>
426
 
427
- <tr class="cnb_show_vat_toggle" style="display:none">
428
- <th scope="row"><label for="cnb-euvatbusiness">VAT registered business?</label></th>
429
- <td>
430
- <input type="hidden" name="user[euvatbusiness]" value="0">
431
- <input id="cnb-euvatbusiness" type="checkbox" name="user[euvatbusiness]" value="1"
432
- <?php if(!empty($cnb_user->taxIds[0]->value)) { ?>checked="checked"<?php } ?>
433
- class="regular-text ltr cnb_eu_values_only">
434
- <label for="cnb-euvatbusiness">Yes</label>
435
- </td>
436
- </tr>
437
 
438
  <tr class="cnb_vat_companies_show" style="display:none">
439
- <th scope="row"><label for="cnb_profile_vat">VAT number<span class="cnb_required">*</span></label>
440
  </th>
441
  <td>
442
  <input id="cnb_profile_vat" type="text" name="user[taxIds][0][value]"
@@ -444,8 +448,7 @@ function cnb_admin_page_profile_edit_render_form($modal = false) {
444
  class="regular-text ltr cnb_vat_companies_required cnb_eu_values_only">
445
  <input id="cnb_user_taxids_type" type="hidden" name="user[taxIds][0][type]" value="eu_vat"
446
  class="regular-text ltr cnb_vat_companies_required cnb_eu_values_only">
447
- <p class="description">Required for EU based companies who are registered for
448
- VAT.</p>
449
  <?php
450
  if ($cnb_user_stripe_verified) {
451
  echo '<p class="description"><span class="dashicons dashicons-saved"></span><em>Your VAT number is verified.</em></p>';
1
  <?php
2
+
3
+ use cnb\admin\models\Cnb_User;
4
+
5
  function cnb_add_header_profile_edit() {
6
  echo 'Profile';
7
  }
309
  /**
310
  * @param false $modal
311
  *
312
+ * @return Cnb_User|WP_Error
313
  */
314
  function cnb_admin_page_profile_edit_render_form($modal = false) {
315
  $cnb_user = CnbAppRemote::cnb_remote_get_user_info();
365
  class="regular-text ltr cnb_vat_companies_required">
366
  </td>
367
  </tr>
368
+ <tr>
369
+ <th scope="row"><label for="cnb_profile_country">Country<span class="cnb_required">*</span></label></th>
370
+ <td>
371
+ <label>
372
+ <select id="cnb_profile_country" class="select-menu" name="user[address][country]"
373
+ required="required">
374
+ <option value=""></option>
375
+ <?php
376
+ foreach ( cnb_stripe_get_countries() as $country ) {
377
+ $selected = '';
378
+ if (isset($cnb_user->address)) {
379
+ $selected = $country['code'] === $cnb_user->address->country ? ' selected="selected"' : '';
380
+ }
381
+ echo '<option value="' . $country['code'] . '"' . $selected . '>' . $country['country'] . '</option>' . "\n";
382
+ }
383
+ ?>
384
+ </select>
385
+ </label>
386
+ </td>
387
+ </tr>
388
+
389
+ <tr class="cnb_show_vat_toggle" style="display:none">
390
+ <th scope="row"><label for="cnb-euvatbusiness">VAT registered business?</label></th>
391
+ <td>
392
+ <input type="hidden" name="user[euvatbusiness]" value="0">
393
+ <input id="cnb-euvatbusiness" type="checkbox" name="user[euvatbusiness]" value="1"
394
+ <?php if(!empty($cnb_user->taxIds[0]->value)) { ?>checked="checked"<?php } ?>
395
+ class="ltr cnb_eu_values_only">
396
+ <label for="cnb-euvatbusiness">Yes</label>
397
+ </td>
398
+ </tr>
399
 
400
  <tr class="cnb_vat_companies_show" style="display:none">
401
  <th scope="row"><label for="user[address][line1]">Address<span class="cnb_required">*</span></label></th>
412
  </td>
413
  </tr>
414
 
415
+ <tr>
416
+ <th scope="row"><label for="user[address][postalCode]"><span class="cnb_ie_only" style="display:none">Eircode/</span>Zip/Postal code<span class="cnb_required">*</span></label></th>
417
  <td>
418
  <input type="text" id="user[address][postalCode]" name="user[address][postalCode]"
419
  value="<?php esc_html_e( isset($cnb_user->address) ? $cnb_user->address->postalCode : '' ) ?>"
437
  </td>
438
  </tr>
439
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
440
 
 
 
 
 
 
 
 
 
 
 
441
 
442
  <tr class="cnb_vat_companies_show" style="display:none">
443
+ <th scope="row"><label for="cnb_profile_vat">VAT number<span class="cnb_required">*</span></label>
444
  </th>
445
  <td>
446
  <input id="cnb_profile_vat" type="text" name="user[taxIds][0][value]"
448
  class="regular-text ltr cnb_vat_companies_required cnb_eu_values_only">
449
  <input id="cnb_user_taxids_type" type="hidden" name="user[taxIds][0][type]" value="eu_vat"
450
  class="regular-text ltr cnb_vat_companies_required cnb_eu_values_only">
451
+
 
452
  <?php
453
  if ($cnb_user_stripe_verified) {
454
  echo '<p class="description"><span class="dashicons dashicons-saved"></span><em>Your VAT number is verified.</em></p>';
src/admin/settings.php CHANGED
@@ -93,6 +93,16 @@ function cnb_settings_options_validate($input) {
93
  if (isset($input['api_key']) && empty($input['api_key'])) {
94
  unset($input['api_key']);
95
  }
 
 
 
 
 
 
 
 
 
 
96
 
97
  $updated_options = array_merge($original_settings, $input);
98
 
@@ -105,6 +115,7 @@ function cnb_settings_options_validate($input) {
105
  $updated_options['cloud_use_id'] = $cloud_id;
106
  }
107
  }
 
108
  if (!empty($original_settings['api_key']) && !empty($input['api_key']) && $original_settings['api_key'] !== $input['api_key']) {
109
  unset($updated_options['cloud_use_id']);
110
  $cloud_id = CnbAdminCloud::cnb_set_default_option_for_cloud( $updated_options );
@@ -191,6 +202,28 @@ function cnb_admin_setting_migrate() {
191
  exit;
192
  }
193
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
  function cnb_admin_settings_page() {
195
  global $cnb_options;
196
 
@@ -203,19 +236,7 @@ function cnb_admin_settings_page() {
203
  $api_key = null;
204
  $api_key_ott = filter_input(INPUT_GET, 'api_key_ott', FILTER_SANITIZE_STRING);
205
  if (!empty($api_key_ott)) {
206
- $api_key_obj = CnbAppRemote::cnb_remote_get_apikey_via_ott($api_key_ott);
207
- if ($api_key_obj !== null) {
208
- if (!is_wp_error($api_key_obj)) {
209
- $api_key = $api_key_obj->key;
210
- } else {
211
- if (empty($cnb_options['api_key'])) {
212
- $error_details = CnbAdminCloud::cnb_admin_get_error_message_details( $api_key_obj );
213
- $message = '<p>We could not enable <strong>Premium</strong> with the <em>one-time token</em> <code>' . esc_html( $api_key_ott ) . '</code> :-(.' . $error_details . '</p>';
214
- $notice = new CnbNotice( 'error', $message );
215
- CnbAdminNotices::get_instance()->renderNotice( $notice );
216
- }
217
- }
218
- }
219
  }
220
 
221
  // If the API key was not passed via api_key_ott, see if it was passed directly via api_key
@@ -281,21 +302,7 @@ function cnb_admin_settings_page() {
281
  });
282
  </script>
283
  <?php
284
- // Also warning if timezone is not yet set
285
- $domain_timezone = $cnb_cloud_domain->timezone;
286
- if (empty($domain_timezone)) {
287
- $url = admin_url( 'admin.php' );
288
- $redirect_link =
289
- add_query_arg(
290
- array(
291
- 'page' => 'call-now-button-settings',
292
- 'tab' => 'advanced_options#domain_timezone',
293
-
294
- ),
295
- $url );
296
- $redirect_url = esc_url( $redirect_link );
297
- CnbAdminNotices::get_instance()->renderWarning("<p>Please set your timezone in the <a href=\"". $redirect_url . "\">Advanced settings</a> tab to avoid unpredictable behavior when using the scheduler.</p>");
298
- }
299
  } ?>
300
  <div class="cnb-two-column-section">
301
  <div class="cnb-body-column">
@@ -384,12 +391,27 @@ function cnb_admin_settings_page() {
384
  <tr>
385
  <th scope="row">API key</th>
386
  <td>
387
- <?php if ($cnb_user instanceof WP_Error || $show_advanced_view_only) { ?>
388
- <label><input type="text" class="regular-text" name="cnb[api_key]" id="cnb_api_key"
389
- placeholder="e.g. b52c3f83-38dc-4493-bc90-642da5be7e39"/></label>
390
- <?php if ($cnb_user instanceof WP_Error) { ?><p class="description">Get your API key at <a href="<?php echo CNB_WEBSITE?>"><?php echo CNB_WEBSITE?></a></p><?php } ?>
 
391
  <?php } ?>
392
- <?php if (isset($cnb_options['api_key']) && !is_wp_error($cnb_user)) { ?><p class="description"><span class="dashicons dashicons-saved"></span>Your key is set correctly.</p><?php }?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
393
  </td>
394
  </tr>
395
  <?php if ($cnb_user !== null && !$cnb_user instanceof WP_Error) { ?>
@@ -541,10 +563,10 @@ function cnb_admin_settings_page() {
541
  'Go Premium for free',
542
  'Premium comes in 2 versions: <em>Free</em> and <em>Paid</em>.</p>
543
  <p>Both plans give you access to <strong>all features</strong>.</p>
544
- <p>The only difference is that the Free version shows <em>Powered by Call Now Button</em> next to your buttons.</p>',
545
  'info-outline',
546
  '',
547
- 'Learn more',
548
  cnb_legacy_upgrade_page()
549
  );
550
  } ?>
93
  if (isset($input['api_key']) && empty($input['api_key'])) {
94
  unset($input['api_key']);
95
  }
96
+ // If api_key is "delete_me", this is the special value to trigger "forget this key"
97
+ if ((isset($input['api_key']) && $input['api_key'] === 'delete_me') ||
98
+ (isset($original_settings['api_key']) && $original_settings['api_key'] === 'delete_me') ) {
99
+ $input['api_key'] = '';
100
+ $updated_options['api_key'] = '';
101
+ $input['cloud_use_id'] = '';
102
+ $updated_options['cloud_use_id'] = '';
103
+
104
+ $messages[] = new CnbNotice( 'success', '<p>Your API key has been removed - you can now activate Call Now Button with another API key.</p>' );
105
+ }
106
 
107
  $updated_options = array_merge($original_settings, $input);
108
 
115
  $updated_options['cloud_use_id'] = $cloud_id;
116
  }
117
  }
118
+
119
  if (!empty($original_settings['api_key']) && !empty($input['api_key']) && $original_settings['api_key'] !== $input['api_key']) {
120
  unset($updated_options['cloud_use_id']);
121
  $cloud_id = CnbAdminCloud::cnb_set_default_option_for_cloud( $updated_options );
202
  exit;
203
  }
204
 
205
+ /**
206
+ * @param $api_key_ott
207
+ *
208
+ * @return {string} The API key if found
209
+ */
210
+ function cnb_try_api_key_ott($api_key_ott, $render_notice=true) {
211
+ $api_key_obj = CnbAppRemote::cnb_remote_get_apikey_via_ott($api_key_ott);
212
+ if ($api_key_obj !== null) {
213
+ if (!is_wp_error($api_key_obj)) {
214
+ return $api_key_obj->key;
215
+ } else {
216
+ if (empty($cnb_options['api_key']) && $render_notice) {
217
+ $error_details = CnbAdminCloud::cnb_admin_get_error_message_details( $api_key_obj );
218
+ $message = '<p>We could not enable <strong>Premium</strong> with the <em>one-time token</em> <code>' . esc_html( $api_key_ott ) . '</code> :-(.' . $error_details . '</p>';
219
+ $notice = new CnbNotice( 'error', $message );
220
+ CnbAdminNotices::get_instance()->renderNotice( $notice );
221
+ }
222
+ }
223
+ }
224
+ return null;
225
+ }
226
+
227
  function cnb_admin_settings_page() {
228
  global $cnb_options;
229
 
236
  $api_key = null;
237
  $api_key_ott = filter_input(INPUT_GET, 'api_key_ott', FILTER_SANITIZE_STRING);
238
  if (!empty($api_key_ott)) {
239
+ $api_key = cnb_try_api_key_ott($api_key_ott);
 
 
 
 
 
 
 
 
 
 
 
 
240
  }
241
 
242
  // If the API key was not passed via api_key_ott, see if it was passed directly via api_key
302
  });
303
  </script>
304
  <?php
305
+ cnb_warn_about_timezone($cnb_cloud_domain);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
306
  } ?>
307
  <div class="cnb-two-column-section">
308
  <div class="cnb-body-column">
391
  <tr>
392
  <th scope="row">API key</th>
393
  <td>
394
+ <?php if (is_wp_error($cnb_user) || $show_advanced_view_only) { ?>
395
+ <label>
396
+ <input type="text" class="regular-text" name="cnb[api_key]" id="cnb_api_key" placeholder="e.g. b52c3f83-38dc-4493-bc90-642da5be7e39"/>
397
+ </label>
398
+ <p class="description">Get your API key at <a href="<?php echo CNB_WEBSITE?>"><?php echo CNB_WEBSITE?></a></p>
399
  <?php } ?>
400
+ <?php if (is_wp_error($cnb_user) && !empty($cnb_options['api_key'])) { ?>
401
+ <p><span class="dashicons dashicons-warning"></span> There is an API key, but it seems to be invalid or outdated.</p>
402
+ <p class="description">Clicking "Disconnect account" will drop the API key and disconnect the plugin from your account. You will lose access to your buttons and Premium functionality until you reconnect with a callnowbutton.com account.
403
+ <br>
404
+ <input type="button" name="cnb_api_key_delete" id="cnb_api_key_delete" class="button button-link" value="<?php _e('Disconnect account') ?>" onclick="return cnb_delete_apikey();">
405
+ </p>
406
+ <?php } ?>
407
+ <?php if (!is_wp_error($cnb_user) && isset($cnb_options['api_key'])) { ?>
408
+ <p><strong><span class="dashicons dashicons-saved"></span>Success!</strong> <Br>The plugin is connected to your callnowbutton.com account.</p>
409
+ <p class="description">Clicking "Disconnect account" will drop the API key and disconnect the plugin from your account. You will lose access to your buttons and Premium functionality until you reconnect with a callnowbutton.com account.
410
+ <br>
411
+ <input type="button" name="cnb_api_key_delete" id="cnb_api_key_delete" class="button button-link" value="<?php _e('Disconnect account') ?>" onclick="return cnb_delete_apikey();">
412
+ </p>
413
+ <input type="hidden" name="cnb[api_key]" id="cnb_api_key" value="delete_me" disabled="disabled" />
414
+ <?php }?>
415
  </td>
416
  </tr>
417
  <?php if ($cnb_user !== null && !$cnb_user instanceof WP_Error) { ?>
563
  'Go Premium for free',
564
  'Premium comes in 2 versions: <em>Free</em> and <em>Paid</em>.</p>
565
  <p>Both plans give you access to <strong>all features</strong>.</p>
566
+ <p>The only differences are that the Free version shows <em>Powered by Call Now Button</em> next to your buttons and there\'s a monthly pageviews limit of 20k.</p>',
567
  'info-outline',
568
  '',
569
+ 'Try Premium',
570
  cnb_legacy_upgrade_page()
571
  );
572
  } ?>
src/autoload.php CHANGED
@@ -16,6 +16,7 @@ spl_autoload_register(
16
  'cnbactionproperties' => '/admin/models/CnbAction.class.php',
17
  'cnbadmincloud' => '/admin/api/CnbAdminCloud.php',
18
  'cnbadminnotices' => '/utils/CnbAdminNotices.class.php',
 
19
  'cnbappremote' => '/admin/api/CnbAppRemote.php',
20
  'cnbappremotehelper' => '/admin/api/CnbAppRemote.php',
21
  'cnbappremotepayment' => '/admin/api/CnbAppRemotePayment.php',
16
  'cnbactionproperties' => '/admin/models/CnbAction.class.php',
17
  'cnbadmincloud' => '/admin/api/CnbAdminCloud.php',
18
  'cnbadminnotices' => '/utils/CnbAdminNotices.class.php',
19
+ 'cnbapikey' => '/admin/models/CnbApiKey.class.php',
20
  'cnbappremote' => '/admin/api/CnbAppRemote.php',
21
  'cnbappremotehelper' => '/admin/api/CnbAppRemote.php',
22
  'cnbappremotepayment' => '/admin/api/CnbAppRemotePayment.php',
src/call-now-button.php CHANGED
@@ -6,6 +6,11 @@ require_once dirname( __FILE__ ) . '/admin/admin-ajax.php';
6
  require_once dirname( __FILE__ ) . '/utils/CnbAdminNotices.class.php';
7
  require_once dirname( __FILE__ ) . '/admin/partials/admin-header.php';
8
 
 
 
 
 
 
9
  /**
10
  * There are a few global used throughout the plugin.
11
  *
@@ -98,8 +103,8 @@ function cnb_register_admin_pages() {
98
  $has_changelog = has_changelog($cnb_options);
99
  if ($has_changelog) $counter++;
100
 
101
- $has_welcome_panel = cnb_show_welcome_panel() && !$cnb_cloud_hosting;
102
- if ($has_welcome_panel) $counter++;
103
 
104
  // Detect errors (specific, - Premium enabled, but API key is not present yet)
105
  if ($cnb_cloud_hosting && !array_key_exists('api_key', $cnb_options)) {
@@ -350,6 +355,12 @@ function cnb_options_init() {
350
  array(CNB_SLUG . '-call-now-button'),
351
  CNB_VERSION,
352
  true);
 
 
 
 
 
 
353
 
354
  // Special case: since the preview functionality depends on this,
355
  // and the source is always changing - we include it as external script
6
  require_once dirname( __FILE__ ) . '/utils/CnbAdminNotices.class.php';
7
  require_once dirname( __FILE__ ) . '/admin/partials/admin-header.php';
8
 
9
+ // Only include the WP_CLI suite when it is available
10
+ if ( class_exists( 'WP_CLI' ) && class_exists( 'WP_CLI_Command' ) ) {
11
+ require_once dirname( __FILE__ ) . '/cli/CNB_CLI.class.php';
12
+ }
13
+
14
  /**
15
  * There are a few global used throughout the plugin.
16
  *
103
  $has_changelog = has_changelog($cnb_options);
104
  if ($has_changelog) $counter++;
105
 
106
+ $has_welcome_banner = cnb_show_welcome_banner() && !$cnb_cloud_hosting;
107
+ if ($has_welcome_banner) $counter++;
108
 
109
  // Detect errors (specific, - Premium enabled, but API key is not present yet)
110
  if ($cnb_cloud_hosting && !array_key_exists('api_key', $cnb_options)) {
355
  array(CNB_SLUG . '-call-now-button'),
356
  CNB_VERSION,
357
  true);
358
+ wp_register_script(
359
+ CNB_SLUG . '-action-edit-scheduler',
360
+ plugins_url( '../resources/js/action-edit-scheduler.js', __FILE__ ),
361
+ array(CNB_SLUG . '-call-now-button'),
362
+ CNB_VERSION,
363
+ true);
364
 
365
  // Special case: since the preview functionality depends on this,
366
  // and the source is always changing - we include it as external script
src/cli/CNB_CLI.class.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace cnb\cli;
4
+
5
+ use WP_CLI;
6
+ use WP_CLI_Command;
7
+
8
+ require_once dirname( __FILE__ ) . '/CNB_CLI_Api.class.php';
9
+ require_once dirname( __FILE__ ) . '/CNB_CLI_User.class.php';
10
+ require_once dirname( __FILE__ ) . '/CNB_CLI_Button.class.php';
11
+
12
+ /**
13
+ * Adds, removes and fetches Call Now Buttons objects
14
+ *
15
+ * @since 1.0.6
16
+ * @author Jasper Roel
17
+ *
18
+ * @noinspection PhpUnused (it is used as a WP CLI class)
19
+ */
20
+ class CNB_CLI extends WP_CLI_Command {
21
+
22
+ /**
23
+ * Registers the Call Now Button commands when CLI gets initialized.
24
+ *
25
+ * @noinspection PhpUnused (it is used via cli_init)
26
+ */
27
+ static function cli_register_command() {
28
+ WP_CLI::add_command( 'cnb', 'cnb\cli\CNB_CLI' );
29
+ }
30
+ }
31
+
32
+ add_action( 'cli_init', '\cnb\cli\CNB_CLI::cli_register_command' );
src/cli/CNB_CLI_Api.class.php ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace cnb\cli;
4
+
5
+ use CnbAdminCloud;
6
+ use WP_CLI;
7
+ use WP_CLI_Command;
8
+
9
+ /**
10
+ * Adds, removes and fetches Call Now Buttons API keys
11
+ *
12
+ * @since 1.0.6
13
+ * @author Jasper Roel
14
+ *
15
+ * @noinspection PhpUnused (it is used as a WP CLI class)
16
+ */
17
+ class CNB_CLI_Api extends WP_CLI_Command {
18
+ private $adminCloud;
19
+
20
+ public function __construct() {
21
+ $this->adminCloud = new CnbAdminCloud();
22
+ }
23
+
24
+ /**
25
+ * Gets the stored API key
26
+ *
27
+ * It will only show the first part by default, so it can be safely passed to Support
28
+ *
29
+ * ## OPTIONS
30
+ *
31
+ * [--reveal]
32
+ * : Shows the full API key
33
+ *
34
+ * ## Examples
35
+ *
36
+ * # Get the API key
37
+ * $ wp cnb api get
38
+ * Success: 0a45e023-1242-4410-a17a-xxxxxxxxxxxx
39
+ *
40
+ * # Get the API key and reveal it
41
+ * $ wp cnb api get --reveal
42
+ * Success: 0a45e023-1242-4410-a17a-123456789012
43
+ *
44
+ * @param $args array [optional]
45
+ * @param $assoc_args array [optional]
46
+ *
47
+ *
48
+ * @return void
49
+ *
50
+ * @noinspection PhpUnusedParameterInspection ($args is required for a CLI command)
51
+ */
52
+ function get($args = array(), $assoc_args = array()) {
53
+
54
+ $options = get_option('cnb');
55
+ if (is_array($options) && key_exists('api_key', $options)) {
56
+ $key = $options['api_key'];
57
+ if (!isset($assoc_args['reveal'])) {
58
+ $key = $this->obfuscate_key( $options['api_key'] );
59
+ }
60
+ WP_CLI::success( $key );
61
+ return;
62
+ }
63
+ WP_CLI::warning('API key not found');
64
+ }
65
+
66
+ /**
67
+ * Set the provided API key
68
+ *
69
+ * ## OPTIONS
70
+ *
71
+ * <api_key>
72
+ * : The API key to set.
73
+ *
74
+ * [--set-if-valid]
75
+ * : Only store the API key if valid
76
+ *
77
+ * ## EXAMPLES
78
+ *
79
+ * # Set an API key
80
+ * $ wp cnb api set 0a45e023-...
81
+ *
82
+ * # Set an API key (but only if the key is tested and valid)
83
+ * $ wp cnb api set 0a45e023-... --set-if-valid
84
+ *
85
+ * @param $args array should contain 1 entry, only the API key
86
+ * @param $assoc_args array [optional]
87
+ *
88
+ * @return void
89
+ *
90
+ */
91
+ public function set($args, $assoc_args) {
92
+ // Check of key is provided
93
+ if (sizeof($args) !== 1) {
94
+ WP_CLI::error('Wrong amount of arguments, 1 (the API key) expected');
95
+ return;
96
+ }
97
+
98
+ $api_key = $args[0];
99
+ WP_CLI::log( sprintf('Key provided: %s',
100
+ WP_CLI::colorize(sprintf('%%G%s%%n', $api_key))
101
+ ));
102
+
103
+ if (isset($assoc_args['set-if-valid'])) {
104
+ WP_CLI::log('Testing key...');
105
+ $is_valid = $this->test_api_key($api_key);
106
+ if ($is_valid) {
107
+ $options = array();
108
+ $options['api_key'] = $api_key;
109
+ update_option('cnb', $options);
110
+ WP_CLI::success('API key updated');
111
+ } else {
112
+ WP_CLI::error('API key incorrect, nothing updated');
113
+ }
114
+ return;
115
+ }
116
+
117
+ // No options provided, just set the key
118
+ $options = array();
119
+ $options['api_key'] = $api_key;
120
+ update_option('cnb', $options);
121
+ WP_CLI::success('API key updated');
122
+ }
123
+
124
+ /**
125
+ * Test the provided API key
126
+ *
127
+ * ## OPTIONS
128
+ *
129
+ * <api_key>
130
+ * : The API key to test.
131
+ *
132
+ * ## EXAMPLES
133
+ *
134
+ * # Test an API key
135
+ * $ wp cnb api test 0a45e023-...
136
+ *
137
+ *
138
+ * @param $args {array} should contain 1 entry, only the API key
139
+ *
140
+ * @return boolean true if valid
141
+ *
142
+ */
143
+ public function test($args) {
144
+ // Check of key is provided
145
+ if (sizeof($args) !== 1) {
146
+ WP_CLI::error('Wrong amount of arguments, 1 (the API key) expected');
147
+ return false;
148
+ }
149
+ $api_key = $args[0];
150
+
151
+ WP_CLI::log('Testing key...');
152
+ $is_valid = $this->test_api_key($api_key);
153
+ if ($is_valid) {
154
+ WP_CLI::success('API key valid');
155
+ } else {
156
+ WP_CLI::error('API key invalid');
157
+ }
158
+ return $is_valid;
159
+ }
160
+
161
+ /**
162
+ * @param $api_key string the API key to test
163
+ *
164
+ * @return boolean true if the key is valid
165
+ */
166
+ private function test_api_key($api_key) {
167
+ return $this->adminCloud->is_api_key_valid($api_key);
168
+ }
169
+
170
+ /**
171
+ * @param $api_key string any API key
172
+ *
173
+ * @return string Obfucscated string (last part of key removed)
174
+ */
175
+ private function obfuscate_key($api_key) {
176
+ $hide_chars = 12;
177
+ return substr($api_key, 0, -$hide_chars)
178
+ . str_repeat('x', $hide_chars);
179
+ }
180
+ /**
181
+ * Registers the Call Now Button commands when CLI gets initialized.
182
+ *
183
+ * @noinspection PhpUnused (it is used via cli_init)
184
+ */
185
+ static function cli_register_command() {
186
+ WP_CLI::add_command( 'cnb api', 'cnb\cli\CNB_CLI_Api' );
187
+ }
188
+ }
189
+
190
+ add_action( 'cli_init', '\cnb\cli\CNB_CLI_Api::cli_register_command' );
src/cli/CNB_CLI_Button.class.php ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace cnb\cli;
4
+
5
+ use CnbAdminCloud;
6
+ use CnbAppRemote;
7
+ use CnbButton;
8
+ use CnbButtonOptions;
9
+ use WP_CLI;
10
+ use WP_CLI_Command;
11
+
12
+ /**
13
+ * Query the Call Now Button Buttons
14
+ *
15
+ * @since 1.0.6
16
+ * @author Jasper Roel
17
+ *
18
+ * @noinspection PhpUnused (it is used as a WP CLI class)
19
+ */
20
+ class CNB_CLI_Button extends WP_CLI_Command {
21
+ /**
22
+ * List all the buttons for this User
23
+ *
24
+ * ## OPTIONS
25
+ *
26
+ * [--format=<format>]
27
+ * : Render output in a particular format.
28
+ * ---
29
+ * default: table
30
+ *
31
+ * options:
32
+ * - table
33
+ * - csv
34
+ * - json
35
+ * - count
36
+ * - yaml
37
+ * ---
38
+ *
39
+ * [--fields=<fields>]
40
+ * : Render specific fields. Default is to show all available fields.
41
+ * ---
42
+ * default: id,name,active,type,domain,actions,conditions,options
43
+ *
44
+ * options:
45
+ * - id
46
+ * - name
47
+ * - active
48
+ * - type
49
+ * - domain
50
+ * - actions
51
+ * - conditions
52
+ * - options
53
+ * ---
54
+ *
55
+ * ## EXAMPLES
56
+ *
57
+ * # List all buttons
58
+ * $ wp cnb button list
59
+ *
60
+ * # Specify fields
61
+ * $ wp cnb button list --fields=id,name,active
62
+ *
63
+ * # Specify output
64
+ * $ wp cnb button list --output=yaml
65
+ *
66
+ * # Specify fields and output
67
+ * $ wp cnb button list --fields=id,name --output=yaml
68
+ *
69
+ * @subcommand list
70
+ */
71
+ public function list_($args, $assoc_args) {
72
+ $buttons = CnbAppRemote::cnb_remote_get_buttons();
73
+ if (!is_wp_error($buttons)) {
74
+ // Convert into array
75
+ $items = CnbButton::convert($buttons);
76
+
77
+ $format = WP_CLI\Utils\get_flag_value( $assoc_args, 'format', 'table' );
78
+ $fields = WP_CLI\Utils\get_flag_value( $assoc_args, 'fields', array( 'id', 'name', 'active', 'type', 'domain', 'actions', 'conditions', 'options' ) );
79
+ WP_CLI::log($fields);
80
+ WP_CLI\Utils\format_items( $format, $items, $fields );
81
+ WP_CLI::log(''); // Force a new line since that isn't included in the format_items output
82
+ } else {
83
+ WP_CLI::error($buttons, false);
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Create a Button
89
+ *
90
+ * ## OPTIONS
91
+ *
92
+ * <name>
93
+ * : The name of the Button
94
+ *
95
+ * [--type=<type>]
96
+ * : default: SINGLE
97
+ * ---
98
+ * options:
99
+ * - SINGLE
100
+ * - FULL
101
+ * - MULTI
102
+ * ---
103
+ *
104
+ * [--active=<action>]
105
+ * : default: true
106
+ * ---
107
+ * options:
108
+ * - true
109
+ * - false
110
+ * ---
111
+ *
112
+ * [--placement=<placement>]
113
+ * : default: BOTTOM_RIGHT
114
+ * ---
115
+ * options:
116
+ * - BOTTOM_LEFT
117
+ * - BOTTOM_CENTER
118
+ * - BOTTOM_RIGHT
119
+ * - MIDDLE_LEFT
120
+ * - MIDDLE_RIGHT
121
+ * - TOP_LEFT
122
+ * - TOP_CENTER
123
+ * - TOP_RIGHT
124
+ *
125
+ * ## EXAMPLES
126
+ *
127
+ * # Create a button
128
+ * $ wp cnb button create "Button via WP CLI"
129
+ *
130
+ * # Create a MULTI button
131
+ * $ wp cnb button create "Multi Button via WP CLI" --type=MULTI
132
+ *
133
+ * # Create an inactive FULL button
134
+ * $ wp cnb button create "Full Button via WP CLI" --type=FULL --active=false
135
+ *
136
+ * # Create an FULL button with TOP_CENTER placement
137
+ * $ wp cnb button create "Full Button at top via WP CLI" --type=FULL --placement=TOP_CENTER
138
+ * @return void
139
+ */
140
+ public function create($args, $assoc_args) {
141
+ // Check of key is provided
142
+ if (sizeof($args) !== 1) {
143
+ WP_CLI::error('Wrong amount of arguments, 1 (the name) expected');
144
+ return;
145
+ }
146
+
147
+ list ($button_name) = $args;
148
+
149
+ $notifications = array();
150
+
151
+ $default_domain = CnbAppRemote::cnb_remote_get_wp_domain();
152
+
153
+ $button = new CnbButton();
154
+ $button->name = $button_name;
155
+ $button->type = WP_CLI\Utils\get_flag_value( $assoc_args, 'type', 'SINGLE' );
156
+ $button->active = WP_CLI\Utils\get_flag_value( $assoc_args, 'active', 'true' ) == 'true';
157
+ $button->domain = $default_domain->id;
158
+
159
+ $options = new CnbButtonOptions();
160
+ $options->placement = WP_CLI\Utils\get_flag_value( $assoc_args, 'placement', 'BOTTOM_RIGHT' );
161
+ $button->options = $options;
162
+
163
+ $result = CnbAdminCloud::cnb_create_button( $notifications, $button->toArray() );
164
+ if ( is_wp_error( $result ) ) {
165
+ WP_CLI::error( $result );
166
+ }
167
+ WP_CLI::success('Created ' . $result->name . ' with ID ' .$result->id);
168
+
169
+ }
170
+ /**
171
+ * Registers the Call Now Button commands when CLI gets initialized.
172
+ *
173
+ * @noinspection PhpUnused (it is used via cli_init)
174
+ */
175
+ static function cli_register_command() {
176
+ WP_CLI::add_command( 'cnb button', 'cnb\cli\CNB_CLI_Button' );
177
+ }
178
+ }
179
+
180
+ add_action( 'cli_init', '\cnb\cli\CNB_CLI_Button::cli_register_command' );
src/cli/CNB_CLI_User.class.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace cnb\cli;
4
+
5
+ use cnb\admin\models\Cnb_User;
6
+ use CnbAppRemote;
7
+ use WP_CLI;
8
+ use WP_CLI_Command;
9
+ use WP_Error;
10
+
11
+ /**
12
+ * Query the Call Now Button User
13
+ *
14
+ * @since 1.0.6
15
+ * @author Jasper Roel
16
+ *
17
+ * @noinspection PhpUnused (it is used as a WP CLI class)
18
+ */
19
+ class CNB_CLI_User extends WP_CLI_Command {
20
+ /**
21
+ * Returns the logged-in User
22
+ *
23
+ * ## EXAMPLES
24
+ *
25
+ * # Get the current logged-in User
26
+ * $ wp cnb user get
27
+ * ID: 67a54fd0-b048-4522-8e23-ddfeffa6b129
28
+ * Name: Jasper Roel
29
+ * Email: jasper@studiostacks.com
30
+ *
31
+ * @return Cnb_User|WP_Error
32
+ */
33
+ public function get() {
34
+ $user = CnbAppRemote::cnb_remote_get_user_info();
35
+ if (!is_wp_error($user)) {
36
+ WP_CLI::log( sprintf( 'ID: %s', $user->id ) );
37
+ WP_CLI::log( sprintf( 'Name: %s', $user->name ) );
38
+ WP_CLI::log(
39
+ sprintf( 'Email: %s',
40
+ WP_CLI::colorize(
41
+ sprintf( '%%G%s%%n', $user->email )
42
+ )
43
+ )
44
+ );
45
+ } else {
46
+ WP_CLI::error($user, false);
47
+ }
48
+ return $user;
49
+ }
50
+
51
+ /**
52
+ * Registers the Call Now Button commands when CLI gets initialized.
53
+ *
54
+ * @noinspection PhpUnused (it is used via cli_init)
55
+ */
56
+ static function cli_register_command() {
57
+ WP_CLI::add_command( 'cnb user', 'cnb\cli\CNB_CLI_User' );
58
+ }
59
+ }
60
+
61
+ add_action( 'cli_init', '\cnb\cli\CNB_CLI_User::cli_register_command' );
src/cli/mocks/WP_CLI.class.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // This file should not be "require"d anywhere
4
+
5
+ // These are not a real classes, but mocks only used during development
6
+ // So the IDE is "tricked" into knowing the class and functions without
7
+ // having to include the whole WP_CLI suite during development.
8
+
9
+ if (!class_exists('WP_CLI')) {
10
+ class WP_CLI {
11
+
12
+ public static function error( $string, $die=true ) {
13
+ }
14
+
15
+ public static function colorize( $sprintf ) {
16
+ return '';
17
+ }
18
+
19
+ public static function success( $string ) {
20
+ }
21
+
22
+ public static function log( $string ) {
23
+ }
24
+
25
+ public static function add_command( $string, $string1 ) {
26
+ }
27
+
28
+ public static function warning( $string ) {
29
+ }
30
+ }
31
+ }
32
+
33
+ if (!class_exists('WP_CLI_Command')) {
34
+ class WP_CLI_Command {}
35
+ }
src/cli/mocks/WP_CLI_Utils.class.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // This file should not be "require"d anywhere
3
+
4
+ // These are not a real classes or functions, but mocks only used during development
5
+ // So the IDE is "tricked" into knowing the class and functions without
6
+ // having to include the whole WP_CLI suite during development.
7
+
8
+ namespace WP_CLI\Utils;
9
+
10
+ function get_flag_value($array, $value, $default) { return ''; }
11
+ function format_items($format, $items, $fields) {}
src/utils/notices.php CHANGED
@@ -49,12 +49,13 @@ function cnb_upgrade_notice($cnb_options, $cnb_changelog) {
49
  }
50
 
51
  function cnb_settings_email_activation_input() {
52
- $message = '<div id="cnb_email_activation_alternate_formd">';
53
- $message .= '<input type="text" class="regular-text" name="cnb_email_activation_alternate_address" id="cnb_email_activation_alternate_address" placeholder="email@example.com" value="' . esc_attr(get_bloginfo('admin_email')) . '"/> ';
54
  $message .= get_submit_button(__('Activate Premium'), 'primary', 'cnb_email_activation_alternate', false, array('onclick' => 'return cnb_email_activation_alternate()'));
55
  $message .= '</div>';
56
  $message .= '<p id="cnb_email_activation"></p>';
57
- $message .= '<p class="nonessential">By clicking <u>Activate Premium</u> an account will be created with your email address on callnowbutton.com and you agree to our <a href="https://callnowbutton.com/terms.html" target="_blank">Terms & Conditions</a> and <a href="https://callnowbutton.com/privacy.html" target="_blank">Privacy statement</a>.</p>';
 
58
 
59
  return $message;
60
  }
@@ -117,7 +118,7 @@ function cnb_settings_api_key_input() {
117
  $message .= ob_get_clean();
118
  $message .= '<input type="hidden" name="page" value="call-now-button-settings" />
119
  <div>
120
- <input type="text" class="regular-text" name="cnb[api_key]"
121
  placeholder="Paste API key here"/>
122
  '. get_submit_button(__('Store API key'), 'primary', 'submit', false).'
123
  </div>
@@ -199,7 +200,7 @@ function cnb_button_disabled_notice() {
199
  $url );
200
  $redirect_url = esc_url( $redirect_link );
201
 
202
- $message = '<p>Your button is currently <strong>not enabled</strong>. Click <a href="'.$redirect_url.'">here</a> to enable your button.</p>';
203
 
204
  $adminNotices = CnbAdminNotices::get_instance();
205
  $adminNotices->warning($message);
@@ -231,16 +232,16 @@ function cnb_caching_plugin_warning_notice($caching_plugin_name) {
231
  $adminNotices->error($message);
232
  }
233
 
234
- function cnb_show_welcome_panel() {
235
- $dimiss_value = 'welcome-panel';
236
- $dismissed_option = CnbAdminNotices::get_instance()->get_dismiss_option_name($dimiss_value);
237
  $is_dismissed = CnbAdminNotices::get_instance()->is_dismissed($dismissed_option);
238
  return !$is_dismissed;
239
  }
240
 
241
- function cnb_get_welcome_panel() {
242
- if (!cnb_show_welcome_panel()) return;
243
- $dimiss_value = 'welcome-panel';
244
 
245
  $url = admin_url('admin.php');
246
  $upgrade_link =
@@ -251,47 +252,73 @@ function cnb_get_welcome_panel() {
251
 
252
  $dismiss_data_url = '';
253
  $dismiss_url = add_query_arg( array(
254
- CNB_SLUG . '_dismiss' => $dimiss_value
255
  ), $url );
256
 
257
  $dismiss_data_url .= ' data-dismiss-url="' . esc_url( $dismiss_url ) . '"';
258
 
259
  ?>
260
- <div id="welcome-panel" class="welcome-panel is-dismissible notice-call-now-button" <?php echo $dismiss_data_url ?>>
261
- <button type="button" class="welcome-panel-close notice-dismiss">Dismiss<span class="screen-reader-text">Dismiss this notice.</span></button>
262
-
263
- <div class="welcome-panel-content">
264
- <h2>Welcome to Call Now Button verson 1.0</h2>
265
  <p class="about-description">After 10 years we have finally reached v1!</p>
266
- <div class="welcome-panel-column-container">
267
- <div class="welcome-panel-column">
268
  <h3>Here's why</h3>
269
  <div class="welcome-column-box">
270
  <p class="only-in-columns">Why we promoted the plugin from the zeros to a 1:</p>
271
 
272
- <p class="cnb-mobile-inline">🎉 The Call Now Button is turning 10!</p>
273
  <p class="cnb-mobile-inline">❤️ 200k+ active installs and rated 4.9!</p>
274
- <p class="cnb-mobile-inline">💎 Premium went live!</p>
275
  </div>
276
  </div>
277
- <div class="welcome-panel-column">
278
- <h3>What's in Premium?</h3>
279
  <p class="cnb-mobile-inline">+ Create multiple buttons</p>
280
- <p class="cnb-mobile-inline">+ WhatsApp, Email, Maps and Links</p>
281
- <p class="cnb-mobile-inline">+ Multibutton&trade; and Buttonbar&trade;</p>
282
  <p class="cnb-mobile-inline">+ Button scheduler</p>
283
  <p class="cnb-mobile-inline">+ Advanced page targeting</p>
284
  </div>
285
- <div class="welcome-panel-column">
286
- <a class="button button-primary button-hero" href="<?php echo $upgrade_url ?>">Activate Premium</a>
287
- <p>Or, <a href="<?php echo $upgrade_url ?>">learn more.</a></p>
288
  <h3>Other resources</h3>
289
- <p>
290
- <a href="<?php echo CNB_SUPPORT; ?>wordpress-free/">The new help center</a><br>
291
- <a href="<?php echo CNB_SUPPORT; ?>wordpress-free/#faq">FAQ</a>
292
- </p>
293
  </div>
294
  </div>
295
  </div>
 
296
  </div>
297
  <?php }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  }
50
 
51
  function cnb_settings_email_activation_input() {
52
+ $message = '<div id="cnb_email_activation_alternate_formd">';
53
+ $message .= '<input type="text" class="cnb_activation_input_field" name="cnb_email_activation_alternate_address" id="cnb_email_activation_alternate_address" placeholder="email@example.com" value="' . esc_attr(get_bloginfo('admin_email')) . '"/> ';
54
  $message .= get_submit_button(__('Activate Premium'), 'primary', 'cnb_email_activation_alternate', false, array('onclick' => 'return cnb_email_activation_alternate()'));
55
  $message .= '</div>';
56
  $message .= '<p id="cnb_email_activation"></p>';
57
+
58
+ $message .= '<p class="nonessential">By clicking <u>Activate Premium</u> an account will be created with your email address on callnowbutton.com and you agree to our <a href="https://callnowbutton.com/terms.html" target="_blank">Terms & Conditions</a> and <a href="https://callnowbutton.com/privacy.html" target="_blank">Privacy statement</a>. You also acknowledge that the Free plan allows up to 20k pageviews per month.</p>';
59
 
60
  return $message;
61
  }
118
  $message .= ob_get_clean();
119
  $message .= '<input type="hidden" name="page" value="call-now-button-settings" />
120
  <div>
121
+ <input type="text" class="cnb_activation_input_field" name="cnb[api_key]"
122
  placeholder="Paste API key here"/>
123
  '. get_submit_button(__('Store API key'), 'primary', 'submit', false).'
124
  </div>
200
  $url );
201
  $redirect_url = esc_url( $redirect_link );
202
 
203
+ $message = '<p>The Call Now Button is currently <strong>inactive</strong>.';
204
 
205
  $adminNotices = CnbAdminNotices::get_instance();
206
  $adminNotices->warning($message);
232
  $adminNotices->error($message);
233
  }
234
 
235
+ function cnb_show_welcome_banner() {
236
+ $dismiss_value = 'welcome-panel';
237
+ $dismissed_option = CnbAdminNotices::get_instance()->get_dismiss_option_name($dismiss_value);
238
  $is_dismissed = CnbAdminNotices::get_instance()->is_dismissed($dismissed_option);
239
  return !$is_dismissed;
240
  }
241
 
242
+ function cnb_get_welcome_banner() {
243
+ if (!cnb_show_welcome_banner()) return;
244
+ $dismiss_value = 'welcome-panel';
245
 
246
  $url = admin_url('admin.php');
247
  $upgrade_link =
252
 
253
  $dismiss_data_url = '';
254
  $dismiss_url = add_query_arg( array(
255
+ CNB_SLUG . '_dismiss' => $dismiss_value
256
  ), $url );
257
 
258
  $dismiss_data_url .= ' data-dismiss-url="' . esc_url( $dismiss_url ) . '"';
259
 
260
  ?>
261
+ <div id="welcome-banner" class="welcome-banner is-dismissible notice-call-now-button" <?php echo $dismiss_data_url ?>>
262
+ <div class="welcome-banner-content">
263
+ <h2>Welcome to Call&nbsp;Now&nbsp;Button&nbsp;verson&nbsp;1.0</h2>
 
 
264
  <p class="about-description">After 10 years we have finally reached v1!</p>
265
+ <div class="welcome-banner-column-container">
266
+ <div class="welcome-banner-column">
267
  <h3>Here's why</h3>
268
  <div class="welcome-column-box">
269
  <p class="only-in-columns">Why we promoted the plugin from the zeros to a 1:</p>
270
 
271
+ <p class="cnb-mobile-inline">🎉 The Call Now Button is turning 10 years!</p>
272
  <p class="cnb-mobile-inline">❤️ 200k+ active installs and rated 4.9!</p>
273
+ <p class="cnb-mobile-inline">💎 Call Now Button <strong>Premium</strong> is finally here!</p>
274
  </div>
275
  </div>
276
+ <div class="welcome-banner-column">
277
+ <h3>What's Premium?</h3>
278
  <p class="cnb-mobile-inline">+ Create multiple buttons</p>
279
+ <p class="cnb-mobile-inline">+ WhatsApp, SMS/text, Email, Maps and Links</p>
280
+ <p class="cnb-mobile-inline">+ Multi action buttons</p>
281
  <p class="cnb-mobile-inline">+ Button scheduler</p>
282
  <p class="cnb-mobile-inline">+ Advanced page targeting</p>
283
  </div>
284
+ <div class="welcome-banner-column">
285
+ <a class="button button-primary button-hero" href="<?php echo $upgrade_url ?>">Try Premium for Free</a>
286
+ <p><a href="<?php echo $upgrade_url ?>">More info about Premium</a></p>
287
  <h3>Other resources</h3>
288
+ <p><a href="<?php echo CNB_SUPPORT; ?>wordpress-free/">The new help center</a></p>
289
+ <p><a href="<?php echo CNB_SUPPORT; ?>wordpress-free/#faq">FAQ</a></p>
 
 
290
  </div>
291
  </div>
292
  </div>
293
+ <button type="button" class="notice-dismiss"><span class="screen-reader-text"><?php _e('Dismiss this notice.') ?></span></button>
294
  </div>
295
  <?php }
296
+
297
+ /**
298
+ *
299
+ * Also warning if timezone is not yet set
300
+ * @param $domain
301
+ *
302
+ * @return boolean true is all is alright
303
+ */
304
+ function cnb_warn_about_timezone($domain) {
305
+ if ($domain && !is_wp_error($domain)) {
306
+ $domain_timezone = $domain->timezone;
307
+ if ( empty( $domain_timezone ) ) {
308
+ $url = admin_url( 'admin.php' );
309
+ $redirect_link =
310
+ add_query_arg(
311
+ array(
312
+ 'page' => 'call-now-button-settings',
313
+ 'tab' => 'advanced_options#domain_timezone',
314
+
315
+ ),
316
+ $url );
317
+ $redirect_url = esc_url( $redirect_link );
318
+ CnbAdminNotices::get_instance()->renderWarning( "<p>Please set your timezone in the <a href=\"" . $redirect_url . "\">Advanced settings</a> tab to avoid unpredictable behavior when using the scheduler.</p>" );
319
+
320
+ return false;
321
+ }
322
+ }
323
+ return true;
324
+ }
src/utils/utils.php CHANGED
@@ -71,10 +71,14 @@ function cnb_update_needed($cnb_options) {
71
  function cnb_update_options() {
72
  $cnb_options = get_option('cnb');
73
  $cnb_defaults = cnb_get_defaults();
 
74
  // Bugfix for v0.4.5 - a wrong semicolon caused this variable to become a boolean(true) instead of a string
75
  // This fix checks for that exact scenario and forces it back to the default "right" if needed.
76
  // If this variable is set correctly (i.e. is a string), it will retain that value
77
- $cnb_options['appearance'] = ($cnb_options['appearance'] === true) ? $cnb_defaults['appearance'] : $cnb_options['appearance'];
 
 
 
78
  if(cnb_update_needed($cnb_options)) { // Check current version and if it needs an update
79
  $cnb_options['active'] = isset($cnb_options['active'])
80
  ? $cnb_options['active'] == 1 ? 1 : 0
@@ -239,15 +243,24 @@ function zindexToOrder($zindex) {
239
  return 1;
240
  }
241
 
 
 
 
 
 
 
 
 
242
  function cnb_actiontype_to_icontext($actionType) {
243
  switch ($actionType) {
244
  case 'ANCHOR': return 'anchor';
245
- case 'WHATSAPP': return 'whatsapp';
246
  case 'EMAIL': return 'email';
 
247
  case 'LINK': return 'link';
248
  case 'MAP': return 'directions';
249
- case 'HOURS': return 'access_time';
250
- case 'PHONE':
 
251
  default:
252
  return 'call';
253
  }
71
  function cnb_update_options() {
72
  $cnb_options = get_option('cnb');
73
  $cnb_defaults = cnb_get_defaults();
74
+
75
  // Bugfix for v0.4.5 - a wrong semicolon caused this variable to become a boolean(true) instead of a string
76
  // This fix checks for that exact scenario and forces it back to the default "right" if needed.
77
  // If this variable is set correctly (i.e. is a string), it will retain that value
78
+ $cnb_options['appearance'] = (!isset($cnb_options['appearance']) || $cnb_options['appearance'] === true)
79
+ ? $cnb_defaults['appearance']
80
+ : $cnb_options['appearance'];
81
+
82
  if(cnb_update_needed($cnb_options)) { // Check current version and if it needs an update
83
  $cnb_options['active'] = isset($cnb_options['active'])
84
  ? $cnb_options['active'] == 1 ? 1 : 0
243
  return 1;
244
  }
245
 
246
+ /**
247
+ * Get the default glyph for a particular action type.
248
+ *
249
+ * This should have the same content as the JS function cnbActiontypeToIcontext
250
+ * @param $actionType string
251
+ *
252
+ * @return string
253
+ */
254
  function cnb_actiontype_to_icontext($actionType) {
255
  switch ($actionType) {
256
  case 'ANCHOR': return 'anchor';
 
257
  case 'EMAIL': return 'email';
258
+ case 'HOURS': return 'access_time';
259
  case 'LINK': return 'link';
260
  case 'MAP': return 'directions';
261
+ case 'PHONE': return 'call';
262
+ case 'SMS': return 'chat';
263
+ case 'WHATSAPP': return 'whatsapp';
264
  default:
265
  return 'call';
266
  }