Restaurant Reservations - Version 1.3

Version Description

This update adds support for adding and editing bookings from the admin panel. The bookings panel now shows upcoming bookings by default. The Party field in the booking form is now a dropdown selection. Plus a bunch of new features and fixes. Read the full changelog for details.

Download this release

Release Info

Developer NateWr
Plugin Icon 128x128 Restaurant Reservations
Version 1.3
Comparing to
See all releases

Code changes from version 1.2.3 to 1.3

Files changed (70) hide show
  1. assets/addons-backup.json +11 -0
  2. assets/css/admin.css +243 -2
  3. assets/js/admin.js +312 -9
  4. assets/js/booking-form.js +170 -112
  5. includes/Addons.class.php +27 -16
  6. includes/AdminBookings.class.php +455 -0
  7. includes/Booking.class.php +88 -27
  8. includes/Compatibility.class.php +47 -0
  9. includes/CustomPostTypes.class.php +18 -18
  10. includes/Notification.Email.class.php +30 -16
  11. includes/Notification.class.php +3 -3
  12. includes/Notifications.class.php +17 -22
  13. includes/Settings.class.php +333 -117
  14. includes/WP_List_Table.BookingsTable.class.php +122 -53
  15. includes/WP_Widget.BookingFormWidget.class.php +2 -2
  16. includes/template-functions.php +98 -99
  17. languages/restaurant-reservations-de_DE.mo +0 -0
  18. languages/restaurant-reservations-de_DE.po +1441 -0
  19. languages/restaurant-reservations-es_CL.mo +0 -0
  20. languages/restaurant-reservations-es_CL.po +1201 -0
  21. languages/restaurant-reservations-nl_NL.MO +0 -0
  22. languages/{rtbdomain.pot → restaurant-reservations-nl_NL.PO} +747 -643
  23. languages/restaurant-reservations.pot +1263 -0
  24. lib/simple-admin-pages/README.md +37 -22
  25. lib/simple-admin-pages/classes/AdminPage.Menu.class.php +1 -1
  26. lib/simple-admin-pages/classes/AdminPage.Submenu.class.php +1 -1
  27. lib/simple-admin-pages/classes/AdminPage.Themes.class.php +1 -1
  28. lib/simple-admin-pages/classes/AdminPage.class.php +1 -1
  29. lib/simple-admin-pages/classes/AdminPageSection.class.php +1 -1
  30. lib/simple-admin-pages/classes/AdminPageSetting.Address.class.php +29 -24
  31. lib/simple-admin-pages/classes/AdminPageSetting.Editor.class.php +1 -1
  32. lib/simple-admin-pages/classes/AdminPageSetting.HTML.class.php +1 -1
  33. lib/simple-admin-pages/classes/AdminPageSetting.OpeningHours.class.php +1 -1
  34. lib/simple-admin-pages/classes/AdminPageSetting.Scheduler.class.php +65 -40
  35. lib/simple-admin-pages/classes/AdminPageSetting.Select.class.php +1 -1
  36. lib/simple-admin-pages/classes/AdminPageSetting.SelectPost.class.php +7 -5
  37. lib/simple-admin-pages/classes/AdminPageSetting.SelectTaxonomy.class.php +1 -1
  38. lib/simple-admin-pages/classes/AdminPageSetting.Text.class.php +1 -1
  39. lib/simple-admin-pages/classes/AdminPageSetting.Textarea.class.php +1 -1
  40. lib/simple-admin-pages/classes/AdminPageSetting.Toggle.class.php +1 -1
  41. lib/simple-admin-pages/classes/AdminPageSetting.class.php +34 -6
  42. lib/simple-admin-pages/classes/Library.class.php +3 -3
  43. lib/simple-admin-pages/lib/pickadate/legacy.js +125 -2
  44. lib/simple-admin-pages/lib/pickadate/picker.date.js +1347 -2
  45. lib/simple-admin-pages/lib/pickadate/picker.js +1117 -2
  46. lib/simple-admin-pages/lib/pickadate/picker.time.js +1007 -2
  47. lib/simple-admin-pages/lib/pickadate/themes/default.css +170 -2
  48. lib/simple-admin-pages/lib/pickadate/themes/default.date.css +301 -1
  49. lib/simple-admin-pages/lib/pickadate/themes/default.time.css +126 -1
  50. lib/simple-admin-pages/lib/pickadate/themes/rtl.css +27 -1
  51. lib/simple-admin-pages/lib/pickadate/translations/ca_ES.js +1 -0
  52. lib/simple-admin-pages/lib/pickadate/translations/da_DK.js +1 -0
  53. lib/simple-admin-pages/lib/pickadate/translations/de_DE.js +1 -0
  54. lib/simple-admin-pages/lib/pickadate/translations/fr_FR.js +4 -4
  55. lib/simple-admin-pages/lib/pickadate/translations/it_IT.js +10 -4
  56. lib/simple-admin-pages/lib/pickadate/translations/lt_LT.js +24 -0
  57. lib/simple-admin-pages/lib/pickadate/translations/lv_LV.js +13 -0
  58. lib/simple-admin-pages/lib/pickadate/translations/{no_NO.js → nb_NO.js} +1 -0
  59. lib/simple-admin-pages/lib/pickadate/translations/nl_NL.js +1 -0
  60. lib/simple-admin-pages/lib/pickadate/translations/pl_PL.js +4 -3
  61. lib/simple-admin-pages/lib/pickadate/translations/pt_BR.js +3 -2
  62. lib/simple-admin-pages/lib/pickadate/translations/pt_PT.js +6 -5
  63. lib/simple-admin-pages/lib/pickadate/translations/sl_SI.js +1 -0
  64. lib/simple-admin-pages/lib/pickadate/translations/sv_SE.js +10 -5
  65. lib/simple-admin-pages/lib/pickadate/translations/tr_TR.js +3 -2
  66. lib/simple-admin-pages/simple-admin-pages.php +0 -5
  67. readme.txt +28 -7
  68. restaurant-reservations.php +53 -60
  69. screenshot-10.png +0 -0
  70. screenshot-9.png +0 -0
assets/addons-backup.json ADDED
@@ -0,0 +1,11 @@
1
+ [
2
+ {
3
+ "id":"mailchimp-for-rtb",
4
+ "title":"MailChimp for Restaurant Reservations",
5
+ "url":"http:\/\/themeofthecrop.com\/plugin\/mailchimp-restaurant-reservations\/",
6
+ "price":"$20",
7
+ "img":"https:\/\/themeofthecrop.com\/promo\/plugins\/mailchimp-for-rtb.png",
8
+ "status":"released",
9
+ "description":"Subscribe emails from new restaurant reservations to your MailChimp mailing list."
10
+ }
11
+ ]
assets/css/admin.css CHANGED
@@ -56,8 +56,8 @@
56
}
57
58
#rtb-bookings-table tr.closed {
59
- opacity: 0.7;
60
- filter: opacity(alpha=70);
61
}
62
#rtb-bookings-table tr.closed:hover {
63
opacity: 1;
@@ -69,11 +69,252 @@
69
border-left: 4px solid #dd3d36;
70
}
71
72
@media screen and (max-width: 782px) {
73
74
#rtb-bookings-table .fixed .column-date {
75
display: table-cell;
76
}
77
}
78
79
/* Settings Pages (most of this is handled by the Simple Admin Pages library) */
56
}
57
58
#rtb-bookings-table tr.closed {
59
+ opacity: 0.6;
60
+ filter: opacity(alpha=60);
61
}
62
#rtb-bookings-table tr.closed:hover {
63
opacity: 1;
69
border-left: 4px solid #dd3d36;
70
}
71
72
+ #rtb-bookings-table tr.pending .check-column input[type=checkbox] {
73
+ margin-left: 4px;
74
+ }
75
+
76
+ #rtb-bookings-table th#date {
77
+ width: auto;
78
+ }
79
+
80
+ #rtb-bookings-table th#party {
81
+ width: 3em;
82
+ }
83
+
84
+ #rtb-bookings-table th#message {
85
+ width: 5em;
86
+ }
87
+
88
+ #rtb-bookings-table .column-date .actions {
89
+ line-height: 28px;
90
+ opacity: 0;
91
+ -webkit-transition: opacity 0.3s 0;
92
+ -moz-transition: opacity 0.3s 0;
93
+ transition: opacity 0.3s 0;
94
+ }
95
+
96
+ #rtb-bookings-table tr:hover .column-date .actions {
97
+ opacity: 1;
98
+ }
99
+
100
+ #rtb-bookings-table .column-date .actions .trash {
101
+ color: #a00;
102
+ }
103
+
104
+ #rtb-bookings-table .column-date .actions .trash:hover {
105
+ color: red;
106
+ }
107
+
108
+ #rtb-bookings-table .column-date .status {
109
+ width: 0;
110
+ height: 0;
111
+ overflow: hidden;
112
+ line-height: 28px;
113
+ opacity: 0;
114
+ -webkit-transition: opacity 0.6s 0;
115
+ -moz-transition: opacity 0.6s 0;
116
+ transition: opacity 0.6s 0;
117
+ }
118
+
119
+ #rtb-bookings-table .column-date .status .spinner {
120
+ display: inline-block;
121
+ float: left;
122
+ margin: 4px 4px 0 0;
123
+ vertical-align: middle;
124
+ }
125
+
126
+ #rtb-bookings-table .column-date.loading .actions {
127
+ display: none;
128
+ }
129
+
130
+ #rtb-bookings-table .column-date.loading .status {
131
+ width: auto;
132
+ height: auto;
133
+ overflow: visible;
134
+ opacity: 0.5;
135
+ }
136
+
137
+ #rtb-bookings-table tr.notice td {
138
+ background: #2ea2cc;
139
+ color: #fff;
140
+ }
141
+
142
+ #rtb-bookings-table tr.notice.trash td {
143
+ background: #dd3d36;
144
+ }
145
+
146
@media screen and (max-width: 782px) {
147
148
#rtb-bookings-table .fixed .column-date {
149
display: table-cell;
150
}
151
+
152
+ #rtb-bookings-table th#party,
153
+ #rtb-bookings-table th#message {
154
+ width: auto;
155
+ }
156
+ }
157
+
158
+ /* Add/edit bookings modal */
159
+ .rtb-admin-modal {
160
+ position: fixed;
161
+ top: 0;
162
+ left: 0;
163
+ width: 100%;
164
+ height: 100%;
165
+ background: rgba(0, 0, 0, 0.8);
166
+ z-index: 3;
167
+ overflow-y: auto;
168
+ cursor: pointer;
169
+ visibility: hidden;
170
+ opacity: 0;
171
+ -webkit-transition: opacity 0.3s 0, visibility 0 0.3s;
172
+ -moz-transition: opacity 0.3s 0, visibility 0 0.3s;
173
+ transition: opacity 0.3s 0, visibility 0 0.3s;
174
+ }
175
+
176
+ .rtb-admin-modal.is-visible {
177
+ visibility: visible;
178
+ opacity: 1;
179
+ -webkit-transition: opacity 0.3s 0, visibility 0 0;
180
+ -moz-transition: opacity 0.3s 0, visibility 0 0;
181
+ transition: opacity 0.3s 0, visibility 0 0;
182
+ }
183
+
184
+ .rtb-admin-modal.is-visible .rtb-container {
185
+ -webkit-transform: translateY(0);
186
+ -moz-transform: translateY(0);
187
+ -ms-transform: translateY(0);
188
+ -o-transform: translateY(0);
189
+ transform: translateY(0);
190
+ }
191
+
192
+ .rtb-admin-modal .rtb-container {
193
+ position: relative;
194
+ width: 90%;
195
+ max-width: 20em;
196
+ padding: 2em;
197
+ background: #FFF;
198
+ margin: 3em auto 4em;
199
+ cursor: auto;
200
+ border-radius: 0.25em;
201
+ -webkit-transform: translateY(-30px);
202
+ -moz-transform: translateY(-30px);
203
+ -ms-transform: translateY(-30px);
204
+ -o-transform: translateY(-30px);
205
+ transform: translateY(-30px);
206
+ -webkit-transition-property: -webkit-transform;
207
+ -moz-transition-property: -moz-transform;
208
+ transition-property: transform;
209
+ -webkit-transition-duration: 0.3s;
210
+ -moz-transition-duration: 0.3s;
211
+ transition-duration: 0.3s;
212
+ }
213
+
214
+ #rtb-booking-modal fieldset {
215
+ padding: 0;
216
+ }
217
+
218
+ #rtb-booking-modal label {
219
+ color: #777;
220
+ font-style: italic;
221
+ }
222
+
223
+ #rtb-booking-modal button {
224
+ margin-top: 1em;
225
+ margin-right: 1em;
226
+ }
227
+
228
+ #rtb-booking-modal .button {
229
+ margin-top: 1em;
230
+ }
231
+
232
+ #rtb-booking-modal button,
233
+ #rtb-booking-modal .button {
234
+ opacity: 1;
235
+ transition: opacity .25s ease-in-out;
236
+ -moz-transition: opacity .25s ease-in-out;
237
+ -webkit-transition: opacity .25s ease-in-out;
238
+ }
239
+
240
+ #rtb-booking-modal button:disabled,
241
+ #rtb-booking-modal .button:disabled {
242
+ opacity: 0.5;
243
+ transition: opacity .25s ease-in-out;
244
+ -moz-transition: opacity .25s ease-in-out;
245
+ -webkit-transition: opacity .25s ease-in-out;
246
+ }
247
+
248
+ #rtb-booking-modal .action-status {
249
+ display: inline-block;
250
+ float: right;
251
+ margin-top: 1em;
252
+ opacity: 0;
253
+ transition: opacity .25s ease-in-out;
254
+ -moz-transition: opacity .25s ease-in-out;
255
+ -webkit-transition: opacity .25s ease-in-out;
256
+ }
257
+
258
+ #rtb-booking-modal .action-status.is-visible {
259
+ opacity: 1;
260
+ }
261
+
262
+ #rtb-booking-modal .action-status > span {
263
+ display: none;
264
+ background-position: 4px;
265
+ margin: 0;
266
+ height: 28px;
267
+ width: 28px;
268
+ line-height: 28px;
269
+ font-size: 28px;
270
+ }
271
+
272
+ #rtb-booking-modal .action-status > .spinner {
273
+ display: block;
274
+ }
275
+
276
+ #rtb-booking-modal .action-status > .success {
277
+ color: #7ad03a;
278
+ }
279
+
280
+ #rtb-booking-modal .action-status > .error {
281
+ color: #dd3d36;
282
+ }
283
+
284
+ #rtb-booking-modal .rtb-error {
285
+ background: #dd3d36;
286
+ }
287
+
288
+ #rtb-booking-modal .rtb-error a {
289
+ color: #fff;
290
+ }
291
+
292
+ #rtb-booking-modal input[type="text"],
293
+ #rtb-booking-modal input[type="email"],
294
+ #rtb-booking-modal textarea {
295
+ max-width: 100%;
296
+ }
297
+
298
+ .rtb-description {
299
+ margin: 0.5em 0;
300
+ color: #777;
301
+ max-height: 0;
302
+ overflow: hidden;
303
+ -webkit-transition: max-height 0.5s 0;
304
+ -moz-transition: max-height 0.5s 0;
305
+ transition: max-height 0.5s 0;
306
+ }
307
+
308
+ .rtb-description.is-visible {
309
+ max-height: 20em;
310
+ }
311
+
312
+ #rtb-error-modal .rtb-error-msg {
313
+ margin-bottom: 1em;
314
+ }
315
+ /* Hide the outer scrollbar when the modal is open */
316
+ .rtb-hide-body-scroll {
317
+ overflow: hidden !important;
318
}
319
320
/* Settings Pages (most of this is handled by the Simple Admin Pages library) */
assets/js/admin.js CHANGED
@@ -15,13 +15,316 @@ jQuery(document).ready(function ($) {
15
$( '#' + message_id ).fadeIn();
16
$(this).children( '.dashicons' ).removeClass( 'dashicons-testimonial' ).addClass( 'dashicons-welcome-comments' );
17
}
18
-
19
return false;
20
});
21
22
// Show the addons
23
if ( $( '#rtb-addons' ).length ) {
24
-
25
var rtbAddons = {
26
27
el: $( '#rtb-addons' ),
@@ -45,7 +348,7 @@ jQuery(document).ready(function ($) {
45
rtbAddons.showError( r );
46
}
47
});
48
-
49
50
});
51
},
@@ -86,19 +389,19 @@ jQuery(document).ready(function ($) {
86
} else if ( typeof addon.img !== 'undefind' ) {
87
html += '<img src="' + addon.img + '">';
88
}
89
-
90
html += '<h3>' + addon.title + '</h3>';
91
92
html += '<div class="details">';
93
-
94
if ( typeof addon.description !== 'undefined' ) {
95
html += '<div class="description">' + addon.description + '</div>';
96
}
97
-
98
if ( typeof addon.status !== 'undefined' ) {
99
100
html += '<div class="action">';
101
-
102
if ( addon.status === 'released' && typeof addon.url !== 'undefined' ) {
103
html += '<a href="' + addon.url + '" class="button button-primary" target="_blank">';
104
@@ -119,7 +422,7 @@ jQuery(document).ready(function ($) {
119
120
html += '</div>'; // .action
121
}
122
-
123
html += '</div>'; // .details
124
125
html += '</div>'; // .addon
@@ -127,7 +430,7 @@ jQuery(document).ready(function ($) {
127
return html;
128
}
129
};
130
-
131
rtbAddons.load();
132
}
133
15
$( '#' + message_id ).fadeIn();
16
$(this).children( '.dashicons' ).removeClass( 'dashicons-testimonial' ).addClass( 'dashicons-welcome-comments' );
17
}
18
+
19
return false;
20
});
21
22
+ // Register clicks on edit/delete links
23
+ $( '#rtb-bookings-table .column-date .actions' ).click( function(e) {
24
+
25
+ var target = $(e.target);
26
+ var cell = target.parent().parent();
27
+
28
+ rtb_booking_loading_spinner( true, cell );
29
+
30
+ if ( target.data( 'action' ) == 'edit' ) {
31
+ rtb_get_booking( target.data( 'id' ), cell );
32
+
33
+ } else if ( target.data( 'action' ) == 'trash' ) {
34
+ rtb_trash_booking( target.data( 'id' ), cell );
35
+ }
36
+
37
+ e.preventDefault();
38
+ });
39
+
40
+ /**
41
+ * Show/hide loading spinner when edit/delete link clicked
42
+ */
43
+ function rtb_booking_loading_spinner( loading, cell ) {
44
+ if ( loading ) {
45
+ cell.addClass( 'loading' );
46
+ } else {
47
+ cell.removeClass( 'loading' );
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Modal to add/edit bookings from the admin
53
+ */
54
+ var rtb_booking_modal = $( '#rtb-booking-modal' );
55
+ var rtb_booking_modal_fields = rtb_booking_modal.find( '#rtb-booking-form-fields' );
56
+ var rtb_booking_modal_submit = rtb_booking_modal.find( 'button' );
57
+ var rtb_booking_modal_cancel = rtb_booking_modal.find( '#rtb-cancel-booking-modal' );
58
+ var rtb_booking_modal_action_status = rtb_booking_modal.find( '.action-status' );
59
+ var rtb_booking_modal_error = $( '#rtb-error-modal' );
60
+ var rtb_booking_modal_error_msg = rtb_booking_modal_error.find( '.rtb-error-msg' );
61
+
62
+ /**
63
+ * Show or hide the booking form modal
64
+ */
65
+ function rtb_toggle_booking_form_modal( show, fields, booking ) {
66
+
67
+ if ( show ) {
68
+ rtb_booking_modal.scrollTop( 0 ).addClass( 'is-visible' );
69
+
70
+ if ( typeof fields !== 'undefined' ) {
71
+ rtb_booking_modal_fields.html( fields );
72
+ rtb_booking_form.init();
73
+ rtb_init_booking_form_modal_fields();
74
+ }
75
+
76
+ if ( typeof booking == 'undefined' ) {
77
+ rtb_booking_modal_fields.find( '#rtb-post-status' ).val( 'confirmed' );
78
+ rtb_booking_modal_submit.html( rtb_admin.strings.add_booking );
79
+ } else {
80
+ rtb_booking_modal_submit.html( rtb_admin.strings.edit_booking );
81
+ rtb_booking_modal.find( 'input[name=ID]' ).val( booking.ID );
82
+ }
83
+
84
+ $( 'body' ).addClass( 'rtb-hide-body-scroll' );
85
+
86
+ } else {
87
+ rtb_booking_modal.removeClass( 'is-visible' );
88
+ rtb_booking_modal.find( '.rtb-error' ).remove();
89
+ rtb_booking_modal.find( '.notifications-description' ).removeClass( 'is-visible' );
90
+ rtb_booking_modal_action_status.removeClass( 'is-visible' );
91
+ rtb_reset_booking_form_modal_fields();
92
+ rtb_booking_modal_submit.removeData( 'id' );
93
+ rtb_booking_modal_submit.prop( 'disabled', false );
94
+ rtb_booking_modal_cancel.prop( 'disabled', false );
95
+ rtb_booking_modal.find( 'input[name=ID]' ).val( '' );
96
+
97
+ $( 'body' ).removeClass( 'rtb-hide-body-scroll' );
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Show or hide the booking form error modal
103
+ */
104
+ function rtb_toggle_booking_form_error_modal( show, msg ) {
105
+
106
+ if ( show ) {
107
+ rtb_booking_modal_error_msg.html( msg );
108
+ rtb_booking_modal_error.addClass( 'is-visible' );
109
+
110
+ } else {
111
+ rtb_booking_modal_error.removeClass( 'is-visible' );
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Initialize form field events
117
+ */
118
+ function rtb_init_booking_form_modal_fields() {
119
+
120
+ // Show full description for notifications toggle
121
+ rtb_booking_modal_fields.find( '.rtb-description-prompt' ).click( function() {
122
+ $(this).parent().siblings( '.rtb-description' ).addClass( 'is-visible' );
123
+ });
124
+ }
125
+
126
+ /**
127
+ * Reset booking form fields
128
+ */
129
+ function rtb_reset_booking_form_modal_fields() {
130
+ rtb_booking_modal_fields.find( 'input, select, textarea' ).val( '' );
131
+ rtb_booking_modal_fields.find( 'input[name=rtb-notifications]' ).removeAttr( 'checked' );
132
+ }
133
+
134
+ /**
135
+ * Retrieve booking from the database
136
+ */
137
+ function rtb_get_booking( id, cell ) {
138
+
139
+ var params = {};
140
+
141
+ params.action = 'rtb-admin-booking-modal';
142
+ params.nonce = rtb_admin.nonce;
143
+ params.booking = {
144
+ 'ID': id
145
+ };
146
+
147
+ var data = $.param( params );
148
+
149
+ var jqhxr = $.get( ajaxurl, data, function( r ) {
150
+
151
+ if ( r.success ) {
152
+ rtb_toggle_booking_form_modal( true, r.data.fields, r.data.booking );
153
+
154
+ } else {
155
+
156
+ if ( typeof r.data.error == 'undefined' ) {
157
+ rtb_toggle_booking_form_error_modal( true, rtb_admin.strings.error_unspecified );
158
+ } else {
159
+ rtb_toggle_booking_form_error_modal( true, r.data.msg );
160
+ }
161
+ }
162
+
163
+ rtb_booking_loading_spinner( false, cell );
164
+ });
165
+ }
166
+
167
+ /**
168
+ * Trash booking
169
+ */
170
+ function rtb_trash_booking( id, cell ) {
171
+
172
+ var params = {};
173
+
174
+ params.action = 'rtb-admin-trash-booking';
175
+ params.nonce = rtb_admin.nonce;
176
+ params.booking = id;
177
+
178
+ var data = $.param( params );
179
+
180
+ var jqhxr = $.post( ajaxurl, data, function( r ) {
181
+
182
+ if ( r.success ) {
183
+
184
+ cell.parent().fadeOut( 500, function() {
185
+ $(this).remove();
186
+ });
187
+
188
+ var trash_count_el = $( '#rtb-bookings-table .subsubsub .trash .count' );
189
+ var trash_count = parseInt( trash_count_el.html().match(/\d+/), 10 ) + 1;
190
+ trash_count_el.html( '(' + trash_count + ')' );
191
+
192
+ } else {
193
+
194
+ if ( typeof r.data == 'undefined' || typeof r.data.error == 'undefined' ) {
195
+ rtb_toggle_booking_form_error_modal( true, rtb_admin.strings.error_unspecified );
196
+ } else {
197
+ rtb_toggle_booking_form_error_modal( true, r.data.msg );
198
+ }
199
+ }
200
+
201
+ rtb_booking_loading_spinner( false, cell );
202
+ });
203
+
204
+ }
205
+
206
+ /**
207
+ * Show the appropriate result status icon
208
+ */
209
+ function rtb_show_action_status( status ) {
210
+
211
+ rtb_booking_modal_action_status.find( 'span' ).hide();
212
+
213
+ if ( status === true ) {
214
+ rtb_booking_modal_action_status.find( '.success' ).show();
215
+ } else if ( status === false ) {
216
+ rtb_booking_modal_action_status.find( '.error' ).show();
217
+ } else {
218
+ rtb_booking_modal_action_status.find( '.spinner' ).show();
219
+ }
220
+ }
221
+
222
+ // Initialize form field events on load
223
+ rtb_init_booking_form_modal_fields();
224
+
225
+ // Reset the form on load
226
+ // This fixes a strange bug in Firefox where disabled buttons would
227
+ // persist after the page refreshed. I'm guessing its a cache issue
228
+ // but this will just reset everything again
229
+ rtb_toggle_booking_form_modal( false );
230
+
231
+ // Show booking form modal
232
+ $( '.add-booking' ).click( function() {
233
+ rtb_toggle_booking_form_modal( true );
234
+ });
235
+
236
+ // Close booking form modal when background or cancel button is clicked
237
+ rtb_booking_modal.click( function(e) {
238
+ if ( $(e.target).is( rtb_booking_modal ) ) {
239
+ rtb_toggle_booking_form_modal( false );
240
+ }
241
+
242
+ if ( $(e.target).is( rtb_booking_modal_cancel ) && rtb_booking_modal_cancel.prop( 'disabled' ) === false ) {
243
+ rtb_toggle_booking_form_modal( false );
244
+ }
245
+ });
246
+
247
+ // Close booking form modal and error modal when ESC is keyed
248
+ $(document).keyup( function(e) {
249
+ if ( e.which == '27' ) {
250
+ rtb_toggle_booking_form_modal( false );
251
+ rtb_toggle_booking_form_error_modal( false );
252
+ }
253
+ });
254
+
255
+ // Close booking form error modal when background or cancel button is clicked
256
+ rtb_booking_modal_error.click( function(e) {
257
+ if ( $(e.target).is( rtb_booking_modal_error ) || $(e.target).is( rtb_booking_modal_error.find( 'a.button' ) ) ) {
258
+ rtb_toggle_booking_form_error_modal( false );
259
+ }
260
+ });
261
+
262
+ // Submit booking form modal
263
+ rtb_booking_modal_submit.click( function(e) {
264
+
265
+ e.preventDefault();
266
+ e.stopPropagation();
267
+
268
+ if ( $(this).prop( 'disabled' ) === true ) {
269
+ return;
270
+ }
271
+
272
+ // Loading
273
+ rtb_booking_modal_submit.prop( 'disabled', true );
274
+ rtb_booking_modal_cancel.prop( 'disabled', true );
275
+ rtb_booking_modal_action_status.addClass( 'is-visible' );
276
+ rtb_show_action_status( 'loading' );
277
+
278
+ var params = {};
279
+
280
+ params.action = 'rtb-admin-booking-modal';
281
+ params.nonce = rtb_admin.nonce;
282
+ params.booking = rtb_booking_modal.find( 'form' ).serializeArray();
283
+
284
+ var data = $.param( params );
285
+
286
+ var jqhxr = $.post( ajaxurl, data, function( r ) {
287
+
288
+ if ( r.success ) {
289
+
290
+ // Refresh the page so that the new details are visible
291
+ window.location.reload();
292
+
293
+ } else {
294
+
295
+ // Validation failed
296
+ if ( r.data.error == 'invalid_booking_data' ) {
297
+
298
+ // Replace form fields with HTML returned
299
+ rtb_booking_modal_fields.html( r.data.fields );
300
+ rtb_booking_form.init();
301
+ rtb_init_booking_form_modal_fields();
302
+
303
+ // Logged out
304
+ } else if ( r.data.error == 'loggedout' ) {
305
+ rtb_booking_modal_fields.after( '<div class="rtb-error">' + r.data.msg + '</div>' );
306
+
307
+ // Unspecified error
308
+ } else {
309
+ rtb_booking_modal_fields.after( '<div class="rtb-error">' + rtb_admin.strings.error_unspecified + '</div>' );
310
+ }
311
+
312
+ rtb_booking_modal_cancel.prop( 'disabled', false );
313
+ rtb_booking_modal_submit.prop( 'disabled', false );
314
+ }
315
+
316
+ rtb_show_action_status( r.success );
317
+
318
+ // Hide result status icon after a few seconds
319
+ setTimeout( function() {
320
+ rtb_booking_modal.find( '.action-status' ).removeClass( 'is-visible' );
321
+ }, 4000 );
322
+ });
323
+ });
324
+
325
// Show the addons
326
if ( $( '#rtb-addons' ).length ) {
327
+
328
var rtbAddons = {
329
330
el: $( '#rtb-addons' ),
348
rtbAddons.showError( r );
349
}
350
});
351
+
352
353
});
354
},
389
} else if ( typeof addon.img !== 'undefind' ) {
390
html += '<img src="' + addon.img + '">';
391
}
392
+
393
html += '<h3>' + addon.title + '</h3>';
394
395
html += '<div class="details">';
396
+
397
if ( typeof addon.description !== 'undefined' ) {
398
html += '<div class="description">' + addon.description + '</div>';
399
}
400
+
401
if ( typeof addon.status !== 'undefined' ) {
402
403
html += '<div class="action">';
404
+
405
if ( addon.status === 'released' && typeof addon.url !== 'undefined' ) {
406
html += '<a href="' + addon.url + '" class="button button-primary" target="_blank">';
407
422
423
html += '</div>'; // .action
424
}
425
+
426
html += '</div>'; // .details
427
428
html += '</div>'; // .addon
430
return html;
431
}
432
};
433
+
434
rtbAddons.load();
435
}
436
assets/js/booking-form.js CHANGED
@@ -1,138 +1,170 @@
1
/* Javascript for Restaurant Reservations booking form */
2
jQuery(document).ready(function ($) {
3
4
/**
5
- * Scroll to the first error message on the booking form
6
*/
7
- if ( $( '.rtb-booking-form .rtb-error' ).length ) {
8
- $('html, body').animate({
9
- scrollTop: $( '.rtb-booking-form .rtb-error' ).first().offset().top + -40
10
- }, 500);
11
- }
12
13
- /**
14
- * Show the message field on the booking form
15
- */
16
- $( '.rtb-booking-form .add-message a' ).click( function() {
17
- $(this).hide();
18
- $(this).parent().siblings( '.message' ).addClass( 'message-open' );
19
20
- return false;
21
- });
22
23
- /**
24
- * Enable datepickers on load
25
- */
26
- if ( typeof rtb_pickadate !== 'undefined' ) {
27
-
28
- // Declare datepicker
29
- var date_input = $( '#rtb-date' ).pickadate({
30
- format: rtb_pickadate.date_format,
31
- formatSubmit: 'yyyy/mm/dd',
32
- hiddenName: true,
33
- min: true,
34
- container: 'body',
35
-
36
- // Select the value when loaded if a value has been set
37
- onStart: function() {
38
- if ( $( '#rtb-date' ).val() !== '' ) {
39
- var date = new Date( $( '#rtb-date' ).val() );
40
- if ( Object.prototype.toString.call( date ) === "[object Date]" ) {
41
- this.set( 'select', date );
42
- }
43
- }
44
- }
45
});
46
47
- // Declare timepicker
48
- var time_input = $( '#rtb-time' ).pickatime({
49
- format: rtb_pickadate.time_format,
50
- formatSubmit: 'h:i A',
51
- hiddenName: true,
52
- interval: parseInt( rtb_pickadate.time_interval, 10 ),
53
- container: 'body',
54
-
55
- // Select the value when loaded if a value has been set
56
- onStart: function() {
57
- if ( $( '#rtb-time' ).val() !== '' ) {
58
- var today = new Date();
59
- var today_date = today.getFullYear() + '/' + ( today.getMonth() + 1 ) + '/' + today.getDate();
60
- var time = new Date( today_date + ' ' + $( '#rtb-time' ).val() );
61
- if ( Object.prototype.toString.call( time ) === "[object Date]" ) {
62
- this.set( 'select', time );
63
}
64
}
65
- }
66
- });
67
68
- var datepicker = date_input.pickadate( 'picker' );
69
- var timepicker = time_input.pickatime( 'picker' );
70
71
- if ( typeof datepicker == 'undefined' ) {
72
- return;
73
- }
74
75
- // Pass conditional configuration parameters
76
- if ( rtb_pickadate.disable_dates.length ) {
77
-
78
- // Update weekday dates if start of the week has been modified
79
- if ( typeof datepicker.component.settings.firstDay == 'number' ) {
80
- var weekday_num = 0;
81
- for ( var disable_key in rtb_pickadate.disable_dates ) {
82
- if ( typeof rtb_pickadate.disable_dates[disable_key] == 'number' ) {
83
- weekday_num = rtb_pickadate.disable_dates[disable_key] - datepicker.component.settings.firstDay;
84
- if ( weekday_num < 1 ) {
85
- weekday_num = 7;
86
}
87
- rtb_pickadate.disable_dates[disable_key] = weekday_num;
88
}
89
}
90
- }
91
92
- datepicker.set( 'disable', rtb_pickadate.disable_dates );
93
- }
94
95
- if ( rtb_pickadate.late_bookings === '1440' ) {
96
- datepicker.set( 'min', 1 );
97
- }
98
99
- // If no date has been set, select today's date if it's a valid
100
- // date. User may opt not to do this in the settings.
101
- if ( $( '#rtb-date' ).val() === '' && !$( '.rtb-booking-form .date .rtb-error' ).length ) {
102
-
103
- if ( rtb_pickadate.date_onload == 'soonest' ) {
104
- datepicker.set( 'select', new Date() );
105
- } else if ( rtb_pickadate.date_onload !== 'empty' ) {
106
- var dateToVerify = datepicker.component.create( new Date() );
107
- var isDisabled = datepicker.component.disabled( dateToVerify );
108
- if ( !isDisabled ) {
109
- datepicker.set( 'select', dateToVerify );
110
}
111
}
112
- }
113
114
- // Update timepicker on pageload and whenever the datepicker is closed
115
- rtb_update_timepicker_range();
116
- datepicker.on( {
117
- close: function() {
118
- rtb_update_timepicker_range();
119
- }
120
- });
121
- }
122
123
- // Update the timepicker's range based on the currently selected date
124
- function rtb_update_timepicker_range() {
125
126
// Reset enabled/disabled rules on this timepicker
127
- timepicker.set( 'enable', false );
128
- timepicker.set( 'disable', false );
129
130
- if ( datepicker.get() === '' ) {
131
- timepicker.set( 'disable', true );
132
return;
133
}
134
135
- selected_date = new Date( datepicker.get( 'select', 'yyyy/mm/dd' ) );
136
var selected_date_year = selected_date.getFullYear();
137
var selected_date_month = selected_date.getMonth();
138
var selected_date_date = selected_date.getDate();
@@ -141,7 +173,7 @@ jQuery(document).ready(function ($) {
141
// times subsequently declared are valid. Any time that doesn't fall
142
// within those declarations is invalid.
143
// See: http://amsul.ca/pickadate.js/time.htm#disable-times-all
144
- var valid_times = [ { from: [0, 0], to: [24, 0] } ];
145
146
// Check if this date is an exception to the rules
147
if ( typeof rtb_pickadate.schedule_closed != 'undefined' ) {
@@ -161,7 +193,7 @@ jQuery(document).ready(function ($) {
161
162
// Closed all day
163
if ( typeof rtb_pickadate.schedule_closed[closed_key].time == 'undefined' ) {
164
- timepicker.set( 'disable', [ true ] );
165
166
return;
167
}
@@ -188,7 +220,7 @@ jQuery(document).ready(function ($) {
188
189
// Exit early if this date is an exception
190
if ( valid_times.length > 1 ) {
191
- timepicker.set( 'disable', valid_times );
192
193
return;
194
}
@@ -221,7 +253,7 @@ jQuery(document).ready(function ($) {
221
222
// Closed all day
223
if ( typeof rtb_pickadate.schedule_open[open_key].time == 'undefined' ) {
224
- timepicker.set( 'disable', [ true ] );
225
226
return;
227
}
@@ -251,7 +283,7 @@ jQuery(document).ready(function ($) {
251
252
// Pass any valid times located
253
if ( valid_times.length > 1 ) {
254
- timepicker.set( 'disable', valid_times );
255
256
return;
257
}
@@ -259,10 +291,36 @@ jQuery(document).ready(function ($) {
259
}
260
261
// Set it to always open if no rules have been defined
262
- timepicker.set( 'enable', true );
263
- timepicker.set( 'disable', false );
264
265
return;
266
- }
267
268
});
1
/* Javascript for Restaurant Reservations booking form */
2
+
3
+ var rtb_booking_form = rtb_booking_form || {};
4
+
5
jQuery(document).ready(function ($) {
6
7
/**
8
+ * Initialize the booking form when loaded
9
*/
10
+ rtb_booking_form.init = function() {
11
12
+ // Scroll to the first error message on the booking form
13
+ if ( $( '.rtb-booking-form .rtb-error' ).length ) {
14
+ $('html, body').animate({
15
+ scrollTop: $( '.rtb-booking-form .rtb-error' ).first().offset().top + -40
16
+ }, 500);
17
+ }
18
19
+ // Show the message field on the booking form
20
+ $( '.rtb-booking-form .add-message a' ).click( function() {
21
+ $(this).hide();
22
+ $(this).parent().siblings( '.message' ).addClass( 'message-open' );
23
24
+ return false;
25
});
26
27
+ // Show the message field on load if not empty
28
+ if ( $.trim( $( '.rtb-booking-form .message textarea' ).val() ) ) {
29
+ $( '.rtb-booking-form .add-message a' ).trigger( 'click' );
30
+ }
31
+
32
+ // Enable datepickers on load
33
+ if ( typeof rtb_pickadate !== 'undefined' ) {
34
+
35
+ // Declare datepicker
36
+ var date_input = $( '#rtb-date' ).pickadate({
37
+ format: rtb_pickadate.date_format,
38
+ formatSubmit: 'yyyy/mm/dd',
39
+ hiddenName: true,
40
+ min: true,
41
+ container: 'body',
42
+
43
+ // Workaround bug in current version of pickadate.js
44
+ // https://github.com/amsul/pickadate.js/issues/609
45
+ onOpen: function() {
46
+ input = document.getElementById( 'rtb-date' );
47
+ window.scrollTo( 0, input.offsetTop );
48
+ },
49
+ onClose: function() {
50
+ input = document.getElementById( 'rtb-date' );
51
+ window.scrollTo( 0, input.offsetTop );
52
+ },
53
+
54
+ // Select the value when loaded if a value has been set
55
+ onStart: function() {
56
+ if ( $( '#rtb-date' ).val() !== '' ) {
57
+ var date = new Date( $( '#rtb-date' ).val() );
58
+ if ( Object.prototype.toString.call( date ) === "[object Date]" ) {
59
+ this.set( 'select', date );
60
+ }
61
}
62
}
63
+ });
64
+
65
+ // Declare timepicker
66
+ var time_input = $( '#rtb-time' ).pickatime({
67
+ format: rtb_pickadate.time_format,
68
+ formatSubmit: 'h:i A',
69
+ hiddenName: true,
70
+ interval: parseInt( rtb_pickadate.time_interval, 10 ),
71
+ container: 'body',
72
+
73
+ // Workaround bug in current version of pickadate.js
74
+ // https://github.com/amsul/pickadate.js/issues/609
75
+ onOpen: function() {
76
+ input = document.getElementById( 'rtb-time' );
77
+ window.scrollTo( 0, input.offsetTop );
78
+ },
79
+ onClose: function() {
80
+ input = document.getElementById( 'rtb-time' );
81
+ window.scrollTo( 0, input.offsetTop );
82
+ },
83
+
84
+ // Select the value when loaded if a value has been set
85
+ onStart: function() {
86
+ if ( $( '#rtb-time' ).val() !== '' ) {
87
+ var today = new Date();
88
+ var today_date = today.getFullYear() + '/' + ( today.getMonth() + 1 ) + '/' + today.getDate();
89
+ var time = new Date( today_date + ' ' + $( '#rtb-time' ).val() );
90
+ if ( Object.prototype.toString.call( time ) === "[object Date]" ) {
91
+ this.set( 'select', time );
92
+ }
93
+ }
94
+ }
95
+ });
96
97
+ rtb_booking_form.datepicker = date_input.pickadate( 'picker' );
98
+ rtb_booking_form.timepicker = time_input.pickatime( 'picker' );
99
100
+ if ( typeof rtb_booking_form.datepicker == 'undefined' ) {
101
+ return;
102
+ }
103
104
+ // Pass conditional configuration parameters
105
+ if ( rtb_pickadate.disable_dates.length ) {
106
+
107
+ // Update weekday dates if start of the week has been modified
108
+ if ( typeof rtb_booking_form.datepicker.component.settings.firstDay == 'number' ) {
109
+ var weekday_num = 0;
110
+ for ( var disable_key in rtb_pickadate.disable_dates ) {
111
+ if ( typeof rtb_pickadate.disable_dates[disable_key] == 'number' ) {
112
+ weekday_num = rtb_pickadate.disable_dates[disable_key] - rtb_booking_form.datepicker.component.settings.firstDay;
113
+ if ( weekday_num < 1 ) {
114
+ weekday_num = 7;
115
+ }
116
+ rtb_pickadate.disable_dates[disable_key] = weekday_num;
117
}
118
}
119
}
120
121
+ rtb_booking_form.datepicker.set( 'disable', rtb_pickadate.disable_dates );
122
+ }
123
124
+ if ( rtb_pickadate.late_bookings === '1440' ) {
125
+ rtb_booking_form.datepicker.set( 'min', 1 );
126
+ }
127
128
+ // If no date has been set, select today's date if it's a valid
129
+ // date. User may opt not to do this in the settings.
130
+ if ( $( '#rtb-date' ).val() === '' && !$( '.rtb-booking-form .date .rtb-error' ).length ) {
131
+
132
+ if ( rtb_pickadate.date_onload == 'soonest' ) {
133
+ rtb_booking_form.datepicker.set( 'select', new Date() );
134
+ } else if ( rtb_pickadate.date_onload !== 'empty' ) {
135
+ var dateToVerify = rtb_booking_form.datepicker.component.create( new Date() );
136
+ var isDisabled = rtb_booking_form.datepicker.component.disabled( dateToVerify );
137
+ if ( !isDisabled ) {
138
+ rtb_booking_form.datepicker.set( 'select', dateToVerify );
139
+ }
140
}
141
}
142
143
+ // Update timepicker on pageload and whenever the datepicker is closed
144
+ rtb_booking_form.update_timepicker_range();
145
+ rtb_booking_form.datepicker.on( {
146
+ close: function() {
147
+ rtb_booking_form.update_timepicker_range();
148
+ }
149
+ });
150
+ }
151
+ };
152
153
+ /**
154
+ * Update the timepicker's range based on the currently selected date
155
+ */
156
+ rtb_booking_form.update_timepicker_range = function() {
157
158
// Reset enabled/disabled rules on this timepicker
159
+ rtb_booking_form.timepicker.set( 'enable', false );
160
+ rtb_booking_form.timepicker.set( 'disable', false );
161
162
+ if ( rtb_booking_form.datepicker.get() === '' ) {
163
+ rtb_booking_form.timepicker.set( 'disable', true );
164
return;
165
}
166
167
+ selected_date = new Date( rtb_booking_form.datepicker.get( 'select', 'yyyy/mm/dd' ) );
168
var selected_date_year = selected_date.getFullYear();
169
var selected_date_month = selected_date.getMonth();
170
var selected_date_date = selected_date.getDate();
173
// times subsequently declared are valid. Any time that doesn't fall
174
// within those declarations is invalid.
175
// See: http://amsul.ca/pickadate.js/time.htm#disable-times-all
176
+ var valid_times = [ rtb_booking_form.get_outer_time_range() ];
177
178
// Check if this date is an exception to the rules
179
if ( typeof rtb_pickadate.schedule_closed != 'undefined' ) {
193
194
// Closed all day
195
if ( typeof rtb_pickadate.schedule_closed[closed_key].time == 'undefined' ) {
196
+ rtb_booking_form.timepicker.set( 'disable', [ true ] );
197
198
return;
199
}
220
221
// Exit early if this date is an exception
222
if ( valid_times.length > 1 ) {
223
+ rtb_booking_form.timepicker.set( 'disable', valid_times );
224
225
return;
226
}
253
254
// Closed all day
255
if ( typeof rtb_pickadate.schedule_open[open_key].time == 'undefined' ) {
256
+ rtb_booking_form.timepicker.set( 'disable', [ true ] );
257
258
return;
259
}
283
284
// Pass any valid times located
285
if ( valid_times.length > 1 ) {
286
+ rtb_booking_form.timepicker.set( 'disable', valid_times );
287
288
return;
289
}
291
}
292
293
// Set it to always open if no rules have been defined
294
+ rtb_booking_form.timepicker.set( 'enable', true );
295
+ rtb_booking_form.timepicker.set( 'disable', false );
296
297
return;
298
+ };
299
+
300
+ /**
301
+ * Get the outer times to exclude based on the time interval
302
+ *
303
+ * This is a work-around for a bug in pickadate.js
304
+ * https://github.com/amsul/pickadate.js/issues/614
305
+ */
306
+ rtb_booking_form.get_outer_time_range = function() {
307
+
308
+ var interval = rtb_booking_form.timepicker.get( 'interval' );
309
+
310
+ var hour = 24;
311
+
312
+ while ( interval >= 60 ) {
313
+ hour--;
314
+ interval -= 60;
315
+ }
316
+
317
+ if ( interval > 0 ) {
318
+ hour--;
319
+ interval = 60 - interval;
320
+ }
321
+
322
+ return { from: [0, 0], to: [ hour, interval ] };
323
+ };
324
325
+ rtb_booking_form.init();
326
});
includes/Addons.class.php CHANGED
@@ -78,20 +78,29 @@ class rtbAddons {
78
*/
79
public function enqueue_admin_assets() {
80
81
$screen = get_current_screen();
82
- if ( $screen->base == 'bookings_page_rtb-addons' ) {
83
wp_localize_script(
84
'rtb-admin',
85
'rtb_addons',
86
array(
87
'nonce' => wp_create_nonce( 'rtb-addons' ),
88
'strings' => array(
89
- 'loading' => __( 'Loading', RTB_TEXTDOMAIN ),
90
- 'error_unknown' => _x( 'An unknown error occured.', 'Error message when retrieving list of addons', RTB_TEXTDOMAIN ),
91
- 'installed' => _x( 'Already Installed', 'Label for an addon that is already installed and activated.', RTB_TEXTDOMAIN ),
92
- 'coming_soon' => _x( 'Coming Soon', 'Label for an addon that is not yet released.', RTB_TEXTDOMAIN ),
93
- 'free' => _x( 'Free', 'Label for an addon that is free.', RTB_TEXTDOMAIN ),
94
- 'learn_more' => _x( 'Get It', 'Label for an addon that is released.', RTB_TEXTDOMAIN ),
95
)
96
)
97
);
@@ -105,8 +114,8 @@ class rtbAddons {
105
106
add_submenu_page(
107
'rtb-bookings',
108
- _x( 'Addons', 'Title of addons page', RTB_TEXTDOMAIN ),
109
- _x( 'Addons', 'Title of addons page in the admin menu', RTB_TEXTDOMAIN ),
110
'manage_options',
111
'rtb-addons',
112
array( $this, 'show_admin_addons_page' )
@@ -123,7 +132,7 @@ class rtbAddons {
123
?>
124
125
<div class="wrap">
126
- <h2><?php _e( 'Addons for Restaurant Reservations', RTB_TEXTDOMAIN ); ?></h2>
127
<?php do_action( 'rtb_addons_pre' ); ?>
128
<div id="rtb-addons">
129
<div class="rtb-loading">
@@ -145,7 +154,7 @@ class rtbAddons {
145
wp_send_json_error(
146
array(
147
'error' => 'loggedout',
148
- 'msg' => __( 'You have been logged out. Please login again to retrieve the addons.', RTB_TEXTDOMAIN ),
149
)
150
);
151
}
@@ -161,7 +170,7 @@ class rtbAddons {
161
wp_send_json_error(
162
array(
163
'error' => 'nopriv',
164
- 'msg' => __( 'You do not have permission to access this page. Please login to an administrator account if you have one.', RTB_TEXTDOMAIN ),
165
)
166
);
167
}
@@ -170,14 +179,16 @@ class rtbAddons {
170
$ch = curl_init();
171
curl_setopt( $ch, CURLOPT_URL, $url );
172
curl_setopt( $ch, CURLOPT_HTTPHEADER, array( 'Content-Type: application/json' ) );
173
- curl_setopt( $ch, CURLOPT_USERAGENT, 'PHP-MCAPI/2.0' );
174
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
175
curl_setopt( $ch, CURLOPT_TIMEOUT, 10 );
176
$result = curl_exec($ch);
177
curl_close($ch);
178
-
179
- } else {
180
$result = @file_get_contents( $url );
181
}
182
183
if ( $result ) {
@@ -187,7 +198,7 @@ class rtbAddons {
187
wp_send_json_error(
188
array(
189
'error' => 'apifailed',
190
- 'msg' => __( 'The addons list could not be retrieved. Please <a href="">try again</a>. If the problem persists over time, please report it on the <a href="http://wordpress.org/support/plugin/restaurant-reservations" target="_blank">support forums</a>.', RTB_TEXTDOMAIN ),
191
)
192
);
193
}
78
*/
79
public function enqueue_admin_assets() {
80
81
+ // Use the page reference in $admin_page_hooks because
82
+ // it changes in SOME hooks when it is translated.
83
+ // https://core.trac.wordpress.org/ticket/18857
84
+ global $admin_page_hooks;
85
+
86
$screen = get_current_screen();
87
+ if ( empty( $screen ) || empty( $admin_page_hooks['rtb-bookings'] ) ) {
88
+ return;
89
+ }
90
+
91
+ if ( $screen->base == $admin_page_hooks['rtb-bookings'] . '_page_rtb-addons' ) {
92
wp_localize_script(
93
'rtb-admin',
94
'rtb_addons',
95
array(
96
'nonce' => wp_create_nonce( 'rtb-addons' ),
97
'strings' => array(
98
+ 'loading' => __( 'Loading', 'restaurant-reservations' ),
99
+ 'error_unknown' => _x( 'An unknown error occured.', 'Error message when retrieving list of addons', 'restaurant-reservations' ),
100
+ 'installed' => _x( 'Already Installed', 'Label for an addon that is already installed and activated.', 'restaurant-reservations' ),
101
+ 'coming_soon' => _x( 'Coming Soon', 'Label for an addon that is not yet released.', 'restaurant-reservations' ),
102
+ 'free' => _x( 'Free', 'Label for an addon that is free.', 'restaurant-reservations' ),
103
+ 'learn_more' => _x( 'Get It', 'Label for an addon that is released.', 'restaurant-reservations' ),
104
)
105
)
106
);
114
115
add_submenu_page(
116
'rtb-bookings',
117
+ _x( 'Addons', 'Title of addons page', 'restaurant-reservations' ),
118
+ _x( 'Addons', 'Title of addons page in the admin menu', 'restaurant-reservations' ),
119
'manage_options',
120
'rtb-addons',
121
array( $this, 'show_admin_addons_page' )
132
?>
133
134
<div class="wrap">
135
+ <h2><?php _e( 'Addons for Restaurant Reservations', 'restaurant-reservations' ); ?></h2>
136
<?php do_action( 'rtb_addons_pre' ); ?>
137
<div id="rtb-addons">
138
<div class="rtb-loading">
154
wp_send_json_error(
155
array(
156
'error' => 'loggedout',
157
+ 'msg' => __( 'You have been logged out. Please login again to retrieve the addons.', 'restaurant-reservations' ),
158
)
159
);
160
}
170
wp_send_json_error(
171
array(
172
'error' => 'nopriv',
173
+ 'msg' => __( 'You do not have permission to access this page. Please login to an administrator account if you have one.', 'restaurant-reservations' ),
174
)
175
);
176
}
179
$ch = curl_init();
180
curl_setopt( $ch, CURLOPT_URL, $url );
181
curl_setopt( $ch, CURLOPT_HTTPHEADER, array( 'Content-Type: application/json' ) );
182
+ curl_setopt( $ch, CURLOPT_USERAGENT, 'PHP-MCAPI/2.0' );
183
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
184
curl_setopt( $ch, CURLOPT_TIMEOUT, 10 );
185
$result = curl_exec($ch);
186
curl_close($ch);
187
+
188
+ } elseif ( ini_get( 'allow_url_fopen' ) ) {
189
$result = @file_get_contents( $url );
190
+ } else {
191
+ $result = @file_get_contents( RTB_PLUGIN_DIR . '/assets/addons-backup.json' );
192
}
193
194
if ( $result ) {
198
wp_send_json_error(
199
array(
200
'error' => 'apifailed',
201
+ 'msg' => __( 'The addons list could not be retrieved. Please <a href="">try again</a>. If the problem persists over time, please report it on the <a href="http://wordpress.org/support/plugin/restaurant-reservations" target="_blank">support forums</a>.', 'restaurant-reservations' ),
202
)
203
);
204
}
includes/AdminBookings.class.php ADDED
@@ -0,0 +1,455 @@
1
+ <?php
2
+ if ( !defined( 'ABSPATH' ) ) exit;
3
+
4
+ if ( !class_exists( 'rtbAdminBookings' ) ) {
5
+ /**
6
+ * Class to handle the admin bookings page for Restaurant Reservations
7
+ *
8
+ * @since 1.3
9
+ */
10
+ class rtbAdminBookings {
11
+
12
+ public function __construct() {
13
+
14
+ // Add the admin menu
15
+ add_action( 'admin_menu', array( $this, 'add_menu_page' ) );
16
+
17
+ // Print the booking form modal
18
+ add_action( 'admin_footer-toplevel_page_rtb-bookings', array( $this, 'print_booking_form_modal' ) );
19
+
20
+ // Receive Ajax requests
21
+ add_action( 'wp_ajax_nopriv_rtb-admin-booking-modal' , array( $this , 'nopriv_ajax' ) );
22
+ add_action( 'wp_ajax_rtb-admin-booking-modal', array( $this, 'booking_modal_ajax' ) );
23
+ add_action( 'wp_ajax_nopriv_rtb-admin-trash-booking' , array( $this , 'nopriv_ajax' ) );
24
+ add_action( 'wp_ajax_rtb-admin-trash-booking', array( $this, 'trash_booking_ajax' ) );
25
+
26
+ // Validate post status and notification fields
27
+ add_action( 'rtb_validate_booking_submission', array( $this, 'validate_admin_fields' ) );
28
+
29
+ // Set post status when adding to the database
30
+ add_filter( 'rtb_insert_booking_data', array( $this, 'insert_booking_data' ), 10, 2 );
31
+
32
+ }
33
+
34
+ /**
35
+ * Add the top-level admin menu page
36
+ * @since 0.0.1
37
+ */
38
+ public function add_menu_page() {
39
+
40
+ add_menu_page(
41
+ _x( 'Bookings', 'Title of admin page that lists bookings', 'restaurant-reservations' ),
42
+ _x( 'Bookings', 'Title of bookings admin menu item', 'restaurant-reservations' ),
43
+ 'manage_bookings',
44
+ 'rtb-bookings',
45
+ array( $this, 'show_admin_bookings_page' ),
46
+ 'dashicons-calendar',
47
+ '26.2987'
48
+ );
49
+
50
+ }
51
+
52
+ /**
53
+ * Display the admin bookings page
54
+ * @since 0.0.1
55
+ */
56
+ public function show_admin_bookings_page() {
57
+
58
+ require_once( RTB_PLUGIN_DIR . '/includes/WP_List_Table.BookingsTable.class.php' );
59
+ $bookings_table = new rtbBookingsTable();
60
+ $bookings_table->prepare_items();
61
+ ?>
62
+
63
+ <div class="wrap">
64
+ <h2>
65
+ <?php _e( 'Restaurant Bookings', 'restaurant-reservations' ); ?>
66
+ <a href="#" class="add-new-h2 add-booking"><?php _e( 'Add New', 'restaurant-reservations' ); ?></a>
67
+ </h2>
68
+
69
+ <?php do_action( 'rtb_bookings_table_top' ); ?>
70
+ <form id="rtb-bookings-table" method="POST" action="">
71
+ <input type="hidden" name="post_type" value="<?php echo RTB_BOOKING_POST_TYPE; ?>" />
72
+ <input type="hidden" name="page" value="rtb-bookings">
73
+
74
+ <?php $bookings_table->views(); ?>
75
+ <?php $bookings_table->advanced_filters(); ?>
76
+ <?php $bookings_table->display(); ?>
77
+ </form>
78
+ <?php do_action( 'rtb_bookings_table_btm' ); ?>
79
+ </div>
80
+
81
+ <?php
82
+ }
83
+
84
+ /**
85
+ * Print the booking form modal container on the admin bookings page
86
+ * @since 0.0.1
87
+ */
88
+ public function print_booking_form_modal() {
89
+ ?>
90
+
91
+ <div id="rtb-booking-modal" class="rtb-admin-modal">
92
+ <div class="rtb-booking-form rtb-container">
93
+ <form method="POST">
94
+ <input type="hidden" name="action" value="admin_booking_request">
95
+ <input type="hidden" name="ID" value="">
96
+
97
+ <?php
98
+ /**
99
+ * The generated fields are wrapped in a div so we can
100
+ * replace its contents with an HTML blob passed back from
101
+ * an Ajax request. This way the field data and error
102
+ * messages are always populated from the same server-side
103
+ * code.
104
+ */
105
+ ?>
106
+ <div id="rtb-booking-form-fields">
107
+ <?php echo $this->print_booking_form_fields(); ?>
108
+ </div>
109
+
110
+ <button type="submit" class="button-primary">
111
+ <?php _e( 'Add Booking', 'restaurant-reservations' ); ?>
112
+ </button>
113
+ <a href="#" class="button" id="rtb-cancel-booking-modal">
114
+ <?php _e( 'Cancel', 'restaurant-reservations' ); ?>
115
+ </a>
116
+ <div class="action-status">
117
+ <span class="spinner loading"></span>
118
+ <span class="dashicons dashicons-no-alt error"></span>
119
+ <span class="dashicons dashicons-yes success"></span>
120
+ </div>
121
+ </form>
122
+ </div>
123
+ </div>
124
+ <!-- Restaurant Reservations error message modal -->
125
+ <div id="rtb-error-modal" class="rtb-admin-modal">
126
+ <div class="rtb-error rtb-container">
127
+ <div class="rtb-error-msg"></div>
128
+ <a href="#" class="button"><?php _e( 'Close', 'restaurant-reservations' ); ?></a>
129
+ </div>
130
+ </div>
131
+
132
+ <?php
133
+ }
134
+
135
+ /**
136
+ * Retrieve booking form fields used in the admin booking modal. These
137
+ * fields are also passed back with ajax requests since they render error
138
+ * messages and populate fields with validated data.
139
+ * @since 0.0.1
140
+ */
141
+ public function print_booking_form_fields() {
142
+
143
+ global $rtb_controller;
144
+
145
+ // Add post status and notification fields to admin booking form
146
+ add_filter( 'rtb_booking_form_fields', array( $this, 'add_admin_fields' ), 20, 2 );
147
+
148
+ // Retrieve the form fields
149
+ $fields = $rtb_controller->settings->get_booking_form_fields( $rtb_controller->request );
150
+
151
+ ob_start();
152
+ ?>
153
+
154
+ <?php foreach( $fields as $fieldset => $contents ) : ?>
155
+ <fieldset class="<?php echo $fieldset; ?>">
156
+ <?php
157
+ foreach( $contents['fields'] as $slug => $field ) {
158
+
159
+ $args = empty( $field['callback_args'] ) ? null : $field['callback_args'];
160
+
161
+ call_user_func( $field['callback'], $slug, $field['title'], $field['request_input'], $args );
162
+ }
163
+ ?>
164
+ </fieldset>
165
+ <?php endforeach;
166
+
167
+ // Remove the admin fields filter
168
+ remove_filter( 'rtb_booking_form_fields', array( $this, 'add_admin_fields' ) );
169
+
170
+ return ob_get_clean();
171
+ }
172
+
173
+ /**
174
+ * Add the post status and notifications fields to the booking form fields
175
+ * @since 1.3
176
+ */
177
+ public function add_admin_fields( $fields, $request ) {
178
+
179
+ if ( !is_admin() || !current_user_can( 'manage_bookings' ) ) {
180
+ return $fields;
181
+ }
182
+
183
+ global $rtb_controller;
184
+
185
+ // Get all valid booking statuses
186
+ $booking_statuses = array();
187
+ foreach( $rtb_controller->cpts->booking_statuses as $status => $args ) {
188
+ $booking_statuses[ $status ] = $args['label'];
189
+ }
190
+
191
+ $fields['admin'] = array(
192
+ 'fields' => array(
193
+ 'post-status' => array(
194
+ 'title' => __( 'Booking Status', 'restaurant-reservations' ),
195
+ 'request_input' => empty( $request->post_status ) ? '' : $request->post_status,
196
+ 'callback' => 'rtb_print_form_select_field',
197
+ 'callback_args' => array(
198
+ 'options' => $booking_statuses,
199
+ )
200
+ ),
201
+ 'notifications' => array(
202
+ 'title' => __( 'Send notifications', 'restaurant-reservations' ),
203
+ 'request_input' => empty( $request->send_notifications ) ? false : $request->send_notifications,
204
+ 'callback' => array( $this, 'print_form_send_notifications_field' ),
205
+ 'callback_args' => array(
206
+ 'description' => array(
207
+ 'prompt' => __( 'Learn more', 'restaurant-reservations' ),
208
+ 'text' => __( "When adding a booking or changing a booking's status with this form, no email notifications will be sent. Check this option if you want to send email notifications.", 'restaurant-reservations' ),
209
+ ),
210
+ ),
211
+ ),
212
+ ),
213
+ );
214
+
215
+ return $fields;
216
+ }
217
+
218
+
219
+ /**
220
+ * Print a field to toggle notifications when adding/editing a booking from
221
+ * the admin
222
+ * @since 1.3
223
+ */
224
+ function print_form_send_notifications_field( $slug, $title, $value, $args ) {
225
+
226
+ $slug = esc_attr( $slug );
227
+ $title = esc_attr( $title );
228
+ $value = (bool) $value;
229
+ $description = empty( $args['description'] ) || empty( $args['description']['prompt'] ) || empty( $args['description']['text'] ) ? null : $args['description'];
230
+ ?>
231
+
232
+ <div class="<?php echo $slug; ?>">
233
+ <?php echo rtb_print_form_error( $slug ); ?>
234
+ <label>
235
+ <input type="checkbox" name="rtb-<?php echo esc_attr( $slug ); ?>" value="1"<?php checked( $value ); ?>>
236
+ <?php echo $title; ?>
237
+ <?php if ( !empty( $description ) ) : ?>
238
+ <a href="#" class="rtb-description-prompt">
239
+ <?php echo $description['prompt']; ?>
240
+ </a>
241
+ <?php endif; ?>
242
+ </label>
243
+ <?php if ( !empty( $description ) ) : ?>
244
+ <div class="rtb-description">
245
+ <?php echo $description['text']; ?>
246
+ </div>
247
+ <?php endif; ?>
248
+ </div>
249
+
250
+ <?php
251
+ }
252
+
253
+ /**
254
+ * Handle ajax requests from the admin bookings area from logged out users
255
+ * @since 1.3
256
+ */
257
+ public function nopriv_ajax() {
258
+
259
+ wp_send_json_error(
260
+ array(
261
+ 'error' => 'loggedout',
262
+ 'msg' => sprintf( __( 'You have been logged out. Please %slogin again%s.', 'restaurant-reservations' ), '<a href="' . wp_login_url( admin_url( 'admin.php?page=rtb-bookings&status=pending' ) ) . '">', '</a>' ),
263
+ )
264
+ );
265
+ }
266
+
267
+ /**
268
+ * Handle ajax requests from the admin bookings area
269
+ * @since 1.3
270
+ */
271
+ public function booking_modal_ajax() {
272
+
273
+ global $rtb_controller;
274
+
275
+ // Authenticate request
276
+ if ( !check_ajax_referer( 'rtb-admin', 'nonce' ) || !current_user_can( 'manage_bookings' ) ) {
277
+ $this->nopriv_ajax();
278
+ }
279
+
280
+ // Retrieve a booking with a GET request
281
+ if ( !empty( $_GET['booking'] ) && !empty( $_GET['booking']['ID'] ) ) {
282
+
283
+ $id = (int) $_GET['booking']['ID'];
284
+
285
+ require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
286
+ $rtb_controller->request = new rtbBooking();
287
+ $result = $rtb_controller->request->load_post( $id );
288
+
289
+ if ( $result ) {
290
+
291
+ // Don't allow editing of trashed bookings. This wil force
292
+ // appropriate use of the trash status and (hopefully) prevent
293
+ // mistakes in booking management.
294
+ if ( $rtb_controller->request->post_status == 'trash' ) {
295
+ wp_send_json_error(
296
+ array(
297
+ 'error' => 'booking_trashed',
298
+ 'msg' => sprintf( __( 'This booking has been sent to the %sTrash%s where it can not be edited. Set the booking to Pending or Confirmed to edit it.', 'restaurant-reservations' ), '<a href="' . admin_url( 'admin.php?page=rtb-bookings&status=trash' ) . '">', '</a>' ),
299
+ )
300
+ );
301
+ }
302
+
303
+ $rtb_controller->request->prepare_request_data();
304
+ wp_send_json_success(
305
+ array(
306
+ 'booking' => $rtb_controller->request,
307
+ 'fields' => $this->print_booking_form_fields(),
308
+ )
309
+ );
310
+
311
+ } else {
312
+ wp_send_json_error(
313
+ array(
314
+ 'error' => 'booking_not_found',
315
+ 'msg' => __( 'The booking could not be retrieved. Please reload the page and try again.', 'restaurant-reservations' ),
316
+ )
317
+ );
318
+ }
319
+
320
+ // Insert or update a booking with a POST request
321
+ } elseif ( !empty( $_POST['booking'] ) ) {
322
+
323
+ // Set up $_POST object for validation
324
+ foreach( $_POST['booking'] as $field ) {
325
+ $_POST[ $field['name'] ] = $field['value'];
326
+ }
327
+
328
+ require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
329
+ $rtb_controller->request = new rtbBooking();
330
+
331
+ // Add an ID if we're updating the post
332
+ if ( !empty( $_POST['ID'] ) ) {
333
+ $rtb_controller->request->ID = (int) $_POST['ID'];
334
+ }
335
+
336
+ // Disable notifications
337
+ $this->maybe_disable_notifications();
338
+
339
+ $result = $rtb_controller->request->insert_booking();
340
+
341
+ if ( $result ) {
342
+ wp_send_json_success(
343
+ array(
344
+ 'booking' => $rtb_controller->request,
345
+ )
346
+ );
347
+ } else {
348
+ wp_send_json_error(
349
+ array(
350
+ 'error' => 'invalid_booking_data',
351
+ 'booking' => $rtb_controller->request,
352
+ 'fields' => $this->print_booking_form_fields(),
353
+ )
354
+ );
355
+ }
356
+ }
357
+
358
+ // Fallback to a valid error
359
+ wp_send_json_error();
360
+ }
361
+
362
+ /**
363
+ * Set booking status to trash
364
+ * @since 1.3
365
+ */
366
+ public function trash_booking_ajax() {
367
+
368
+ global $rtb_controller;
369
+
370
+ // Authenticate request
371
+ if ( !check_ajax_referer( 'rtb-admin', 'nonce' ) || !current_user_can( 'manage_bookings' ) || empty( $_POST['booking'] ) ) {
372
+ $this->nopriv_ajax();
373
+ }
374
+
375
+ $id = (int) $_POST['booking'];
376
+
377
+ $result = wp_trash_post( $id );
378
+
379
+ if ( $result === false ) {
380
+ wp_send_json_error(
381
+ array(
382
+ 'error' => 'trash_failed',
383
+ 'msg' => __( 'Unable to trash this post. Please try again. If you continue to have trouble, please refresh the page.', 'restaurant-reservations' ),
384
+ )
385
+ );
386
+
387
+ } else {
388
+ wp_send_json_success(
389
+ array(
390
+ 'booking' => $id,
391
+ )
392
+ );
393
+ }
394
+
395
+
396
+
397
+ }
398
+
399
+ /**
400
+ * Validate post status and notification fields
401
+ * @since 1.3
402
+ */
403
+ public function validate_admin_fields( $booking ) {
404
+
405
+ // Only validate in the admin
406
+ if ( !$_POST['action'] || $_POST['action'] !== 'admin_booking_request' ) {
407
+ return;
408
+ }
409
+
410
+ global $rtb_controller;
411
+
412
+ // Disable Notifications
413
+ $booking->send_notifications = empty( $_POST['rtb-notifications'] ) ? false : true;
414
+ }
415
+
416
+ /**
417
+ * Adjust post status when adding/editing a booking from the admin area
418
+ * @since 1.3
419
+ */
420
+ public function insert_booking_data( $args, $booking ) {
421
+
422
+ // Validate user request
423
+ if ( empty( $_POST['action'] ) || $_POST['action'] !== 'admin_booking_request' || !current_user_can( 'manage_bookings' ) ) {
424
+ return $args;
425
+ }
426
+
427
+ if ( !empty( $booking->post_status ) ) {
428
+ $args['post_status'] = $booking->post_status;
429
+ }
430
+
431
+ return $args;
432
+ }
433
+
434
+ /**
435
+ * Maybe disable notifications when adding/editing bookings from the
436
+ * admin booking modal
437
+ * @since 1.3
438
+ */
439
+ public function maybe_disable_notifications() {
440
+
441
+ // Don't disable notifications if they have opted to send them
442
+ if ( !empty( $_POST['rtb-notifications'] ) ) {
443
+ return;
444
+ }
445
+
446
+ // Disable all notifications. This filter is here in case a
447
+ // third-party sets up a notification that they don't want to be
448
+ // disabled even if the user has opted not to send notifications
449
+ // To exempt a notification, hook into the filter and copy it
450
+ // from $rtb_notifications to the empty array.
451
+ global $rtb_controller;
452
+ $rtb_controller->notifications->notifications = apply_filters( 'rtb_admin_disabled_notifications_exemption', array(), $rtb_controller->notifications->notifications );
453
+ }
454
+ }
455
+ } // endif;
includes/Booking.class.php CHANGED
@@ -60,7 +60,7 @@ class rtbBooking {
60
$this->ID = $post->ID;
61
$this->name = $post->post_title;
62
$this->date = $post->post_date;
63
- $this->message = apply_filters( 'the_content', $post->post_content );
64
$this->post_status = $post->post_status;
65
66
$this->load_post_metadata();
@@ -95,6 +95,22 @@ class rtbBooking {
95
$this->date_submission = $meta['date_submission'];
96
}
97
98
/**
99
* Format date
100
* @since 0.0.1
@@ -121,6 +137,12 @@ class rtbBooking {
121
122
$this->request_processed = true;
123
124
$this->validate_submission();
125
if ( $this->is_valid_submission() === false ) {
126
return false;
@@ -132,7 +154,7 @@ class rtbBooking {
132
$this->request_inserted = true;
133
}
134
135
- do_action( 'rtb_insert_booking', $this );
136
137
return true;
138
}
@@ -153,7 +175,7 @@ class rtbBooking {
153
$this->validation_errors[] = array(
154
'field' => 'date',
155
'error_msg' => 'Booking request missing date',
156
- 'message' => __( 'Please enter the date you would like to book.', RTB_TEXTDOMAIN ),
157
);
158
159
} else {
@@ -163,7 +185,7 @@ class rtbBooking {
163
$this->validation_errors[] = array(
164
'field' => 'date',
165
'error_msg' => $e->getMessage(),
166
- 'message' => __( 'The date you entered is not valid. Please select from one of the dates in the calendar.', RTB_TEXTDOMAIN ),
167
);
168
}
169
}
@@ -174,7 +196,7 @@ class rtbBooking {
174
$this->validation_errors[] = array(
175
'field' => 'time',
176
'error_msg' => 'Booking request missing time',
177
- 'message' => __( 'Please enter the time you would like to book.', RTB_TEXTDOMAIN ),
178
);
179
180
} else {
@@ -184,7 +206,7 @@ class rtbBooking {
184
$this->validation_errors[] = array(
185
'field' => 'time',
186
'error_msg' => $e->getMessage(),
187
- 'message' => __( 'The time you entered is not valid. Please select from one of the times provided.', RTB_TEXTDOMAIN ),
188
);
189
}
190
}
@@ -201,7 +223,7 @@ class rtbBooking {
201
$this->validation_errors[] = array(
202
'field' => 'time',
203
'error_msg' => 'Booking request too far in the future',
204
- 'message' => sprintf( __( 'Sorry, bookings can not be made more than %s days in advance.', RTB_TEXTDOMAIN ), $early_bookings ),
205
);
206
}
207
}
@@ -212,7 +234,7 @@ class rtbBooking {
212
$this->validation_errors[] = array(
213
'field' => 'time',
214
'error_msg' => 'Booking request in the past',
215
- 'message' => __( 'Sorry, bookings can not be made in the past.', RTB_TEXTDOMAIN ),
216
);
217
}
218
@@ -220,11 +242,11 @@ class rtbBooking {
220
$late_bookings_seconds = $late_bookings * 60; // Late bookings allowance in seconds
221
if ( $request->format( 'U' ) < ( current_time( 'timestamp' ) + $late_bookings_seconds ) ) {
222
if ( $late_bookings >= 1440 ) {
223
- $late_bookings_message = sprintf( __( 'Sorry, bookings must be made more than %s days in advance.', RTB_TEXTDOMAIN ), $late_bookings / 1440 );
224
} elseif ( $late_bookings >= 60 ) {
225
- $late_bookings_message = sprintf( __( 'Sorry, bookings must be made more than %s hours in advance.', RTB_TEXTDOMAIN ), $late_bookings / 60 );
226
} else {
227
- $late_bookings_message = sprintf( __( 'Sorry, bookings must be made more than %s mings in advance.', RTB_TEXTDOMAIN ), $late_bookings );
228
}
229
$this->validation_errors[] = array(
230
'field' => 'time',
@@ -263,7 +285,7 @@ class rtbBooking {
263
$this->validation_errors[] = array(
264
'field' => 'date',
265
'error_msg' => 'Booking request made on invalid date or time in an exception rule',
266
- 'message' => __( 'Sorry, no bookings are being accepted then.', RTB_TEXTDOMAIN ),
267
);
268
}
269
}
@@ -315,13 +337,13 @@ class rtbBooking {
315
$this->validation_errors[] = array(
316
'field' => 'date',
317
'error_msg' => 'Booking request made on an invalid date',
318
- 'message' => __( 'Sorry, no bookings are being accepted on that date.', RTB_TEXTDOMAIN ),
319
);
320
} elseif ( !$time_is_valid ) {
321
$this->validation_errors[] = array(
322
'field' => 'time',
323
'error_msg' => 'Booking request made at an invalid time',
324
- 'message' => __( 'Sorry, no bookings are being accepted at that time.', RTB_TEXTDOMAIN ),
325
);
326
}
327
}
@@ -343,31 +365,27 @@ class rtbBooking {
343
$this->validation_errors[] = array(
344
'field' => 'name',
345
'post_variable' => $this->name,
346
- 'message' => __( 'Please enter a name for this booking.', RTB_TEXTDOMAIN ),
347
);
348
}
349
350
// Party
351
- $this->party = empty( $_POST['rtb-party'] ) ? '' : sanitize_text_field( stripslashes_deep( $_POST['rtb-party'] ) );
352
if ( empty( $this->party ) ) {
353
$this->validation_errors[] = array(
354
'field' => 'party',
355
'post_variable' => $this->party,
356
- 'message' => __( 'Please let us know how many people will be in your party.', RTB_TEXTDOMAIN ),
357
);
358
359
// Check party size
360
- // If the user has entered a party size like "five" or "3-4" or
361
- // something like that, let's go ahead and allow it. Restaurant manager
362
- // can choose to accept or deny it. We'll only evaluate the max party
363
- // size criteria on pure digits ("2" or "10").
364
- } elseif ( ctype_digit( $this->party ) ) {
365
$party_size = $rtb_controller->settings->get_setting( 'party-size' );
366
- if ( !empty( $party_size ) && $party_size < (int) $this->party ) {
367
$this->validation_errors[] = array(
368
'field' => 'party',
369
'post_variable' => $this->party,
370
- 'message' => sprintf( __( 'We only accept bookings for parties of up to %d people.', RTB_TEXTDOMAIN ), $party_size ),
371
);
372
}
373
}
@@ -378,13 +396,32 @@ class rtbBooking {
378
$this->validation_errors[] = array(
379
'field' => 'email',
380
'post_variable' => $this->email,
381
- 'message' => __( 'Please enter an email address so we can confirm your booking.', RTB_TEXTDOMAIN ),
382
);
383
}
384
385
// Phone/Message
386
$this->phone = empty( $_POST['rtb-phone'] ) ? '' : sanitize_text_field( stripslashes_deep( $_POST['rtb-phone'] ) );
387
- $this->message = empty( $_POST['rtb-message'] ) ? '' : sanitize_text_field( stripslashes_deep( $_POST['rtb-message'] ) );
388
389
do_action( 'rtb_validate_booking_submission', $this );
390
@@ -392,12 +429,32 @@ class rtbBooking {
392
393
/**
394
* Check if submission is valid
395
* @since 0.0.1
396
*/
397
public function is_valid_submission() {
398
if ( !count( $this->validation_errors ) ) {
399
return true;
400
}
401
return false;
402
}
403
@@ -412,9 +469,13 @@ class rtbBooking {
412
'post_title' => $this->name,
413
'post_content' => $this->message,
414
'post_date' => $this->date,
415
- 'post_status' => 'pending',
416
);
417
418
$args = apply_filters( 'rtb_insert_booking_data', $args, $this );
419
420
$id = wp_insert_post( $args );
60
$this->ID = $post->ID;
61
$this->name = $post->post_title;
62
$this->date = $post->post_date;
63
+ $this->message = $post->post_content;
64
$this->post_status = $post->post_status;
65
66
$this->load_post_metadata();
95
$this->date_submission = $meta['date_submission'];
96
}
97
98
+ /**
99
+ * Prepare booking data loaded from the database for display in a booking
100
+ * form as request fields. This is used, eg, for splitting datetime values
101
+ * into date and time fields.
102
+ * @since 1.3
103
+ */
104
+ public function prepare_request_data() {
105
+
106
+ // Split $date to $request_date and $request_time
107
+ if ( empty( $this->request_date ) || empty( $this->request_time ) ) {
108
+ $date = new DateTime( $this->date );
109
+ $this->request_date = $date->format( 'Y/m/d' );
110
+ $this->request_time = $date->format( 'h:i A' );
111
+ }
112
+ }
113
+
114
/**
115
* Format date
116
* @since 0.0.1
137
138
$this->request_processed = true;
139
140
+ if ( empty( $this->ID ) ) {
141
+ $action = 'insert';
142
+ } else {
143
+ $action = 'update';
144
+ }
145
+
146
$this->validate_submission();
147
if ( $this->is_valid_submission() === false ) {
148
return false;
154
$this->request_inserted = true;
155
}
156
157
+ do_action( 'rtb_' . $action . '_booking', $this );
158
159
return true;
160
}
175
$this->validation_errors[] = array(
176
'field' => 'date',
177
'error_msg' => 'Booking request missing date',
178
+ 'message' => __( 'Please enter the date you would like to book.', 'restaurant-reservations' ),
179
);
180
181
} else {
185
$this->validation_errors[] = array(
186
'field' => 'date',
187
'error_msg' => $e->getMessage(),
188
+ 'message' => __( 'The date you entered is not valid. Please select from one of the dates in the calendar.', 'restaurant-reservations' ),
189
);
190
}
191
}
196
$this->validation_errors[] = array(
197
'field' => 'time',
198
'error_msg' => 'Booking request missing time',
199
+ 'message' => __( 'Please enter the time you would like to book.', 'restaurant-reservations' ),
200
);
201
202
} else {
206
$this->validation_errors[] = array(
207
'field' => 'time',
208
'error_msg' => $e->getMessage(),
209
+ 'message' => __( 'The time you entered is not valid. Please select from one of the times provided.', 'restaurant-reservations' ),
210
);
211
}
212
}
223
$this->validation_errors[] = array(
224
'field' => 'time',
225
'error_msg' => 'Booking request too far in the future',
226
+ 'message' => sprintf( __( 'Sorry, bookings can not be made more than %s days in advance.', 'restaurant-reservations' ), $early_bookings ),
227
);
228
}
229
}
234
$this->validation_errors[] = array(
235
'field' => 'time',
236
'error_msg' => 'Booking request in the past',
237
+ 'message' => __( 'Sorry, bookings can not be made in the past.', 'restaurant-reservations' ),
238
);
239
}
240
242
$late_bookings_seconds = $late_bookings * 60; // Late bookings allowance in seconds
243
if ( $request->format( 'U' ) < ( current_time( 'timestamp' ) + $late_bookings_seconds ) ) {
244
if ( $late_bookings >= 1440 ) {
245
+ $late_bookings_message = sprintf( __( 'Sorry, bookings must be made more than %s days in advance.', 'restaurant-reservations' ), $late_bookings / 1440 );
246
} elseif ( $late_bookings >= 60 ) {
247
+ $late_bookings_message = sprintf( __( 'Sorry, bookings must be made more than %s hours in advance.', 'restaurant-reservations' ), $late_bookings / 60 );
248
} else {
249
+ $late_bookings_message = sprintf( __( 'Sorry, bookings must be made more than %s mings in advance.', 'restaurant-reservations' ), $late_bookings );
250
}
251
$this->validation_errors[] = array(
252
'field' => 'time',
285
$this->validation_errors[] = array(
286
'field' => 'date',
287
'error_msg' => 'Booking request made on invalid date or time in an exception rule',
288
+ 'message' => __( 'Sorry, no bookings are being accepted then.', 'restaurant-reservations' ),
289
);
290
}
291
}
337
$this->validation_errors[] = array(
338
'field' => 'date',
339
'error_msg' => 'Booking request made on an invalid date',
340
+ 'message' => __( 'Sorry, no bookings are being accepted on that date.', 'restaurant-reservations' ),
341
);
342
} elseif ( !$time_is_valid ) {
343
$this->validation_errors[] = array(
344
'field' => 'time',
345
'error_msg' => 'Booking request made at an invalid time',
346
+ 'message' => __( 'Sorry, no bookings are being accepted at that time.', 'restaurant-reservations' ),
347
);
348
}
349
}
365
$this->validation_errors[] = array(
366
'field' => 'name',
367
'post_variable' => $this->name,
368
+ 'message' => __( 'Please enter a name for this booking.', 'restaurant-reservations' ),
369
);
370
}
371
372
// Party
373
+ $this->party = empty( $_POST['rtb-party'] ) ? '' : absint( $_POST['rtb-party'] );
374
if ( empty( $this->party ) ) {
375
$this->validation_errors[] = array(
376
'field' => 'party',
377
'post_variable' => $this->party,
378
+ 'message' => __( 'Please let us know how many people will be in your party.', 'restaurant-reservations' ),
379
);
380
381
// Check party size
382
+ } else {
383
$party_size = $rtb_controller->settings->get_setting( 'party-size' );
384
+ if ( !empty( $party_size ) && $party_size < $this->party ) {
385
$this->validation_errors[] = array(
386
'field' => 'party',
387
'post_variable' => $this->party,
388
+ 'message' => sprintf( __( 'We only accept bookings for parties of up to %d people.', 'restaurant-reservations' ), $party_size ),
389
);
390
}
391
}
396
$this->validation_errors[] = array(
397
'field' => 'email',
398
'post_variable' => $this->email,
399
+ 'message' => __( 'Please enter an email address so we can confirm your booking.', 'restaurant-reservations' ),
400
);
401
}
402
403
// Phone/Message
404
$this->phone = empty( $_POST['rtb-phone'] ) ? '' : sanitize_text_field( stripslashes_deep( $_POST['rtb-phone'] ) );
405
+ $this->message = empty( $_POST['rtb-message'] ) ? '' : nl2br( wp_kses_post( stripslashes_deep( $_POST['rtb-message'] ) ) );
406
+
407
+ // Post Status (define a default post status is none passed)
408
+ if ( !empty( $_POST['rtb-post-status'] ) && array_key_exists( $_POST['rtb-post-status'], $rtb_controller->cpts->booking_statuses ) ) {
409
+ $this->status = sanitize_text_field( stripslashes_deep( $_POST['rtb-post-status'] ) );
410
+ } else {
411
+ $this->status = 'pending';
412
+ }
413
+
414
+ // Check if any required fields are empty
415
+ $required_fields = $rtb_controller->settings->get_required_fields();
416
+ foreach( $required_fields as $slug => $field ) {
417
+ if ( !$this->field_has_error( $slug ) && empty( $_POST[ 'rtb-' . $slug ] ) ) {
418
+ $this->validation_errors[] = array(
419
+ 'field' => $slug,
420
+ 'post_variable' => '',
421
+ 'message' => __( 'Please complete this field to request a booking.', 'restaurant-reservations' ),
422
+ );
423
+ }
424
+ }
425
426
do_action( 'rtb_validate_booking_submission', $this );
427
429
430
/**
431
* Check if submission is valid
432
+ *
433
* @since 0.0.1
434
*/
435
public function is_valid_submission() {
436
+
437
if ( !count( $this->validation_errors ) ) {
438
return true;
439
}
440
+
441
+ return false;
442
+ }
443
+
444
+ /**
445
+ * Check if a field already has an error attached to it
446
+ *
447
+ * @field string Field slug
448
+ * @since 1.3
449
+ */
450
+ public function field_has_error( $field_slug ) {
451
+
452
+ foreach( $this->validation_errors as $error ) {
453
+ if ( $error['field'] == $field_slug ) {
454
+ return true;
455
+ }
456
+ }
457
+
458
return false;
459
}
460
469
'post_title' => $this->name,
470
'post_content' => $this->message,
471
'post_date' => $this->date,
472
+ 'post_status' => $this->status,
473
);
474
475
+ if ( !empty( $this->ID ) ) {
476
+ $args['ID'] = $this->ID;
477
+ }
478
+
479
$args = apply_filters( 'rtb_insert_booking_data', $args, $this );
480
481
$id = wp_insert_post( $args );
includes/Compatibility.class.php ADDED
@@ -0,0 +1,47 @@
1
+ <?php
2
+ if ( !defined( 'ABSPATH' ) ) exit;
3
+
4
+ if ( !class_exists( 'rtbCompatibility' ) ) {
5
+ /**
6
+ * Class to handle backwards compatibilty issues for Restaurant Reservations
7
+ *
8
+ * @since 1.3
9
+ */
10
+ class rtbCompatibility {
11
+
12
+ /**
13
+ * Set up hooks
14
+ */
15
+ public function __construct() {
16
+
17
+ // Preserve this defined constant in case anyone relied on it
18
+ // to check if the plugin was active
19
+ define( 'RTB_TEXTDOMAIN', 'rtbdomain' );
20
+
21
+ // Load a .mo file for an old textdomain if one exists
22
+ add_filter( 'load_textdomain_mofile', array( $this, 'load_old_textdomain' ), 10, 2 );
23
+
24
+ }
25
+
26
+ /**
27
+ * Load a .mo file for an old textdomain if one exists
28
+ *
29
+ * In versions prior to 1.3, the textdomain did not match the plugin
30
+ * slug. This had to be changed to comply with upcoming changes to
31
+ * how translations are managed in the .org repo. This function
32
+ * checks to see if an old translation file exists and loads it if
33
+ * it does, so that people don't lose their translations.
34
+ *
35
+ * Old textdomain: rtbdomain
36
+ */
37
+ public function load_old_textdomain( $mofile, $textdomain ) {
38
+
39
+ if ( $textdomain === 'restaurant-reservations' && 0 === strpos( $mofile, WP_LANG_DIR . '/plugins/' ) && !file_exists( $mofile ) ) {
40
+ $mofile = dirname( $mofile ) . DIRECTORY_SEPARATOR . str_replace( $textdomain, 'rtbdomain', basename( $mofile ) );
41
+ }
42
+
43
+ return $mofile;
44
+ }
45
+
46
+ }
47
+ } // endif
includes/CustomPostTypes.class.php CHANGED
@@ -35,19 +35,19 @@ class rtbCustomPostTypes {
35
// Define the booking custom post type
36
$args = array(
37
'labels' => array(
38
- 'name' => __( 'Bookings', RTB_TEXTDOMAIN ),
39
- 'singular_name' => __( 'Booking', RTB_TEXTDOMAIN ),
40
- 'menu_name' => __( 'Bookings', RTB_TEXTDOMAIN ),
41
- 'name_admin_bar' => __( 'Bookings', RTB_TEXTDOMAIN ),
42
- 'add_new' => __( 'Add New', RTB_TEXTDOMAIN ),
43
- 'add_new_item' => __( 'Add New Booking', RTB_TEXTDOMAIN ),
44
- 'edit_item' => __( 'Edit Booking', RTB_TEXTDOMAIN ),
45
- 'new_item' => __( 'New Booking', RTB_TEXTDOMAIN ),
46
- 'view_item' => __( 'View Booking', RTB_TEXTDOMAIN ),
47
- 'search_items' => __( 'Search Bookings', RTB_TEXTDOMAIN ),
48
- 'not_found' => __( 'No bookings found', RTB_TEXTDOMAIN ),
49
- 'not_found_in_trash' => __( 'No bookings found in trash', RTB_TEXTDOMAIN ),
50
- 'all_items' => __( 'All Bookings', RTB_TEXTDOMAIN ),
51
),
52
'menu_icon' => 'dashicons-calendar',
53
'public' => false,
@@ -77,31 +77,31 @@ class rtbCustomPostTypes {
77
public function set_booking_statuses() {
78
79
$this->booking_statuses['pending'] = array(
80
- 'label' => _x( 'Pending', 'Booking status when it is pending review', RTB_TEXTDOMAIN ),
81
'default' => true, // Whether or not this status is part of WP Core
82
'user_selectable' => true, // Whether or not a user can set a booking to this status
83
);
84
85
$this->booking_statuses['confirmed'] = array (
86
- 'label' => _x( 'Confirmed', 'Booking status for a confirmed booking', RTB_TEXTDOMAIN ),
87
'default' => false, // Whether or not this status is part of WP Core
88
'user_selectable' => true, // Whether or not a user can set a booking to this status
89
'public' => false,
90
'exclude_from_search' => true,
91
'show_in_admin_all_list' => true,
92
'show_in_admin_status_list' => true,
93
- 'label_count' => _n_noop( 'Confirmed <span class="count">(%s)</span>', 'Confirmed <span class="count">(%s)</span>', RTB_TEXTDOMAIN ),
94
);
95
96
$this->booking_statuses['closed'] = array(
97
- 'label' => _x( 'Closed', 'Booking status for a closed booking', RTB_TEXTDOMAIN ),
98
'default' => false, // Whether or not this status is part of WP Core
99
'user_selectable' => true, // Whether or not a user can set a booking to this status
100
'public' => false,