Version Description
(2022-07-12) = - Fixed a string/array variable-type confusion error.
Download this release
Release Info
Developer | Rustaurius |
Plugin | Restaurant Reservations |
Version | 2.5.13 |
Comparing to | |
See all releases |
Code changes from version 2.5.12 to 2.5.13
- GPL.txt +279 -279
- assets/css/admin-rtb-welcome-screen.css +367 -367
- assets/css/booking-form.css +395 -391
- assets/css/dashboard-review-ask.css +60 -60
- assets/css/editor.css +581 -581
- assets/css/helper-install-notice.css +19 -19
- assets/css/plugin-deactivation.css +67 -67
- assets/js/admin-rtb-welcome-screen.js +92 -92
- assets/js/admin-settings.js +76 -76
- assets/js/block-booking-form.js +48 -48
- assets/js/booking-form.js +982 -982
- assets/js/columns.js +10 -10
- assets/js/dashboard-review-ask.js +79 -79
- assets/js/editor.js +1236 -1236
- assets/js/helper-install-notice.js +10 -10
- assets/js/mailchimp-admin.js +1 -1
- assets/js/plugin-deactivation.js +52 -52
- assets/js/rtb-recaptcha.js +4 -4
- assets/js/stripe-payment.js +196 -196
- includes/Addons.class.php +362 -362
- includes/AdminBookings.class.php +928 -928
- includes/AdminPageSettingLicenseKey.class.php +429 -429
- includes/Ajax.class.php +950 -950
- includes/Blocks.class.php +99 -99
- includes/Booking.class.php +1510 -1510
- includes/Compatibility.class.php +191 -191
- includes/Cron.class.php +252 -252
- includes/CustomFields.class.php +193 -193
- includes/CustomPostTypes.class.php +445 -445
- includes/DeactivationSurvey.class.php +83 -83
- includes/Editor.class.php +845 -845
- includes/Export.CSV.class.php +381 -381
- includes/Export.PDF.class.php +365 -365
- includes/ExportHandler.class.php +309 -309
- includes/Field.class.php +611 -611
- includes/Helper.class.php +103 -103
- includes/Import.class.php +279 -279
- includes/InstallationWalkthrough.class.php +424 -424
- includes/Licenses.class.php +270 -270
- includes/MailChimp.class.php +615 -615
- includes/Migration.class.php +140 -140
- includes/MultipleLocations.class.php +1098 -1098
- includes/Notification.Email.class.php +308 -308
- includes/Notification.SMS.class.php +151 -151
- includes/Notification.class.php +126 -126
- includes/Notifications.class.php +211 -360
GPL.txt
CHANGED
@@ -1,280 +1,280 @@
|
|
1 |
-
GNU GENERAL PUBLIC LICENSE
|
2 |
-
Version 2, June 1991
|
3 |
-
|
4 |
-
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
5 |
-
51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
6 |
-
|
7 |
-
Everyone is permitted to copy and distribute verbatim copies
|
8 |
-
of this license document, but changing it is not allowed.
|
9 |
-
|
10 |
-
Preamble
|
11 |
-
|
12 |
-
The licenses for most software are designed to take away your
|
13 |
-
freedom to share and change it. By contrast, the GNU General Public
|
14 |
-
License is intended to guarantee your freedom to share and change free
|
15 |
-
software--to make sure the software is free for all its users. This
|
16 |
-
General Public License applies to most of the Free Software
|
17 |
-
Foundation's software and to any other program whose authors commit to
|
18 |
-
using it. (Some other Free Software Foundation software is covered by
|
19 |
-
the GNU Library General Public License instead.) You can apply it to
|
20 |
-
your programs, too.
|
21 |
-
|
22 |
-
When we speak of free software, we are referring to freedom, not
|
23 |
-
price. Our General Public Licenses are designed to make sure that you
|
24 |
-
have the freedom to distribute copies of free software (and charge for
|
25 |
-
this service if you wish), that you receive source code or can get it
|
26 |
-
if you want it, that you can change the software or use pieces of it
|
27 |
-
in new free programs; and that you know you can do these things.
|
28 |
-
|
29 |
-
To protect your rights, we need to make restrictions that forbid
|
30 |
-
anyone to deny you these rights or to ask you to surrender the rights.
|
31 |
-
These restrictions translate to certain responsibilities for you if you
|
32 |
-
distribute copies of the software, or if you modify it.
|
33 |
-
|
34 |
-
For example, if you distribute copies of such a program, whether
|
35 |
-
gratis or for a fee, you must give the recipients all the rights that
|
36 |
-
you have. You must make sure that they, too, receive or can get the
|
37 |
-
source code. And you must show them these terms so they know their
|
38 |
-
rights.
|
39 |
-
|
40 |
-
We protect your rights with two steps: (1) copyright the software, and
|
41 |
-
(2) offer you this license which gives you legal permission to copy,
|
42 |
-
distribute and/or modify the software.
|
43 |
-
|
44 |
-
Also, for each author's protection and ours, we want to make certain
|
45 |
-
that everyone understands that there is no warranty for this free
|
46 |
-
software. If the software is modified by someone else and passed on, we
|
47 |
-
want its recipients to know that what they have is not the original, so
|
48 |
-
that any problems introduced by others will not reflect on the original
|
49 |
-
authors' reputations.
|
50 |
-
|
51 |
-
Finally, any free program is threatened constantly by software
|
52 |
-
patents. We wish to avoid the danger that redistributors of a free
|
53 |
-
program will individually obtain patent licenses, in effect making the
|
54 |
-
program proprietary. To prevent this, we have made it clear that any
|
55 |
-
patent must be licensed for everyone's free use or not licensed at all.
|
56 |
-
|
57 |
-
The precise terms and conditions for copying, distribution and
|
58 |
-
modification follow.
|
59 |
-
|
60 |
-
GNU GENERAL PUBLIC LICENSE
|
61 |
-
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
62 |
-
|
63 |
-
0. This License applies to any program or other work which contains
|
64 |
-
a notice placed by the copyright holder saying it may be distributed
|
65 |
-
under the terms of this General Public License. The "Program", below,
|
66 |
-
refers to any such program or work, and a "work based on the Program"
|
67 |
-
means either the Program or any derivative work under copyright law:
|
68 |
-
that is to say, a work containing the Program or a portion of it,
|
69 |
-
either verbatim or with modifications and/or translated into another
|
70 |
-
language. (Hereinafter, translation is included without limitation in
|
71 |
-
the term "modification".) Each licensee is addressed as "you".
|
72 |
-
|
73 |
-
Activities other than copying, distribution and modification are not
|
74 |
-
covered by this License; they are outside its scope. The act of
|
75 |
-
running the Program is not restricted, and the output from the Program
|
76 |
-
is covered only if its contents constitute a work based on the
|
77 |
-
Program (independent of having been made by running the Program).
|
78 |
-
Whether that is true depends on what the Program does.
|
79 |
-
|
80 |
-
1. You may copy and distribute verbatim copies of the Program's
|
81 |
-
source code as you receive it, in any medium, provided that you
|
82 |
-
conspicuously and appropriately publish on each copy an appropriate
|
83 |
-
copyright notice and disclaimer of warranty; keep intact all the
|
84 |
-
notices that refer to this License and to the absence of any warranty;
|
85 |
-
and give any other recipients of the Program a copy of this License
|
86 |
-
along with the Program.
|
87 |
-
|
88 |
-
You may charge a fee for the physical act of transferring a copy, and
|
89 |
-
you may at your option offer warranty protection in exchange for a fee.
|
90 |
-
|
91 |
-
2. You may modify your copy or copies of the Program or any portion
|
92 |
-
of it, thus forming a work based on the Program, and copy and
|
93 |
-
distribute such modifications or work under the terms of Section 1
|
94 |
-
above, provided that you also meet all of these conditions:
|
95 |
-
|
96 |
-
a) You must cause the modified files to carry prominent notices
|
97 |
-
stating that you changed the files and the date of any change.
|
98 |
-
|
99 |
-
b) You must cause any work that you distribute or publish, that in
|
100 |
-
whole or in part contains or is derived from the Program or any
|
101 |
-
part thereof, to be licensed as a whole at no charge to all third
|
102 |
-
parties under the terms of this License.
|
103 |
-
|
104 |
-
c) If the modified program normally reads commands interactively
|
105 |
-
when run, you must cause it, when started running for such
|
106 |
-
interactive use in the most ordinary way, to print or display an
|
107 |
-
announcement including an appropriate copyright notice and a
|
108 |
-
notice that there is no warranty (or else, saying that you provide
|
109 |
-
a warranty) and that users may redistribute the program under
|
110 |
-
these conditions, and telling the user how to view a copy of this
|
111 |
-
License. (Exception: if the Program itself is interactive but
|
112 |
-
does not normally print such an announcement, your work based on
|
113 |
-
the Program is not required to print an announcement.)
|
114 |
-
|
115 |
-
These requirements apply to the modified work as a whole. If
|
116 |
-
identifiable sections of that work are not derived from the Program,
|
117 |
-
and can be reasonably considered independent and separate works in
|
118 |
-
themselves, then this License, and its terms, do not apply to those
|
119 |
-
sections when you distribute them as separate works. But when you
|
120 |
-
distribute the same sections as part of a whole which is a work based
|
121 |
-
on the Program, the distribution of the whole must be on the terms of
|
122 |
-
this License, whose permissions for other licensees extend to the
|
123 |
-
entire whole, and thus to each and every part regardless of who wrote it.
|
124 |
-
Thus, it is not the intent of this section to claim rights or contest
|
125 |
-
your rights to work written entirely by you; rather, the intent is to
|
126 |
-
exercise the right to control the distribution of derivative or
|
127 |
-
collective works based on the Program.
|
128 |
-
|
129 |
-
In addition, mere aggregation of another work not based on the Program
|
130 |
-
with the Program (or with a work based on the Program) on a volume of
|
131 |
-
a storage or distribution medium does not bring the other work under
|
132 |
-
the scope of this License.
|
133 |
-
|
134 |
-
3. You may copy and distribute the Program (or a work based on it,
|
135 |
-
under Section 2) in object code or executable form under the terms of
|
136 |
-
Sections 1 and 2 above provided that you also do one of the following:
|
137 |
-
|
138 |
-
a) Accompany it with the complete corresponding machine-readable
|
139 |
-
source code, which must be distributed under the terms of Sections
|
140 |
-
1 and 2 above on a medium customarily used for software interchange; or,
|
141 |
-
|
142 |
-
b) Accompany it with a written offer, valid for at least three
|
143 |
-
years, to give any third party, for a charge no more than your
|
144 |
-
cost of physically performing source distribution, a complete
|
145 |
-
machine-readable copy of the corresponding source code, to be
|
146 |
-
distributed under the terms of Sections 1 and 2 above on a medium
|
147 |
-
customarily used for software interchange; or,
|
148 |
-
|
149 |
-
c) Accompany it with the information you received as to the offer
|
150 |
-
to distribute corresponding source code. (This alternative is
|
151 |
-
allowed only for noncommercial distribution and only if you
|
152 |
-
received the program in object code or executable form with such
|
153 |
-
an offer, in accord with Subsection b above.)
|
154 |
-
|
155 |
-
The source code for a work means the preferred form of the work for
|
156 |
-
making modifications to it. For an executable work, complete source
|
157 |
-
code means all the source code for all modules it contains, plus any
|
158 |
-
associated interface definition files, plus the scripts used to
|
159 |
-
control compilation and installation of the executable. However, as a
|
160 |
-
special exception, the source code distributed need not include
|
161 |
-
anything that is normally distributed (in either source or binary
|
162 |
-
form) with the major components (compiler, kernel, and so on) of the
|
163 |
-
operating system on which the executable runs, unless that component
|
164 |
-
itself accompanies the executable.
|
165 |
-
|
166 |
-
If distribution of executable or object code is made by offering
|
167 |
-
access to copy from a designated place, then offering equivalent
|
168 |
-
access to copy the source code from the same place counts as
|
169 |
-
distribution of the source code, even though third parties are not
|
170 |
-
compelled to copy the source along with the object code.
|
171 |
-
|
172 |
-
4. You may not copy, modify, sublicense, or distribute the Program
|
173 |
-
except as expressly provided under this License. Any attempt
|
174 |
-
otherwise to copy, modify, sublicense or distribute the Program is
|
175 |
-
void, and will automatically terminate your rights under this License.
|
176 |
-
However, parties who have received copies, or rights, from you under
|
177 |
-
this License will not have their licenses terminated so long as such
|
178 |
-
parties remain in full compliance.
|
179 |
-
|
180 |
-
5. You are not required to accept this License, since you have not
|
181 |
-
signed it. However, nothing else grants you permission to modify or
|
182 |
-
distribute the Program or its derivative works. These actions are
|
183 |
-
prohibited by law if you do not accept this License. Therefore, by
|
184 |
-
modifying or distributing the Program (or any work based on the
|
185 |
-
Program), you indicate your acceptance of this License to do so, and
|
186 |
-
all its terms and conditions for copying, distributing or modifying
|
187 |
-
the Program or works based on it.
|
188 |
-
|
189 |
-
6. Each time you redistribute the Program (or any work based on the
|
190 |
-
Program), the recipient automatically receives a license from the
|
191 |
-
original licensor to copy, distribute or modify the Program subject to
|
192 |
-
these terms and conditions. You may not impose any further
|
193 |
-
restrictions on the recipients' exercise of the rights granted herein.
|
194 |
-
You are not responsible for enforcing compliance by third parties to
|
195 |
-
this License.
|
196 |
-
|
197 |
-
7. If, as a consequence of a court judgment or allegation of patent
|
198 |
-
infringement or for any other reason (not limited to patent issues),
|
199 |
-
conditions are imposed on you (whether by court order, agreement or
|
200 |
-
otherwise) that contradict the conditions of this License, they do not
|
201 |
-
excuse you from the conditions of this License. If you cannot
|
202 |
-
distribute so as to satisfy simultaneously your obligations under this
|
203 |
-
License and any other pertinent obligations, then as a consequence you
|
204 |
-
may not distribute the Program at all. For example, if a patent
|
205 |
-
license would not permit royalty-free redistribution of the Program by
|
206 |
-
all those who receive copies directly or indirectly through you, then
|
207 |
-
the only way you could satisfy both it and this License would be to
|
208 |
-
refrain entirely from distribution of the Program.
|
209 |
-
|
210 |
-
If any portion of this section is held invalid or unenforceable under
|
211 |
-
any particular circumstance, the balance of the section is intended to
|
212 |
-
apply and the section as a whole is intended to apply in other
|
213 |
-
circumstances.
|
214 |
-
|
215 |
-
It is not the purpose of this section to induce you to infringe any
|
216 |
-
patents or other property right claims or to contest validity of any
|
217 |
-
such claims; this section has the sole purpose of protecting the
|
218 |
-
integrity of the free software distribution system, which is
|
219 |
-
implemented by public license practices. Many people have made
|
220 |
-
generous contributions to the wide range of software distributed
|
221 |
-
through that system in reliance on consistent application of that
|
222 |
-
system; it is up to the author/donor to decide if he or she is willing
|
223 |
-
to distribute software through any other system and a licensee cannot
|
224 |
-
impose that choice.
|
225 |
-
|
226 |
-
This section is intended to make thoroughly clear what is believed to
|
227 |
-
be a consequence of the rest of this License.
|
228 |
-
|
229 |
-
8. If the distribution and/or use of the Program is restricted in
|
230 |
-
certain countries either by patents or by copyrighted interfaces, the
|
231 |
-
original copyright holder who places the Program under this License
|
232 |
-
may add an explicit geographical distribution limitation excluding
|
233 |
-
those countries, so that distribution is permitted only in or among
|
234 |
-
countries not thus excluded. In such case, this License incorporates
|
235 |
-
the limitation as if written in the body of this License.
|
236 |
-
|
237 |
-
9. The Free Software Foundation may publish revised and/or new versions
|
238 |
-
of the General Public License from time to time. Such new versions will
|
239 |
-
be similar in spirit to the present version, but may differ in detail to
|
240 |
-
address new problems or concerns.
|
241 |
-
|
242 |
-
Each version is given a distinguishing version number. If the Program
|
243 |
-
specifies a version number of this License which applies to it and "any
|
244 |
-
later version", you have the option of following the terms and conditions
|
245 |
-
either of that version or of any later version published by the Free
|
246 |
-
Software Foundation. If the Program does not specify a version number of
|
247 |
-
this License, you may choose any version ever published by the Free Software
|
248 |
-
Foundation.
|
249 |
-
|
250 |
-
10. If you wish to incorporate parts of the Program into other free
|
251 |
-
programs whose distribution conditions are different, write to the author
|
252 |
-
to ask for permission. For software which is copyrighted by the Free
|
253 |
-
Software Foundation, write to the Free Software Foundation; we sometimes
|
254 |
-
make exceptions for this. Our decision will be guided by the two goals
|
255 |
-
of preserving the free status of all derivatives of our free software and
|
256 |
-
of promoting the sharing and reuse of software generally.
|
257 |
-
|
258 |
-
NO WARRANTY
|
259 |
-
|
260 |
-
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
261 |
-
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
262 |
-
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
263 |
-
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
264 |
-
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
265 |
-
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
266 |
-
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
267 |
-
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
268 |
-
REPAIR OR CORRECTION.
|
269 |
-
|
270 |
-
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
271 |
-
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
272 |
-
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
273 |
-
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
274 |
-
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
275 |
-
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
276 |
-
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
277 |
-
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
278 |
-
POSSIBILITY OF SUCH DAMAGES.
|
279 |
-
|
280 |
END OF TERMS AND CONDITIONS
|
1 |
+
GNU GENERAL PUBLIC LICENSE
|
2 |
+
Version 2, June 1991
|
3 |
+
|
4 |
+
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
5 |
+
51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
6 |
+
|
7 |
+
Everyone is permitted to copy and distribute verbatim copies
|
8 |
+
of this license document, but changing it is not allowed.
|
9 |
+
|
10 |
+
Preamble
|
11 |
+
|
12 |
+
The licenses for most software are designed to take away your
|
13 |
+
freedom to share and change it. By contrast, the GNU General Public
|
14 |
+
License is intended to guarantee your freedom to share and change free
|
15 |
+
software--to make sure the software is free for all its users. This
|
16 |
+
General Public License applies to most of the Free Software
|
17 |
+
Foundation's software and to any other program whose authors commit to
|
18 |
+
using it. (Some other Free Software Foundation software is covered by
|
19 |
+
the GNU Library General Public License instead.) You can apply it to
|
20 |
+
your programs, too.
|
21 |
+
|
22 |
+
When we speak of free software, we are referring to freedom, not
|
23 |
+
price. Our General Public Licenses are designed to make sure that you
|
24 |
+
have the freedom to distribute copies of free software (and charge for
|
25 |
+
this service if you wish), that you receive source code or can get it
|
26 |
+
if you want it, that you can change the software or use pieces of it
|
27 |
+
in new free programs; and that you know you can do these things.
|
28 |
+
|
29 |
+
To protect your rights, we need to make restrictions that forbid
|
30 |
+
anyone to deny you these rights or to ask you to surrender the rights.
|
31 |
+
These restrictions translate to certain responsibilities for you if you
|
32 |
+
distribute copies of the software, or if you modify it.
|
33 |
+
|
34 |
+
For example, if you distribute copies of such a program, whether
|
35 |
+
gratis or for a fee, you must give the recipients all the rights that
|
36 |
+
you have. You must make sure that they, too, receive or can get the
|
37 |
+
source code. And you must show them these terms so they know their
|
38 |
+
rights.
|
39 |
+
|
40 |
+
We protect your rights with two steps: (1) copyright the software, and
|
41 |
+
(2) offer you this license which gives you legal permission to copy,
|
42 |
+
distribute and/or modify the software.
|
43 |
+
|
44 |
+
Also, for each author's protection and ours, we want to make certain
|
45 |
+
that everyone understands that there is no warranty for this free
|
46 |
+
software. If the software is modified by someone else and passed on, we
|
47 |
+
want its recipients to know that what they have is not the original, so
|
48 |
+
that any problems introduced by others will not reflect on the original
|
49 |
+
authors' reputations.
|
50 |
+
|
51 |
+
Finally, any free program is threatened constantly by software
|
52 |
+
patents. We wish to avoid the danger that redistributors of a free
|
53 |
+
program will individually obtain patent licenses, in effect making the
|
54 |
+
program proprietary. To prevent this, we have made it clear that any
|
55 |
+
patent must be licensed for everyone's free use or not licensed at all.
|
56 |
+
|
57 |
+
The precise terms and conditions for copying, distribution and
|
58 |
+
modification follow.
|
59 |
+
|
60 |
+
GNU GENERAL PUBLIC LICENSE
|
61 |
+
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
62 |
+
|
63 |
+
0. This License applies to any program or other work which contains
|
64 |
+
a notice placed by the copyright holder saying it may be distributed
|
65 |
+
under the terms of this General Public License. The "Program", below,
|
66 |
+
refers to any such program or work, and a "work based on the Program"
|
67 |
+
means either the Program or any derivative work under copyright law:
|
68 |
+
that is to say, a work containing the Program or a portion of it,
|
69 |
+
either verbatim or with modifications and/or translated into another
|
70 |
+
language. (Hereinafter, translation is included without limitation in
|
71 |
+
the term "modification".) Each licensee is addressed as "you".
|
72 |
+
|
73 |
+
Activities other than copying, distribution and modification are not
|
74 |
+
covered by this License; they are outside its scope. The act of
|
75 |
+
running the Program is not restricted, and the output from the Program
|
76 |
+
is covered only if its contents constitute a work based on the
|
77 |
+
Program (independent of having been made by running the Program).
|
78 |
+
Whether that is true depends on what the Program does.
|
79 |
+
|
80 |
+
1. You may copy and distribute verbatim copies of the Program's
|
81 |
+
source code as you receive it, in any medium, provided that you
|
82 |
+
conspicuously and appropriately publish on each copy an appropriate
|
83 |
+
copyright notice and disclaimer of warranty; keep intact all the
|
84 |
+
notices that refer to this License and to the absence of any warranty;
|
85 |
+
and give any other recipients of the Program a copy of this License
|
86 |
+
along with the Program.
|
87 |
+
|
88 |
+
You may charge a fee for the physical act of transferring a copy, and
|
89 |
+
you may at your option offer warranty protection in exchange for a fee.
|
90 |
+
|
91 |
+
2. You may modify your copy or copies of the Program or any portion
|
92 |
+
of it, thus forming a work based on the Program, and copy and
|
93 |
+
distribute such modifications or work under the terms of Section 1
|
94 |
+
above, provided that you also meet all of these conditions:
|
95 |
+
|
96 |
+
a) You must cause the modified files to carry prominent notices
|
97 |
+
stating that you changed the files and the date of any change.
|
98 |
+
|
99 |
+
b) You must cause any work that you distribute or publish, that in
|
100 |
+
whole or in part contains or is derived from the Program or any
|
101 |
+
part thereof, to be licensed as a whole at no charge to all third
|
102 |
+
parties under the terms of this License.
|
103 |
+
|
104 |
+
c) If the modified program normally reads commands interactively
|
105 |
+
when run, you must cause it, when started running for such
|
106 |
+
interactive use in the most ordinary way, to print or display an
|
107 |
+
announcement including an appropriate copyright notice and a
|
108 |
+
notice that there is no warranty (or else, saying that you provide
|
109 |
+
a warranty) and that users may redistribute the program under
|
110 |
+
these conditions, and telling the user how to view a copy of this
|
111 |
+
License. (Exception: if the Program itself is interactive but
|
112 |
+
does not normally print such an announcement, your work based on
|
113 |
+
the Program is not required to print an announcement.)
|
114 |
+
|
115 |
+
These requirements apply to the modified work as a whole. If
|
116 |
+
identifiable sections of that work are not derived from the Program,
|
117 |
+
and can be reasonably considered independent and separate works in
|
118 |
+
themselves, then this License, and its terms, do not apply to those
|
119 |
+
sections when you distribute them as separate works. But when you
|
120 |
+
distribute the same sections as part of a whole which is a work based
|
121 |
+
on the Program, the distribution of the whole must be on the terms of
|
122 |
+
this License, whose permissions for other licensees extend to the
|
123 |
+
entire whole, and thus to each and every part regardless of who wrote it.
|
124 |
+
Thus, it is not the intent of this section to claim rights or contest
|
125 |
+
your rights to work written entirely by you; rather, the intent is to
|
126 |
+
exercise the right to control the distribution of derivative or
|
127 |
+
collective works based on the Program.
|
128 |
+
|
129 |
+
In addition, mere aggregation of another work not based on the Program
|
130 |
+
with the Program (or with a work based on the Program) on a volume of
|
131 |
+
a storage or distribution medium does not bring the other work under
|
132 |
+
the scope of this License.
|
133 |
+
|
134 |
+
3. You may copy and distribute the Program (or a work based on it,
|
135 |
+
under Section 2) in object code or executable form under the terms of
|
136 |
+
Sections 1 and 2 above provided that you also do one of the following:
|
137 |
+
|
138 |
+
a) Accompany it with the complete corresponding machine-readable
|
139 |
+
source code, which must be distributed under the terms of Sections
|
140 |
+
1 and 2 above on a medium customarily used for software interchange; or,
|
141 |
+
|
142 |
+
b) Accompany it with a written offer, valid for at least three
|
143 |
+
years, to give any third party, for a charge no more than your
|
144 |
+
cost of physically performing source distribution, a complete
|
145 |
+
machine-readable copy of the corresponding source code, to be
|
146 |
+
distributed under the terms of Sections 1 and 2 above on a medium
|
147 |
+
customarily used for software interchange; or,
|
148 |
+
|
149 |
+
c) Accompany it with the information you received as to the offer
|
150 |
+
to distribute corresponding source code. (This alternative is
|
151 |
+
allowed only for noncommercial distribution and only if you
|
152 |
+
received the program in object code or executable form with such
|
153 |
+
an offer, in accord with Subsection b above.)
|
154 |
+
|
155 |
+
The source code for a work means the preferred form of the work for
|
156 |
+
making modifications to it. For an executable work, complete source
|
157 |
+
code means all the source code for all modules it contains, plus any
|
158 |
+
associated interface definition files, plus the scripts used to
|
159 |
+
control compilation and installation of the executable. However, as a
|
160 |
+
special exception, the source code distributed need not include
|
161 |
+
anything that is normally distributed (in either source or binary
|
162 |
+
form) with the major components (compiler, kernel, and so on) of the
|
163 |
+
operating system on which the executable runs, unless that component
|
164 |
+
itself accompanies the executable.
|
165 |
+
|
166 |
+
If distribution of executable or object code is made by offering
|
167 |
+
access to copy from a designated place, then offering equivalent
|
168 |
+
access to copy the source code from the same place counts as
|
169 |
+
distribution of the source code, even though third parties are not
|
170 |
+
compelled to copy the source along with the object code.
|
171 |
+
|
172 |
+
4. You may not copy, modify, sublicense, or distribute the Program
|
173 |
+
except as expressly provided under this License. Any attempt
|
174 |
+
otherwise to copy, modify, sublicense or distribute the Program is
|
175 |
+
void, and will automatically terminate your rights under this License.
|
176 |
+
However, parties who have received copies, or rights, from you under
|
177 |
+
this License will not have their licenses terminated so long as such
|
178 |
+
parties remain in full compliance.
|
179 |
+
|
180 |
+
5. You are not required to accept this License, since you have not
|
181 |
+
signed it. However, nothing else grants you permission to modify or
|
182 |
+
distribute the Program or its derivative works. These actions are
|
183 |
+
prohibited by law if you do not accept this License. Therefore, by
|
184 |
+
modifying or distributing the Program (or any work based on the
|
185 |
+
Program), you indicate your acceptance of this License to do so, and
|
186 |
+
all its terms and conditions for copying, distributing or modifying
|
187 |
+
the Program or works based on it.
|
188 |
+
|
189 |
+
6. Each time you redistribute the Program (or any work based on the
|
190 |
+
Program), the recipient automatically receives a license from the
|
191 |
+
original licensor to copy, distribute or modify the Program subject to
|
192 |
+
these terms and conditions. You may not impose any further
|
193 |
+
restrictions on the recipients' exercise of the rights granted herein.
|
194 |
+
You are not responsible for enforcing compliance by third parties to
|
195 |
+
this License.
|
196 |
+
|
197 |
+
7. If, as a consequence of a court judgment or allegation of patent
|
198 |
+
infringement or for any other reason (not limited to patent issues),
|
199 |
+
conditions are imposed on you (whether by court order, agreement or
|
200 |
+
otherwise) that contradict the conditions of this License, they do not
|
201 |
+
excuse you from the conditions of this License. If you cannot
|
202 |
+
distribute so as to satisfy simultaneously your obligations under this
|
203 |
+
License and any other pertinent obligations, then as a consequence you
|
204 |
+
may not distribute the Program at all. For example, if a patent
|
205 |
+
license would not permit royalty-free redistribution of the Program by
|
206 |
+
all those who receive copies directly or indirectly through you, then
|
207 |
+
the only way you could satisfy both it and this License would be to
|
208 |
+
refrain entirely from distribution of the Program.
|
209 |
+
|
210 |
+
If any portion of this section is held invalid or unenforceable under
|
211 |
+
any particular circumstance, the balance of the section is intended to
|
212 |
+
apply and the section as a whole is intended to apply in other
|
213 |
+
circumstances.
|
214 |
+
|
215 |
+
It is not the purpose of this section to induce you to infringe any
|
216 |
+
patents or other property right claims or to contest validity of any
|
217 |
+
such claims; this section has the sole purpose of protecting the
|
218 |
+
integrity of the free software distribution system, which is
|
219 |
+
implemented by public license practices. Many people have made
|
220 |
+
generous contributions to the wide range of software distributed
|
221 |
+
through that system in reliance on consistent application of that
|
222 |
+
system; it is up to the author/donor to decide if he or she is willing
|
223 |
+
to distribute software through any other system and a licensee cannot
|
224 |
+
impose that choice.
|
225 |
+
|
226 |
+
This section is intended to make thoroughly clear what is believed to
|
227 |
+
be a consequence of the rest of this License.
|
228 |
+
|
229 |
+
8. If the distribution and/or use of the Program is restricted in
|
230 |
+
certain countries either by patents or by copyrighted interfaces, the
|
231 |
+
original copyright holder who places the Program under this License
|
232 |
+
may add an explicit geographical distribution limitation excluding
|
233 |
+
those countries, so that distribution is permitted only in or among
|
234 |
+
countries not thus excluded. In such case, this License incorporates
|
235 |
+
the limitation as if written in the body of this License.
|
236 |
+
|
237 |
+
9. The Free Software Foundation may publish revised and/or new versions
|
238 |
+
of the General Public License from time to time. Such new versions will
|
239 |
+
be similar in spirit to the present version, but may differ in detail to
|
240 |
+
address new problems or concerns.
|
241 |
+
|
242 |
+
Each version is given a distinguishing version number. If the Program
|
243 |
+
specifies a version number of this License which applies to it and "any
|
244 |
+
later version", you have the option of following the terms and conditions
|
245 |
+
either of that version or of any later version published by the Free
|
246 |
+
Software Foundation. If the Program does not specify a version number of
|
247 |
+
this License, you may choose any version ever published by the Free Software
|
248 |
+
Foundation.
|
249 |
+
|
250 |
+
10. If you wish to incorporate parts of the Program into other free
|
251 |
+
programs whose distribution conditions are different, write to the author
|
252 |
+
to ask for permission. For software which is copyrighted by the Free
|
253 |
+
Software Foundation, write to the Free Software Foundation; we sometimes
|
254 |
+
make exceptions for this. Our decision will be guided by the two goals
|
255 |
+
of preserving the free status of all derivatives of our free software and
|
256 |
+
of promoting the sharing and reuse of software generally.
|
257 |
+
|
258 |
+
NO WARRANTY
|
259 |
+
|
260 |
+
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
261 |
+
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
262 |
+
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
263 |
+
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
264 |
+
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
265 |
+
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
266 |
+
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
267 |
+
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
268 |
+
REPAIR OR CORRECTION.
|
269 |
+
|
270 |
+
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
271 |
+
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
272 |
+
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
273 |
+
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
274 |
+
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
275 |
+
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
276 |
+
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
277 |
+
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
278 |
+
POSSIBILITY OF SUCH DAMAGES.
|
279 |
+
|
280 |
END OF TERMS AND CONDITIONS
|
assets/css/admin-rtb-welcome-screen.css
CHANGED
@@ -1,367 +1,367 @@
|
|
1 |
-
.rtb-hidden {
|
2 |
-
display: none;
|
3 |
-
}
|
4 |
-
.rtb-welcome-clear {
|
5 |
-
clear: both;
|
6 |
-
}
|
7 |
-
|
8 |
-
.rtb-welcome-screen-header h1 {
|
9 |
-
padding: 48px 0 16px 0;
|
10 |
-
margin: 0;
|
11 |
-
}
|
12 |
-
.rtb-welcome-screen-header p {
|
13 |
-
margin-bottom: 32px;
|
14 |
-
font-size: 15px;
|
15 |
-
}
|
16 |
-
.rtb-welcome-screen h2 {
|
17 |
-
background: #1b335f;
|
18 |
-
color: #fff;
|
19 |
-
padding: 18px;
|
20 |
-
margin: 0;
|
21 |
-
z-index: 2;
|
22 |
-
position: relative;
|
23 |
-
float: left;
|
24 |
-
width: calc(100% - 36px);
|
25 |
-
border-top: 1px solid #42639F;
|
26 |
-
}
|
27 |
-
.rtb-welcome-screen h2:hover {
|
28 |
-
background: #365388;
|
29 |
-
}
|
30 |
-
|
31 |
-
.rtb-welcome-screen-box .rtb-welcome-screen-box-content {
|
32 |
-
display: none;
|
33 |
-
}
|
34 |
-
|
35 |
-
.rtb-welcome-screen-box.rtb-welcome-screen-open .rtb-welcome-screen-box-content {
|
36 |
-
display: block;
|
37 |
-
}
|
38 |
-
|
39 |
-
.rtb-welcome-screen-box {
|
40 |
-
padding: 0px;
|
41 |
-
max-width: 800px;
|
42 |
-
margin: 0 auto;
|
43 |
-
}
|
44 |
-
|
45 |
-
.rtb-welcome-screen-header,
|
46 |
-
.rtb-welcome-screen-skip-container {
|
47 |
-
max-width: 800px;
|
48 |
-
margin: 0 auto;
|
49 |
-
}
|
50 |
-
|
51 |
-
.rtb-welcome-screen-header {
|
52 |
-
text-align: center;
|
53 |
-
}
|
54 |
-
|
55 |
-
.rtb-welcome-screen-box h2 {
|
56 |
-
cursor: pointer;
|
57 |
-
}
|
58 |
-
|
59 |
-
.rtb-welcome-screen-box-content {
|
60 |
-
position: relative;
|
61 |
-
float: left;
|
62 |
-
background: #fff;
|
63 |
-
width: 90%;
|
64 |
-
margin: 0 auto 16px;
|
65 |
-
box-shadow: 0 1px 12px #ddd;
|
66 |
-
display: block;
|
67 |
-
padding: 18px 5%;
|
68 |
-
}
|
69 |
-
|
70 |
-
.rtb-welcome-screen-add-reservations-page-button,
|
71 |
-
.rtb-welcome-screen-save-schedule-open-button,
|
72 |
-
.rtb-welcome-screen-save-options-button {
|
73 |
-
position: relative;
|
74 |
-
float: left;
|
75 |
-
cursor: pointer;
|
76 |
-
padding: 4px 8px;
|
77 |
-
border: 2px solid #1b335f;
|
78 |
-
color: #fff;
|
79 |
-
border-radius: 2px;
|
80 |
-
background: #1b335f;
|
81 |
-
width: auto;
|
82 |
-
text-align: center;
|
83 |
-
margin-top: 12px;
|
84 |
-
}
|
85 |
-
.rtb-welcome-screen-add-reservations-page-button:hover,
|
86 |
-
.rtb-welcome-screen-save-schedule-open-button:hover,
|
87 |
-
.rtb-welcome-screen-save-options-button:hover {
|
88 |
-
border-color: #365388;
|
89 |
-
background: #365388;
|
90 |
-
}
|
91 |
-
|
92 |
-
.rtb-welcome-screen-next-button,
|
93 |
-
.rtb-welcome-screen-previous-button,
|
94 |
-
.rtb-welcome-screen-finish-button a {
|
95 |
-
position: relative;
|
96 |
-
float: right;
|
97 |
-
cursor: pointer;
|
98 |
-
padding: 4px 8px;
|
99 |
-
border: 2px solid #1b335f;
|
100 |
-
color: #1b335f;
|
101 |
-
border-radius: 2px;
|
102 |
-
background: transparent;
|
103 |
-
width: auto;
|
104 |
-
text-align: center;
|
105 |
-
margin-top: 32px;
|
106 |
-
text-decoration: none;
|
107 |
-
}
|
108 |
-
.rtb-welcome-screen-next-button:hover,
|
109 |
-
.rtb-welcome-screen-previous-button:hover,
|
110 |
-
.rtb-welcome-screen-finish-button a:hover {
|
111 |
-
color: #fff;
|
112 |
-
background: #1b335f;
|
113 |
-
text-decoration: none;
|
114 |
-
}
|
115 |
-
.rtb-welcome-screen-next-button-not-top-margin {
|
116 |
-
margin-top: 12px;
|
117 |
-
}
|
118 |
-
.rtb-welcome-screen-previous-button {
|
119 |
-
float: left;
|
120 |
-
}
|
121 |
-
|
122 |
-
.rtb-welcome-screen-skip-button {
|
123 |
-
position: relative;
|
124 |
-
float: right;
|
125 |
-
cursor: pointer;
|
126 |
-
padding: 4px 8px;
|
127 |
-
border: 2px solid #ccc;
|
128 |
-
color: #999;
|
129 |
-
border-radius: 2px;
|
130 |
-
background: transparent;
|
131 |
-
width: auto;
|
132 |
-
text-align: center;
|
133 |
-
margin-top: 12px;
|
134 |
-
}
|
135 |
-
.rtb-welcome-screen-skip-button:hover {
|
136 |
-
border-color: #aaa;
|
137 |
-
color: #fff;
|
138 |
-
background: #aaa;
|
139 |
-
}
|
140 |
-
|
141 |
-
.rtb-welcome-screen-box-content label {
|
142 |
-
width: 160px;
|
143 |
-
position: relative;
|
144 |
-
float: left;
|
145 |
-
font-weight: bold;
|
146 |
-
}
|
147 |
-
.rtb-welcome-screen-option {
|
148 |
-
margin: 7px 0;
|
149 |
-
}
|
150 |
-
.rtb-welcome-screen-option label {
|
151 |
-
width: 100px;
|
152 |
-
float: left;
|
153 |
-
font-weight: normal;
|
154 |
-
}
|
155 |
-
.rtb-welcome-screen-option label.rtb-admin-switch {
|
156 |
-
width: 40px
|
157 |
-
}
|
158 |
-
|
159 |
-
.rtb-welcome-screen-box-content input {
|
160 |
-
margin: 0;
|
161 |
-
}
|
162 |
-
.rtb-welcome-screen-box-content-divs {
|
163 |
-
margin-bottom: 8px;
|
164 |
-
}
|
165 |
-
|
166 |
-
|
167 |
-
.rtb-welcome-screen-show-created-sections {
|
168 |
-
display: none;
|
169 |
-
}
|
170 |
-
|
171 |
-
.rtb-welcome-screen-show-created-sections-name {
|
172 |
-
position: relative;
|
173 |
-
float: left;
|
174 |
-
width: 40%;
|
175 |
-
width: calc(40% - 2px);
|
176 |
-
padding: 9px 5%;
|
177 |
-
border-left: 1px solid #ccc;
|
178 |
-
border-right: 1px solid #ccc;
|
179 |
-
font-size: 15px;
|
180 |
-
font-weight: bold;
|
181 |
-
border-top: 1px solid #ccc;
|
182 |
-
border-bottom: 1px solid #ccc;
|
183 |
-
}
|
184 |
-
.rtb-welcome-screen-show-created-sections-description {
|
185 |
-
position: relative;
|
186 |
-
float: right;
|
187 |
-
width: 40%;
|
188 |
-
width: calc(40% - 1px);
|
189 |
-
border-right: 1px solid #ccc;
|
190 |
-
padding: 9px 5%;
|
191 |
-
font-size: 15px;
|
192 |
-
font-weight: bold;
|
193 |
-
border-top: 1px solid #ccc;
|
194 |
-
border-bottom: 1px solid #ccc;
|
195 |
-
}
|
196 |
-
.rtb-welcome-screen-section-name {
|
197 |
-
position: relative;
|
198 |
-
float: left;
|
199 |
-
width: 40%;
|
200 |
-
width: calc(40% - 2px);
|
201 |
-
padding: 7px 5%;
|
202 |
-
height: 18px;
|
203 |
-
overflow: hidden;
|
204 |
-
border-left: 1px solid #ccc;
|
205 |
-
border-right: 1px solid #ccc;
|
206 |
-
border-bottom: 1px solid #ccc;
|
207 |
-
}
|
208 |
-
.rtb-welcome-screen-section-description {
|
209 |
-
position: relative;
|
210 |
-
float: right;
|
211 |
-
width: 40%;
|
212 |
-
width: calc(40% - 1px);
|
213 |
-
padding: 7px 5%;
|
214 |
-
height: 18px;
|
215 |
-
overflow: hidden;
|
216 |
-
border-right: 1px solid #ccc;
|
217 |
-
border-bottom: 1px solid #ccc;
|
218 |
-
}
|
219 |
-
|
220 |
-
|
221 |
-
.rtb-welcome-screen-show-created-menu_items {
|
222 |
-
display: none;
|
223 |
-
}
|
224 |
-
|
225 |
-
.rtb-welcome-screen-show-created-menu_items-image {
|
226 |
-
position: relative;
|
227 |
-
float: left;
|
228 |
-
width: 20%;
|
229 |
-
width: calc(20% - 2px);
|
230 |
-
padding: 9px 2.5%;
|
231 |
-
border-left: 1px solid #ccc;
|
232 |
-
border-right: 1px solid #ccc;
|
233 |
-
font-size: 15px;
|
234 |
-
font-weight: bold;
|
235 |
-
border-top: 1px solid #ccc;
|
236 |
-
border-bottom: 1px solid #ccc;
|
237 |
-
}
|
238 |
-
.rtb-welcome-screen-show-created-menu_items-name {
|
239 |
-
position: relative;
|
240 |
-
float: left;
|
241 |
-
width: 20%;
|
242 |
-
width: calc(20% - 1px);
|
243 |
-
border-right: 1px solid #ccc;
|
244 |
-
padding: 9px 2.5%;
|
245 |
-
font-size: 15px;
|
246 |
-
font-weight: bold;
|
247 |
-
border-top: 1px solid #ccc;
|
248 |
-
border-bottom: 1px solid #ccc;
|
249 |
-
}
|
250 |
-
.rtb-welcome-screen-show-created-menu_items-description {
|
251 |
-
position: relative;
|
252 |
-
float: left;
|
253 |
-
width: 20%;
|
254 |
-
width: calc(20% - 1px);
|
255 |
-
border-right: 1px solid #ccc;
|
256 |
-
padding: 9px 2.5%;
|
257 |
-
font-size: 15px;
|
258 |
-
font-weight: bold;
|
259 |
-
border-top: 1px solid #ccc;
|
260 |
-
border-bottom: 1px solid #ccc;
|
261 |
-
}
|
262 |
-
.rtb-welcome-screen-show-created-menu_items-price {
|
263 |
-
position: relative;
|
264 |
-
float: left;
|
265 |
-
width: 20%;
|
266 |
-
width: calc(20% - 1px);
|
267 |
-
border-right: 1px solid #ccc;
|
268 |
-
padding: 9px 2.5%;
|
269 |
-
font-size: 15px;
|
270 |
-
font-weight: bold;
|
271 |
-
border-top: 1px solid #ccc;
|
272 |
-
border-bottom: 1px solid #ccc;
|
273 |
-
}
|
274 |
-
.rtb-welcome-screen-menu_item-image {
|
275 |
-
position: relative;
|
276 |
-
float: left;
|
277 |
-
width: 20%;
|
278 |
-
width: calc(20% - 2px);
|
279 |
-
padding: 7px 2.5%;
|
280 |
-
height: 100px;
|
281 |
-
overflow: hidden;
|
282 |
-
border-left: 1px solid #ccc;
|
283 |
-
border-right: 1px solid #ccc;
|
284 |
-
border-bottom: 1px solid #ccc;
|
285 |
-
}
|
286 |
-
.rtb-welcome-screen-menu_item-image img {
|
287 |
-
width: auto;
|
288 |
-
height: auto;
|
289 |
-
max-width: 100%;
|
290 |
-
max-height: 100%;
|
291 |
-
}
|
292 |
-
.rtb-welcome-screen-menu_item-name {
|
293 |
-
position: relative;
|
294 |
-
float: left;
|
295 |
-
width: 20%;
|
296 |
-
width: calc(20% - 1px);
|
297 |
-
padding: 7px 2.5%;
|
298 |
-
height: 100px;
|
299 |
-
overflow: hidden;
|
300 |
-
border-right: 1px solid #ccc;
|
301 |
-
border-bottom: 1px solid #ccc;
|
302 |
-
}
|
303 |
-
.rtb-welcome-screen-menu_item-description {
|
304 |
-
position: relative;
|
305 |
-
float: left;
|
306 |
-
width: 20%;
|
307 |
-
width: calc(20% - 1px);
|
308 |
-
padding: 7px 2.5%;
|
309 |
-
height: 100px;
|
310 |
-
overflow: hidden;
|
311 |
-
border-right: 1px solid #ccc;
|
312 |
-
border-bottom: 1px solid #ccc;
|
313 |
-
}
|
314 |
-
.rtb-welcome-screen-menu_item-price {
|
315 |
-
position: relative;
|
316 |
-
float: left;
|
317 |
-
width: 20%;
|
318 |
-
width: calc(20% - 1px);
|
319 |
-
padding: 7px 2.5%;
|
320 |
-
height: 100px;
|
321 |
-
overflow: hidden;
|
322 |
-
border-right: 1px solid #ccc;
|
323 |
-
border-bottom: 1px solid #ccc;
|
324 |
-
}
|
325 |
-
|
326 |
-
.rtb-welcome-screen-add-create_menu-sections h3 {
|
327 |
-
padding-bottom: 0;
|
328 |
-
margin-bottom: 0;
|
329 |
-
}
|
330 |
-
|
331 |
-
.rtb-save-message {
|
332 |
-
clear: both;
|
333 |
-
position: relative;
|
334 |
-
float: left;
|
335 |
-
width: 100%;
|
336 |
-
margin: 16px 0;
|
337 |
-
}
|
338 |
-
.rtb-save-message-inside {
|
339 |
-
position: relative;
|
340 |
-
float: left;
|
341 |
-
padding: 8px 12px;
|
342 |
-
background: #eee;
|
343 |
-
}
|
344 |
-
|
345 |
-
.rtb-welcome-screen-image-preview-container {
|
346 |
-
float: left;
|
347 |
-
width: calc(100% - 160px);
|
348 |
-
margin-bottom: 10px;
|
349 |
-
}
|
350 |
-
.rtb-welcome-screen-image-preview img {
|
351 |
-
width: 180px;
|
352 |
-
height: auto;
|
353 |
-
}
|
354 |
-
|
355 |
-
/*scheduler*/
|
356 |
-
.rtb-welcome-screen-box-content .sap-scheduler label {
|
357 |
-
width: auto;
|
358 |
-
float: none;
|
359 |
-
text-align: center;
|
360 |
-
font-weight: normal;
|
361 |
-
}
|
362 |
-
.rtb-welcome-screen-box-content .sap-scheduler .sap-scheduler-time-input label {
|
363 |
-
text-align: left;
|
364 |
-
}
|
365 |
-
.rtb-welcome-screen-box-content .sap-scheduler input[type="checkbox"] {
|
366 |
-
margin: 6px 4px 7px 0;
|
367 |
-
}
|
1 |
+
.rtb-hidden {
|
2 |
+
display: none;
|
3 |
+
}
|
4 |
+
.rtb-welcome-clear {
|
5 |
+
clear: both;
|
6 |
+
}
|
7 |
+
|
8 |
+
.rtb-welcome-screen-header h1 {
|
9 |
+
padding: 48px 0 16px 0;
|
10 |
+
margin: 0;
|
11 |
+
}
|
12 |
+
.rtb-welcome-screen-header p {
|
13 |
+
margin-bottom: 32px;
|
14 |
+
font-size: 15px;
|
15 |
+
}
|
16 |
+
.rtb-welcome-screen h2 {
|
17 |
+
background: #1b335f;
|
18 |
+
color: #fff;
|
19 |
+
padding: 18px;
|
20 |
+
margin: 0;
|
21 |
+
z-index: 2;
|
22 |
+
position: relative;
|
23 |
+
float: left;
|
24 |
+
width: calc(100% - 36px);
|
25 |
+
border-top: 1px solid #42639F;
|
26 |
+
}
|
27 |
+
.rtb-welcome-screen h2:hover {
|
28 |
+
background: #365388;
|
29 |
+
}
|
30 |
+
|
31 |
+
.rtb-welcome-screen-box .rtb-welcome-screen-box-content {
|
32 |
+
display: none;
|
33 |
+
}
|
34 |
+
|
35 |
+
.rtb-welcome-screen-box.rtb-welcome-screen-open .rtb-welcome-screen-box-content {
|
36 |
+
display: block;
|
37 |
+
}
|
38 |
+
|
39 |
+
.rtb-welcome-screen-box {
|
40 |
+
padding: 0px;
|
41 |
+
max-width: 800px;
|
42 |
+
margin: 0 auto;
|
43 |
+
}
|
44 |
+
|
45 |
+
.rtb-welcome-screen-header,
|
46 |
+
.rtb-welcome-screen-skip-container {
|
47 |
+
max-width: 800px;
|
48 |
+
margin: 0 auto;
|
49 |
+
}
|
50 |
+
|
51 |
+
.rtb-welcome-screen-header {
|
52 |
+
text-align: center;
|
53 |
+
}
|
54 |
+
|
55 |
+
.rtb-welcome-screen-box h2 {
|
56 |
+
cursor: pointer;
|
57 |
+
}
|
58 |
+
|
59 |
+
.rtb-welcome-screen-box-content {
|
60 |
+
position: relative;
|
61 |
+
float: left;
|
62 |
+
background: #fff;
|
63 |
+
width: 90%;
|
64 |
+
margin: 0 auto 16px;
|
65 |
+
box-shadow: 0 1px 12px #ddd;
|
66 |
+
display: block;
|
67 |
+
padding: 18px 5%;
|
68 |
+
}
|
69 |
+
|
70 |
+
.rtb-welcome-screen-add-reservations-page-button,
|
71 |
+
.rtb-welcome-screen-save-schedule-open-button,
|
72 |
+
.rtb-welcome-screen-save-options-button {
|
73 |
+
position: relative;
|
74 |
+
float: left;
|
75 |
+
cursor: pointer;
|
76 |
+
padding: 4px 8px;
|
77 |
+
border: 2px solid #1b335f;
|
78 |
+
color: #fff;
|
79 |
+
border-radius: 2px;
|
80 |
+
background: #1b335f;
|
81 |
+
width: auto;
|
82 |
+
text-align: center;
|
83 |
+
margin-top: 12px;
|
84 |
+
}
|
85 |
+
.rtb-welcome-screen-add-reservations-page-button:hover,
|
86 |
+
.rtb-welcome-screen-save-schedule-open-button:hover,
|
87 |
+
.rtb-welcome-screen-save-options-button:hover {
|
88 |
+
border-color: #365388;
|
89 |
+
background: #365388;
|
90 |
+
}
|
91 |
+
|
92 |
+
.rtb-welcome-screen-next-button,
|
93 |
+
.rtb-welcome-screen-previous-button,
|
94 |
+
.rtb-welcome-screen-finish-button a {
|
95 |
+
position: relative;
|
96 |
+
float: right;
|
97 |
+
cursor: pointer;
|
98 |
+
padding: 4px 8px;
|
99 |
+
border: 2px solid #1b335f;
|
100 |
+
color: #1b335f;
|
101 |
+
border-radius: 2px;
|
102 |
+
background: transparent;
|
103 |
+
width: auto;
|
104 |
+
text-align: center;
|
105 |
+
margin-top: 32px;
|
106 |
+
text-decoration: none;
|
107 |
+
}
|
108 |
+
.rtb-welcome-screen-next-button:hover,
|
109 |
+
.rtb-welcome-screen-previous-button:hover,
|
110 |
+
.rtb-welcome-screen-finish-button a:hover {
|
111 |
+
color: #fff;
|
112 |
+
background: #1b335f;
|
113 |
+
text-decoration: none;
|
114 |
+
}
|
115 |
+
.rtb-welcome-screen-next-button-not-top-margin {
|
116 |
+
margin-top: 12px;
|
117 |
+
}
|
118 |
+
.rtb-welcome-screen-previous-button {
|
119 |
+
float: left;
|
120 |
+
}
|
121 |
+
|
122 |
+
.rtb-welcome-screen-skip-button {
|
123 |
+
position: relative;
|
124 |
+
float: right;
|
125 |
+
cursor: pointer;
|
126 |
+
padding: 4px 8px;
|
127 |
+
border: 2px solid #ccc;
|
128 |
+
color: #999;
|
129 |
+
border-radius: 2px;
|
130 |
+
background: transparent;
|
131 |
+
width: auto;
|
132 |
+
text-align: center;
|
133 |
+
margin-top: 12px;
|
134 |
+
}
|
135 |
+
.rtb-welcome-screen-skip-button:hover {
|
136 |
+
border-color: #aaa;
|
137 |
+
color: #fff;
|
138 |
+
background: #aaa;
|
139 |
+
}
|
140 |
+
|
141 |
+
.rtb-welcome-screen-box-content label {
|
142 |
+
width: 160px;
|
143 |
+
position: relative;
|
144 |
+
float: left;
|
145 |
+
font-weight: bold;
|
146 |
+
}
|
147 |
+
.rtb-welcome-screen-option {
|
148 |
+
margin: 7px 0;
|
149 |
+
}
|
150 |
+
.rtb-welcome-screen-option label {
|
151 |
+
width: 100px;
|
152 |
+
float: left;
|
153 |
+
font-weight: normal;
|
154 |
+
}
|
155 |
+
.rtb-welcome-screen-option label.rtb-admin-switch {
|
156 |
+
width: 40px
|
157 |
+
}
|
158 |
+
|
159 |
+
.rtb-welcome-screen-box-content input {
|
160 |
+
margin: 0;
|
161 |
+
}
|
162 |
+
.rtb-welcome-screen-box-content-divs {
|
163 |
+
margin-bottom: 8px;
|
164 |
+
}
|
165 |
+
|
166 |
+
|
167 |
+
.rtb-welcome-screen-show-created-sections {
|
168 |
+
display: none;
|
169 |
+
}
|
170 |
+
|
171 |
+
.rtb-welcome-screen-show-created-sections-name {
|
172 |
+
position: relative;
|
173 |
+
float: left;
|
174 |
+
width: 40%;
|
175 |
+
width: calc(40% - 2px);
|
176 |
+
padding: 9px 5%;
|
177 |
+
border-left: 1px solid #ccc;
|
178 |
+
border-right: 1px solid #ccc;
|
179 |
+
font-size: 15px;
|
180 |
+
font-weight: bold;
|
181 |
+
border-top: 1px solid #ccc;
|
182 |
+
border-bottom: 1px solid #ccc;
|
183 |
+
}
|
184 |
+
.rtb-welcome-screen-show-created-sections-description {
|
185 |
+
position: relative;
|
186 |
+
float: right;
|
187 |
+
width: 40%;
|
188 |
+
width: calc(40% - 1px);
|
189 |
+
border-right: 1px solid #ccc;
|
190 |
+
padding: 9px 5%;
|
191 |
+
font-size: 15px;
|
192 |
+
font-weight: bold;
|
193 |
+
border-top: 1px solid #ccc;
|
194 |
+
border-bottom: 1px solid #ccc;
|
195 |
+
}
|
196 |
+
.rtb-welcome-screen-section-name {
|
197 |
+
position: relative;
|
198 |
+
float: left;
|
199 |
+
width: 40%;
|
200 |
+
width: calc(40% - 2px);
|
201 |
+
padding: 7px 5%;
|
202 |
+
height: 18px;
|
203 |
+
overflow: hidden;
|
204 |
+
border-left: 1px solid #ccc;
|
205 |
+
border-right: 1px solid #ccc;
|
206 |
+
border-bottom: 1px solid #ccc;
|
207 |
+
}
|
208 |
+
.rtb-welcome-screen-section-description {
|
209 |
+
position: relative;
|
210 |
+
float: right;
|
211 |
+
width: 40%;
|
212 |
+
width: calc(40% - 1px);
|
213 |
+
padding: 7px 5%;
|
214 |
+
height: 18px;
|
215 |
+
overflow: hidden;
|
216 |
+
border-right: 1px solid #ccc;
|
217 |
+
border-bottom: 1px solid #ccc;
|
218 |
+
}
|
219 |
+
|
220 |
+
|
221 |
+
.rtb-welcome-screen-show-created-menu_items {
|
222 |
+
display: none;
|
223 |
+
}
|
224 |
+
|
225 |
+
.rtb-welcome-screen-show-created-menu_items-image {
|
226 |
+
position: relative;
|
227 |
+
float: left;
|
228 |
+
width: 20%;
|
229 |
+
width: calc(20% - 2px);
|
230 |
+
padding: 9px 2.5%;
|
231 |
+
border-left: 1px solid #ccc;
|
232 |
+
border-right: 1px solid #ccc;
|
233 |
+
font-size: 15px;
|
234 |
+
font-weight: bold;
|
235 |
+
border-top: 1px solid #ccc;
|
236 |
+
border-bottom: 1px solid #ccc;
|
237 |
+
}
|
238 |
+
.rtb-welcome-screen-show-created-menu_items-name {
|
239 |
+
position: relative;
|
240 |
+
float: left;
|
241 |
+
width: 20%;
|
242 |
+
width: calc(20% - 1px);
|
243 |
+
border-right: 1px solid #ccc;
|
244 |
+
padding: 9px 2.5%;
|
245 |
+
font-size: 15px;
|
246 |
+
font-weight: bold;
|
247 |
+
border-top: 1px solid #ccc;
|
248 |
+
border-bottom: 1px solid #ccc;
|
249 |
+
}
|
250 |
+
.rtb-welcome-screen-show-created-menu_items-description {
|
251 |
+
position: relative;
|
252 |
+
float: left;
|
253 |
+
width: 20%;
|
254 |
+
width: calc(20% - 1px);
|
255 |
+
border-right: 1px solid #ccc;
|
256 |
+
padding: 9px 2.5%;
|
257 |
+
font-size: 15px;
|
258 |
+
font-weight: bold;
|
259 |
+
border-top: 1px solid #ccc;
|
260 |
+
border-bottom: 1px solid #ccc;
|
261 |
+
}
|
262 |
+
.rtb-welcome-screen-show-created-menu_items-price {
|
263 |
+
position: relative;
|
264 |
+
float: left;
|
265 |
+
width: 20%;
|
266 |
+
width: calc(20% - 1px);
|
267 |
+
border-right: 1px solid #ccc;
|
268 |
+
padding: 9px 2.5%;
|
269 |
+
font-size: 15px;
|
270 |
+
font-weight: bold;
|
271 |
+
border-top: 1px solid #ccc;
|
272 |
+
border-bottom: 1px solid #ccc;
|
273 |
+
}
|
274 |
+
.rtb-welcome-screen-menu_item-image {
|
275 |
+
position: relative;
|
276 |
+
float: left;
|
277 |
+
width: 20%;
|
278 |
+
width: calc(20% - 2px);
|
279 |
+
padding: 7px 2.5%;
|
280 |
+
height: 100px;
|
281 |
+
overflow: hidden;
|
282 |
+
border-left: 1px solid #ccc;
|
283 |
+
border-right: 1px solid #ccc;
|
284 |
+
border-bottom: 1px solid #ccc;
|
285 |
+
}
|
286 |
+
.rtb-welcome-screen-menu_item-image img {
|
287 |
+
width: auto;
|
288 |
+
height: auto;
|
289 |
+
max-width: 100%;
|
290 |
+
max-height: 100%;
|
291 |
+
}
|
292 |
+
.rtb-welcome-screen-menu_item-name {
|
293 |
+
position: relative;
|
294 |
+
float: left;
|
295 |
+
width: 20%;
|
296 |
+
width: calc(20% - 1px);
|
297 |
+
padding: 7px 2.5%;
|
298 |
+
height: 100px;
|
299 |
+
overflow: hidden;
|
300 |
+
border-right: 1px solid #ccc;
|
301 |
+
border-bottom: 1px solid #ccc;
|
302 |
+
}
|
303 |
+
.rtb-welcome-screen-menu_item-description {
|
304 |
+
position: relative;
|
305 |
+
float: left;
|
306 |
+
width: 20%;
|
307 |
+
width: calc(20% - 1px);
|
308 |
+
padding: 7px 2.5%;
|
309 |
+
height: 100px;
|
310 |
+
overflow: hidden;
|
311 |
+
border-right: 1px solid #ccc;
|
312 |
+
border-bottom: 1px solid #ccc;
|
313 |
+
}
|
314 |
+
.rtb-welcome-screen-menu_item-price {
|
315 |
+
position: relative;
|
316 |
+
float: left;
|
317 |
+
width: 20%;
|
318 |
+
width: calc(20% - 1px);
|
319 |
+
padding: 7px 2.5%;
|
320 |
+
height: 100px;
|
321 |
+
overflow: hidden;
|
322 |
+
border-right: 1px solid #ccc;
|
323 |
+
border-bottom: 1px solid #ccc;
|
324 |
+
}
|
325 |
+
|
326 |
+
.rtb-welcome-screen-add-create_menu-sections h3 {
|
327 |
+
padding-bottom: 0;
|
328 |
+
margin-bottom: 0;
|
329 |
+
}
|
330 |
+
|
331 |
+
.rtb-save-message {
|
332 |
+
clear: both;
|
333 |
+
position: relative;
|
334 |
+
float: left;
|
335 |
+
width: 100%;
|
336 |
+
margin: 16px 0;
|
337 |
+
}
|
338 |
+
.rtb-save-message-inside {
|
339 |
+
position: relative;
|
340 |
+
float: left;
|
341 |
+
padding: 8px 12px;
|
342 |
+
background: #eee;
|
343 |
+
}
|
344 |
+
|
345 |
+
.rtb-welcome-screen-image-preview-container {
|
346 |
+
float: left;
|
347 |
+
width: calc(100% - 160px);
|
348 |
+
margin-bottom: 10px;
|
349 |
+
}
|
350 |
+
.rtb-welcome-screen-image-preview img {
|
351 |
+
width: 180px;
|
352 |
+
height: auto;
|
353 |
+
}
|
354 |
+
|
355 |
+
/*scheduler*/
|
356 |
+
.rtb-welcome-screen-box-content .sap-scheduler label {
|
357 |
+
width: auto;
|
358 |
+
float: none;
|
359 |
+
text-align: center;
|
360 |
+
font-weight: normal;
|
361 |
+
}
|
362 |
+
.rtb-welcome-screen-box-content .sap-scheduler .sap-scheduler-time-input label {
|
363 |
+
text-align: left;
|
364 |
+
}
|
365 |
+
.rtb-welcome-screen-box-content .sap-scheduler input[type="checkbox"] {
|
366 |
+
margin: 6px 4px 7px 0;
|
367 |
+
}
|
assets/css/booking-form.css
CHANGED
@@ -1,392 +1,396 @@
|
|
1 |
-
/* Frontend CSS Stylesheet for Restaurant Reservations */
|
2 |
-
|
3 |
-
.rtb-clear {
|
4 |
-
clear: both;
|
5 |
-
}
|
6 |
-
.rtb-hidden {
|
7 |
-
display: none;
|
8 |
-
}
|
9 |
-
.rtb-booking-form {
|
10 |
-
position: relative;
|
11 |
-
/*float: left;*/
|
12 |
-
width: 100%;
|
13 |
-
clear: both;
|
14 |
-
margin-bottom: 48px;
|
15 |
-
}
|
16 |
-
.rtb-booking-form fieldset {
|
17 |
-
padding-bottom: 1em;
|
18 |
-
margin-bottom: 1em;
|
19 |
-
}
|
20 |
-
.rtb-booking-form legend {
|
21 |
-
padding: 0 0.5em;
|
22 |
-
}
|
23 |
-
.rtb-booking-form fieldset>div {
|
24 |
-
margin-top: 1em;
|
25 |
-
}
|
26 |
-
.rtb-booking-form fieldset>div:first-child {
|
27 |
-
margin-top: 0;
|
28 |
-
}
|
29 |
-
.rtb-booking-form label {
|
30 |
-
display: block;
|
31 |
-
}
|
32 |
-
.rtb-booking-form input,
|
33 |
-
#stripe-payment-form #cardElement {
|
34 |
-
width: 100%;
|
35 |
-
max-width: 15em;
|
36 |
-
}
|
37 |
-
#stripe-payment-form #cardElement {
|
38 |
-
max-width: 500px;
|
39 |
-
width: 100%;
|
40 |
-
display: block;
|
41 |
-
border: 1px solid #ccc;
|
42 |
-
border-radius: 0;
|
43 |
-
background-color: white;
|
44 |
-
color: #777;
|
45 |
-
padding: 12px 21px;
|
46 |
-
margin: 18px 0;
|
47 |
-
}
|
48 |
-
.rtb-booking-form input[type="checkbox"],
|
49 |
-
.rtb-booking-form input[type="radio"] {
|
50 |
-
width: auto;
|
51 |
-
margin-right: 0.25em;
|
52 |
-
vertical-align: middle;
|
53 |
-
}
|
54 |
-
.rtb-booking-form textarea {
|
55 |
-
width: 100%;
|
56 |
-
max-width: 30em;
|
57 |
-
}
|
58 |
-
.rtb-booking-form .add-message {
|
59 |
-
margin-top: 1em;
|
60 |
-
}
|
61 |
-
.rtb-booking-form .message {
|
62 |
-
position: relative;
|
63 |
-
top: auto;
|
64 |
-
left: auto;
|
65 |
-
display: none;
|
66 |
-
}
|
67 |
-
.rtb-booking-form .message-open {
|
68 |
-
display: block;
|
69 |
-
}
|
70 |
-
.rtb-booking-form .message textarea {
|
71 |
-
min-height: 6em;
|
72 |
-
}
|
73 |
-
.rtb-booking-form .rtb-error {
|
74 |
-
font-size: 0.85em;
|
75 |
-
padding: 0.25em;
|
76 |
-
background: #f50;
|
77 |
-
color: #fff;
|
78 |
-
}
|
79 |
-
.rtb-booking-form .rtb-error:before {
|
80 |
-
content: ' ';
|
81 |
-
border-top: 4px solid;
|
82 |
-
border-right: 4px solid transparent;
|
83 |
-
border-left: 4px solid transparent;
|
84 |
-
width: 0;
|
85 |
-
height: 0;
|
86 |
-
display: inline-block;
|
87 |
-
margin: 0.25em 0.5em;
|
88 |
-
}
|
89 |
-
|
90 |
-
/* Compatibility styles for pickadate on common themes */
|
91 |
-
.picker {
|
92 |
-
outline: 0;
|
93 |
-
}
|
94 |
-
#rtb-date_root .picker__button--clear,
|
95 |
-
#rtb-date_root .picker__button--today,
|
96 |
-
#rtb-date_root .picker__button--close {
|
97 |
-
/* don't adopt the theme's button text color */
|
98 |
-
color: #000;
|
99 |
-
}
|
100 |
-
#rtb-date_root .picker__nav--next,
|
101 |
-
#rtb-date_root .picker__nav--prev {
|
102 |
-
/* next/prev calendar arrows button areas sometimes don't cover the arrows */
|
103 |
-
min-height: 1em;
|
104 |
-
}
|
105 |
-
#rtb-time_root .picker__list,
|
106 |
-
#rtb-time_root .picker__list li {
|
107 |
-
/* override some theme's list styles */
|
108 |
-
list-style: none;
|
109 |
-
margin: 0 0 0 1px;
|
110 |
-
}
|
111 |
-
|
112 |
-
|
113 |
-
/* VIEW BOOKINGS TABLE AND ARRIVAL LIGHTBOX */
|
114 |
-
|
115 |
-
.rtb-view-bookings-table {
|
116 |
-
margin-top: 24px;
|
117 |
-
border-collapse: collapse;
|
118 |
-
}
|
119 |
-
.rtb-view-bookings-table,
|
120 |
-
.rtb-view-bookings-table tr,
|
121 |
-
.rtb-view-bookings-table th,
|
122 |
-
.rtb-view-bookings-table td {
|
123 |
-
border: 1px solid #ccc;
|
124 |
-
}
|
125 |
-
.rtb-view-bookings-table th {
|
126 |
-
font-weight: bold;
|
127 |
-
text-align: center;
|
128 |
-
}
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
.rtb-view-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
#
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
margin-
|
290 |
-
}
|
291 |
-
.rtb-cancel-booking-div
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
}
|
298 |
-
.rtb-cancel-booking-div
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
.rtb-cancel-booking {
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
}
|
317 |
-
.rtb-cancel-booking
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
.rtb-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
}
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
}
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
:
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
.booking-payment-wrapper
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
margin
|
|
|
|
|
|
|
|
|
392 |
}
|
1 |
+
/* Frontend CSS Stylesheet for Restaurant Reservations */
|
2 |
+
|
3 |
+
.rtb-clear {
|
4 |
+
clear: both;
|
5 |
+
}
|
6 |
+
.rtb-hidden {
|
7 |
+
display: none;
|
8 |
+
}
|
9 |
+
.rtb-booking-form {
|
10 |
+
position: relative;
|
11 |
+
/*float: left;*/
|
12 |
+
width: 100%;
|
13 |
+
clear: both;
|
14 |
+
margin-bottom: 48px;
|
15 |
+
}
|
16 |
+
.rtb-booking-form fieldset {
|
17 |
+
padding-bottom: 1em;
|
18 |
+
margin-bottom: 1em;
|
19 |
+
}
|
20 |
+
.rtb-booking-form legend {
|
21 |
+
padding: 0 0.5em;
|
22 |
+
}
|
23 |
+
.rtb-booking-form fieldset>div {
|
24 |
+
margin-top: 1em;
|
25 |
+
}
|
26 |
+
.rtb-booking-form fieldset>div:first-child {
|
27 |
+
margin-top: 0;
|
28 |
+
}
|
29 |
+
.rtb-booking-form label {
|
30 |
+
display: block;
|
31 |
+
}
|
32 |
+
.rtb-booking-form input,
|
33 |
+
#stripe-payment-form #cardElement {
|
34 |
+
width: 100%;
|
35 |
+
max-width: 15em;
|
36 |
+
}
|
37 |
+
#stripe-payment-form #cardElement {
|
38 |
+
max-width: 500px;
|
39 |
+
width: 100%;
|
40 |
+
display: block;
|
41 |
+
border: 1px solid #ccc;
|
42 |
+
border-radius: 0;
|
43 |
+
background-color: white;
|
44 |
+
color: #777;
|
45 |
+
padding: 12px 21px;
|
46 |
+
margin: 18px 0;
|
47 |
+
}
|
48 |
+
.rtb-booking-form input[type="checkbox"],
|
49 |
+
.rtb-booking-form input[type="radio"] {
|
50 |
+
width: auto;
|
51 |
+
margin-right: 0.25em;
|
52 |
+
vertical-align: middle;
|
53 |
+
}
|
54 |
+
.rtb-booking-form textarea {
|
55 |
+
width: 100%;
|
56 |
+
max-width: 30em;
|
57 |
+
}
|
58 |
+
.rtb-booking-form .add-message {
|
59 |
+
margin-top: 1em;
|
60 |
+
}
|
61 |
+
.rtb-booking-form .message {
|
62 |
+
position: relative;
|
63 |
+
top: auto;
|
64 |
+
left: auto;
|
65 |
+
display: none;
|
66 |
+
}
|
67 |
+
.rtb-booking-form .message-open {
|
68 |
+
display: block;
|
69 |
+
}
|
70 |
+
.rtb-booking-form .message textarea {
|
71 |
+
min-height: 6em;
|
72 |
+
}
|
73 |
+
.rtb-booking-form .rtb-error {
|
74 |
+
font-size: 0.85em;
|
75 |
+
padding: 0.25em;
|
76 |
+
background: #f50;
|
77 |
+
color: #fff;
|
78 |
+
}
|
79 |
+
.rtb-booking-form .rtb-error:before {
|
80 |
+
content: ' ';
|
81 |
+
border-top: 4px solid;
|
82 |
+
border-right: 4px solid transparent;
|
83 |
+
border-left: 4px solid transparent;
|
84 |
+
width: 0;
|
85 |
+
height: 0;
|
86 |
+
display: inline-block;
|
87 |
+
margin: 0.25em 0.5em;
|
88 |
+
}
|
89 |
+
|
90 |
+
/* Compatibility styles for pickadate on common themes */
|
91 |
+
.picker {
|
92 |
+
outline: 0;
|
93 |
+
}
|
94 |
+
#rtb-date_root .picker__button--clear,
|
95 |
+
#rtb-date_root .picker__button--today,
|
96 |
+
#rtb-date_root .picker__button--close {
|
97 |
+
/* don't adopt the theme's button text color */
|
98 |
+
color: #000;
|
99 |
+
}
|
100 |
+
#rtb-date_root .picker__nav--next,
|
101 |
+
#rtb-date_root .picker__nav--prev {
|
102 |
+
/* next/prev calendar arrows button areas sometimes don't cover the arrows */
|
103 |
+
min-height: 1em;
|
104 |
+
}
|
105 |
+
#rtb-time_root .picker__list,
|
106 |
+
#rtb-time_root .picker__list li {
|
107 |
+
/* override some theme's list styles */
|
108 |
+
list-style: none;
|
109 |
+
margin: 0 0 0 1px;
|
110 |
+
}
|
111 |
+
|
112 |
+
|
113 |
+
/* VIEW BOOKINGS TABLE AND ARRIVAL LIGHTBOX */
|
114 |
+
|
115 |
+
.rtb-view-bookings-table {
|
116 |
+
margin-top: 24px;
|
117 |
+
border-collapse: collapse;
|
118 |
+
}
|
119 |
+
.rtb-view-bookings-table,
|
120 |
+
.rtb-view-bookings-table tr,
|
121 |
+
.rtb-view-bookings-table th,
|
122 |
+
.rtb-view-bookings-table td {
|
123 |
+
border: 1px solid #ccc;
|
124 |
+
}
|
125 |
+
.rtb-view-bookings-table th {
|
126 |
+
font-weight: bold;
|
127 |
+
text-align: center;
|
128 |
+
}
|
129 |
+
|
130 |
+
.rtb-view-booking-details-label {
|
131 |
+
font-weight: 600;
|
132 |
+
}
|
133 |
+
|
134 |
+
@media screen and (max-width: 900px) {
|
135 |
+
.rtb-view-bookings-table th:nth-of-type(n+6),
|
136 |
+
.rtb-view-bookings-table td:nth-of-type(n+6) {
|
137 |
+
display: none;
|
138 |
+
}
|
139 |
+
}
|
140 |
+
@media screen and (max-width: 600px) {
|
141 |
+
.rtb-view-bookings-table th:nth-of-type(n+5),
|
142 |
+
.rtb-view-bookings-table td:nth-of-type(n+5) {
|
143 |
+
display: none;
|
144 |
+
}
|
145 |
+
}
|
146 |
+
|
147 |
+
.rtb-edit-view-booking {
|
148 |
+
width: 20px;
|
149 |
+
height: 20px;
|
150 |
+
margin-left: calc(50% - 10px);
|
151 |
+
}
|
152 |
+
.rtb-edit-view-booking[disabled] {
|
153 |
+
opacity: .75;
|
154 |
+
}
|
155 |
+
|
156 |
+
.rtb-view-bookings-form-confirmation-background-div {
|
157 |
+
position: fixed;
|
158 |
+
top: 0;
|
159 |
+
left: 0;
|
160 |
+
width: 100%;
|
161 |
+
height: 100%;
|
162 |
+
z-index: 999999;
|
163 |
+
background: rgba(0,0,0,0.5);
|
164 |
+
}
|
165 |
+
.rtb-view-bookings-form-confirmation-div {
|
166 |
+
position: fixed;
|
167 |
+
top: 200px;
|
168 |
+
width: 480px;
|
169 |
+
left: calc(50% - 240px);
|
170 |
+
z-index: 1000000;
|
171 |
+
margin: 0;
|
172 |
+
background: #fff;
|
173 |
+
font-size: 18px;
|
174 |
+
text-align: center;
|
175 |
+
border-radius: 2px;
|
176 |
+
box-shadow: 0 0 4px #555;
|
177 |
+
}
|
178 |
+
.rtb-view-bookings-form-confirmation-div-inside {
|
179 |
+
position: relative;
|
180 |
+
float: left;
|
181 |
+
width: 100%;
|
182 |
+
}
|
183 |
+
.rtb-view-bookings-form-confirmation-div-title {
|
184 |
+
position: relative;
|
185 |
+
float: left;
|
186 |
+
width: 90%;
|
187 |
+
margin: 24px 5%;
|
188 |
+
color: #333;
|
189 |
+
}
|
190 |
+
.rtb-view-bookings-form-confirmation-accept {
|
191 |
+
box-sizing: border-box;
|
192 |
+
position: relative;
|
193 |
+
float: left;
|
194 |
+
width: 32%;
|
195 |
+
margin: 24px 34% 0;
|
196 |
+
padding: 8px 0;
|
197 |
+
background: #222;
|
198 |
+
color: #fff;
|
199 |
+
border-radius: 2px;
|
200 |
+
cursor: pointer;
|
201 |
+
transition: background .35s;
|
202 |
+
}
|
203 |
+
.rtb-view-bookings-form-confirmation-accept:hover {
|
204 |
+
background: #555;
|
205 |
+
}
|
206 |
+
.rtb-view-bookings-form-confirmation-decline {
|
207 |
+
box-sizing: border-box;
|
208 |
+
position: relative;
|
209 |
+
float: left;
|
210 |
+
width: 32%;
|
211 |
+
margin: 8px 34% 24px;
|
212 |
+
padding: 6px 0;
|
213 |
+
background: transparent;
|
214 |
+
color: #222;
|
215 |
+
border: 2px solid #222;
|
216 |
+
border-radius: 2px;
|
217 |
+
cursor: pointer;
|
218 |
+
transition: background .35s, border-color .35s;
|
219 |
+
}
|
220 |
+
.rtb-view-bookings-form-confirmation-decline:hover {
|
221 |
+
background: #555;
|
222 |
+
color: #fff;
|
223 |
+
border-color: #555;
|
224 |
+
}
|
225 |
+
|
226 |
+
#rtb-view-bookings-form-close {
|
227 |
+
position: absolute;
|
228 |
+
display: flex;
|
229 |
+
justify-content: center;
|
230 |
+
align-items: center;
|
231 |
+
top: 0;
|
232 |
+
right: 0;
|
233 |
+
width: 32px;
|
234 |
+
height: 32px;
|
235 |
+
background: #555;
|
236 |
+
color: #fff;
|
237 |
+
border-bottom-left-radius: 2px;
|
238 |
+
font-size: 15px;
|
239 |
+
cursor: pointer;
|
240 |
+
transition: background .35s;
|
241 |
+
}
|
242 |
+
#rtb-view-bookings-form-close:hover {
|
243 |
+
background: #222;
|
244 |
+
}
|
245 |
+
|
246 |
+
@media screen and (max-width: 568px) {
|
247 |
+
.rtb-view-bookings-form-confirmation-div {
|
248 |
+
top: 100px;
|
249 |
+
width: 300px;
|
250 |
+
left: calc(50% - 150px);
|
251 |
+
}
|
252 |
+
}
|
253 |
+
|
254 |
+
|
255 |
+
/*CANCEL LINK*/
|
256 |
+
.rtb-modification-toggle {
|
257 |
+
position: relative;
|
258 |
+
float: left;
|
259 |
+
padding: 10px 15px;
|
260 |
+
margin-bottom: 24px;
|
261 |
+
background: #444;
|
262 |
+
color: #fff;
|
263 |
+
border-radius: 3px;
|
264 |
+
cursor: pointer;
|
265 |
+
}
|
266 |
+
label[for="rtb-modification-email"],
|
267 |
+
input[name="modification"] {
|
268 |
+
float: left;
|
269 |
+
margin-top: 20px;
|
270 |
+
}
|
271 |
+
label[for="rtb-modification-email"] {
|
272 |
+
margin-right: 12px;
|
273 |
+
}
|
274 |
+
.rtb-find-reservation-button {
|
275 |
+
position: relative;
|
276 |
+
float: left;
|
277 |
+
padding: 10px 15px;
|
278 |
+
margin-top: 24px;
|
279 |
+
background: #444;
|
280 |
+
color: #fff;
|
281 |
+
border-radius: 3px;
|
282 |
+
cursor: pointer;
|
283 |
+
}
|
284 |
+
|
285 |
+
.rtb-bookings-results {
|
286 |
+
position: relative;
|
287 |
+
float: left;
|
288 |
+
width: 100%;
|
289 |
+
margin-top: 16px;
|
290 |
+
}
|
291 |
+
.rtb-cancel-booking-div {
|
292 |
+
border: 1px solid #ddd;
|
293 |
+
margin-bottom: 8px;
|
294 |
+
}
|
295 |
+
.rtb-cancel-booking-div + .alert {
|
296 |
+
margin: 0;
|
297 |
+
}
|
298 |
+
.rtb-cancel-booking-div + .alert.error {
|
299 |
+
color: #f24a4d;
|
300 |
+
background: #f24a4d47;
|
301 |
+
}
|
302 |
+
.rtb-cancel-booking-div *:not(:first-child) {
|
303 |
+
margin-left: -5px;
|
304 |
+
}
|
305 |
+
|
306 |
+
.rtb-cancel-booking {
|
307 |
+
text-align: center;
|
308 |
+
padding: 10px 0;
|
309 |
+
background: #fe4e4e;
|
310 |
+
color: #fff;
|
311 |
+
cursor: pointer;
|
312 |
+
display: inline-block;
|
313 |
+
max-width: 100px;
|
314 |
+
min-width: 100px;
|
315 |
+
width: 100%;
|
316 |
+
}
|
317 |
+
.rtb-cancel-booking:hover {
|
318 |
+
background: #ff6b6b;
|
319 |
+
color: #fff;
|
320 |
+
}
|
321 |
+
.rtb-cancel-booking.cancelled {
|
322 |
+
background: #24b124;
|
323 |
+
}
|
324 |
+
|
325 |
+
.rtb-deposit-booking {
|
326 |
+
text-align: center;
|
327 |
+
padding: 10px 0;
|
328 |
+
background: green;
|
329 |
+
color: #fff;
|
330 |
+
cursor: pointer;
|
331 |
+
display: inline-block;
|
332 |
+
max-width: 100px;
|
333 |
+
min-width: 100px;
|
334 |
+
width: 100%;
|
335 |
+
}
|
336 |
+
.rtb-deposit-booking:hover {
|
337 |
+
background: #008000cf;
|
338 |
+
color: #fff;
|
339 |
+
}
|
340 |
+
|
341 |
+
.rtb-booking-information {
|
342 |
+
padding-left: 5px;
|
343 |
+
display: inline-block;
|
344 |
+
}
|
345 |
+
|
346 |
+
#rtb_recaptcha {
|
347 |
+
position: relative;
|
348 |
+
float: left;
|
349 |
+
width: 100%;
|
350 |
+
margin: 16px 0;
|
351 |
+
}
|
352 |
+
|
353 |
+
.stripe-payment-help-text {
|
354 |
+
display: none;
|
355 |
+
}
|
356 |
+
.payment-errors {
|
357 |
+
margin: 32px 0;
|
358 |
+
border-left: 4px solid #000;
|
359 |
+
padding-left: 16px;
|
360 |
+
}
|
361 |
+
:is(.rtb-booking-form, #stripe-booking-form) button:disabled {
|
362 |
+
background-color: gray;
|
363 |
+
}
|
364 |
+
:is(.rtb-booking-form, #stripe-booking-form) button:disabled:hover {
|
365 |
+
text-decoration: none;
|
366 |
+
}
|
367 |
+
|
368 |
+
/* Payment Detail Summary */
|
369 |
+
.booking-payment-wrapper .summary-title {
|
370 |
+
width: 100%;
|
371 |
+
display: block;
|
372 |
+
}
|
373 |
+
.booking-payment-wrapper dl.summary-detail {
|
374 |
+
box-sizing: border-box;
|
375 |
+
width: 100%;
|
376 |
+
display: block;
|
377 |
+
padding: 12px 16px;
|
378 |
+
background: #fafafa;
|
379 |
+
border: 1px solid #ddd;
|
380 |
+
border-radius: 2px;
|
381 |
+
}
|
382 |
+
.booking-payment-wrapper dl.summary-detail dt {
|
383 |
+
width: 120px;
|
384 |
+
padding-right: 20px;
|
385 |
+
display: inline-block;
|
386 |
+
margin: 0 0 8px;
|
387 |
+
}
|
388 |
+
.booking-payment-wrapper dl.summary-detail dd {
|
389 |
+
width: calc(100% - 140px);
|
390 |
+
display: inline-block;
|
391 |
+
margin: 0 0 8px;
|
392 |
+
}
|
393 |
+
.booking-payment-wrapper dl.summary-detail dt:last-of-type,
|
394 |
+
.booking-payment-wrapper dl.summary-detail dd:last-of-type {
|
395 |
+
margin-bottom: 0;
|
396 |
}
|
assets/css/dashboard-review-ask.css
CHANGED
@@ -1,61 +1,61 @@
|
|
1 |
-
.rtb-hidden {
|
2 |
-
display: none;
|
3 |
-
}
|
4 |
-
.rtb-clear {
|
5 |
-
clear: both;
|
6 |
-
}
|
7 |
-
.rtb-main-dashboard-review-ask {
|
8 |
-
border-left-color: #1b335f;
|
9 |
-
}
|
10 |
-
.rtb-review-ask-plugin-icon {
|
11 |
-
height: 100px;
|
12 |
-
width: 100px;
|
13 |
-
float: left;
|
14 |
-
margin: 12px 24px 16px 10px;
|
15 |
-
background-image: url(../img/rtb-icon.png);
|
16 |
-
background-size: contain;
|
17 |
-
}
|
18 |
-
.rtb-review-ask-text p {
|
19 |
-
padding: 10px 20px;
|
20 |
-
font-weight: 600;
|
21 |
-
font-size: 20px;
|
22 |
-
}
|
23 |
-
.rtb-review-ask-action {
|
24 |
-
width: 90px;
|
25 |
-
padding: 6px;
|
26 |
-
margin-right: 18px;
|
27 |
-
text-align: center;
|
28 |
-
float: left;
|
29 |
-
border-radius: 4px;
|
30 |
-
cursor: pointer;
|
31 |
-
}
|
32 |
-
.rtb-review-ask-white {
|
33 |
-
color: #999999;
|
34 |
-
border: 1px solid #bbbbbb;
|
35 |
-
}
|
36 |
-
.rtb-review-ask-green {
|
37 |
-
color: #ffffff;
|
38 |
-
background: #7CA3BF;
|
39 |
-
border: 1px solid #7CA3BF;
|
40 |
-
font-weight: 600;
|
41 |
-
}
|
42 |
-
.rtb-review-ask-green a {
|
43 |
-
color: #ffffff;
|
44 |
-
text-decoration: none;
|
45 |
-
}
|
46 |
-
.rtb-review-ask-feedback-form {
|
47 |
-
position: relative;
|
48 |
-
float: left;
|
49 |
-
}.rtb-review-ask-feedback-explanation textarea {
|
50 |
-
height: 70px;
|
51 |
-
width: 480px;
|
52 |
-
}
|
53 |
-
.rtb-review-ask-send-feedback {
|
54 |
-
margin: 10px 0px;
|
55 |
-
}
|
56 |
-
|
57 |
-
|
58 |
-
.rtb-review-ask-review-text span {
|
59 |
-
font-size: .8em;
|
60 |
-
font-weight: normal;
|
61 |
}
|
1 |
+
.rtb-hidden {
|
2 |
+
display: none;
|
3 |
+
}
|
4 |
+
.rtb-clear {
|
5 |
+
clear: both;
|
6 |
+
}
|
7 |
+
.rtb-main-dashboard-review-ask {
|
8 |
+
border-left-color: #1b335f;
|
9 |
+
}
|
10 |
+
.rtb-review-ask-plugin-icon {
|
11 |
+
height: 100px;
|
12 |
+
width: 100px;
|
13 |
+
float: left;
|
14 |
+
margin: 12px 24px 16px 10px;
|
15 |
+
background-image: url(../img/rtb-icon.png);
|
16 |
+
background-size: contain;
|
17 |
+
}
|
18 |
+
.rtb-review-ask-text p {
|
19 |
+
padding: 10px 20px;
|
20 |
+
font-weight: 600;
|
21 |
+
font-size: 20px;
|
22 |
+
}
|
23 |
+
.rtb-review-ask-action {
|
24 |
+
width: 90px;
|
25 |
+
padding: 6px;
|
26 |
+
margin-right: 18px;
|
27 |
+
text-align: center;
|
28 |
+
float: left;
|
29 |
+
border-radius: 4px;
|
30 |
+
cursor: pointer;
|
31 |
+
}
|
32 |
+
.rtb-review-ask-white {
|
33 |
+
color: #999999;
|
34 |
+
border: 1px solid #bbbbbb;
|
35 |
+
}
|
36 |
+
.rtb-review-ask-green {
|
37 |
+
color: #ffffff;
|
38 |
+
background: #7CA3BF;
|
39 |
+
border: 1px solid #7CA3BF;
|
40 |
+
font-weight: 600;
|
41 |
+
}
|
42 |
+
.rtb-review-ask-green a {
|
43 |
+
color: #ffffff;
|
44 |
+
text-decoration: none;
|
45 |
+
}
|
46 |
+
.rtb-review-ask-feedback-form {
|
47 |
+
position: relative;
|
48 |
+
float: left;
|
49 |
+
}.rtb-review-ask-feedback-explanation textarea {
|
50 |
+
height: 70px;
|
51 |
+
width: 480px;
|
52 |
+
}
|
53 |
+
.rtb-review-ask-send-feedback {
|
54 |
+
margin: 10px 0px;
|
55 |
+
}
|
56 |
+
|
57 |
+
|
58 |
+
.rtb-review-ask-review-text span {
|
59 |
+
font-size: .8em;
|
60 |
+
font-weight: normal;
|
61 |
}
|
assets/css/editor.css
CHANGED
@@ -1,582 +1,582 @@
|
|
1 |
-
@keyframes cffrtbrotate {
|
2 |
-
0% {
|
3 |
-
transform: rotateZ(-360deg);
|
4 |
-
-webkit-transform: rotateZ(-360deg);
|
5 |
-
-moz-transform: rotateZ(-360deg);
|
6 |
-
-o-transform: rotateZ(-360deg);
|
7 |
-
}
|
8 |
-
100% {
|
9 |
-
transform: rotateZ(0deg);
|
10 |
-
-webkit-transform: rotateZ(0deg);
|
11 |
-
-moz-transform: rotateZ(0deg);
|
12 |
-
-o-transform: rotateZ(0deg);
|
13 |
-
}
|
14 |
-
}
|
15 |
-
@-webkit-keyframes cffrtbrotate {
|
16 |
-
0% {
|
17 |
-
transform: rotateZ(-360deg);
|
18 |
-
-webkit-transform: rotateZ(-360deg);
|
19 |
-
-moz-transform: rotateZ(-360deg);
|
20 |
-
-o-transform: rotateZ(-360deg);
|
21 |
-
}
|
22 |
-
100% {
|
23 |
-
transform: rotateZ(0deg);
|
24 |
-
-webkit-transform: rotateZ(0deg);
|
25 |
-
-moz-transform: rotateZ(0deg);
|
26 |
-
-o-transform: rotateZ(0deg);
|
27 |
-
}
|
28 |
-
}
|
29 |
-
@-moz-keyframes cffrtbrotate {
|
30 |
-
0% {
|
31 |
-
transform: rotateZ(-360deg);
|
32 |
-
-webkit-transform: rotateZ(-360deg);
|
33 |
-
-moz-transform: rotateZ(-360deg);
|
34 |
-
-o-transform: rotateZ(-360deg);
|
35 |
-
}
|
36 |
-
100% {
|
37 |
-
transform: rotateZ(0deg);
|
38 |
-
-webkit-transform: rotateZ(0deg);
|
39 |
-
-moz-transform: rotateZ(0deg);
|
40 |
-
-o-transform: rotateZ(0deg);
|
41 |
-
}
|
42 |
-
}
|
43 |
-
@-o-keyframes cffrtbrotate {
|
44 |
-
0% {
|
45 |
-
transform: rotateZ(-360deg);
|
46 |
-
-webkit-transform: rotateZ(-360deg);
|
47 |
-
-moz-transform: rotateZ(-360deg);
|
48 |
-
-o-transform: rotateZ(-360deg);
|
49 |
-
}
|
50 |
-
100% {
|
51 |
-
transform: rotateZ(0deg);
|
52 |
-
-webkit-transform: rotateZ(0deg);
|
53 |
-
-moz-transform: rotateZ(0deg);
|
54 |
-
-o-transform: rotateZ(0deg);
|
55 |
-
}
|
56 |
-
}
|
57 |
-
#cffrtb-editor .load-spinner:after,
|
58 |
-
#cffrtb-field-editor-form .load-spinner:after {
|
59 |
-
display: block;
|
60 |
-
position: relative;
|
61 |
-
width: 20px;
|
62 |
-
height: 20px;
|
63 |
-
-webkit-animation: cffrtbrotate 0.6s linear infinite;
|
64 |
-
-moz-animation: cffrtbrotate 0.6s linear infinite;
|
65 |
-
-ms-animation: cffrtbrotate 0.6s linear infinite;
|
66 |
-
-o-animation: cffrtbrotate 0.6s linear infinite;
|
67 |
-
animation: cffrtbrotate 0.6s linear infinite;
|
68 |
-
border-radius: 100%;
|
69 |
-
border-top: 1px solid #777;
|
70 |
-
border-bottom: 1px solid #D1DDE2;
|
71 |
-
border-left: 1px solid #777;
|
72 |
-
border-right: 1px solid #D1DDE2;
|
73 |
-
content: '';
|
74 |
-
opacity: 0.5;
|
75 |
-
}
|
76 |
-
#cffrtb-editor {
|
77 |
-
margin-top: 2em;
|
78 |
-
}
|
79 |
-
.cffrtb-list {
|
80 |
-
margin: 0;
|
81 |
-
padding: 0;
|
82 |
-
/* Ensure there is space to drop a field into an empty fieldset */
|
83 |
-
}
|
84 |
-
.cffrtb-list .title {
|
85 |
-
position: relative;
|
86 |
-
padding: 1em;
|
87 |
-
min-height: 1.4em;
|
88 |
-
background: #D1DDE2;
|
89 |
-
border: 1px solid #82878c;
|
90 |
-
margin-bottom: 30px;
|
91 |
-
overflow: hidden;
|
92 |
-
cursor: pointer;
|
93 |
-
cursor: hand;
|
94 |
-
opacity: 1;
|
95 |
-
-webkit-transition: opacity 0.3s ease-in-out;
|
96 |
-
-moz-transition: opacity 0.3s ease-in-out;
|
97 |
-
-ms-transition: opacity 0.3s ease-in-out;
|
98 |
-
-o-transition: opacity 0.3s ease-in-out;
|
99 |
-
transition: opacity 0.3s ease-in-out;
|
100 |
-
}
|
101 |
-
.cffrtb-list .title .view .controls .load-spinner {
|
102 |
-
float: left;
|
103 |
-
margin: 1em;
|
104 |
-
}
|
105 |
-
.cffrtb-list .title .edit {
|
106 |
-
position: absolute;
|
107 |
-
overflow: hidden;
|
108 |
-
background: #fff;
|
109 |
-
width: 0;
|
110 |
-
top: 0;
|
111 |
-
left: 0;
|
112 |
-
-webkit-transition: width 0.3s ease-in-out;
|
113 |
-
-moz-transition: width 0.3s ease-in-out;
|
114 |
-
-ms-transition: width 0.3s ease-in-out;
|
115 |
-
-o-transition: width 0.3s ease-in-out;
|
116 |
-
transition: width 0.3s ease-in-out;
|
117 |
-
}
|
118 |
-
.cffrtb-list .title .edit input {
|
119 |
-
box-shadow: none;
|
120 |
-
margin: 1em;
|
121 |
-
padding: 0;
|
122 |
-
font-size: 13px;
|
123 |
-
line-height: 1.4em;
|
124 |
-
border: none;
|
125 |
-
border-bottom: 1px solid #aaa;
|
126 |
-
}
|
127 |
-
.cffrtb-list .title .edit .controls {
|
128 |
-
opacity: 0;
|
129 |
-
-webkit-transition: opacity 0.6s ease-in-out;
|
130 |
-
-moz-transition: opacity 0.6s ease-in-out;
|
131 |
-
-ms-transition: opacity 0.6s ease-in-out;
|
132 |
-
-o-transition: opacity 0.6s ease-in-out;
|
133 |
-
transition: opacity 0.6s ease-in-out;
|
134 |
-
}
|
135 |
-
.cffrtb-list .title .controls,
|
136 |
-
.cffrtb-list .title .status {
|
137 |
-
position: absolute;
|
138 |
-
top: 0;
|
139 |
-
right: 0;
|
140 |
-
display: none;
|
141 |
-
}
|
142 |
-
.cffrtb-list .title .controls {
|
143 |
-
display: block;
|
144 |
-
}
|
145 |
-
.cffrtb-list .title .controls a {
|
146 |
-
display: block;
|
147 |
-
float: left;
|
148 |
-
padding: 1em;
|
149 |
-
margin-left: 1px;
|
150 |
-
text-decoration: none;
|
151 |
-
background: #0073aa;
|
152 |
-
color: #fff;
|
153 |
-
transition: none;
|
154 |
-
}
|
155 |
-
.cffrtb-list .title .controls a:hover,
|
156 |
-
.cffrtb-list .title .controls a:active,
|
157 |
-
.cffrtb-list .title .controls a:focus {
|
158 |
-
background: #00b9eb;
|
159 |
-
box-shadow: none;
|
160 |
-
}
|
161 |
-
.cffrtb-list .title .controls a:active {
|
162 |
-
background: #fff;
|
163 |
-
color: #0073aa;
|
164 |
-
}
|
165 |
-
.cffrtb-list .title .controls a.save {
|
166 |
-
background: #fff;
|
167 |
-
color: #0073aa;
|
168 |
-
}
|
169 |
-
.cffrtb-list .title .controls a.save:active,
|
170 |
-
.cffrtb-list .title .controls a.save:focus {
|
171 |
-
color: #00b9eb;
|
172 |
-
}
|
173 |
-
.cffrtb-list .title .status {
|
174 |
-
padding: 1em;
|
175 |
-
}
|
176 |
-
.cffrtb-list .title .status span {
|
177 |
-
display: block;
|
178 |
-
text-decoration: none;
|
179 |
-
color: #777;
|
180 |
-
}
|
181 |
-
.cffrtb-list .title.editing {
|
182 |
-
border-color: #fff;
|
183 |
-
border-radius: 4px;
|
184 |
-
box-shadow: 0 0 3px rgba(0, 0, 0, 0.2);
|
185 |
-
}
|
186 |
-
.cffrtb-list .title.editing .edit {
|
187 |
-
width: 100%;
|
188 |
-
}
|
189 |
-
.cffrtb-list .title.editing .edit .controls {
|
190 |
-
opacity: 1;
|
191 |
-
}
|
192 |
-
.cffrtb-list .title.saving .controls {
|
193 |
-
display: none;
|
194 |
-
}
|
195 |
-
.cffrtb-list .title.saving .status {
|
196 |
-
display: block;
|
197 |
-
}
|
198 |
-
.cffrtb-list .fieldset > .title {
|
199 |
-
background: #d54e21;
|
200 |
-
border: none;
|
201 |
-
color: #fff;
|
202 |
-
}
|
203 |
-
.cffrtb-list .fieldset > .title .view .controls a {
|
204 |
-
background: #933617;
|
205 |
-
}
|
206 |
-
.cffrtb-list .fieldset > .title .view .controls a:hover,
|
207 |
-
.cffrtb-list .fieldset > .title .view .controls a:focus {
|
208 |
-
background: #e67f5d;
|
209 |
-
}
|
210 |
-
.cffrtb-list .fieldset > .title .view .controls a:active {
|
211 |
-
background: #fff;
|
212 |
-
color: #d54e21;
|
213 |
-
}
|
214 |
-
.cffrtb-list .cffrtb-list-placeholder {
|
215 |
-
height: 1.4em;
|
216 |
-
padding: 1em;
|
217 |
-
margin-bottom: 30px;
|
218 |
-
border: 1px dashed #D1DDE2;
|
219 |
-
background: #fff;
|
220 |
-
}
|
221 |
-
.cffrtb-list .fields {
|
222 |
-
min-height: 3.4em;
|
223 |
-
}
|
224 |
-
#cffrtb-editor .add-field {
|
225 |
-
display: block;
|
226 |
-
line-height: 1.4em;
|
227 |
-
text-align: center;
|
228 |
-
text-decoration: none;
|
229 |
-
padding: 1em;
|
230 |
-
background: #fff;
|
231 |
-
border: 1px dashed #b2c6ce;
|
232 |
-
opacity: 0.7;
|
233 |
-
}
|
234 |
-
#cffrtb-editor .add-field:hover,
|
235 |
-
#cffrtb-editor .add-field:focus {
|
236 |
-
color: #fff;
|
237 |
-
background: #0073aa;
|
238 |
-
opacity: 1;
|
239 |
-
border-color: transparent;
|
240 |
-
}
|
241 |
-
#cffrtb-editor .add-field:active {
|
242 |
-
background: #00b9eb;
|
243 |
-
}
|
244 |
-
#cffrtb-disabled h3 {
|
245 |
-
border-bottom: 1px solid #ddd;
|
246 |
-
padding-bottom: 0.5em;
|
247 |
-
color: #777;
|
248 |
-
}
|
249 |
-
#cffrtb-disabled .field,
|
250 |
-
#cffrtb-disabled .fieldset {
|
251 |
-
min-width: 20em;
|
252 |
-
max-width: 20em;
|
253 |
-
opacity: 0.7;
|
254 |
-
display: inline-block;
|
255 |
-
margin-right: 1em;
|
256 |
-
}
|
257 |
-
#cffrtb-disabled .field:hover,
|
258 |
-
#cffrtb-disabled .fieldset:hover,
|
259 |
-
#cffrtb-disabled .field:focus,
|
260 |
-
#cffrtb-disabled .fieldset:focus {
|
261 |
-
opacity: 1;
|
262 |
-
}
|
263 |
-
#cffrtb-disabled .field .title,
|
264 |
-
#cffrtb-disabled .fieldset .title {
|
265 |
-
border-color: #aaadb1;
|
266 |
-
background: #ecedee;
|
267 |
-
}
|
268 |
-
#cffrtb-disabled .field .controls a,
|
269 |
-
#cffrtb-disabled .fieldset .controls a {
|
270 |
-
background: #82878c;
|
271 |
-
}
|
272 |
-
#cffrtb-disabled .field .controls a:hover,
|
273 |
-
#cffrtb-disabled .fieldset .controls a:hover,
|
274 |
-
#cffrtb-disabled .field .controls a:active,
|
275 |
-
#cffrtb-disabled .fieldset .controls a:active,
|
276 |
-
#cffrtb-disabled .field .controls a:focus,
|
277 |
-
#cffrtb-disabled .fieldset .controls a:focus {
|
278 |
-
background: #aaadb1;
|
279 |
-
}
|
280 |
-
#cffrtb-disabled .field .controls a:active,
|
281 |
-
#cffrtb-disabled .fieldset .controls a:active {
|
282 |
-
color: #82878c;
|
283 |
-
}
|
284 |
-
#cffrtb-disabled .fieldset .title {
|
285 |
-
color: #444;
|
286 |
-
}
|
287 |
-
#cffrtb-disabled .fieldset .title .view .controls a {
|
288 |
-
background: #82878c;
|
289 |
-
}
|
290 |
-
#cffrtb-disabled .fieldset .title .view .controls a:hover,
|
291 |
-
#cffrtb-disabled .fieldset .title .view .controls a:active,
|
292 |
-
#cffrtb-disabled .fieldset .title .view .controls a:focus {
|
293 |
-
background: #aaadb1;
|
294 |
-
}
|
295 |
-
#cffrtb-disabled .fieldset .title .view .controls a:active {
|
296 |
-
color: #82878c;
|
297 |
-
}
|
298 |
-
#cffrtb-disabled .reset {
|
299 |
-
opacity: 0;
|
300 |
-
display: hidden;
|
301 |
-
-webkit-transition: opacity 0.3s ease-in-out;
|
302 |
-
-moz-transition: opacity 0.3s ease-in-out;
|
303 |
-
-ms-transition: opacity 0.3s ease-in-out;
|
304 |
-
-o-transition: opacity 0.3s ease-in-out;
|
305 |
-
transition: opacity 0.3s ease-in-out;
|
306 |
-
}
|
307 |
-
#cffrtb-disabled .is-visible {
|
308 |
-
display: block;
|
309 |
-
opacity: 1;
|
310 |
-
}
|
311 |
-
#cffrtb-disabled .learn-more {
|
312 |
-
line-height: 26px;
|
313 |
-
margin-left: 1em;
|
314 |
-
}
|
315 |
-
#cffrtb-disabled .description {
|
316 |
-
margin: 1em 0;
|
317 |
-
max-width: 20em;
|
318 |
-
}
|
319 |
-
#cffrtb-disabled .no-disabled-fields {
|
320 |
-
margin-bottom: 30px;
|
321 |
-
padding-bottom: 30px;
|
322 |
-
}
|
323 |
-
#cffrtb-disabled .learn-more-details {
|
324 |
-
max-height: 0;
|
325 |
-
overflow: hidden;
|
326 |
-
-webkit-transition: max-height 0.5s ease-in-out;
|
327 |
-
-moz-transition: max-height 0.5s ease-in-out;
|
328 |
-
-ms-transition: max-height 0.5s ease-in-out;
|
329 |
-
-o-transition: max-height 0.5s ease-in-out;
|
330 |
-
transition: max-height 0.5s ease-in-out;
|
331 |
-
}
|
332 |
-
#cffrtb-disabled .learn-more-details.is-visible {
|
333 |
-
max-height: 15em;
|
334 |
-
}
|
335 |
-
.rtb-admin-modal {
|
336 |
-
position: fixed;
|
337 |
-
top: 0;
|
338 |
-
left: 0;
|
339 |
-
width: 100%;
|
340 |
-
height: 100%;
|
341 |
-
background: rgba(0, 0, 0, 0.8);
|
342 |
-
z-index: 3;
|
343 |
-
overflow-y: auto;
|
344 |
-
cursor: pointer;
|
345 |
-
visibility: hidden;
|
346 |
-
opacity: 0;
|
347 |
-
-webkit-transition: opacity 0.3s 0, visibility 0 0.3s;
|
348 |
-
-moz-transition: opacity 0.3s 0, visibility 0 0.3s;
|
349 |
-
transition: opacity 0.3s 0, visibility 0 0.3s;
|
350 |
-
}
|
351 |
-
.rtb-admin-modal.is-visible {
|
352 |
-
visibility: visible;
|
353 |
-
opacity: 1;
|
354 |
-
-webkit-transition: opacity 0.3s 0, visibility 0 0;
|
355 |
-
-moz-transition: opacity 0.3s 0, visibility 0 0;
|
356 |
-
transition: opacity 0.3s 0, visibility 0 0;
|
357 |
-
}
|
358 |
-
.rtb-admin-modal.is-visible .rtb-container {
|
359 |
-
-webkit-transform: translateY(0);
|
360 |
-
-moz-transform: translateY(0);
|
361 |
-
-ms-transform: translateY(0);
|
362 |
-
-o-transform: translateY(0);
|
363 |
-
transform: translateY(0);
|
364 |
-
}
|
365 |
-
.rtb-admin-modal .rtb-container {
|
366 |
-
position: relative;
|
367 |
-
width: 90%;
|
368 |
-
max-width: 20em;
|
369 |
-
padding: 2em;
|
370 |
-
background: #FFF;
|
371 |
-
margin: 3em auto 4em;
|
372 |
-
cursor: auto;
|
373 |
-
border-radius: 0.25em;
|
374 |
-
-webkit-transform: translateY(-30px);
|
375 |
-
-moz-transform: translateY(-30px);
|
376 |
-
-ms-transform: translateY(-30px);
|
377 |
-
-o-transform: translateY(-30px);
|
378 |
-
transform: translateY(-30px);
|
379 |
-
-webkit-transition-property: -webkit-transform;
|
380 |
-
-moz-transition-property: -moz-transform;
|
381 |
-
transition-property: transform;
|
382 |
-
-webkit-transition-duration: 0.3s;
|
383 |
-
-moz-transition-duration: 0.3s;
|
384 |
-
transition-duration: 0.3s;
|
385 |
-
}
|
386 |
-
/* Hide the outer scrollbar when the modal is open */
|
387 |
-
.rtb-hide-body-scroll {
|
388 |
-
overflow: hidden !important;
|
389 |
-
}
|
390 |
-
#cffrtb-field-editor h2 {
|
391 |
-
margin: 0;
|
392 |
-
}
|
393 |
-
#cffrtb-field-editor a {
|
394 |
-
text-decoration: none;
|
395 |
-
}
|
396 |
-
#cffrtb-field-editor label {
|
397 |
-
color: #777;
|
398 |
-
font-style: italic;
|
399 |
-
}
|
400 |
-
#cffrtb-field-editor .type {
|
401 |
-
margin: 2em 0 0;
|
402 |
-
}
|
403 |
-
#cffrtb-field-editor .type label {
|
404 |
-
display: block;
|
405 |
-
}
|
406 |
-
#cffrtb-field-editor .type .selector {
|
407 |
-
background: #ddd;
|
408 |
-
border: 1px solid #ddd;
|
409 |
-
border-radius: 4px;
|
410 |
-
}
|
411 |
-
#cffrtb-field-editor .type ul {
|
412 |
-
text-align: center;
|
413 |
-
}
|
414 |
-
#cffrtb-field-editor .types {
|
415 |
-
margin: 0;
|
416 |
-
background: #fff;
|
417 |
-
border-top-left-radius: 4px;
|
418 |
-
border-top-right-radius: 4px;
|
419 |
-
}
|
420 |
-
#cffrtb-field-editor .types li {
|
421 |
-
display: inline-block;
|
422 |
-
margin: 0;
|
423 |
-
}
|
424 |
-
#cffrtb-field-editor .types li a {
|
425 |
-
display: block;
|
426 |
-
padding: 0.5em 1em;
|
427 |
-
}
|
428 |
-
#cffrtb-field-editor .types li a.current {
|
429 |
-
font-weight: 600;
|
430 |
-
color: #666;
|
431 |
-
position: relative;
|
432 |
-
}
|
433 |
-
#cffrtb-field-editor .types li a.current:after {
|
434 |
-
content: ' ';
|
435 |
-
position: absolute;
|
436 |
-
left: 50%;
|
437 |
-
margin-left: -6px;
|
438 |
-
width: 0;
|
439 |
-
height: 0;
|
440 |
-
border-left: 6px solid transparent;
|
441 |
-
border-right: 6px solid transparent;
|
442 |
-
border-bottom: 6px solid #ddd;
|
443 |
-
}
|
444 |
-
#cffrtb-field-editor .subtypes {
|
445 |
-
display: none;
|
446 |
-
margin: 0;
|
447 |
-
}
|
448 |
-
#cffrtb-field-editor .subtypes.current {
|
449 |
-
display: block;
|
450 |
-
}
|
451 |
-
#cffrtb-field-editor .subtypes li {
|
452 |
-
margin: 0;
|
453 |
-
display: inline-block;
|
454 |
-
}
|
455 |
-
#cffrtb-field-editor .subtypes li a {
|
456 |
-
display: block;
|
457 |
-
padding: 0.5em 1em;
|
458 |
-
}
|
459 |
-
#cffrtb-field-editor .subtypes li a.current {
|
460 |
-
font-weight: 600;
|
461 |
-
color: #666;
|
462 |
-
}
|
463 |
-
#cffrtb-field-editor .settings {
|
464 |
-
margin-top: 2em;
|
465 |
-
}
|
466 |
-
#cffrtb-field-editor .settings .item {
|
467 |
-
margin: 0 0 2em;
|
468 |
-
}
|
469 |
-
#cffrtb-field-editor .settings label,
|
470 |
-
#cffrtb-field-editor .settings input {
|
471 |
-
display: block;
|
472 |
-
}
|
473 |
-
#cffrtb-field-editor .settings input {
|
474 |
-
width: 100%;
|
475 |
-
}
|
476 |
-
#cffrtb-field-editor .settings-panel {
|
477 |
-
height: auto;
|
478 |
-
overflow: hidden;
|
479 |
-
max-height: 0;
|
480 |
-
-webkit-transition: max-height 0.6s ease-in-out;
|
481 |
-
-moz-transition: max-height 0.6s ease-in-out;
|
482 |
-
-ms-transition: max-height 0.6s ease-in-out;
|
483 |
-
-o-transition: max-height 0.6s ease-in-out;
|
484 |
-
transition: max-height 0.6s ease-in-out;
|
485 |
-
}
|
486 |
-
#cffrtb-field-editor .settings-panel.current {
|
487 |
-
overflow: visible;
|
488 |
-
max-height: 40em;
|
489 |
-
}
|
490 |
-
#cffrtb-field-editor .settings-panel.options .add {
|
491 |
-
position: relative;
|
492 |
-
}
|
493 |
-
#cffrtb-field-editor .settings-panel.options .add a {
|
494 |
-
position: absolute;
|
495 |
-
top: 0;
|
496 |
-
right: 0;
|
497 |
-
padding: 4px;
|
498 |
-
}
|
499 |
-
#cffrtb-field-editor .settings-panel.options .add input {
|
500 |
-
padding-right: 4em;
|
501 |
-
}
|
502 |
-
#cffrtb-field-editor .settings-panel.options .options {
|
503 |
-
margin: 1em 0 0;
|
504 |
-
max-height: 325px;
|
505 |
-
}
|
506 |
-
#cffrtb-field-editor .settings-panel.options .options.scroll {
|
507 |
-
overflow-y: scroll;
|
508 |
-
}
|
509 |
-
#cffrtb-field-editor .settings-panel.options .options li {
|
510 |
-
margin: 0.5em 0 0;
|
511 |
-
padding: 0.25em 0.5em;
|
512 |
-
height: 1.4em;
|
513 |
-
background: #eee;
|
514 |
-
border: 1px solid #ddd;
|
515 |
-
border-radius: 4px;
|
516 |
-
cursor: pointer;
|
517 |
-
cursor: hand;
|
518 |
-
}
|
519 |
-
#cffrtb-field-editor .settings-panel.options .options li.cffrtb-editor-options-placeholder {
|
520 |
-
background: #fff;
|
521 |
-
border: 1px dashed #aaa;
|
522 |
-
}
|
523 |
-
#cffrtb-field-editor .settings-panel.options .options a {
|
524 |
-
display: inline-block;
|
525 |
-
color: #a00;
|
526 |
-
}
|
527 |
-
#cffrtb-field-editor .settings-panel.options .options a:hover,
|
528 |
-
#cffrtb-field-editor .settings-panel.options .options a:focus {
|
529 |
-
color: red;
|
530 |
-
}
|
531 |
-
#cffrtb-field-editor .required {
|
532 |
-
padding: 1em 0;
|
533 |
-
}
|
534 |
-
#cffrtb-field-editor .actions {
|
535 |
-
padding: 1em 0 0;
|
536 |
-
}
|
537 |
-
#cffrtb-field-editor .actions .save {
|
538 |
-
vertical-align: top;
|
539 |
-
}
|
540 |
-
#cffrtb-field-editor .actions .save.fieldset {
|
541 |
-
background: #d54e21;
|
542 |
-
color: #fff;
|
543 |
-
box-shadow: inset 0 1px 0 #e67f5d, 0 1px 0 rgba(0, 0, 0, 0.08);
|
544 |
-
border-color: #933617;
|
545 |
-
text-shadow: none;
|
546 |
-
}
|
547 |
-
#cffrtb-field-editor .actions .status {
|
548 |
-
display: none;
|
549 |
-
padding: 4px;
|
550 |
-
height: 14px;
|
551 |
-
}
|
552 |
-
#cffrtb-field-editor .actions.working .status {
|
553 |
-
display: inline-block;
|
554 |
-
}
|
555 |
-
#cffrtb-field-editor > .fieldset .type,
|
556 |
-
#cffrtb-field-editor > .fieldset .settings-panel,
|
557 |
-
#cffrtb-field-editor > .fieldset .required {
|
558 |
-
display: none;
|
559 |
-
}
|
560 |
-
#cffrtb-field-editor-option .fieldset.button {
|
561 |
-
background: #d54e21;
|
562 |
-
color: #fff;
|
563 |
-
box-shadow: inset 0 1px 0 #e67f5d, 0 1px 0 rgba(0, 0, 0, 0.08);
|
564 |
-
border-color: #933617;
|
565 |
-
margin-top: 2em;
|
566 |
-
}
|
567 |
-
#cffrtb-field-editor-option .option p {
|
568 |
-
margin-bottom: 0;
|
569 |
-
}
|
570 |
-
#rtb-error-modal .rtb-error-msg {
|
571 |
-
margin-bottom: 1em;
|
572 |
-
}
|
573 |
-
@media (min-width: 767px) {
|
574 |
-
.cffrtb-lft {
|
575 |
-
width: 30em;
|
576 |
-
float: left;
|
577 |
-
}
|
578 |
-
.cffrtb-rgt {
|
579 |
-
margin-left: 33em;
|
580 |
-
max-width: 100%;
|
581 |
-
}
|
582 |
}
|
1 |
+
@keyframes cffrtbrotate {
|
2 |
+
0% {
|
3 |
+
transform: rotateZ(-360deg);
|
4 |
+
-webkit-transform: rotateZ(-360deg);
|
5 |
+
-moz-transform: rotateZ(-360deg);
|
6 |
+
-o-transform: rotateZ(-360deg);
|
7 |
+
}
|
8 |
+
100% {
|
9 |
+
transform: rotateZ(0deg);
|
10 |
+
-webkit-transform: rotateZ(0deg);
|
11 |
+
-moz-transform: rotateZ(0deg);
|
12 |
+
-o-transform: rotateZ(0deg);
|
13 |
+
}
|
14 |
+
}
|
15 |
+
@-webkit-keyframes cffrtbrotate {
|
16 |
+
0% {
|
17 |
+
transform: rotateZ(-360deg);
|
18 |
+
-webkit-transform: rotateZ(-360deg);
|
19 |
+
-moz-transform: rotateZ(-360deg);
|
20 |
+
-o-transform: rotateZ(-360deg);
|
21 |
+
}
|
22 |
+
100% {
|
23 |
+
transform: rotateZ(0deg);
|
24 |
+
-webkit-transform: rotateZ(0deg);
|
25 |
+
-moz-transform: rotateZ(0deg);
|
26 |
+
-o-transform: rotateZ(0deg);
|
27 |
+
}
|
28 |
+
}
|
29 |
+
@-moz-keyframes cffrtbrotate {
|
30 |
+
0% {
|
31 |
+
transform: rotateZ(-360deg);
|
32 |
+
-webkit-transform: rotateZ(-360deg);
|
33 |
+
-moz-transform: rotateZ(-360deg);
|
34 |
+
-o-transform: rotateZ(-360deg);
|
35 |
+
}
|
36 |
+
100% {
|
37 |
+
transform: rotateZ(0deg);
|
38 |
+
-webkit-transform: rotateZ(0deg);
|
39 |
+
-moz-transform: rotateZ(0deg);
|
40 |
+
-o-transform: rotateZ(0deg);
|
41 |
+
}
|
42 |
+
}
|
43 |
+
@-o-keyframes cffrtbrotate {
|
44 |
+
0% {
|
45 |
+
transform: rotateZ(-360deg);
|
46 |
+
-webkit-transform: rotateZ(-360deg);
|
47 |
+
-moz-transform: rotateZ(-360deg);
|
48 |
+
-o-transform: rotateZ(-360deg);
|
49 |
+
}
|
50 |
+
100% {
|
51 |
+
transform: rotateZ(0deg);
|
52 |
+
-webkit-transform: rotateZ(0deg);
|
53 |
+
-moz-transform: rotateZ(0deg);
|
54 |
+
-o-transform: rotateZ(0deg);
|
55 |
+
}
|
56 |
+
}
|
57 |
+
#cffrtb-editor .load-spinner:after,
|
58 |
+
#cffrtb-field-editor-form .load-spinner:after {
|
59 |
+
display: block;
|
60 |
+
position: relative;
|
61 |
+
width: 20px;
|
62 |
+
height: 20px;
|
63 |
+
-webkit-animation: cffrtbrotate 0.6s linear infinite;
|
64 |
+
-moz-animation: cffrtbrotate 0.6s linear infinite;
|
65 |
+
-ms-animation: cffrtbrotate 0.6s linear infinite;
|
66 |
+
-o-animation: cffrtbrotate 0.6s linear infinite;
|
67 |
+
animation: cffrtbrotate 0.6s linear infinite;
|
68 |
+
border-radius: 100%;
|
69 |
+
border-top: 1px solid #777;
|
70 |
+
border-bottom: 1px solid #D1DDE2;
|
71 |
+
border-left: 1px solid #777;
|
72 |
+
border-right: 1px solid #D1DDE2;
|
73 |
+
content: '';
|
74 |
+
opacity: 0.5;
|
75 |
+
}
|
76 |
+
#cffrtb-editor {
|
77 |
+
margin-top: 2em;
|
78 |
+
}
|
79 |
+
.cffrtb-list {
|
80 |
+
margin: 0;
|
81 |
+
padding: 0;
|
82 |
+
/* Ensure there is space to drop a field into an empty fieldset */
|
83 |
+
}
|
84 |
+
.cffrtb-list .title {
|
85 |
+
position: relative;
|
86 |
+
padding: 1em;
|
87 |
+
min-height: 1.4em;
|
88 |
+
background: #D1DDE2;
|
89 |
+
border: 1px solid #82878c;
|
90 |
+
margin-bottom: 30px;
|
91 |
+
overflow: hidden;
|
92 |
+
cursor: pointer;
|
93 |
+
cursor: hand;
|
94 |
+
opacity: 1;
|
95 |
+
-webkit-transition: opacity 0.3s ease-in-out;
|
96 |
+
-moz-transition: opacity 0.3s ease-in-out;
|
97 |
+
-ms-transition: opacity 0.3s ease-in-out;
|
98 |
+
-o-transition: opacity 0.3s ease-in-out;
|
99 |
+
transition: opacity 0.3s ease-in-out;
|
100 |
+
}
|
101 |
+
.cffrtb-list .title .view .controls .load-spinner {
|
102 |
+
float: left;
|
103 |
+
margin: 1em;
|
104 |
+
}
|
105 |
+
.cffrtb-list .title .edit {
|
106 |
+
position: absolute;
|
107 |
+
overflow: hidden;
|
108 |
+
background: #fff;
|
109 |
+
width: 0;
|
110 |
+
top: 0;
|
111 |
+
left: 0;
|
112 |
+
-webkit-transition: width 0.3s ease-in-out;
|
113 |
+
-moz-transition: width 0.3s ease-in-out;
|
114 |
+
-ms-transition: width 0.3s ease-in-out;
|
115 |
+
-o-transition: width 0.3s ease-in-out;
|
116 |
+
transition: width 0.3s ease-in-out;
|
117 |
+
}
|
118 |
+
.cffrtb-list .title .edit input {
|
119 |
+
box-shadow: none;
|
120 |
+
margin: 1em;
|
121 |
+
padding: 0;
|
122 |
+
font-size: 13px;
|
123 |
+
line-height: 1.4em;
|
124 |
+
border: none;
|
125 |
+
border-bottom: 1px solid #aaa;
|
126 |
+
}
|
127 |
+
.cffrtb-list .title .edit .controls {
|
128 |
+
opacity: 0;
|
129 |
+
-webkit-transition: opacity 0.6s ease-in-out;
|
130 |
+
-moz-transition: opacity 0.6s ease-in-out;
|
131 |
+
-ms-transition: opacity 0.6s ease-in-out;
|
132 |
+
-o-transition: opacity 0.6s ease-in-out;
|
133 |
+
transition: opacity 0.6s ease-in-out;
|
134 |
+
}
|
135 |
+
.cffrtb-list .title .controls,
|
136 |
+
.cffrtb-list .title .status {
|
137 |
+
position: absolute;
|
138 |
+
top: 0;
|
139 |
+
right: 0;
|
140 |
+
display: none;
|
141 |
+
}
|
142 |
+
.cffrtb-list .title .controls {
|
143 |
+
display: block;
|
144 |
+
}
|
145 |
+
.cffrtb-list .title .controls a {
|
146 |
+
display: block;
|
147 |
+
float: left;
|
148 |
+
padding: 1em;
|
149 |
+
margin-left: 1px;
|
150 |
+
text-decoration: none;
|
151 |
+
background: #0073aa;
|
152 |
+
color: #fff;
|
153 |
+
transition: none;
|
154 |
+
}
|
155 |
+
.cffrtb-list .title .controls a:hover,
|
156 |
+
.cffrtb-list .title .controls a:active,
|
157 |
+
.cffrtb-list .title .controls a:focus {
|
158 |
+
background: #00b9eb;
|
159 |
+
box-shadow: none;
|
160 |
+
}
|
161 |
+
.cffrtb-list .title .controls a:active {
|
162 |
+
background: #fff;
|
163 |
+
color: #0073aa;
|
164 |
+
}
|
165 |
+
.cffrtb-list .title .controls a.save {
|
166 |
+
background: #fff;
|
167 |
+
color: #0073aa;
|
168 |
+
}
|
169 |
+
.cffrtb-list .title .controls a.save:active,
|
170 |
+
.cffrtb-list .title .controls a.save:focus {
|
171 |
+
color: #00b9eb;
|
172 |
+
}
|
173 |
+
.cffrtb-list .title .status {
|
174 |
+
padding: 1em;
|
175 |
+
}
|
176 |
+
.cffrtb-list .title .status span {
|
177 |
+
display: block;
|
178 |
+
text-decoration: none;
|
179 |
+
color: #777;
|
180 |
+
}
|
181 |
+
.cffrtb-list .title.editing {
|
182 |
+
border-color: #fff;
|
183 |
+
border-radius: 4px;
|
184 |
+
box-shadow: 0 0 3px rgba(0, 0, 0, 0.2);
|
185 |
+
}
|
186 |
+
.cffrtb-list .title.editing .edit {
|
187 |
+
width: 100%;
|
188 |
+
}
|
189 |
+
.cffrtb-list .title.editing .edit .controls {
|
190 |
+
opacity: 1;
|
191 |
+
}
|
192 |
+
.cffrtb-list .title.saving .controls {
|
193 |
+
display: none;
|
194 |
+
}
|
195 |
+
.cffrtb-list .title.saving .status {
|
196 |
+
display: block;
|
197 |
+
}
|
198 |
+
.cffrtb-list .fieldset > .title {
|
199 |
+
background: #d54e21;
|
200 |
+
border: none;
|
201 |
+
color: #fff;
|
202 |
+
}
|
203 |
+
.cffrtb-list .fieldset > .title .view .controls a {
|
204 |
+
background: #933617;
|
205 |
+
}
|
206 |
+
.cffrtb-list .fieldset > .title .view .controls a:hover,
|
207 |
+
.cffrtb-list .fieldset > .title .view .controls a:focus {
|
208 |
+
background: #e67f5d;
|
209 |
+
}
|
210 |
+
.cffrtb-list .fieldset > .title .view .controls a:active {
|
211 |
+
background: #fff;
|
212 |
+
color: #d54e21;
|
213 |
+
}
|
214 |
+
.cffrtb-list .cffrtb-list-placeholder {
|
215 |
+
height: 1.4em;
|
216 |
+
padding: 1em;
|
217 |
+
margin-bottom: 30px;
|
218 |
+
border: 1px dashed #D1DDE2;
|
219 |
+
background: #fff;
|
220 |
+
}
|
221 |
+
.cffrtb-list .fields {
|
222 |
+
min-height: 3.4em;
|
223 |
+
}
|
224 |
+
#cffrtb-editor .add-field {
|
225 |
+
display: block;
|
226 |
+
line-height: 1.4em;
|
227 |
+
text-align: center;
|
228 |
+
text-decoration: none;
|
229 |
+
padding: 1em;
|
230 |
+
background: #fff;
|
231 |
+
border: 1px dashed #b2c6ce;
|
232 |
+
opacity: 0.7;
|
233 |
+
}
|
234 |
+
#cffrtb-editor .add-field:hover,
|
235 |
+
#cffrtb-editor .add-field:focus {
|
236 |
+
color: #fff;
|
237 |
+
background: #0073aa;
|
238 |
+
opacity: 1;
|
239 |
+
border-color: transparent;
|
240 |
+
}
|
241 |
+
#cffrtb-editor .add-field:active {
|
242 |
+
background: #00b9eb;
|
243 |
+
}
|
244 |
+
#cffrtb-disabled h3 {
|
245 |
+
border-bottom: 1px solid #ddd;
|
246 |
+
padding-bottom: 0.5em;
|
247 |
+
color: #777;
|
248 |
+
}
|
249 |
+
#cffrtb-disabled .field,
|
250 |
+
#cffrtb-disabled .fieldset {
|
251 |
+
min-width: 20em;
|
252 |
+
max-width: 20em;
|
253 |
+
opacity: 0.7;
|
254 |
+
display: inline-block;
|
255 |
+
margin-right: 1em;
|
256 |
+
}
|
257 |
+
#cffrtb-disabled .field:hover,
|
258 |
+
#cffrtb-disabled .fieldset:hover,
|
259 |
+
#cffrtb-disabled .field:focus,
|
260 |
+
#cffrtb-disabled .fieldset:focus {
|
261 |
+
opacity: 1;
|
262 |
+
}
|
263 |
+
#cffrtb-disabled .field .title,
|
264 |
+
#cffrtb-disabled .fieldset .title {
|
265 |
+
border-color: #aaadb1;
|
266 |
+
background: #ecedee;
|
267 |
+
}
|
268 |
+
#cffrtb-disabled .field .controls a,
|
269 |
+
#cffrtb-disabled .fieldset .controls a {
|
270 |
+
background: #82878c;
|
271 |
+
}
|
272 |
+
#cffrtb-disabled .field .controls a:hover,
|
273 |
+
#cffrtb-disabled .fieldset .controls a:hover,
|
274 |
+
#cffrtb-disabled .field .controls a:active,
|
275 |
+
#cffrtb-disabled .fieldset .controls a:active,
|
276 |
+
#cffrtb-disabled .field .controls a:focus,
|
277 |
+
#cffrtb-disabled .fieldset .controls a:focus {
|
278 |
+
background: #aaadb1;
|
279 |
+
}
|
280 |
+
#cffrtb-disabled .field .controls a:active,
|
281 |
+
#cffrtb-disabled .fieldset .controls a:active {
|
282 |
+
color: #82878c;
|
283 |
+
}
|
284 |
+
#cffrtb-disabled .fieldset .title {
|
285 |
+
color: #444;
|
286 |
+
}
|
287 |
+
#cffrtb-disabled .fieldset .title .view .controls a {
|
288 |
+
background: #82878c;
|
289 |
+
}
|
290 |
+
#cffrtb-disabled .fieldset .title .view .controls a:hover,
|
291 |
+
#cffrtb-disabled .fieldset .title .view .controls a:active,
|
292 |
+
#cffrtb-disabled .fieldset .title .view .controls a:focus {
|
293 |
+
background: #aaadb1;
|
294 |
+
}
|
295 |
+
#cffrtb-disabled .fieldset .title .view .controls a:active {
|
296 |
+
color: #82878c;
|
297 |
+
}
|
298 |
+
#cffrtb-disabled .reset {
|
299 |
+
opacity: 0;
|
300 |
+
display: hidden;
|
301 |
+
-webkit-transition: opacity 0.3s ease-in-out;
|
302 |
+
-moz-transition: opacity 0.3s ease-in-out;
|
303 |
+
-ms-transition: opacity 0.3s ease-in-out;
|
304 |
+
-o-transition: opacity 0.3s ease-in-out;
|
305 |
+
transition: opacity 0.3s ease-in-out;
|
306 |
+
}
|
307 |
+
#cffrtb-disabled .is-visible {
|
308 |
+
display: block;
|
309 |
+
opacity: 1;
|
310 |
+
}
|
311 |
+
#cffrtb-disabled .learn-more {
|
312 |
+
line-height: 26px;
|
313 |
+
margin-left: 1em;
|
314 |
+
}
|
315 |
+
#cffrtb-disabled .description {
|
316 |
+
margin: 1em 0;
|
317 |
+
max-width: 20em;
|
318 |
+
}
|
319 |
+
#cffrtb-disabled .no-disabled-fields {
|
320 |
+
margin-bottom: 30px;
|
321 |
+
padding-bottom: 30px;
|
322 |
+
}
|
323 |
+
#cffrtb-disabled .learn-more-details {
|
324 |
+
max-height: 0;
|
325 |
+
overflow: hidden;
|
326 |
+
-webkit-transition: max-height 0.5s ease-in-out;
|
327 |
+
-moz-transition: max-height 0.5s ease-in-out;
|
328 |
+
-ms-transition: max-height 0.5s ease-in-out;
|
329 |
+
-o-transition: max-height 0.5s ease-in-out;
|
330 |
+
transition: max-height 0.5s ease-in-out;
|
331 |
+
}
|
332 |
+
#cffrtb-disabled .learn-more-details.is-visible {
|
333 |
+
max-height: 15em;
|
334 |
+
}
|
335 |
+
.rtb-admin-modal {
|
336 |
+
position: fixed;
|
337 |
+
top: 0;
|
338 |
+
left: 0;
|
339 |
+
width: 100%;
|
340 |
+
height: 100%;
|
341 |
+
background: rgba(0, 0, 0, 0.8);
|
342 |
+
z-index: 3;
|
343 |
+
overflow-y: auto;
|
344 |
+
cursor: pointer;
|
345 |
+
visibility: hidden;
|
346 |
+
opacity: 0;
|
347 |
+
-webkit-transition: opacity 0.3s 0, visibility 0 0.3s;
|
348 |
+
-moz-transition: opacity 0.3s 0, visibility 0 0.3s;
|
349 |
+
transition: opacity 0.3s 0, visibility 0 0.3s;
|
350 |
+
}
|
351 |
+
.rtb-admin-modal.is-visible {
|
352 |
+
visibility: visible;
|
353 |
+
opacity: 1;
|
354 |
+
-webkit-transition: opacity 0.3s 0, visibility 0 0;
|
355 |
+
-moz-transition: opacity 0.3s 0, visibility 0 0;
|
356 |
+
transition: opacity 0.3s 0, visibility 0 0;
|
357 |
+
}
|
358 |
+
.rtb-admin-modal.is-visible .rtb-container {
|
359 |
+
-webkit-transform: translateY(0);
|
360 |
+
-moz-transform: translateY(0);
|
361 |
+
-ms-transform: translateY(0);
|
362 |
+
-o-transform: translateY(0);
|
363 |
+
transform: translateY(0);
|
364 |
+
}
|
365 |
+
.rtb-admin-modal .rtb-container {
|
366 |
+
position: relative;
|
367 |
+
width: 90%;
|
368 |
+
max-width: 20em;
|
369 |
+
padding: 2em;
|
370 |
+
background: #FFF;
|
371 |
+
margin: 3em auto 4em;
|
372 |
+
cursor: auto;
|
373 |
+
border-radius: 0.25em;
|
374 |
+
-webkit-transform: translateY(-30px);
|
375 |
+
-moz-transform: translateY(-30px);
|
376 |
+
-ms-transform: translateY(-30px);
|
377 |
+
-o-transform: translateY(-30px);
|
378 |
+
transform: translateY(-30px);
|
379 |
+
-webkit-transition-property: -webkit-transform;
|
380 |
+
-moz-transition-property: -moz-transform;
|
381 |
+
transition-property: transform;
|
382 |
+
-webkit-transition-duration: 0.3s;
|
383 |
+
-moz-transition-duration: 0.3s;
|
384 |
+
transition-duration: 0.3s;
|
385 |
+
}
|
386 |
+
/* Hide the outer scrollbar when the modal is open */
|
387 |
+
.rtb-hide-body-scroll {
|
388 |
+
overflow: hidden !important;
|
389 |
+
}
|
390 |
+
#cffrtb-field-editor h2 {
|
391 |
+
margin: 0;
|
392 |
+
}
|
393 |
+
#cffrtb-field-editor a {
|
394 |
+
text-decoration: none;
|
395 |
+
}
|
396 |
+
#cffrtb-field-editor label {
|
397 |
+
color: #777;
|
398 |
+
font-style: italic;
|
399 |
+
}
|
400 |
+
#cffrtb-field-editor .type {
|
401 |
+
margin: 2em 0 0;
|
402 |
+
}
|
403 |
+
#cffrtb-field-editor .type label {
|
404 |
+
display: block;
|
405 |
+
}
|
406 |
+
#cffrtb-field-editor .type .selector {
|
407 |
+
background: #ddd;
|
408 |
+
border: 1px solid #ddd;
|
409 |
+
border-radius: 4px;
|
410 |
+
}
|
411 |
+
#cffrtb-field-editor .type ul {
|
412 |
+
text-align: center;
|
413 |
+
}
|
414 |
+
#cffrtb-field-editor .types {
|
415 |
+
margin: 0;
|
416 |
+
background: #fff;
|
417 |
+
border-top-left-radius: 4px;
|
418 |
+
border-top-right-radius: 4px;
|
419 |
+
}
|
420 |
+
#cffrtb-field-editor .types li {
|
421 |
+
display: inline-block;
|
422 |
+
margin: 0;
|
423 |
+
}
|
424 |
+
#cffrtb-field-editor .types li a {
|
425 |
+
display: block;
|
426 |
+
padding: 0.5em 1em;
|
427 |
+
}
|
428 |
+
#cffrtb-field-editor .types li a.current {
|
429 |
+
font-weight: 600;
|
430 |
+
color: #666;
|
431 |
+
position: relative;
|
432 |
+
}
|
433 |
+
#cffrtb-field-editor .types li a.current:after {
|
434 |
+
content: ' ';
|
435 |
+
position: absolute;
|
436 |
+
left: 50%;
|
437 |
+
margin-left: -6px;
|
438 |
+
width: 0;
|
439 |
+
height: 0;
|
440 |
+
border-left: 6px solid transparent;
|
441 |
+
border-right: 6px solid transparent;
|
442 |
+
border-bottom: 6px solid #ddd;
|
443 |
+
}
|
444 |
+
#cffrtb-field-editor .subtypes {
|
445 |
+
display: none;
|
446 |
+
margin: 0;
|
447 |
+
}
|
448 |
+
#cffrtb-field-editor .subtypes.current {
|
449 |
+
display: block;
|
450 |
+
}
|
451 |
+
#cffrtb-field-editor .subtypes li {
|
452 |
+
margin: 0;
|
453 |
+
display: inline-block;
|
454 |
+
}
|
455 |
+
#cffrtb-field-editor .subtypes li a {
|
456 |
+
display: block;
|
457 |
+
padding: 0.5em 1em;
|
458 |
+
}
|
459 |
+
#cffrtb-field-editor .subtypes li a.current {
|
460 |
+
font-weight: 600;
|
461 |
+
color: #666;
|
462 |
+
}
|
463 |
+
#cffrtb-field-editor .settings {
|
464 |
+
margin-top: 2em;
|
465 |
+
}
|
466 |
+
#cffrtb-field-editor .settings .item {
|
467 |
+
margin: 0 0 2em;
|
468 |
+
}
|
469 |
+
#cffrtb-field-editor .settings label,
|
470 |
+
#cffrtb-field-editor .settings input {
|
471 |
+
display: block;
|
472 |
+
}
|
473 |
+
#cffrtb-field-editor .settings input {
|
474 |
+
width: 100%;
|
475 |
+
}
|
476 |
+
#cffrtb-field-editor .settings-panel {
|
477 |
+
height: auto;
|
478 |
+
overflow: hidden;
|
479 |
+
max-height: 0;
|
480 |
+
-webkit-transition: max-height 0.6s ease-in-out;
|
481 |
+
-moz-transition: max-height 0.6s ease-in-out;
|
482 |
+
-ms-transition: max-height 0.6s ease-in-out;
|
483 |
+
-o-transition: max-height 0.6s ease-in-out;
|
484 |
+
transition: max-height 0.6s ease-in-out;
|
485 |
+
}
|
486 |
+
#cffrtb-field-editor .settings-panel.current {
|
487 |
+
overflow: visible;
|
488 |
+
max-height: 40em;
|
489 |
+
}
|
490 |
+
#cffrtb-field-editor .settings-panel.options .add {
|
491 |
+
position: relative;
|
492 |
+
}
|
493 |
+
#cffrtb-field-editor .settings-panel.options .add a {
|
494 |
+
position: absolute;
|
495 |
+
top: 0;
|
496 |
+
right: 0;
|
497 |
+
padding: 4px;
|
498 |
+
}
|
499 |
+
#cffrtb-field-editor .settings-panel.options .add input {
|
500 |
+
padding-right: 4em;
|
501 |
+
}
|
502 |
+
#cffrtb-field-editor .settings-panel.options .options {
|
503 |
+
margin: 1em 0 0;
|
504 |
+
max-height: 325px;
|
505 |
+
}
|
506 |
+
#cffrtb-field-editor .settings-panel.options .options.scroll {
|
507 |
+
overflow-y: scroll;
|
508 |
+
}
|
509 |
+
#cffrtb-field-editor .settings-panel.options .options li {
|
510 |
+
margin: 0.5em 0 0;
|
511 |
+
padding: 0.25em 0.5em;
|
512 |
+
height: 1.4em;
|
513 |
+
background: #eee;
|
514 |
+
border: 1px solid #ddd;
|
515 |
+
border-radius: 4px;
|
516 |
+
cursor: pointer;
|
517 |
+
cursor: hand;
|
518 |
+
}
|
519 |
+
#cffrtb-field-editor .settings-panel.options .options li.cffrtb-editor-options-placeholder {
|
520 |
+
background: #fff;
|
521 |
+
border: 1px dashed #aaa;
|
522 |
+
}
|
523 |
+
#cffrtb-field-editor .settings-panel.options .options a {
|
524 |
+
display: inline-block;
|
525 |
+
color: #a00;
|
526 |
+
}
|
527 |
+
#cffrtb-field-editor .settings-panel.options .options a:hover,
|
528 |
+
#cffrtb-field-editor .settings-panel.options .options a:focus {
|
529 |
+
color: red;
|
530 |
+
}
|
531 |
+
#cffrtb-field-editor .required {
|
532 |
+
padding: 1em 0;
|
533 |
+
}
|
534 |
+
#cffrtb-field-editor .actions {
|
535 |
+
padding: 1em 0 0;
|
536 |
+
}
|
537 |
+
#cffrtb-field-editor .actions .save {
|
538 |
+
vertical-align: top;
|
539 |
+
}
|
540 |
+
#cffrtb-field-editor .actions .save.fieldset {
|
541 |
+
background: #d54e21;
|
542 |
+
color: #fff;
|
543 |
+
box-shadow: inset 0 1px 0 #e67f5d, 0 1px 0 rgba(0, 0, 0, 0.08);
|
544 |
+
border-color: #933617;
|
545 |
+
text-shadow: none;
|
546 |
+
}
|
547 |
+
#cffrtb-field-editor .actions .status {
|
548 |
+
display: none;
|
549 |
+
padding: 4px;
|
550 |
+
height: 14px;
|
551 |
+
}
|
552 |
+
#cffrtb-field-editor .actions.working .status {
|
553 |
+
display: inline-block;
|
554 |
+
}
|
555 |
+
#cffrtb-field-editor > .fieldset .type,
|
556 |
+
#cffrtb-field-editor > .fieldset .settings-panel,
|
557 |
+
#cffrtb-field-editor > .fieldset .required {
|
558 |
+
display: none;
|
559 |
+
}
|
560 |
+
#cffrtb-field-editor-option .fieldset.button {
|
561 |
+
background: #d54e21;
|
562 |
+
color: #fff;
|
563 |
+
box-shadow: inset 0 1px 0 #e67f5d, 0 1px 0 rgba(0, 0, 0, 0.08);
|
564 |
+
border-color: #933617;
|
565 |
+
margin-top: 2em;
|
566 |
+
}
|
567 |
+
#cffrtb-field-editor-option .option p {
|
568 |
+
margin-bottom: 0;
|
569 |
+
}
|
570 |
+
#rtb-error-modal .rtb-error-msg {
|
571 |
+
margin-bottom: 1em;
|
572 |
+
}
|
573 |
+
@media (min-width: 767px) {
|
574 |
+
.cffrtb-lft {
|
575 |
+
width: 30em;
|
576 |
+
float: left;
|
577 |
+
}
|
578 |
+
.cffrtb-rgt {
|
579 |
+
margin-left: 33em;
|
580 |
+
max-width: 100%;
|
581 |
+
}
|
582 |
}
|
assets/css/helper-install-notice.css
CHANGED
@@ -1,19 +1,19 @@
|
|
1 |
-
.rtb-clear {
|
2 |
-
clear: both;
|
3 |
-
}
|
4 |
-
|
5 |
-
.rtb-helper-install-notice {
|
6 |
-
display: inline-block;
|
7 |
-
clear: both;
|
8 |
-
}
|
9 |
-
|
10 |
-
.rtb-helper-install-notice-img,
|
11 |
-
.rtb-helper-install-notice-txt
|
12 |
-
{
|
13 |
-
float: left;
|
14 |
-
padding: 8px 8px 8px 0px;
|
15 |
-
}
|
16 |
-
|
17 |
-
.rtb-helper-install-notice div img {
|
18 |
-
max-height: 36px;
|
19 |
-
}
|
1 |
+
.rtb-clear {
|
2 |
+
clear: both;
|
3 |
+
}
|
4 |
+
|
5 |
+
.rtb-helper-install-notice {
|
6 |
+
display: inline-block;
|
7 |
+
clear: both;
|
8 |
+
}
|
9 |
+
|
10 |
+
.rtb-helper-install-notice-img,
|
11 |
+
.rtb-helper-install-notice-txt
|
12 |
+
{
|
13 |
+
float: left;
|
14 |
+
padding: 8px 8px 8px 0px;
|
15 |
+
}
|
16 |
+
|
17 |
+
.rtb-helper-install-notice div img {
|
18 |
+
max-height: 36px;
|
19 |
+
}
|
assets/css/plugin-deactivation.css
CHANGED
@@ -1,68 +1,68 @@
|
|
1 |
-
.rtb-deactivate-survey-modal {
|
2 |
-
display: none;
|
3 |
-
table-layout: fixed;
|
4 |
-
position: fixed;
|
5 |
-
z-index: 9999;
|
6 |
-
width: 100%;
|
7 |
-
height: 100%;
|
8 |
-
text-align: center;
|
9 |
-
font-size: 14px;
|
10 |
-
top: 0;
|
11 |
-
left: 0;
|
12 |
-
background: rgba(0,0,0,0.8);
|
13 |
-
}
|
14 |
-
.rtb-deactivate-survey-wrap {
|
15 |
-
display: table-cell;
|
16 |
-
vertical-align: middle;
|
17 |
-
}
|
18 |
-
.rtb-deactivate-survey {
|
19 |
-
background-color: #fff;
|
20 |
-
max-width: 550px;
|
21 |
-
margin: 0 auto;
|
22 |
-
padding: 30px;
|
23 |
-
text-align: left;
|
24 |
-
}
|
25 |
-
.rtb-deactivate-survey .error {
|
26 |
-
display: block;
|
27 |
-
color: red;
|
28 |
-
margin: 0 0 10px 0;
|
29 |
-
}
|
30 |
-
.rtb-deactivate-survey-title {
|
31 |
-
display: block;
|
32 |
-
font-size: 18px;
|
33 |
-
font-weight: 700;
|
34 |
-
text-transform: uppercase;
|
35 |
-
border-bottom: 1px solid #ddd;
|
36 |
-
padding: 0 0 18px 0;
|
37 |
-
margin: 0 0 18px 0;
|
38 |
-
}
|
39 |
-
.rtb-deactivate-survey-title span {
|
40 |
-
color: #999;
|
41 |
-
margin-right: 10px;
|
42 |
-
}
|
43 |
-
.rtb-deactivate-survey-desc {
|
44 |
-
display: block;
|
45 |
-
font-weight: 600;
|
46 |
-
margin: 0 0 18px 0;
|
47 |
-
}
|
48 |
-
.rtb-deactivate-survey-option {
|
49 |
-
margin: 0 0 10px 0;
|
50 |
-
}
|
51 |
-
.rtb-deactivate-survey-option-input {
|
52 |
-
margin-right: 10px !important;
|
53 |
-
}
|
54 |
-
.rtb-deactivate-survey-option-details {
|
55 |
-
display: none;
|
56 |
-
width: 90%;
|
57 |
-
margin: 10px 0 0 30px;
|
58 |
-
}
|
59 |
-
.rtb-deactivate-survey-footer {
|
60 |
-
margin-top: 18px;
|
61 |
-
}
|
62 |
-
.rtb-deactivate-survey-deactivate {
|
63 |
-
float: right;
|
64 |
-
font-size: 13px;
|
65 |
-
color: #ccc;
|
66 |
-
text-decoration: none;
|
67 |
-
padding-top: 7px;
|
68 |
}
|
1 |
+
.rtb-deactivate-survey-modal {
|
2 |
+
display: none;
|
3 |
+
table-layout: fixed;
|
4 |
+
position: fixed;
|
5 |
+
z-index: 9999;
|
6 |
+
width: 100%;
|
7 |
+
height: 100%;
|
8 |
+
text-align: center;
|
9 |
+
font-size: 14px;
|
10 |
+
top: 0;
|
11 |
+
left: 0;
|
12 |
+
background: rgba(0,0,0,0.8);
|
13 |
+
}
|
14 |
+
.rtb-deactivate-survey-wrap {
|
15 |
+
display: table-cell;
|
16 |
+
vertical-align: middle;
|
17 |
+
}
|
18 |
+
.rtb-deactivate-survey {
|
19 |
+
background-color: #fff;
|
20 |
+
max-width: 550px;
|
21 |
+
margin: 0 auto;
|
22 |
+
padding: 30px;
|
23 |
+
text-align: left;
|
24 |
+
}
|
25 |
+
.rtb-deactivate-survey .error {
|
26 |
+
display: block;
|
27 |
+
color: red;
|
28 |
+
margin: 0 0 10px 0;
|
29 |
+
}
|
30 |
+
.rtb-deactivate-survey-title {
|
31 |
+
display: block;
|
32 |
+
font-size: 18px;
|
33 |
+
font-weight: 700;
|
34 |
+
text-transform: uppercase;
|
35 |
+
border-bottom: 1px solid #ddd;
|
36 |
+
padding: 0 0 18px 0;
|
37 |
+
margin: 0 0 18px 0;
|
38 |
+
}
|
39 |
+
.rtb-deactivate-survey-title span {
|
40 |
+
color: #999;
|
41 |
+
margin-right: 10px;
|
42 |
+
}
|
43 |
+
.rtb-deactivate-survey-desc {
|
44 |
+
display: block;
|
45 |
+
font-weight: 600;
|
46 |
+
margin: 0 0 18px 0;
|
47 |
+
}
|
48 |
+
.rtb-deactivate-survey-option {
|
49 |
+
margin: 0 0 10px 0;
|
50 |
+
}
|
51 |
+
.rtb-deactivate-survey-option-input {
|
52 |
+
margin-right: 10px !important;
|
53 |
+
}
|
54 |
+
.rtb-deactivate-survey-option-details {
|
55 |
+
display: none;
|
56 |
+
width: 90%;
|
57 |
+
margin: 10px 0 0 30px;
|
58 |
+
}
|
59 |
+
.rtb-deactivate-survey-footer {
|
60 |
+
margin-top: 18px;
|
61 |
+
}
|
62 |
+
.rtb-deactivate-survey-deactivate {
|
63 |
+
float: right;
|
64 |
+
font-size: 13px;
|
65 |
+
color: #ccc;
|
66 |
+
text-decoration: none;
|
67 |
+
padding-top: 7px;
|
68 |
}
|
assets/js/admin-rtb-welcome-screen.js
CHANGED
@@ -1,93 +1,93 @@
|
|
1 |
-
jQuery(document).ready(function() {
|
2 |
-
jQuery('.rtb-welcome-screen-box h2').on('click', function() {
|
3 |
-
var section = jQuery(this).parent().data('screen');
|
4 |
-
rtb_toggle_section(section);
|
5 |
-
});
|
6 |
-
|
7 |
-
jQuery('.rtb-welcome-screen-next-button').on('click', function() {
|
8 |
-
var section = jQuery(this).data('nextaction');
|
9 |
-
rtb_toggle_section(section);
|
10 |
-
});
|
11 |
-
|
12 |
-
jQuery('.rtb-welcome-screen-previous-button').on('click', function() {
|
13 |
-
var section = jQuery(this).data('previousaction');
|
14 |
-
rtb_toggle_section(section);
|
15 |
-
});
|
16 |
-
|
17 |
-
jQuery('.rtb-welcome-screen-add-reservations-page-button').on('click', function() {
|
18 |
-
var reservations_page_title = jQuery('.rtb-welcome-screen-add-reservations-page-name input').val();
|
19 |
-
|
20 |
-
var params = {};
|
21 |
-
|
22 |
-
params.action = 'rtb-welcome-add-menu-page';
|
23 |
-
params.nonce = rtb_getting_started.nonce;
|
24 |
-
params.reservations_page_title = reservations_page_title;
|
25 |
-
|
26 |
-
var data = jQuery.param( params );
|
27 |
-
jQuery.post(ajaxurl, data, function(response) {});
|
28 |
-
|
29 |
-
var section = jQuery(this).data('nextaction');
|
30 |
-
rtb_toggle_section(section);
|
31 |
-
});
|
32 |
-
|
33 |
-
jQuery('.rtb-welcome-screen-save-schedule-open-button').on('click', function() {
|
34 |
-
|
35 |
-
var schedule_open = [];
|
36 |
-
|
37 |
-
jQuery('.sap-scheduler-rule').each(function() {
|
38 |
-
var weekdays ={};
|
39 |
-
|
40 |
-
jQuery(this).find('.sap-scheduler-weekdays input[type="checkbox"]').each(function() {
|
41 |
-
if ( jQuery(this).is(':checked') ) { weekdays[jQuery(this).data('day')] = "1" ; }
|
42 |
-
});
|
43 |
-
|
44 |
-
var start = jQuery(this).find('.sap-scheduler-time-input .start input').first().val();
|
45 |
-
var end = jQuery(this).find('.sap-scheduler-time-input .end input').first().val();
|
46 |
-
|
47 |
-
schedule_open.push({'weekdays': weekdays, 'time': {'start': start, 'end': end }});
|
48 |
-
});
|
49 |
-
|
50 |
-
var params = {};
|
51 |
-
|
52 |
-
params.action = 'rtb-welcome-set-schedule';
|
53 |
-
params.nonce = rtb_getting_started.nonce;
|
54 |
-
params.schedule_open = schedule_open;
|
55 |
-
|
56 |
-
var data = jQuery.param( params );
|
57 |
-
jQuery.post(ajaxurl, data, function(response) {
|
58 |
-
|
59 |
-
jQuery( '.rtb-welcome-screen-save-schedule-open-button' ).after( '<div class="rtb-save-message"><div class="rtb-save-message-inside">Schedule has been saved.</div></div>' );
|
60 |
-
jQuery( '.rtb-save-message' ).delay( 2000 ).fadeOut( 400, function() { jQuery( '.rtb-save-message' ).remove(); } );
|
61 |
-
});
|
62 |
-
});
|
63 |
-
|
64 |
-
jQuery('.rtb-welcome-screen-save-options-button').on('click', function() {
|
65 |
-
var party_size_min = jQuery('select[name="min-party-size"]').val();
|
66 |
-
var party_size = jQuery('select[name="party-size"]').val();
|
67 |
-
var early_bookings = jQuery('select[name="early-bookings"]').val();
|
68 |
-
var late_bookings = jQuery('select[name="late-bookings"]').val();
|
69 |
-
var time_interval = jQuery('select[name="time-interval"]').val();
|
70 |
-
|
71 |
-
var params = {};
|
72 |
-
|
73 |
-
params.action = 'rtb-welcome-set-options';
|
74 |
-
params.nonce = rtb_getting_started.nonce;
|
75 |
-
params.party_size_min = party_size_min;
|
76 |
-
params.party_size = party_size;
|
77 |
-
params.early_bookings = early_bookings;
|
78 |
-
params.late_bookings = late_bookings;
|
79 |
-
params.time_interval = time_interval;
|
80 |
-
|
81 |
-
var data = jQuery.param( params );
|
82 |
-
jQuery.post(ajaxurl, data, function(response) {
|
83 |
-
|
84 |
-
jQuery( '.rtb-welcome-screen-save-options-button' ).after( '<div class="rtb-save-message"><div class="rtb-save-message-inside">Options have been saved.</div></div>' );
|
85 |
-
jQuery( '.rtb-save-message' ).delay( 2000 ).fadeOut( 400, function() { jQuery( '.rtb-save-message' ).remove(); } );
|
86 |
-
});
|
87 |
-
});
|
88 |
-
});
|
89 |
-
|
90 |
-
function rtb_toggle_section(page) {
|
91 |
-
jQuery('.rtb-welcome-screen-box').removeClass('rtb-welcome-screen-open');
|
92 |
-
jQuery('.rtb-welcome-screen-' + page).addClass('rtb-welcome-screen-open');
|
93 |
}
|
1 |
+
jQuery(document).ready(function() {
|
2 |
+
jQuery('.rtb-welcome-screen-box h2').on('click', function() {
|
3 |
+
var section = jQuery(this).parent().data('screen');
|
4 |
+
rtb_toggle_section(section);
|
5 |
+
});
|
6 |
+
|
7 |
+
jQuery('.rtb-welcome-screen-next-button').on('click', function() {
|
8 |
+
var section = jQuery(this).data('nextaction');
|
9 |
+
rtb_toggle_section(section);
|
10 |
+
});
|
11 |
+
|
12 |
+
jQuery('.rtb-welcome-screen-previous-button').on('click', function() {
|
13 |
+
var section = jQuery(this).data('previousaction');
|
14 |
+
rtb_toggle_section(section);
|
15 |
+
});
|
16 |
+
|
17 |
+
jQuery('.rtb-welcome-screen-add-reservations-page-button').on('click', function() {
|
18 |
+
var reservations_page_title = jQuery('.rtb-welcome-screen-add-reservations-page-name input').val();
|
19 |
+
|
20 |
+
var params = {};
|
21 |
+
|
22 |
+
params.action = 'rtb-welcome-add-menu-page';
|
23 |
+
params.nonce = rtb_getting_started.nonce;
|
24 |
+
params.reservations_page_title = reservations_page_title;
|
25 |
+
|
26 |
+
var data = jQuery.param( params );
|
27 |
+
jQuery.post(ajaxurl, data, function(response) {});
|
28 |
+
|
29 |
+
var section = jQuery(this).data('nextaction');
|
30 |
+
rtb_toggle_section(section);
|
31 |
+
});
|
32 |
+
|
33 |
+
jQuery('.rtb-welcome-screen-save-schedule-open-button').on('click', function() {
|
34 |
+
|
35 |
+
var schedule_open = [];
|
36 |
+
|
37 |
+
jQuery('.sap-scheduler-rule').each(function() {
|
38 |
+
var weekdays ={};
|
39 |
+
|
40 |
+
jQuery(this).find('.sap-scheduler-weekdays input[type="checkbox"]').each(function() {
|
41 |
+
if ( jQuery(this).is(':checked') ) { weekdays[jQuery(this).data('day')] = "1" ; }
|
42 |
+
});
|
43 |
+
|
44 |
+
var start = jQuery(this).find('.sap-scheduler-time-input .start input').first().val();
|
45 |
+
var end = jQuery(this).find('.sap-scheduler-time-input .end input').first().val();
|
46 |
+
|
47 |
+
schedule_open.push({'weekdays': weekdays, 'time': {'start': start, 'end': end }});
|
48 |
+
});
|
49 |
+
|
50 |
+
var params = {};
|
51 |
+
|
52 |
+
params.action = 'rtb-welcome-set-schedule';
|
53 |
+
params.nonce = rtb_getting_started.nonce;
|
54 |
+
params.schedule_open = schedule_open;
|
55 |
+
|
56 |
+
var data = jQuery.param( params );
|
57 |
+
jQuery.post(ajaxurl, data, function(response) {
|
58 |
+
|
59 |
+
jQuery( '.rtb-welcome-screen-save-schedule-open-button' ).after( '<div class="rtb-save-message"><div class="rtb-save-message-inside">Schedule has been saved.</div></div>' );
|
60 |
+
jQuery( '.rtb-save-message' ).delay( 2000 ).fadeOut( 400, function() { jQuery( '.rtb-save-message' ).remove(); } );
|
61 |
+
});
|
62 |
+
});
|
63 |
+
|
64 |
+
jQuery('.rtb-welcome-screen-save-options-button').on('click', function() {
|
65 |
+
var party_size_min = jQuery('select[name="min-party-size"]').val();
|
66 |
+
var party_size = jQuery('select[name="party-size"]').val();
|
67 |
+
var early_bookings = jQuery('select[name="early-bookings"]').val();
|
68 |
+
var late_bookings = jQuery('select[name="late-bookings"]').val();
|
69 |
+
var time_interval = jQuery('select[name="time-interval"]').val();
|
70 |
+
|
71 |
+
var params = {};
|
72 |
+
|
73 |
+
params.action = 'rtb-welcome-set-options';
|
74 |
+
params.nonce = rtb_getting_started.nonce;
|
75 |
+
params.party_size_min = party_size_min;
|
76 |
+
params.party_size = party_size;
|
77 |
+
params.early_bookings = early_bookings;
|
78 |
+
params.late_bookings = late_bookings;
|
79 |
+
params.time_interval = time_interval;
|
80 |
+
|
81 |
+
var data = jQuery.param( params );
|
82 |
+
jQuery.post(ajaxurl, data, function(response) {
|
83 |
+
|
84 |
+
jQuery( '.rtb-welcome-screen-save-options-button' ).after( '<div class="rtb-save-message"><div class="rtb-save-message-inside">Options have been saved.</div></div>' );
|
85 |
+
jQuery( '.rtb-save-message' ).delay( 2000 ).fadeOut( 400, function() { jQuery( '.rtb-save-message' ).remove(); } );
|
86 |
+
});
|
87 |
+
});
|
88 |
+
});
|
89 |
+
|
90 |
+
function rtb_toggle_section(page) {
|
91 |
+
jQuery('.rtb-welcome-screen-box').removeClass('rtb-welcome-screen-open');
|
92 |
+
jQuery('.rtb-welcome-screen-' + page).addClass('rtb-welcome-screen-open');
|
93 |
}
|
assets/js/admin-settings.js
CHANGED
@@ -1,76 +1,76 @@
|
|
1 |
-
/* Hiding empty settings sections */
|
2 |
-
jQuery( document ).ready( function() {
|
3 |
-
|
4 |
-
jQuery( 'select[name="rtb-settings[location-select]"]' ).change(function() {
|
5 |
-
|
6 |
-
manageHidingSections();
|
7 |
-
});
|
8 |
-
|
9 |
-
manageHidingSections();
|
10 |
-
});
|
11 |
-
|
12 |
-
function manageHidingSections() {
|
13 |
-
|
14 |
-
jQuery( '.sap-settings-page .form-table' ).each(function(){
|
15 |
-
|
16 |
-
var table = jQuery( this );
|
17 |
-
var action = 'hide';
|
18 |
-
|
19 |
-
if( table.find( '> tbody > tr:not(.sap-hidden)' ).length ) {
|
20 |
-
action = 'show';
|
21 |
-
}
|
22 |
-
|
23 |
-
table[action]();
|
24 |
-
if( ( ovrly = table.prev( '.sap-premium-options-table-overlay' ) ).length ) {
|
25 |
-
ovrly[action]();
|
26 |
-
ovrly.prev( 'h2' )[action]();
|
27 |
-
}
|
28 |
-
else {
|
29 |
-
table.prev( 'h2' )[action]();
|
30 |
-
}
|
31 |
-
});
|
32 |
-
}
|
33 |
-
|
34 |
-
|
35 |
-
jQuery(document).ready(function() {
|
36 |
-
jQuery('.rtb-spectrum').spectrum({
|
37 |
-
showInput: true,
|
38 |
-
showInitial: true,
|
39 |
-
preferredFormat: "hex",
|
40 |
-
allowEmpty: true
|
41 |
-
});
|
42 |
-
|
43 |
-
jQuery('.rtb-spectrum').css('display', 'inline');
|
44 |
-
|
45 |
-
jQuery('.rtb-spectrum').on('change', function() {
|
46 |
-
if (jQuery(this).val() != "") {
|
47 |
-
jQuery(this).css('background', jQuery(this).val());
|
48 |
-
var rgb = RTB_hexToRgb(jQuery(this).val());
|
49 |
-
var Brightness = (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000;
|
50 |
-
if (Brightness < 100) {jQuery(this).css('color', '#ffffff');}
|
51 |
-
else {jQuery(this).css('color', '#000000');}
|
52 |
-
}
|
53 |
-
else {
|
54 |
-
jQuery(this).css('background', 'none');
|
55 |
-
}
|
56 |
-
});
|
57 |
-
|
58 |
-
jQuery('.rtb-spectrum').each(function() {
|
59 |
-
if (jQuery(this).val() != "") {
|
60 |
-
jQuery(this).css('background', jQuery(this).val());
|
61 |
-
var rgb = RTB_hexToRgb(jQuery(this).val());
|
62 |
-
var Brightness = (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000;
|
63 |
-
if (Brightness < 100) {jQuery(this).css('color', '#ffffff');}
|
64 |
-
else {jQuery(this).css('color', '#000000');}
|
65 |
-
}
|
66 |
-
});
|
67 |
-
});
|
68 |
-
|
69 |
-
function RTB_hexToRgb(hex) {
|
70 |
-
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
71 |
-
return result ? {
|
72 |
-
r: parseInt(result[1], 16),
|
73 |
-
g: parseInt(result[2], 16),
|
74 |
-
b: parseInt(result[3], 16)
|
75 |
-
} : null;
|
76 |
-
}
|
1 |
+
/* Hiding empty settings sections */
|
2 |
+
jQuery( document ).ready( function() {
|
3 |
+
|
4 |
+
jQuery( 'select[name="rtb-settings[location-select]"]' ).change(function() {
|
5 |
+
|
6 |
+
manageHidingSections();
|
7 |
+
});
|
8 |
+
|
9 |
+
manageHidingSections();
|
10 |
+
});
|
11 |
+
|
12 |
+
function manageHidingSections() {
|
13 |
+
|
14 |
+
jQuery( '.sap-settings-page .form-table' ).each(function(){
|
15 |
+
|
16 |
+
var table = jQuery( this );
|
17 |
+
var action = 'hide';
|
18 |
+
|
19 |
+
if( table.find( '> tbody > tr:not(.sap-hidden)' ).length ) {
|
20 |
+
action = 'show';
|
21 |
+
}
|
22 |
+
|
23 |
+
table[action]();
|
24 |
+
if( ( ovrly = table.prev( '.sap-premium-options-table-overlay' ) ).length ) {
|
25 |
+
ovrly[action]();
|
26 |
+
ovrly.prev( 'h2' )[action]();
|
27 |
+
}
|
28 |
+
else {
|
29 |
+
table.prev( 'h2' )[action]();
|
30 |
+
}
|
31 |
+
});
|
32 |
+
}
|
33 |
+
|
34 |
+
|
35 |
+
jQuery(document).ready(function() {
|
36 |
+
jQuery('.rtb-spectrum').spectrum({
|
37 |
+
showInput: true,
|
38 |
+
showInitial: true,
|
39 |
+
preferredFormat: "hex",
|
40 |
+
allowEmpty: true
|
41 |
+
});
|
42 |
+
|
43 |
+
jQuery('.rtb-spectrum').css('display', 'inline');
|
44 |
+
|
45 |
+
jQuery('.rtb-spectrum').on('change', function() {
|
46 |
+
if (jQuery(this).val() != "") {
|
47 |
+
jQuery(this).css('background', jQuery(this).val());
|
48 |
+
var rgb = RTB_hexToRgb(jQuery(this).val());
|
49 |
+
var Brightness = (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000;
|
50 |
+
if (Brightness < 100) {jQuery(this).css('color', '#ffffff');}
|
51 |
+
else {jQuery(this).css('color', '#000000');}
|
52 |
+
}
|
53 |
+
else {
|
54 |
+
jQuery(this).css('background', 'none');
|
55 |
+
}
|
56 |
+
});
|
57 |
+
|
58 |
+
jQuery('.rtb-spectrum').each(function() {
|
59 |
+
if (jQuery(this).val() != "") {
|
60 |
+
jQuery(this).css('background', jQuery(this).val());
|
61 |
+
var rgb = RTB_hexToRgb(jQuery(this).val());
|
62 |
+
var Brightness = (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000;
|
63 |
+
if (Brightness < 100) {jQuery(this).css('color', '#ffffff');}
|
64 |
+
else {jQuery(this).css('color', '#000000');}
|
65 |
+
}
|
66 |
+
});
|
67 |
+
});
|
68 |
+
|
69 |
+
function RTB_hexToRgb(hex) {
|
70 |
+
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
71 |
+
return result ? {
|
72 |
+
r: parseInt(result[1], 16),
|
73 |
+
g: parseInt(result[2], 16),
|
74 |
+
b: parseInt(result[3], 16)
|
75 |
+
} : null;
|
76 |
+
}
|
assets/js/block-booking-form.js
CHANGED
@@ -1,48 +1,48 @@
|
|
1 |
-
const { __ } = wp.i18n;
|
2 |
-
const { registerBlockType } = wp.blocks;
|
3 |
-
const { SelectControl, PanelBody, ServerSideRender, Disabled } = wp.components;
|
4 |
-
const { InspectorControls } = wp.editor;
|
5 |
-
const { locationsEnabled, locations } = rtb_blocks;
|
6 |
-
|
7 |
-
registerBlockType( 'restaurant-reservations/booking-form', {
|
8 |
-
title: __( 'Booking Form', 'restaurant-reservations' ),
|
9 |
-
icon: 'calendar',
|
10 |
-
category: 'rtb-blocks',
|
11 |
-
attributes: {
|
12 |
-
location: {
|
13 |
-
type: 'number',
|
14 |
-
default: 0
|
15 |
-
}
|
16 |
-
},
|
17 |
-
supports: {
|
18 |
-
html: false,
|
19 |
-
reusable: false,
|
20 |
-
multiple: false,
|
21 |
-
},
|
22 |
-
edit( { attributes, setAttributes } ) {
|
23 |
-
const { location } = attributes;
|
24 |
-
|
25 |
-
return (
|
26 |
-
<div>
|
27 |
-
{locationsEnabled ? (
|
28 |
-
<InspectorControls>
|
29 |
-
<PanelBody>
|
30 |
-
<SelectControl
|
31 |
-
label={ __( 'Location' ) }
|
32 |
-
value={ location }
|
33 |
-
onChange={ ( location ) => setAttributes( { location: parseInt( location, 10 ) } ) }
|
34 |
-
options={ locations }
|
35 |
-
/>
|
36 |
-
</PanelBody>
|
37 |
-
</InspectorControls>
|
38 |
-
) : '' }
|
39 |
-
<Disabled>
|
40 |
-
<ServerSideRender block="restaurant-reservations/booking-form" attributes={ attributes } />
|
41 |
-
</Disabled>
|
42 |
-
</div>
|
43 |
-
);
|
44 |
-
},
|
45 |
-
save() {
|
46 |
-
return null;
|
47 |
-
},
|
48 |
-
} );
|
1 |
+
const { __ } = wp.i18n;
|
2 |
+
const { registerBlockType } = wp.blocks;
|
3 |
+
const { SelectControl, PanelBody, ServerSideRender, Disabled } = wp.components;
|
4 |
+
const { InspectorControls } = wp.editor;
|
5 |
+
const { locationsEnabled, locations } = rtb_blocks;
|
6 |
+
|
7 |
+
registerBlockType( 'restaurant-reservations/booking-form', {
|
8 |
+
title: __( 'Booking Form', 'restaurant-reservations' ),
|
9 |
+
icon: 'calendar',
|
10 |
+
category: 'rtb-blocks',
|
11 |
+
attributes: {
|
12 |
+
location: {
|
13 |
+
type: 'number',
|
14 |
+
default: 0
|
15 |
+
}
|
16 |
+
},
|
17 |
+
supports: {
|
18 |
+
html: false,
|
19 |
+
reusable: false,
|
20 |
+
multiple: false,
|
21 |
+
},
|
22 |
+
edit( { attributes, setAttributes } ) {
|
23 |
+
const { location } = attributes;
|
24 |
+
|
25 |
+
return (
|
26 |
+
<div>
|
27 |
+
{locationsEnabled ? (
|
28 |
+
<InspectorControls>
|
29 |
+
<PanelBody>
|
30 |
+
<SelectControl
|
31 |
+
label={ __( 'Location' ) }
|
32 |
+
value={ location }
|
33 |
+
onChange={ ( location ) => setAttributes( { location: parseInt( location, 10 ) } ) }
|
34 |
+
options={ locations }
|
35 |
+
/>
|
36 |
+
</PanelBody>
|
37 |
+
</InspectorControls>
|
38 |
+
) : '' }
|
39 |
+
<Disabled>
|
40 |
+
<ServerSideRender block="restaurant-reservations/booking-form" attributes={ attributes } />
|
41 |
+
</Disabled>
|
42 |
+
</div>
|
43 |
+
);
|
44 |
+
},
|
45 |
+
save() {
|
46 |
+
return null;
|
47 |
+
},
|
48 |
+
} );
|
assets/js/booking-form.js
CHANGED
@@ -1,983 +1,983 @@
|
|
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 |
-
.find( 'label' ).focus();
|
24 |
-
|
25 |
-
return false;
|
26 |
-
});
|
27 |
-
|
28 |
-
// Show the message field on load if not empty
|
29 |
-
if ( $.trim( $( '.rtb-booking-form .message textarea' ).val() ) ) {
|
30 |
-
$( '.rtb-booking-form .add-message a' ).trigger( 'click' );
|
31 |
-
}
|
32 |
-
|
33 |
-
// Disable the submit button when the booking form is submitted
|
34 |
-
$( '.rtb-booking-form form' ).submit( function() {
|
35 |
-
$(this).find( 'button[type="submit"]' ).prop( 'disabled', 'disabled' );
|
36 |
-
return true;
|
37 |
-
} );
|
38 |
-
|
39 |
-
// Enable datepickers on load
|
40 |
-
if ( typeof rtb_pickadate !== 'undefined' ) {
|
41 |
-
|
42 |
-
// Declare datepicker
|
43 |
-
var $date_input = $( '#rtb-date' );
|
44 |
-
if ( $date_input.length ) {
|
45 |
-
var date_input = $date_input.pickadate({
|
46 |
-
format: rtb_pickadate.date_format,
|
47 |
-
formatSubmit: 'yyyy/mm/dd',
|
48 |
-
hiddenName: true,
|
49 |
-
min: !rtb_pickadate.allow_past,
|
50 |
-
container: 'body',
|
51 |
-
firstDay: rtb_pickadate.first_day,
|
52 |
-
today: rtb_pickadate.date_today_label,
|
53 |
-
clear: rtb_pickadate.date_clear_label,
|
54 |
-
close: rtb_pickadate.date_close_label,
|
55 |
-
|
56 |
-
onStart: function() {
|
57 |
-
|
58 |
-
// Block dates beyond early bookings window
|
59 |
-
if ( rtb_pickadate.early_bookings !== '' ) {
|
60 |
-
this.set( 'max', parseInt( rtb_pickadate.early_bookings, 10 ) );
|
61 |
-
}
|
62 |
-
|
63 |
-
// Select the value when loaded if a value has been set
|
64 |
-
if ( $date_input.val() !== '' ) {
|
65 |
-
var date = new Date( $date_input.val() );
|
66 |
-
if ( Object.prototype.toString.call( date ) === "[object Date]" ) {
|
67 |
-
this.set( 'select', date );
|
68 |
-
}
|
69 |
-
}
|
70 |
-
else if ( jQuery( '.rtb-selected-date' ).length ) {
|
71 |
-
var date = new Date( jQuery( '.rtb-selected-date' ).val() );
|
72 |
-
if ( Object.prototype.toString.call( date ) === "[object Date]" ) {
|
73 |
-
this.set( 'select', date );
|
74 |
-
}
|
75 |
-
}
|
76 |
-
}
|
77 |
-
});
|
78 |
-
|
79 |
-
rtb_booking_form.datepicker = date_input.pickadate( 'picker' );
|
80 |
-
}
|
81 |
-
|
82 |
-
// Declare timepicker
|
83 |
-
var $time_input = $( '#rtb-time' );
|
84 |
-
if ( $time_input.length ) {
|
85 |
-
var time_input = $time_input.pickatime({
|
86 |
-
format: rtb_pickadate.time_format,
|
87 |
-
formatSubmit: 'h:i A',
|
88 |
-
hiddenName: true,
|
89 |
-
interval: parseInt( rtb_pickadate.time_interval, 10 ),
|
90 |
-
container: 'body',
|
91 |
-
clear: rtb_pickadate.time_clear_label,
|
92 |
-
|
93 |
-
// Select the value when loaded if a value has been set
|
94 |
-
onStart: function() {
|
95 |
-
if ( $time_input.val() !== '' ) {
|
96 |
-
var today = new Date();
|
97 |
-
var today_date = today.getFullYear() + '/' + ( today.getMonth() + 1 ) + '/' + today.getDate();
|
98 |
-
var time = new Date( today_date + ' ' + $time_input.val() );
|
99 |
-
if ( Object.prototype.toString.call( time ) === "[object Date]" ) {
|
100 |
-
this.set( 'select', time );
|
101 |
-
}
|
102 |
-
|
103 |
-
}
|
104 |
-
}
|
105 |
-
});
|
106 |
-
|
107 |
-
rtb_booking_form.timepicker = time_input.pickatime( 'picker' );
|
108 |
-
}
|
109 |
-
|
110 |
-
// We need to check both to support different jQuery versions loaded
|
111 |
-
// by older versions of WordPress. In jQuery v1.10.2, the property
|
112 |
-
// is undefined. But in v1.11.3 it's set to null.
|
113 |
-
if ( rtb_booking_form.datepicker === null || typeof rtb_booking_form.datepicker == 'undefined' ) {
|
114 |
-
return;
|
115 |
-
}
|
116 |
-
|
117 |
-
// Update disabled dates
|
118 |
-
rtb_booking_form.update_disabled_dates();
|
119 |
-
|
120 |
-
if ( typeof rtb_pickadate.late_bookings === 'string' ) {
|
121 |
-
if ( rtb_pickadate.late_bookings == 'same_day' ) {
|
122 |
-
rtb_booking_form.datepicker.set( 'min', 1 );
|
123 |
-
} else if ( rtb_pickadate.late_bookings !== '' ) {
|
124 |
-
rtb_pickadate.late_bookings = parseInt( rtb_pickadate.late_bookings, 10 );
|
125 |
-
if ( rtb_pickadate.late_bookings % 1 === 0 && rtb_pickadate.late_bookings >= 1440 ) {
|
126 |
-
var min = Math.floor( rtb_pickadate.late_bookings / 1440 );
|
127 |
-
rtb_booking_form.datepicker.set( 'min', min );
|
128 |
-
}
|
129 |
-
}
|
130 |
-
}
|
131 |
-
|
132 |
-
// If no date has been set, select today's date if it's a valid
|
133 |
-
// date. User may opt not to do this in the settings.
|
134 |
-
if ( $date_input.val() === '' && !$( '.rtb-booking-form .date .rtb-error' ).length ) {
|
135 |
-
|
136 |
-
if ( rtb_pickadate.date_onload == 'soonest' ) {
|
137 |
-
rtb_booking_form.datepicker.set( 'select', new Date() );
|
138 |
-
} else if ( rtb_pickadate.date_onload !== 'empty' ) {
|
139 |
-
var dateToVerify = rtb_booking_form.datepicker.component.create( new Date() );
|
140 |
-
var isDisabled = rtb_booking_form.datepicker.component.disabled( dateToVerify );
|
141 |
-
if ( !isDisabled ) {
|
142 |
-
rtb_booking_form.datepicker.set( 'select', dateToVerify );
|
143 |
-
}
|
144 |
-
}
|
145 |
-
}
|
146 |
-
|
147 |
-
if ( rtb_booking_form.timepicker === null || typeof rtb_booking_form.timepicker == 'undefined' ) {
|
148 |
-
return;
|
149 |
-
}
|
150 |
-
|
151 |
-
// Update timepicker on pageload and whenever the datepicker is closed
|
152 |
-
rtb_booking_form.update_timepicker_range();
|
153 |
-
rtb_booking_form.datepicker.on( {
|
154 |
-
open: function () {
|
155 |
-
|
156 |
-
rtb_booking_form.before_change_value = rtb_booking_form.datepicker.get();
|
157 |
-
},
|
158 |
-
|
159 |
-
close: function() {
|
160 |
-
|
161 |
-
rtb_booking_form.after_change_value = rtb_booking_form.datepicker.get();
|
162 |
-
|
163 |
-
if(rtb_booking_form.before_change_value != rtb_booking_form.after_change_value) {
|
164 |
-
// clear time value if date changed
|
165 |
-
rtb_booking_form.timepicker.clear();
|
166 |
-
}
|
167 |
-
|
168 |
-
rtb_booking_form.update_timepicker_range();
|
169 |
-
rtb_booking_form.update_party_size_select();
|
170 |
-
rtb_booking_form.update_possible_tables();
|
171 |
-
}
|
172 |
-
});
|
173 |
-
|
174 |
-
rtb_booking_form.timepicker.on( {
|
175 |
-
close: function() {
|
176 |
-
rtb_booking_form.update_party_size_select();
|
177 |
-
rtb_booking_form.update_possible_tables();
|
178 |
-
}
|
179 |
-
});
|
180 |
-
|
181 |
-
$( '#rtb-party' ).on( 'change', function() {
|
182 |
-
rtb_booking_form.update_possible_tables();
|
183 |
-
});
|
184 |
-
|
185 |
-
$( '#rtb-location' ).on( 'change', function() {
|
186 |
-
|
187 |
-
if ( ! rtb_pickadate.multiple_locations_enabled ) { return; }
|
188 |
-
|
189 |
-
rtb_booking_form.timepicker.clear();
|
190 |
-
rtb_booking_form.datepicker.clear();
|
191 |
-
|
192 |
-
rtb_booking_form.update_base_data_for_selected_location();
|
193 |
-
|
194 |
-
rtb_booking_form.update_datepicker();
|
195 |
-
|
196 |
-
rtb_booking_form.update_timepicker_range();
|
197 |
-
|
198 |
-
rtb_booking_form.update_party_size_select();
|
199 |
-
});
|
200 |
-
|
201 |
-
rtb_booking_form.update_possible_tables();
|
202 |
-
}
|
203 |
-
};
|
204 |
-
|
205 |
-
/**
|
206 |
-
* Update base data for date/time picker as per the selected location
|
207 |
-
* @return object
|
208 |
-
*/
|
209 |
-
rtb_booking_form.update_base_data_for_selected_location = function () {
|
210 |
-
const selected_location = jQuery( '#rtb-location' ).length ? jQuery( '#rtb-location' ).val() : '';
|
211 |
-
|
212 |
-
if( '' == selected_location ) {
|
213 |
-
// Set global settings
|
214 |
-
return Object.assign( rtb_pickadate, rtb_location_data.global );
|
215 |
-
}
|
216 |
-
|
217 |
-
return Object.assign( rtb_pickadate, rtb_location_data[selected_location] );
|
218 |
-
}
|
219 |
-
|
220 |
-
rtb_booking_form.update_datepicker = function () {
|
221 |
-
|
222 |
-
// Reset enabled/disabled rules on this datepicker
|
223 |
-
rtb_booking_form.datepicker.set( 'enable', false );
|
224 |
-
rtb_booking_form.datepicker.set( 'disable', false );
|
225 |
-
|
226 |
-
rtb_booking_form.update_disabled_dates();
|
227 |
-
}
|
228 |
-
|
229 |
-
/**
|
230 |
-
* Update datepicker to change the disabled dates based on location
|
231 |
-
*/
|
232 |
-
rtb_booking_form.update_disabled_dates = function() {
|
233 |
-
|
234 |
-
// Pass conditional configuration parameters
|
235 |
-
if ( rtb_pickadate.disable_dates.length ) {
|
236 |
-
|
237 |
-
var disable_dates = jQuery.extend( true, [], rtb_pickadate.disable_dates );
|
238 |
-
|
239 |
-
// Update weekday dates if start of the week has been modified
|
240 |
-
if ( typeof rtb_booking_form.datepicker.component.settings.firstDay == 'number' ) {
|
241 |
-
var weekday_num = 0;
|
242 |
-
for ( var disable_key in rtb_pickadate.disable_dates ) {
|
243 |
-
if ( typeof rtb_pickadate.disable_dates[disable_key] == 'number' ) {
|
244 |
-
weekday_num = rtb_pickadate.disable_dates[disable_key] - rtb_booking_form.datepicker.component.settings.firstDay;
|
245 |
-
if ( weekday_num < 1 ) {
|
246 |
-
weekday_num = 7;
|
247 |
-
}
|
248 |
-
disable_dates[disable_key] = weekday_num;
|
249 |
-
}
|
250 |
-
}
|
251 |
-
}
|
252 |
-
|
253 |
-
rtb_booking_form.datepicker.set( 'disable', disable_dates );
|
254 |
-
|
255 |
-
}
|
256 |
-
}
|
257 |
-
|
258 |
-
/**
|
259 |
-
* Update the timepicker's range based on the currently selected date
|
260 |
-
*/
|
261 |
-
rtb_booking_form.update_timepicker_range = function() {
|
262 |
-
|
263 |
-
// Reset enabled/disabled rules on this timepicker
|
264 |
-
rtb_booking_form.timepicker.set( 'enable', false );
|
265 |
-
rtb_booking_form.timepicker.set( 'disable', false );
|
266 |
-
|
267 |
-
if ( rtb_booking_form.datepicker.get() === '' ) {
|
268 |
-
rtb_booking_form.timepicker.set( 'disable', true );
|
269 |
-
return;
|
270 |
-
}
|
271 |
-
|
272 |
-
var selected_date = new Date( rtb_booking_form.datepicker.get( 'select', 'yyyy/mm/dd' ) ),
|
273 |
-
selected_date_year = selected_date.getFullYear(),
|
274 |
-
selected_date_month = selected_date.getMonth(),
|
275 |
-
selected_date_date = selected_date.getDate(),
|
276 |
-
current_date = new Date();
|
277 |
-
|
278 |
-
selected_date.setHours(0, 0, 0), selected_date.setMilliseconds(100);
|
279 |
-
|
280 |
-
// Declaring the first element true inverts the timepicker settings. All
|
281 |
-
// times subsequently declared are valid. Any time that doesn't fall
|
282 |
-
// within those declarations is invalid.
|
283 |
-
// See: http://amsul.ca/pickadate.js/time/#disable-times-all
|
284 |
-
var valid_times = [ rtb_booking_form.get_outer_time_range() ];
|
285 |
-
|
286 |
-
if ( rtb_pickadate.enable_max_reservations || rtb_pickadate.multiple_locations_enabled ) {
|
287 |
-
selected_location = jQuery( '#rtb-location' ).length ? jQuery( '#rtb-location' ).val() : '';
|
288 |
-
|
289 |
-
let hidden_location = jQuery('.rtb-booking-form-form input[name="rtb-location"]');
|
290 |
-
if('' == selected_location && hidden_location.length ) {
|
291 |
-
selected_location = hidden_location.val();
|
292 |
-
}
|
293 |
-
|
294 |
-
selected_date_month = ('0' + (selected_date_month + 1)).slice(-2);
|
295 |
-
selected_date_date = ('0' + selected_date_date).slice(-2);
|
296 |
-
|
297 |
-
var params = {};
|
298 |
-
|
299 |
-
params.action = 'rtb_get_available_time_slots';
|
300 |
-
params.nonce = rtb_booking_form_js_localize.nonce;
|
301 |
-
params.year = selected_date_year;
|
302 |
-
params.month = selected_date_month;
|
303 |
-
params.day = selected_date_date;
|
304 |
-
params.location = selected_location;
|
305 |
-
|
306 |
-
var data = jQuery.param( params );
|
307 |
-
jQuery.post( ajaxurl, data, function( response ) {
|
308 |
-
|
309 |
-
if ( ! response ) {
|
310 |
-
displayFieldError( 'date', rtb_booking_form_js_localize.error['smthng-wrng-cntct-us'] );
|
311 |
-
rtb_booking_form.timepicker.set( 'disable', true );
|
312 |
-
|
313 |
-
return;
|
314 |
-
}
|
315 |
-
else {
|
316 |
-
clearPrevFieldError( 'date' );
|
317 |
-
}
|
318 |
-
|
319 |
-
var additional_valid_times = jQuery.parseJSON( response );
|
320 |
-
|
321 |
-
// If today is all day open, only add one valid date/time rule
|
322 |
-
let outer_range = valid_times[0];
|
323 |
-
if(
|
324 |
-
additional_valid_times.length == 1
|
325 |
-
&& additional_valid_times[0].from[0] == outer_range.from[0]
|
326 |
-
&& additional_valid_times[0].from[1] == outer_range.from[1]
|
327 |
-
&& additional_valid_times[0].to[0] == outer_range.to[0]
|
328 |
-
&& additional_valid_times[0].to[1] == outer_range.to[1] ) {
|
329 |
-
var all_valid_times = [ additional_valid_times[0] ];
|
330 |
-
}
|
331 |
-
else {
|
332 |
-
var all_valid_times = valid_times.concat( additional_valid_times );
|
333 |
-
}
|
334 |
-
|
335 |
-
if( !Array.isArray( additional_valid_times ) || 1 > additional_valid_times.length ) {
|
336 |
-
displayFieldError( 'time', rtb_booking_form_js_localize.error['no-slots-available'] );
|
337 |
-
}
|
338 |
-
else {
|
339 |
-
clearPrevFieldError( 'time' );
|
340 |
-
}
|
341 |
-
|
342 |
-
rtb_booking_form.timepicker.set( 'disable', all_valid_times );
|
343 |
-
});
|
344 |
-
}
|
345 |
-
|
346 |
-
else {
|
347 |
-
// Check if this date is an exception to the rules
|
348 |
-
if ( typeof rtb_pickadate.schedule_closed !== 'undefined' ) {
|
349 |
-
|
350 |
-
var excp_date = [];
|
351 |
-
var excp_start_date = [];
|
352 |
-
var excp_start_time = [];
|
353 |
-
var excp_end_date = [];
|
354 |
-
var excp_end_time = [];
|
355 |
-
for ( var closed_key in rtb_pickadate.schedule_closed ) {
|
356 |
-
|
357 |
-
let rule = rtb_pickadate.schedule_closed[closed_key];
|
358 |
-
if( rule.hasOwnProperty('date_range') ) {
|
359 |
-
let start = '' != rule.date_range.start ? new Date( rule.date_range.start ) : new Date();
|
360 |
-
start.setHours(0, 0, 0), start.setMilliseconds(0);
|
361 |
-
start = start.getTime();
|
362 |
-
|
363 |
-
let end = '' != rule.date_range.end ? new Date( rule.date_range.end ) : 9999999999999;
|
364 |
-
'number' != typeof end && end.setHours(23, 59, 58) && end.setMilliseconds(0);
|
365 |
-
end = 'number' != typeof end ? end.getTime() : end;
|
366 |
-
|
367 |
-
if( start < selected_date.getTime() && selected_date.getTime() < end ) {
|
368 |
-
excp_date = selected_date;
|
369 |
-
}
|
370 |
-
else {
|
371 |
-
// Set anything to void this rule
|
372 |
-
// Dates assign with copy, thus creating a new one
|
373 |
-
excp_date = new Date( selected_date.getTime() );
|
374 |
-
excp_date.setDate( selected_date_year + 1 );
|
375 |
-
}
|
376 |
-
}
|
377 |
-
else {
|
378 |
-
excp_date = new Date( rule.date );
|
379 |
-
}
|
380 |
-
|
381 |
-
if ( excp_date.getFullYear() == selected_date_year &&
|
382 |
-
excp_date.getMonth() == selected_date_month &&
|
383 |
-
excp_date.getDate() == selected_date_date
|
384 |
-
) {
|
385 |
-
|
386 |
-
// Closed all day
|
387 |
-
if ( typeof rtb_pickadate.schedule_closed[closed_key].time == 'undefined' ) {
|
388 |
-
rtb_booking_form.timepicker.set( 'disable', [ true ] );
|
389 |
-
|
390 |
-
return;
|
391 |
-
}
|
392 |
-
|
393 |
-
if ( typeof rtb_pickadate.schedule_closed[closed_key].time.start !== 'undefined' ) {
|
394 |
-
excp_start_date = new Date( '1 January 2000 ' + rtb_pickadate.schedule_closed[closed_key].time.start );
|
395 |
-
excp_start_time = [ excp_start_date.getHours(), excp_start_date.getMinutes() ];
|
396 |
-
} else {
|
397 |
-
excp_start_time = [ 0, 0 ]; // Start of the day
|
398 |
-
}
|
399 |
-
|
400 |
-
if ( typeof rtb_pickadate.schedule_closed[closed_key].time.end !== 'undefined' ) {
|
401 |
-
excp_end_date = new Date( '1 January 2000 ' + rtb_pickadate.schedule_closed[closed_key].time.end );
|
402 |
-
excp_end_time = [ excp_end_date.getHours(), excp_end_date.getMinutes() ];
|
403 |
-
} else {
|
404 |
-
excp_end_time = [ 24, 0 ]; // End of the day
|
405 |
-
}
|
406 |
-
|
407 |
-
excp_start_time = rtb_booking_form.get_earliest_time( excp_start_time, selected_date, current_date );
|
408 |
-
|
409 |
-
valid_times.push( { from: excp_start_time, to: excp_end_time, inverted: true } );
|
410 |
-
}
|
411 |
-
}
|
412 |
-
|
413 |
-
excp_date = excp_start_date = excp_start_time = excp_end_date = excp_end_time = null;
|
414 |
-
|
415 |
-
// Exit early if this date is an exception
|
416 |
-
if ( valid_times.length > 1 ) {
|
417 |
-
rtb_booking_form.timepicker.set( 'disable', valid_times );
|
418 |
-
|
419 |
-
return;
|
420 |
-
}
|
421 |
-
}
|
422 |
-
|
423 |
-
// Get any rules which apply to this weekday
|
424 |
-
if ( typeof rtb_pickadate.schedule_open != 'undefined' ) {
|
425 |
-
|
426 |
-
var selected_date_weekday = selected_date.getDay();
|
427 |
-
|
428 |
-
var weekdays = {
|
429 |
-
sunday: 0,
|
430 |
-
monday: 1,
|
431 |
-
tuesday: 2,
|
432 |
-
wednesday: 3,
|
433 |
-
thursday: 4,
|
434 |
-
friday: 5,
|
435 |
-
saturday: 6,
|
436 |
-
};
|
437 |
-
|
438 |
-
var rule_start_date = [];
|
439 |
-
var rule_start_time = [];
|
440 |
-
var rule_end_date = [];
|
441 |
-
var rule_end_time = [];
|
442 |
-
for ( var open_key in rtb_pickadate.schedule_open ) {
|
443 |
-
|
444 |
-
if ( typeof rtb_pickadate.schedule_open[open_key].weekdays !== 'undefined' ) {
|
445 |
-
for ( var weekdays_key in rtb_pickadate.schedule_open[open_key].weekdays ) {
|
446 |
-
if ( weekdays[weekdays_key] == selected_date_weekday ) {
|
447 |
-
|
448 |
-
// Closed all day
|
449 |
-
if ( typeof rtb_pickadate.schedule_open[open_key].time == 'undefined' ) {
|
450 |
-
rtb_booking_form.timepicker.set( 'disable', [ true ] );
|
451 |
-
|
452 |
-
return;
|
453 |
-
}
|
454 |
-
|
455 |
-
if ( typeof rtb_pickadate.schedule_open[open_key].time.start !== 'undefined' ) {
|
456 |
-
rule_start_date = new Date( '1 January 2000 ' + rtb_pickadate.schedule_open[open_key].time.start );
|
457 |
-
rule_start_time = [ rule_start_date.getHours(), rule_start_date.getMinutes() ];
|
458 |
-
} else {
|
459 |
-
rule_start_time = [ 0, 0 ]; // Start of the day
|
460 |
-
}
|
461 |
-
|
462 |
-
if ( typeof rtb_pickadate.schedule_open[open_key].time.end !== 'undefined' ) {
|
463 |
-
rule_end_date = new Date( '1 January 2000 ' + rtb_pickadate.schedule_open[open_key].time.end );
|
464 |
-
rule_end_time = rtb_booking_form.get_latest_viable_time( rule_end_date.getHours(), rule_end_date.getMinutes() );
|
465 |
-
} else {
|
466 |
-
rule_end_time = [ 24, 0 ]; // End of the day
|
467 |
-
}
|
468 |
-
|
469 |
-
rule_start_time = rtb_booking_form.get_earliest_time( rule_start_time, selected_date, current_date );
|
470 |
-
|
471 |
-
valid_times.push( { from: rule_start_time, to: rule_end_time, inverted: true } );
|
472 |
-
|
473 |
-
}
|
474 |
-
}
|
475 |
-
}
|
476 |
-
}
|
477 |
-
|
478 |
-
rule_start_date = rule_start_time = rule_end_date = rule_end_time = null;
|
479 |
-
|
480 |
-
// Pass any valid times located
|
481 |
-
if ( valid_times.length > 1 ) {
|
482 |
-
rtb_booking_form.timepicker.set( 'disable', valid_times );
|
483 |
-
|
484 |
-
return;
|
485 |
-
}
|
486 |
-
|
487 |
-
}
|
488 |
-
|
489 |
-
// Set it to always open if no rules have been defined
|
490 |
-
rtb_booking_form.timepicker.set( 'enable', true );
|
491 |
-
rtb_booking_form.timepicker.set( 'disable', false );
|
492 |
-
}
|
493 |
-
|
494 |
-
return;
|
495 |
-
};
|
496 |
-
|
497 |
-
/**
|
498 |
-
* Get the outer times to exclude based on the time interval
|
499 |
-
*
|
500 |
-
* This is a work-around for a bug in pickadate.js
|
501 |
-
* See: https://github.com/amsul/pickadate.js/issues/614
|
502 |
-
*/
|
503 |
-
rtb_booking_form.get_outer_time_range = function() {
|
504 |
-
|
505 |
-
var interval = rtb_booking_form.timepicker.get( 'interval' );
|
506 |
-
|
507 |
-
var hour = 24;
|
508 |
-
|
509 |
-
while ( interval >= 60 ) {
|
510 |
-
hour--;
|
511 |
-
interval -= 60;
|
512 |
-
}
|
513 |
-
|
514 |
-
if ( interval > 0 ) {
|
515 |
-
hour--;
|
516 |
-
// All day open does not work when date-time-range and current open time are exact same
|
517 |
-
interval = 60 - interval;
|
518 |
-
}
|
519 |
-
|
520 |
-
return { from: [0, 0], to: [ hour, interval ] };
|
521 |
-
};
|
522 |
-
|
523 |
-
/**
|
524 |
-
* Get the latest working opening hour/minute value
|
525 |
-
*
|
526 |
-
* This is a workaround for a bug in pickadate.js. The end time of a valid
|
527 |
-
* time value must NOT fall within the last timepicker interval and midnight
|
528 |
-
* See: https://github.com/amsul/pickadate.js/issues/614
|
529 |
-
*/
|
530 |
-
rtb_booking_form.get_latest_viable_time = function( hour, minute ) {
|
531 |
-
|
532 |
-
var interval = rtb_booking_form.timepicker.get( 'interval' );
|
533 |
-
|
534 |
-
var outer_time_range = this.get_outer_time_range();
|
535 |
-
|
536 |
-
/*
|
537 |
-
* Adjust the last time for wide intervals, so that the last time entered
|
538 |
-
* corresponds to an interval time. A pickadate bug causes a later time to
|
539 |
-
* be available for booking otherwise.
|
540 |
-
*/
|
541 |
-
if ( interval > 60) {
|
542 |
-
|
543 |
-
var last_hour = 0;
|
544 |
-
var last_minute = 0;
|
545 |
-
var last_time_minutes = 0;
|
546 |
-
|
547 |
-
var end_time_minutes = 60 * hour + minute;
|
548 |
-
|
549 |
-
while ( ( last_time_minutes + interval ) <= end_time_minutes ) {
|
550 |
-
|
551 |
-
var remainder = interval + last_minute;
|
552 |
-
|
553 |
-
while ( remainder >= 60 ) {
|
554 |
-
last_hour++;
|
555 |
-
remainder -= 60;
|
556 |
-
}
|
557 |
-
|
558 |
-
last_minute = remainder;
|
559 |
-
|
560 |
-
last_time_minutes = 60 * last_hour + last_minute;
|
561 |
-
}
|
562 |
-
|
563 |
-
var long_interval_viable_time = [ last_hour, last_minute ];
|
564 |
-
}
|
565 |
-
|
566 |
-
|
567 |
-
if ( interval > 60 ) {
|
568 |
-
|
569 |
-
return long_interval_viable_time;
|
570 |
-
}
|
571 |
-
else if ( hour > outer_time_range.to[0] || minute > outer_time_range.to[1] ) {
|
572 |
-
|
573 |
-
return [ outer_time_range.to[0], outer_time_range.to[1] ];
|
574 |
-
} else {
|
575 |
-
|
576 |
-
return [ hour, minute ];
|
577 |
-
}
|
578 |
-
};
|
579 |
-
|
580 |
-
/**
|
581 |
-
* Get the earliest valid time
|
582 |
-
*
|
583 |
-
* This checks the valid time for the day and, if a current day, applies
|
584 |
-
* any late booking restrictions. It also ensures that times in the past
|
585 |
-
* are not availabe.
|
586 |
-
*
|
587 |
-
* @param array start_time
|
588 |
-
* @param array selected_date
|
589 |
-
* @param array current_date
|
590 |
-
*/
|
591 |
-
rtb_booking_form.get_earliest_time = function( start_time, selected_date, current_date ) {
|
592 |
-
|
593 |
-
// Only make adjustments for current day selections
|
594 |
-
if ( selected_date.toDateString() !== current_date.toDateString() ) {
|
595 |
-
return start_time;
|
596 |
-
}
|
597 |
-
|
598 |
-
// Get the number of minutes after midnight to compare
|
599 |
-
var start_minutes = ( start_time[0] * 60 ) + start_time[1],
|
600 |
-
current_minutes = ( current_date.getHours() * 60 ) + current_date.getMinutes(),
|
601 |
-
late_booking_minutes;
|
602 |
-
|
603 |
-
start_minutes = start_minutes > current_minutes ? start_minutes : current_minutes;
|
604 |
-
|
605 |
-
if ( typeof rtb_pickadate.late_bookings === 'number' && rtb_pickadate.late_bookings % 1 === 0 ) {
|
606 |
-
late_booking_minutes = current_minutes + rtb_pickadate.late_bookings;
|
607 |
-
if ( late_booking_minutes > start_minutes ) {
|
608 |
-
start_minutes = late_booking_minutes;
|
609 |
-
}
|
610 |
-
}
|
611 |
-
|
612 |
-
start_time = [ Math.floor( start_minutes / 60 ), start_minutes % 60 ];
|
613 |
-
|
614 |
-
return start_time;
|
615 |
-
};
|
616 |
-
|
617 |
-
rtb_booking_form.update_party_size_select = function() {
|
618 |
-
|
619 |
-
if ( rtb_pickadate.enable_max_reservations && ( rtb_pickadate.max_people || rtb_pickadate.multiple_locations_enabled ) ) {
|
620 |
-
var partySelect = $('#rtb-party'),
|
621 |
-
selected_location = jQuery( '#rtb-location' ).length ? jQuery( '#rtb-location' ).val() : '',
|
622 |
-
selected_date = new Date( rtb_booking_form.datepicker.get( 'select', 'yyyy/mm/dd' ) ),
|
623 |
-
selected_date_year = selected_date.getFullYear(),
|
624 |
-
selected_date_month = selected_date.getMonth(),
|
625 |
-
selected_date_date = selected_date.getDate(),
|
626 |
-
selected_time = rtb_booking_form.timepicker.get('value');
|
627 |
-
|
628 |
-
if ( ! selected_time ) { return; }
|
629 |
-
|
630 |
-
selected_date_month = ('0' + (selected_date_month + 1)).slice(-2);
|
631 |
-
selected_date_date = ('0' + selected_date_date).slice(-2);
|
632 |
-
|
633 |
-
//reset party size
|
634 |
-
partySelect.prop("selectedIndex", 0).change();
|
635 |
-
|
636 |
-
var params = {};
|
637 |
-
|
638 |
-
params.action = 'rtb_get_available_party_size';
|
639 |
-
params.nonce = rtb_booking_form_js_localize.nonce;
|
640 |
-
params.year = selected_date_year;
|
641 |
-
params.month = selected_date_month;
|
642 |
-
params.day = selected_date_date;
|
643 |
-
params.time = selected_time;
|
644 |
-
params.location = selected_location;
|
645 |
-
|
646 |
-
var data = jQuery.param( params );
|
647 |
-
jQuery.post( ajaxurl, data, function( response ) {
|
648 |
-
if ( ! response ) {
|
649 |
-
return;
|
650 |
-
}
|
651 |
-
|
652 |
-
response = jQuery.parseJSON(response);
|
653 |
-
|
654 |
-
var available_spots = response.available_spots;
|
655 |
-
|
656 |
-
partySelect.prop('disabled', false);
|
657 |
-
|
658 |
-
partySelect.find('> option').each(function() {
|
659 |
-
var that = $(this);
|
660 |
-
if (this.value > available_spots) {
|
661 |
-
that.prop('disabled', true);
|
662 |
-
} else {
|
663 |
-
that.prop('disabled', false);
|
664 |
-
}
|
665 |
-
});
|
666 |
-
});
|
667 |
-
}
|
668 |
-
}
|
669 |
-
|
670 |
-
rtb_booking_form.update_possible_tables = function() {
|
671 |
-
|
672 |
-
if ( rtb_pickadate.enable_tables ) {
|
673 |
-
|
674 |
-
var table_select = $('#rtb-table'),
|
675 |
-
party = $('#rtb-party').val(),
|
676 |
-
selected_date = new Date( rtb_booking_form.datepicker.get( 'select', 'yyyy/mm/dd' ) ),
|
677 |
-
selected_date_year = selected_date.getFullYear(),
|
678 |
-
selected_date_month = selected_date.getMonth(),
|
679 |
-
selected_date_date = selected_date.getDate(),
|
680 |
-
selected_time = rtb_booking_form.timepicker.get('value');
|
681 |
-
|
682 |
-
if ( ! selected_time || ! party ) { return; }
|
683 |
-
|
684 |
-
selected_date_month = ('0' + (selected_date_month + 1)).slice(-2);
|
685 |
-
selected_date_date = ('0' + selected_date_date).slice(-2);
|
686 |
-
|
687 |
-
//reset table selection
|
688 |
-
table_select.prop("selectedIndex", 0).change();
|
689 |
-
|
690 |
-
//remove table combinations
|
691 |
-
table_select.find('> option').each( function() {
|
692 |
-
if ( $( this ).val().indexOf( ',' ) !== -1 ) { $( this ).remove(); }
|
693 |
-
});
|
694 |
-
|
695 |
-
var booking_id = $( '.rtb-booking-form form input[name="ID"]').length ? $( '.rtb-booking-form form input[name="ID"]').val() : 0;
|
696 |
-
|
697 |
-
var params = {};
|
698 |
-
|
699 |
-
params.action = 'rtb_get_available_tables';
|
700 |
-
params.nonce = rtb_booking_form_js_localize.nonce;
|
701 |
-
params.year = selected_date_year;
|
702 |
-
params.month = selected_date_month;
|
703 |
-
params.day = selected_date_date;
|
704 |
-
params.time = selected_time;
|
705 |
-
params.party = party;
|
706 |
-
params.booking_id = booking_id
|
707 |
-
|
708 |
-
var data = jQuery.param( params );
|
709 |
-
jQuery.post( ajaxurl, data, function( response ) {
|
710 |
-
if ( ! response ) {
|
711 |
-
return;
|
712 |
-
}
|
713 |
-
|
714 |
-
response = jQuery.parseJSON(response);
|
715 |
-
|
716 |
-
var available_tables = response.available_tables;
|
717 |
-
|
718 |
-
table_select.prop('disabled', false);
|
719 |
-
|
720 |
-
table_select.find('> option').hide();
|
721 |
-
table_select.find('> option').attr('disabled', 'disabled');
|
722 |
-
|
723 |
-
if( 1 > available_tables.length ) {
|
724 |
-
displayFieldError( 'table', rtb_booking_form_js_localize.error['no-table-available'] );
|
725 |
-
}
|
726 |
-
else {
|
727 |
-
clearPrevFieldError( 'table' );
|
728 |
-
}
|
729 |
-
|
730 |
-
jQuery.each(available_tables, function(index, element) {
|
731 |
-
|
732 |
-
if ( index.indexOf( ',' ) === -1 ) {
|
733 |
-
table_select.find('> option[value="' + index + '"]').show();
|
734 |
-
table_select.find('> option[value="' + index + '"]').removeAttr('disabled', 'disabled');
|
735 |
-
}
|
736 |
-
else {
|
737 |
-
table_select.append( '<option value="' + index + '">' + element + '</option>' );
|
738 |
-
}
|
739 |
-
|
740 |
-
});
|
741 |
-
|
742 |
-
if ( response.selected_table != -1 ) {
|
743 |
-
table_select.val( response.selected_table );
|
744 |
-
}
|
745 |
-
else if( '' != table_select.data('selected') ) {
|
746 |
-
table_select.val( table_select.data('selected') );
|
747 |
-
}
|
748 |
-
|
749 |
-
// pre-select table if it was selected before and is available
|
750 |
-
|
751 |
-
|
752 |
-
});
|
753 |
-
}
|
754 |
-
|
755 |
-
}
|
756 |
-
|
757 |
-
rtb_booking_form.init();
|
758 |
-
});
|
759 |
-
|
760 |
-
//Handle reservation modification
|
761 |
-
jQuery(document).ready(function() {
|
762 |
-
jQuery('.rtb-modification-toggle').on('click', function() {
|
763 |
-
jQuery('.rtb-modification-form, .rtb-booking-form-form').toggleClass('rtb-hidden');
|
764 |
-
|
765 |
-
if (jQuery('.rtb-modification-form').hasClass('rtb-hidden')) {
|
766 |
-
jQuery('.rtb-modification-toggle').html(rtb_booking_form_js_localize.want_to_modify);
|
767 |
-
}
|
768 |
-
else {
|
769 |
-
jQuery('.rtb-modification-toggle').html(rtb_booking_form_js_localize.make);
|
770 |
-
}
|
771 |
-
});
|
772 |
-
|
773 |
-
var modify_booking = function(ev) {
|
774 |
-
var booking_email = jQuery('input[name="rtb_modification_email"]').val();
|
775 |
-
|
776 |
-
var params = {};
|
777 |
-
|
778 |
-
params.action = 'rtb_find_reservations';
|
779 |
-
params.nonce = rtb_booking_form_js_localize.nonce;
|
780 |
-
params.booking_email = booking_email;
|
781 |
-
|
782 |
-
var data = jQuery.param( params );
|
783 |
-
jQuery.post(ajaxurl, data, function(response) {
|
784 |
-
|
785 |
-
if (response.success) {
|
786 |
-
var booking_html = '';
|
787 |
-
var guest_txt = '';
|
788 |
-
var pay_btn = '';
|
789 |
-
|
790 |
-
jQuery(response.data.bookings).each(function( index, val) {
|
791 |
-
pay_btn = '';
|
792 |
-
guest_txt = val.party > 1 ? rtb_booking_form_js_localize.guests : rtb_booking_form_js_localize.guest;
|
793 |
-
|
794 |
-
if('payment_pending' == val.status || 'payment_failed' == val.status) {
|
795 |
-
pay_btn = `
|
796 |
-
<div class="rtb-deposit-booking" data-bookingid="${val.ID}" data-bookingemail="${val.email}">
|
797 |
-
${rtb_booking_form_js_localize.deposit}
|
798 |
-
</div>
|
799 |
-
`;
|
800 |
-
}
|
801 |
-
|
802 |
-
booking_html += `
|
803 |
-
<div class="rtb-cancel-booking-div">
|
804 |
-
<div class="rtb-cancel-booking" data-bookingid="${val.ID}" data-bookingemail="${val.email}">
|
805 |
-
${rtb_booking_form_js_localize.cancel}
|
806 |
-
</div>
|
807 |
-
${pay_btn}
|
808 |
-
<div class="rtb-booking-information">${val.datetime} - ${val.party} ${guest_txt} (${val.status_lbl})</div>
|
809 |
-
</div>
|
810 |
-
`;
|
811 |
-
});
|
812 |
-
|
813 |
-
jQuery('.rtb-bookings-results').html(booking_html);
|
814 |
-
|
815 |
-
cancellationHandler();
|
816 |
-
delayedPaymentHandler();
|
817 |
-
}
|
818 |
-
else {jQuery('.rtb-bookings-results').html(response.data.msg);}
|
819 |
-
});
|
820 |
-
};
|
821 |
-
|
822 |
-
jQuery(document).on('click', '.rtb-find-reservation-button', modify_booking);
|
823 |
-
jQuery(document).on('keypress', '.rtb-modification-form input', function (ev) {
|
824 |
-
// Capture enter key
|
825 |
-
if(13 == ev.which) {
|
826 |
-
ev.preventDefault();
|
827 |
-
modify_booking(ev);
|
828 |
-
}
|
829 |
-
});
|
830 |
-
});
|
831 |
-
|
832 |
-
function cancellationHandler() {
|
833 |
-
jQuery('.rtb-cancel-booking:not(.cancelled)').off('click');
|
834 |
-
jQuery('.rtb-cancel-booking:not(.cancelled)').on('click', function() {
|
835 |
-
var btn = jQuery(this);
|
836 |
-
|
837 |
-
if(btn.hasClass('processing')) {
|
838 |
-
return;
|
839 |
-
}
|
840 |
-
|
841 |
-
btn.addClass('processing');
|
842 |
-
|
843 |
-
var booking_id = btn.data('bookingid');
|
844 |
-
var booking_email = btn.data('bookingemail');
|
845 |
-
|
846 |
-
var params = {};
|
847 |
-
|
848 |
-
params.action = 'rtb_cancel_reservations';
|
849 |
-
params.nonce = rtb_booking_form_js_localize.nonce;
|
850 |
-
params.booking_id = booking_id;
|
851 |
-
params.booking_email = booking_email;
|
852 |
-
|
853 |
-
var data = jQuery.param( params );
|
854 |
-
jQuery.post(ajaxurl, data, function(response) {
|
855 |
-
if (response.success) {
|
856 |
-
if (response.data.hasOwnProperty('cancelled_redirect')) {
|
857 |
-
window.location.href = response.data.cancelled_redirect;
|
858 |
-
}
|
859 |
-
else {
|
860 |
-
btn.off('click');
|
861 |
-
btn.addClass('cancelled');
|
862 |
-
btn.text(rtb_booking_form_js_localize.cancelled);
|
863 |
-
}
|
864 |
-
}
|
865 |
-
else {
|
866 |
-
btn.parent().after(`<p class="alert error">${response.data.msg}</p>`);
|
867 |
-
}
|
868 |
-
|
869 |
-
btn.removeClass('processing');
|
870 |
-
});
|
871 |
-
});
|
872 |
-
}
|
873 |
-
|
874 |
-
function delayedPaymentHandler() {
|
875 |
-
jQuery('.rtb-deposit-booking').off('click');
|
876 |
-
jQuery('.rtb-deposit-booking').on('click', function() {
|
877 |
-
var btn = jQuery(this);
|
878 |
-
|
879 |
-
if(btn.hasClass('processing')) {
|
880 |
-
return;
|
881 |
-
}
|
882 |
-
|
883 |
-
btn.addClass('processing');
|
884 |
-
|
885 |
-
var booking_id = btn.data('bookingid');
|
886 |
-
var booking_email = btn.data('bookingemail');
|
887 |
-
|
888 |
-
var data = {
|
889 |
-
'booking_id': booking_id,
|
890 |
-
'booking_email': booking_email,
|
891 |
-
'payment': 'rtb-delayed-deposit'
|
892 |
-
};
|
893 |
-
|
894 |
-
let current_loc = window.location;
|
895 |
-
let params = new URLSearchParams();
|
896 |
-
Object.keys( data ).map( function( param ) { params.append( param, data[ param ] ) } );
|
897 |
-
|
898 |
-
window.location = current_loc.origin + current_loc.pathname + '?' + params.toString();
|
899 |
-
|
900 |
-
});
|
901 |
-
}
|
902 |
-
|
903 |
-
function displayFieldError( field, message ) {
|
904 |
-
|
905 |
-
const fieldSelector = '.'+field;
|
906 |
-
var fieldElm = jQuery('form.rtb-booking-form-form '+fieldSelector);
|
907 |
-
|
908 |
-
if( fieldElm.length ) {
|
909 |
-
|
910 |
-
clearPrevFieldError( field );
|
911 |
-
|
912 |
-
fieldElm.prepend(`
|
913 |
-
<div class="rtb-error">${message}</div>
|
914 |
-
`);
|
915 |
-
}
|
916 |
-
}
|
917 |
-
|
918 |
-
function clearPrevFieldError( field ) {
|
919 |
-
const fieldSelector = '.'+field;
|
920 |
-
var errorElms = jQuery('form.rtb-booking-form-form ' + fieldSelector + ' .rtb-error');
|
921 |
-
|
922 |
-
if( errorElms.length ) {
|
923 |
-
errorElms.each((idx, x) => x.remove());
|
924 |
-
}
|
925 |
-
}
|
926 |
-
|
927 |
-
// Functions for the 'View Bookings' shortcode
|
928 |
-
jQuery(document).ready(function ($) {
|
929 |
-
jQuery('.rtb-view-bookings-form-date-selector').on('change', function() {
|
930 |
-
window.location.href = replaceUrlParam(window.location.href, 'date', jQuery(this).val());
|
931 |
-
});
|
932 |
-
|
933 |
-
jQuery('.rtb-edit-view-booking').on('click', function() {
|
934 |
-
jQuery('.rtb-view-bookings-form-confirmation-div, .rtb-view-bookings-form-confirmation-background-div').removeClass('rtb-hidden');
|
935 |
-
|
936 |
-
jQuery('.rtb-view-bookings-form-confirmation-div').data('bookingid', jQuery(this).data('bookingid'));
|
937 |
-
|
938 |
-
jQuery(this).prop('checked', false);
|
939 |
-
});
|
940 |
-
|
941 |
-
jQuery('.rtb-view-bookings-form-confirmation-accept').on('click', function() {
|
942 |
-
var booking_id = jQuery('.rtb-view-bookings-form-confirmation-div').data('bookingid');
|
943 |
-
|
944 |
-
var params = {};
|
945 |
-
|
946 |
-
params.action = 'rtb_set_reservation_arrived';
|
947 |
-
params.nonce = rtb_booking_form_js_localize.nonce;
|
948 |
-
params.booking = {
|
949 |
-
'ID': booking_id
|
950 |
-
};
|
951 |
-
|
952 |
-
var data = $.param( params );
|
953 |
-
|
954 |
-
jQuery.post(ajaxurl, data, function(response) {
|
955 |
-
|
956 |
-
if (response.success) {window.location.href = window.location.href}
|
957 |
-
else {jQuery('.rtb-view-bookings-form-confirmation-div').html(response.data.msg);}
|
958 |
-
});
|
959 |
-
});
|
960 |
-
|
961 |
-
jQuery('.rtb-view-bookings-form-confirmation-decline').on('click', function() {
|
962 |
-
jQuery('.rtb-view-bookings-form-confirmation-div, .rtb-view-bookings-form-confirmation-background-div').addClass('rtb-hidden');
|
963 |
-
});
|
964 |
-
jQuery('.rtb-view-bookings-form-confirmation-background-div').on('click', function() {
|
965 |
-
jQuery('.rtb-view-bookings-form-confirmation-div, .rtb-view-bookings-form-confirmation-background-div').addClass('rtb-hidden');
|
966 |
-
});
|
967 |
-
jQuery('#rtb-view-bookings-form-close').on('click', function() {
|
968 |
-
jQuery('.rtb-view-bookings-form-confirmation-div, .rtb-view-bookings-form-confirmation-background-div').addClass('rtb-hidden');
|
969 |
-
});
|
970 |
-
});
|
971 |
-
|
972 |
-
function replaceUrlParam(url, paramName, paramValue)
|
973 |
-
{
|
974 |
-
if (paramValue == null) {
|
975 |
-
paramValue = '';
|
976 |
-
}
|
977 |
-
var pattern = new RegExp('\\b('+paramName+'=).*?(&|#|$)');
|
978 |
-
if (url.search(pattern)>=0) {
|
979 |
-
return url.replace(pattern,'$1' + paramValue + '$2');
|
980 |
-
}
|
981 |
-
url = url.replace(/[?#]$/,'');
|
982 |
-
return url + (url.indexOf('?')>0 ? '&' : '?') + paramName + '=' + paramValue;
|
983 |
}
|
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 |
+
.find( 'label' ).focus();
|
24 |
+
|
25 |
+
return false;
|
26 |
+
});
|
27 |
+
|
28 |
+
// Show the message field on load if not empty
|
29 |
+
if ( $.trim( $( '.rtb-booking-form .message textarea' ).val() ) ) {
|
30 |
+
$( '.rtb-booking-form .add-message a' ).trigger( 'click' );
|
31 |
+
}
|
32 |
+
|
33 |
+
// Disable the submit button when the booking form is submitted
|
34 |
+
$( '.rtb-booking-form form' ).submit( function() {
|
35 |
+
$(this).find( 'button[type="submit"]' ).prop( 'disabled', 'disabled' );
|
36 |
+
return true;
|
37 |
+
} );
|
38 |
+
|
39 |
+
// Enable datepickers on load
|
40 |
+
if ( typeof rtb_pickadate !== 'undefined' ) {
|
41 |
+
|
42 |
+
// Declare datepicker
|
43 |
+
var $date_input = $( '#rtb-date' );
|
44 |
+
if ( $date_input.length ) {
|
45 |
+
var date_input = $date_input.pickadate({
|
46 |
+
format: rtb_pickadate.date_format,
|
47 |
+
formatSubmit: 'yyyy/mm/dd',
|
48 |
+
hiddenName: true,
|
49 |
+
min: !rtb_pickadate.allow_past,
|
50 |
+
container: 'body',
|
51 |
+
firstDay: rtb_pickadate.first_day,
|
52 |
+
today: rtb_pickadate.date_today_label,
|
53 |
+
clear: rtb_pickadate.date_clear_label,
|
54 |
+
close: rtb_pickadate.date_close_label,
|
55 |
+
|
56 |
+
onStart: function() {
|
57 |
+
|
58 |
+
// Block dates beyond early bookings window
|
59 |
+
if ( rtb_pickadate.early_bookings !== '' ) {
|
60 |
+
this.set( 'max', parseInt( rtb_pickadate.early_bookings, 10 ) );
|
61 |
+
}
|
62 |
+
|
63 |
+
// Select the value when loaded if a value has been set
|
64 |
+
if ( $date_input.val() !== '' ) {
|
65 |
+
var date = new Date( $date_input.val() );
|
66 |
+
if ( Object.prototype.toString.call( date ) === "[object Date]" ) {
|
67 |
+
this.set( 'select', date );
|
68 |
+
}
|
69 |
+
}
|
70 |
+
else if ( jQuery( '.rtb-selected-date' ).length ) {
|
71 |
+
var date = new Date( jQuery( '.rtb-selected-date' ).val() );
|
72 |
+
if ( Object.prototype.toString.call( date ) === "[object Date]" ) {
|
73 |
+
this.set( 'select', date );
|
74 |
+
}
|
75 |
+
}
|
76 |
+
}
|
77 |
+
});
|
78 |
+
|
79 |
+
rtb_booking_form.datepicker = date_input.pickadate( 'picker' );
|
80 |
+
}
|
81 |
+
|
82 |
+
// Declare timepicker
|
83 |
+
var $time_input = $( '#rtb-time' );
|
84 |
+
if ( $time_input.length ) {
|
85 |
+
var time_input = $time_input.pickatime({
|
86 |
+
format: rtb_pickadate.time_format,
|
87 |
+
formatSubmit: 'h:i A',
|
88 |
+
hiddenName: true,
|
89 |
+
interval: parseInt( rtb_pickadate.time_interval, 10 ),
|
90 |
+
container: 'body',
|
91 |
+
clear: rtb_pickadate.time_clear_label,
|
92 |
+
|
93 |
+
// Select the value when loaded if a value has been set
|
94 |
+
onStart: function() {
|
95 |
+
if ( $time_input.val() !== '' ) {
|
96 |
+
var today = new Date();
|
97 |
+
var today_date = today.getFullYear() + '/' + ( today.getMonth() + 1 ) + '/' + today.getDate();
|
98 |
+
var time = new Date( today_date + ' ' + $time_input.val() );
|
99 |
+
if ( Object.prototype.toString.call( time ) === "[object Date]" ) {
|
100 |
+
this.set( 'select', time );
|
101 |
+
}
|
102 |
+
|
103 |
+
}
|
104 |
+
}
|
105 |
+
});
|
106 |
+
|
107 |
+
rtb_booking_form.timepicker = time_input.pickatime( 'picker' );
|
108 |
+
}
|
109 |
+
|
110 |
+
// We need to check both to support different jQuery versions loaded
|
111 |
+
// by older versions of WordPress. In jQuery v1.10.2, the property
|
112 |
+
// is undefined. But in v1.11.3 it's set to null.
|
113 |
+
if ( rtb_booking_form.datepicker === null || typeof rtb_booking_form.datepicker == 'undefined' ) {
|
114 |
+
return;
|
115 |
+
}
|
116 |
+
|
117 |
+
// Update disabled dates
|
118 |
+
rtb_booking_form.update_disabled_dates();
|
119 |
+
|
120 |
+
if ( typeof rtb_pickadate.late_bookings === 'string' ) {
|
121 |
+
if ( rtb_pickadate.late_bookings == 'same_day' ) {
|
122 |
+
rtb_booking_form.datepicker.set( 'min', 1 );
|
123 |
+
} else if ( rtb_pickadate.late_bookings !== '' ) {
|
124 |
+
rtb_pickadate.late_bookings = parseInt( rtb_pickadate.late_bookings, 10 );
|
125 |
+
if ( rtb_pickadate.late_bookings % 1 === 0 && rtb_pickadate.late_bookings >= 1440 ) {
|
126 |
+
var min = Math.floor( rtb_pickadate.late_bookings / 1440 );
|
127 |
+
rtb_booking_form.datepicker.set( 'min', min );
|
128 |
+
}
|
129 |
+
}
|
130 |
+
}
|
131 |
+
|
132 |
+
// If no date has been set, select today's date if it's a valid
|
133 |
+
// date. User may opt not to do this in the settings.
|
134 |
+
if ( $date_input.val() === '' && !$( '.rtb-booking-form .date .rtb-error' ).length ) {
|
135 |
+
|
136 |
+
if ( rtb_pickadate.date_onload == 'soonest' ) {
|
137 |
+
rtb_booking_form.datepicker.set( 'select', new Date() );
|
138 |
+
} else if ( rtb_pickadate.date_onload !== 'empty' ) {
|
139 |
+
var dateToVerify = rtb_booking_form.datepicker.component.create( new Date() );
|
140 |
+
var isDisabled = rtb_booking_form.datepicker.component.disabled( dateToVerify );
|
141 |
+
if ( !isDisabled ) {
|
142 |
+
rtb_booking_form.datepicker.set( 'select', dateToVerify );
|
143 |
+
}
|
144 |
+
}
|
145 |
+
}
|
146 |
+
|
147 |
+
if ( rtb_booking_form.timepicker === null || typeof rtb_booking_form.timepicker == 'undefined' ) {
|
148 |
+
return;
|
149 |
+
}
|
150 |
+
|
151 |
+
// Update timepicker on pageload and whenever the datepicker is closed
|
152 |
+
rtb_booking_form.update_timepicker_range();
|
153 |
+
rtb_booking_form.datepicker.on( {
|
154 |
+
open: function () {
|
155 |
+
|
156 |
+
rtb_booking_form.before_change_value = rtb_booking_form.datepicker.get();
|
157 |
+
},
|
158 |
+
|
159 |
+
close: function() {
|
160 |
+
|
161 |
+
rtb_booking_form.after_change_value = rtb_booking_form.datepicker.get();
|
162 |
+
|
163 |
+
if(rtb_booking_form.before_change_value != rtb_booking_form.after_change_value) {
|
164 |
+
// clear time value if date changed
|
165 |
+
rtb_booking_form.timepicker.clear();
|
166 |
+
}
|
167 |
+
|
168 |
+
rtb_booking_form.update_timepicker_range();
|
169 |
+
rtb_booking_form.update_party_size_select();
|
170 |
+
rtb_booking_form.update_possible_tables();
|
171 |
+
}
|
172 |
+
});
|
173 |
+
|
174 |
+
rtb_booking_form.timepicker.on( {
|
175 |
+
close: function() {
|
176 |
+
rtb_booking_form.update_party_size_select();
|
177 |
+
rtb_booking_form.update_possible_tables();
|
178 |
+
}
|
179 |
+
});
|
180 |
+
|
181 |
+
$( '#rtb-party' ).on( 'change', function() {
|
182 |
+
rtb_booking_form.update_possible_tables();
|
183 |
+
});
|
184 |
+
|
185 |
+
$( '#rtb-location' ).on( 'change', function() {
|
186 |
+
|
187 |
+
if ( ! rtb_pickadate.multiple_locations_enabled ) { return; }
|
188 |
+
|
189 |
+
rtb_booking_form.timepicker.clear();
|
190 |
+
rtb_booking_form.datepicker.clear();
|
191 |
+
|
192 |
+
rtb_booking_form.update_base_data_for_selected_location();
|
193 |
+
|
194 |
+
rtb_booking_form.update_datepicker();
|
195 |
+
|
196 |
+
rtb_booking_form.update_timepicker_range();
|
197 |
+
|
198 |
+
rtb_booking_form.update_party_size_select();
|
199 |
+
});
|
200 |
+
|
201 |
+
rtb_booking_form.update_possible_tables();
|
202 |
+
}
|
203 |
+
};
|
204 |
+
|
205 |
+
/**
|
206 |
+
* Update base data for date/time picker as per the selected location
|
207 |
+
* @return object
|
208 |
+
*/
|
209 |
+
rtb_booking_form.update_base_data_for_selected_location = function () {
|
210 |
+
const selected_location = jQuery( '#rtb-location' ).length ? jQuery( '#rtb-location' ).val() : '';
|
211 |
+
|
212 |
+
if( '' == selected_location ) {
|
213 |
+
// Set global settings
|
214 |
+
return Object.assign( rtb_pickadate, rtb_location_data.global );
|
215 |
+
}
|
216 |
+
|
217 |
+
return Object.assign( rtb_pickadate, rtb_location_data[selected_location] );
|
218 |
+
}
|
219 |
+
|
220 |
+
rtb_booking_form.update_datepicker = function () {
|
221 |
+
|
222 |
+
// Reset enabled/disabled rules on this datepicker
|
223 |
+
rtb_booking_form.datepicker.set( 'enable', false );
|
224 |
+
rtb_booking_form.datepicker.set( 'disable', false );
|
225 |
+
|
226 |
+
rtb_booking_form.update_disabled_dates();
|
227 |
+
}
|
228 |
+
|
229 |
+
/**
|
230 |
+
* Update datepicker to change the disabled dates based on location
|
231 |
+
*/
|
232 |
+
rtb_booking_form.update_disabled_dates = function() {
|
233 |
+
|
234 |
+
// Pass conditional configuration parameters
|
235 |
+
if ( rtb_pickadate.disable_dates.length ) {
|
236 |
+
|
237 |
+
var disable_dates = jQuery.extend( true, [], rtb_pickadate.disable_dates );
|
238 |
+
|
239 |
+
// Update weekday dates if start of the week has been modified
|
240 |
+
if ( typeof rtb_booking_form.datepicker.component.settings.firstDay == 'number' ) {
|
241 |
+
var weekday_num = 0;
|
242 |
+
for ( var disable_key in rtb_pickadate.disable_dates ) {
|
243 |
+
if ( typeof rtb_pickadate.disable_dates[disable_key] == 'number' ) {
|
244 |
+
weekday_num = rtb_pickadate.disable_dates[disable_key] - rtb_booking_form.datepicker.component.settings.firstDay;
|
245 |
+
if ( weekday_num < 1 ) {
|
246 |
+
weekday_num = 7;
|
247 |
+
}
|
248 |
+
disable_dates[disable_key] = weekday_num;
|
249 |
+
}
|
250 |
+
}
|
251 |
+
}
|
252 |
+
|
253 |
+
rtb_booking_form.datepicker.set( 'disable', disable_dates );
|
254 |
+
|
255 |
+
}
|
256 |
+
}
|
257 |
+
|
258 |
+
/**
|
259 |
+
* Update the timepicker's range based on the currently selected date
|
260 |
+
*/
|
261 |
+
rtb_booking_form.update_timepicker_range = function() {
|
262 |
+
|
263 |
+
// Reset enabled/disabled rules on this timepicker
|
264 |
+
rtb_booking_form.timepicker.set( 'enable', false );
|
265 |
+
rtb_booking_form.timepicker.set( 'disable', false );
|
266 |
+
|
267 |
+
if ( rtb_booking_form.datepicker.get() === '' ) {
|
268 |
+
rtb_booking_form.timepicker.set( 'disable', true );
|
269 |
+
return;
|
270 |
+
}
|
271 |
+
|
272 |
+
var selected_date = new Date( rtb_booking_form.datepicker.get( 'select', 'yyyy/mm/dd' ) ),
|
273 |
+
selected_date_year = selected_date.getFullYear(),
|
274 |
+
selected_date_month = selected_date.getMonth(),
|
275 |
+
selected_date_date = selected_date.getDate(),
|
276 |
+
current_date = new Date();
|
277 |
+
|
278 |
+
selected_date.setHours(0, 0, 0), selected_date.setMilliseconds(100);
|
279 |
+
|
280 |
+
// Declaring the first element true inverts the timepicker settings. All
|
281 |
+
// times subsequently declared are valid. Any time that doesn't fall
|
282 |
+
// within those declarations is invalid.
|
283 |
+
// See: http://amsul.ca/pickadate.js/time/#disable-times-all
|
284 |
+
var valid_times = [ rtb_booking_form.get_outer_time_range() ];
|
285 |
+
|
286 |
+
if ( rtb_pickadate.enable_max_reservations || rtb_pickadate.multiple_locations_enabled ) {
|
287 |
+
selected_location = jQuery( '#rtb-location' ).length ? jQuery( '#rtb-location' ).val() : '';
|
288 |
+
|
289 |
+
let hidden_location = jQuery('.rtb-booking-form-form input[name="rtb-location"]');
|
290 |
+
if('' == selected_location && hidden_location.length ) {
|
291 |
+
selected_location = hidden_location.val();
|
292 |
+
}
|
293 |
+
|
294 |
+
selected_date_month = ('0' + (selected_date_month + 1)).slice(-2);
|
295 |
+
selected_date_date = ('0' + selected_date_date).slice(-2);
|
296 |
+
|
297 |
+
var params = {};
|
298 |
+
|
299 |
+
params.action = 'rtb_get_available_time_slots';
|
300 |
+
params.nonce = rtb_booking_form_js_localize.nonce;
|
301 |
+
params.year = selected_date_year;
|
302 |
+
params.month = selected_date_month;
|
303 |
+
params.day = selected_date_date;
|
304 |
+
params.location = selected_location;
|
305 |
+
|
306 |
+
var data = jQuery.param( params );
|
307 |
+
jQuery.post( ajaxurl, data, function( response ) {
|
308 |
+
|
309 |
+
if ( ! response ) {
|
310 |
+
displayFieldError( 'date', rtb_booking_form_js_localize.error['smthng-wrng-cntct-us'] );
|
311 |
+
rtb_booking_form.timepicker.set( 'disable', true );
|
312 |
+
|
313 |
+
return;
|
314 |
+
}
|
315 |
+
else {
|
316 |
+
clearPrevFieldError( 'date' );
|
317 |
+
}
|
318 |
+
|
319 |
+
var additional_valid_times = jQuery.parseJSON( response );
|
320 |
+
|
321 |
+
// If today is all day open, only add one valid date/time rule
|
322 |
+
let outer_range = valid_times[0];
|
323 |
+
if(
|
324 |
+
additional_valid_times.length == 1
|
325 |
+
&& additional_valid_times[0].from[0] == outer_range.from[0]
|
326 |
+
&& additional_valid_times[0].from[1] == outer_range.from[1]
|
327 |
+
&& additional_valid_times[0].to[0] == outer_range.to[0]
|
328 |
+
&& additional_valid_times[0].to[1] == outer_range.to[1] ) {
|
329 |
+
var all_valid_times = [ additional_valid_times[0] ];
|
330 |
+
}
|
331 |
+
else {
|
332 |
+
var all_valid_times = valid_times.concat( additional_valid_times );
|
333 |
+
}
|
334 |
+
|
335 |
+
if( !Array.isArray( additional_valid_times ) || 1 > additional_valid_times.length ) {
|
336 |
+
displayFieldError( 'time', rtb_booking_form_js_localize.error['no-slots-available'] );
|
337 |
+
}
|
338 |
+
else {
|
339 |
+
clearPrevFieldError( 'time' );
|
340 |
+
}
|
341 |
+
|
342 |
+
rtb_booking_form.timepicker.set( 'disable', all_valid_times );
|
343 |
+
});
|
344 |
+
}
|
345 |
+
|
346 |
+
else {
|
347 |
+
// Check if this date is an exception to the rules
|
348 |
+
if ( typeof rtb_pickadate.schedule_closed !== 'undefined' ) {
|
349 |
+
|
350 |
+
var excp_date = [];
|
351 |
+
var excp_start_date = [];
|
352 |
+
var excp_start_time = [];
|
353 |
+
var excp_end_date = [];
|
354 |
+
var excp_end_time = [];
|
355 |
+
for ( var closed_key in rtb_pickadate.schedule_closed ) {
|
356 |
+
|
357 |
+
let rule = rtb_pickadate.schedule_closed[closed_key];
|
358 |
+
if( rule.hasOwnProperty('date_range') ) {
|
359 |
+
let start = '' != rule.date_range.start ? new Date( rule.date_range.start ) : new Date();
|
360 |
+
start.setHours(0, 0, 0), start.setMilliseconds(0);
|
361 |
+
start = start.getTime();
|
362 |
+
|
363 |
+
let end = '' != rule.date_range.end ? new Date( rule.date_range.end ) : 9999999999999;
|
364 |
+
'number' != typeof end && end.setHours(23, 59, 58) && end.setMilliseconds(0);
|
365 |
+
end = 'number' != typeof end ? end.getTime() : end;
|
366 |
+
|
367 |
+
if( start < selected_date.getTime() && selected_date.getTime() < end ) {
|
368 |
+
excp_date = selected_date;
|
369 |
+
}
|
370 |
+
else {
|
371 |
+
// Set anything to void this rule
|
372 |
+
// Dates assign with copy, thus creating a new one
|
373 |
+
excp_date = new Date( selected_date.getTime() );
|
374 |
+
excp_date.setDate( selected_date_year + 1 );
|
375 |
+
}
|
376 |
+
}
|
377 |
+
else {
|
378 |
+
excp_date = new Date( rule.date );
|
379 |
+
}
|
380 |
+
|
381 |
+
if ( excp_date.getFullYear() == selected_date_year &&
|
382 |
+
excp_date.getMonth() == selected_date_month &&
|
383 |
+
excp_date.getDate() == selected_date_date
|
384 |
+
) {
|
385 |
+
|
386 |
+
// Closed all day
|
387 |
+
if ( typeof rtb_pickadate.schedule_closed[closed_key].time == 'undefined' ) {
|
388 |
+
rtb_booking_form.timepicker.set( 'disable', [ true ] );
|
389 |
+
|
390 |
+
return;
|
391 |
+
}
|
392 |
+
|
393 |
+
if ( typeof rtb_pickadate.schedule_closed[closed_key].time.start !== 'undefined' ) {
|
394 |
+
excp_start_date = new Date( '1 January 2000 ' + rtb_pickadate.schedule_closed[closed_key].time.start );
|
395 |
+
excp_start_time = [ excp_start_date.getHours(), excp_start_date.getMinutes() ];
|
396 |
+
} else {
|
397 |
+
excp_start_time = [ 0, 0 ]; // Start of the day
|
398 |
+
}
|
399 |
+
|
400 |
+
if ( typeof rtb_pickadate.schedule_closed[closed_key].time.end !== 'undefined' ) {
|
401 |
+
excp_end_date = new Date( '1 January 2000 ' + rtb_pickadate.schedule_closed[closed_key].time.end );
|
402 |
+
excp_end_time = [ excp_end_date.getHours(), excp_end_date.getMinutes() ];
|
403 |
+
} else {
|
404 |
+
excp_end_time = [ 24, 0 ]; // End of the day
|
405 |
+
}
|
406 |
+
|
407 |
+
excp_start_time = rtb_booking_form.get_earliest_time( excp_start_time, selected_date, current_date );
|
408 |
+
|
409 |
+
valid_times.push( { from: excp_start_time, to: excp_end_time, inverted: true } );
|
410 |
+
}
|
411 |
+
}
|
412 |
+
|
413 |
+
excp_date = excp_start_date = excp_start_time = excp_end_date = excp_end_time = null;
|
414 |
+
|
415 |
+
// Exit early if this date is an exception
|
416 |
+
if ( valid_times.length > 1 ) {
|
417 |
+
rtb_booking_form.timepicker.set( 'disable', valid_times );
|
418 |
+
|
419 |
+
return;
|
420 |
+
}
|
421 |
+
}
|
422 |
+
|
423 |
+
// Get any rules which apply to this weekday
|
424 |
+
if ( typeof rtb_pickadate.schedule_open != 'undefined' ) {
|
425 |
+
|
426 |
+
var selected_date_weekday = selected_date.getDay();
|
427 |
+
|
428 |
+
var weekdays = {
|
429 |
+
sunday: 0,
|
430 |
+
monday: 1,
|
431 |
+
tuesday: 2,
|
432 |
+
wednesday: 3,
|
433 |
+
thursday: 4,
|
434 |
+
friday: 5,
|
435 |
+
saturday: 6,
|
436 |
+
};
|
437 |
+
|
438 |
+
var rule_start_date = [];
|
439 |
+
var rule_start_time = [];
|
440 |
+
var rule_end_date = [];
|
441 |
+
var rule_end_time = [];
|
442 |
+
for ( var open_key in rtb_pickadate.schedule_open ) {
|
443 |
+
|
444 |
+
if ( typeof rtb_pickadate.schedule_open[open_key].weekdays !== 'undefined' ) {
|
445 |
+
for ( var weekdays_key in rtb_pickadate.schedule_open[open_key].weekdays ) {
|
446 |
+
if ( weekdays[weekdays_key] == selected_date_weekday ) {
|
447 |
+
|
448 |
+
// Closed all day
|
449 |
+
if ( typeof rtb_pickadate.schedule_open[open_key].time == 'undefined' ) {
|
450 |
+
rtb_booking_form.timepicker.set( 'disable', [ true ] );
|
451 |
+
|
452 |
+
return;
|
453 |
+
}
|
454 |
+
|
455 |
+
if ( typeof rtb_pickadate.schedule_open[open_key].time.start !== 'undefined' ) {
|
456 |
+
rule_start_date = new Date( '1 January 2000 ' + rtb_pickadate.schedule_open[open_key].time.start );
|
457 |
+
rule_start_time = [ rule_start_date.getHours(), rule_start_date.getMinutes() ];
|
458 |
+
} else {
|
459 |
+
rule_start_time = [ 0, 0 ]; // Start of the day
|
460 |
+
}
|
461 |
+
|
462 |
+
if ( typeof rtb_pickadate.schedule_open[open_key].time.end !== 'undefined' ) {
|
463 |
+
rule_end_date = new Date( '1 January 2000 ' + rtb_pickadate.schedule_open[open_key].time.end );
|
464 |
+
rule_end_time = rtb_booking_form.get_latest_viable_time( rule_end_date.getHours(), rule_end_date.getMinutes() );
|
465 |
+
} else {
|
466 |
+
rule_end_time = [ 24, 0 ]; // End of the day
|
467 |
+
}
|
468 |
+
|
469 |
+
rule_start_time = rtb_booking_form.get_earliest_time( rule_start_time, selected_date, current_date );
|
470 |
+
|
471 |
+
valid_times.push( { from: rule_start_time, to: rule_end_time, inverted: true } );
|
472 |
+
|
473 |
+
}
|
474 |
+
}
|
475 |
+
}
|
476 |
+
}
|
477 |
+
|
478 |
+
rule_start_date = rule_start_time = rule_end_date = rule_end_time = null;
|
479 |
+
|
480 |
+
// Pass any valid times located
|
481 |
+
if ( valid_times.length > 1 ) {
|
482 |
+
rtb_booking_form.timepicker.set( 'disable', valid_times );
|
483 |
+
|
484 |
+
return;
|
485 |
+
}
|
486 |
+
|
487 |
+
}
|
488 |
+
|
489 |
+
// Set it to always open if no rules have been defined
|
490 |
+
rtb_booking_form.timepicker.set( 'enable', true );
|
491 |
+
rtb_booking_form.timepicker.set( 'disable', false );
|
492 |
+
}
|
493 |
+
|
494 |
+
return;
|
495 |
+
};
|
496 |
+
|
497 |
+
/**
|
498 |
+
* Get the outer times to exclude based on the time interval
|
499 |
+
*
|
500 |
+
* This is a work-around for a bug in pickadate.js
|
501 |
+
* See: https://github.com/amsul/pickadate.js/issues/614
|
502 |
+
*/
|
503 |
+
rtb_booking_form.get_outer_time_range = function() {
|
504 |
+
|
505 |
+
var interval = rtb_booking_form.timepicker.get( 'interval' );
|
506 |
+
|
507 |
+
var hour = 24;
|
508 |
+
|
509 |
+
while ( interval >= 60 ) {
|
510 |
+
hour--;
|
511 |
+
interval -= 60;
|
512 |
+
}
|
513 |
+
|
514 |
+
if ( interval > 0 ) {
|
515 |
+
hour--;
|
516 |
+
// All day open does not work when date-time-range and current open time are exact same
|
517 |
+
interval = 60 - interval;
|
518 |
+
}
|
519 |
+
|
520 |
+
return { from: [0, 0], to: [ hour, interval ] };
|
521 |
+
};
|
522 |
+
|
523 |
+
/**
|
524 |
+
* Get the latest working opening hour/minute value
|
525 |
+
*
|
526 |
+
* This is a workaround for a bug in pickadate.js. The end time of a valid
|
527 |
+
* time value must NOT fall within the last timepicker interval and midnight
|
528 |
+
* See: https://github.com/amsul/pickadate.js/issues/614
|
529 |
+
*/
|
530 |
+
rtb_booking_form.get_latest_viable_time = function( hour, minute ) {
|
531 |
+
|
532 |
+
var interval = rtb_booking_form.timepicker.get( 'interval' );
|
533 |
+
|
534 |
+
var outer_time_range = this.get_outer_time_range();
|
535 |
+
|
536 |
+
/*
|
537 |
+
* Adjust the last time for wide intervals, so that the last time entered
|
538 |
+
* corresponds to an interval time. A pickadate bug causes a later time to
|
539 |
+
* be available for booking otherwise.
|
540 |
+
*/
|
541 |
+
if ( interval > 60) {
|
542 |
+
|
543 |
+
var last_hour = 0;
|
544 |
+
var last_minute = 0;
|
545 |
+
var last_time_minutes = 0;
|
546 |
+
|
547 |
+
var end_time_minutes = 60 * hour + minute;
|
548 |
+
|
549 |
+
while ( ( last_time_minutes + interval ) <= end_time_minutes ) {
|
550 |
+
|
551 |
+
var remainder = interval + last_minute;
|
552 |
+
|
553 |
+
while ( remainder >= 60 ) {
|
554 |
+
last_hour++;
|
555 |
+
remainder -= 60;
|
556 |
+
}
|
557 |
+
|
558 |
+
last_minute = remainder;
|
559 |
+
|
560 |
+
last_time_minutes = 60 * last_hour + last_minute;
|
561 |
+
}
|
562 |
+
|
563 |
+
var long_interval_viable_time = [ last_hour, last_minute ];
|
564 |
+
}
|
565 |
+
|
566 |
+
|
567 |
+
if ( interval > 60 ) {
|
568 |
+
|
569 |
+
return long_interval_viable_time;
|
570 |
+
}
|
571 |
+
else if ( hour > outer_time_range.to[0] || minute > outer_time_range.to[1] ) {
|
572 |
+
|
573 |
+
return [ outer_time_range.to[0], outer_time_range.to[1] ];
|
574 |
+
} else {
|
575 |
+
|
576 |
+
return [ hour, minute ];
|
577 |
+
}
|
578 |
+
};
|
579 |
+
|
580 |
+
/**
|
581 |
+
* Get the earliest valid time
|
582 |
+
*
|
583 |
+
* This checks the valid time for the day and, if a current day, applies
|
584 |
+
* any late booking restrictions. It also ensures that times in the past
|
585 |
+
* are not availabe.
|
586 |
+
*
|
587 |
+
* @param array start_time
|
588 |
+
* @param array selected_date
|
589 |
+
* @param array current_date
|
590 |
+
*/
|
591 |
+
rtb_booking_form.get_earliest_time = function( start_time, selected_date, current_date ) {
|
592 |
+
|
593 |
+
// Only make adjustments for current day selections
|
594 |
+
if ( selected_date.toDateString() !== current_date.toDateString() ) {
|
595 |
+
return start_time;
|
596 |
+
}
|
597 |
+
|
598 |
+
// Get the number of minutes after midnight to compare
|
599 |
+
var start_minutes = ( start_time[0] * 60 ) + start_time[1],
|
600 |
+
current_minutes = ( current_date.getHours() * 60 ) + current_date.getMinutes(),
|
601 |
+
late_booking_minutes;
|
602 |
+
|
603 |
+
start_minutes = start_minutes > current_minutes ? start_minutes : current_minutes;
|
604 |
+
|
605 |
+
if ( typeof rtb_pickadate.late_bookings === 'number' && rtb_pickadate.late_bookings % 1 === 0 ) {
|
606 |
+
late_booking_minutes = current_minutes + rtb_pickadate.late_bookings;
|
607 |
+
if ( late_booking_minutes > start_minutes ) {
|
608 |
+
start_minutes = late_booking_minutes;
|
609 |
+
}
|
610 |
+
}
|
611 |
+
|
612 |
+
start_time = [ Math.floor( start_minutes / 60 ), start_minutes % 60 ];
|
613 |
+
|
614 |
+
return start_time;
|
615 |
+
};
|
616 |
+
|
617 |
+
rtb_booking_form.update_party_size_select = function() {
|
618 |
+
|
619 |
+
if ( rtb_pickadate.enable_max_reservations && ( rtb_pickadate.max_people || rtb_pickadate.multiple_locations_enabled ) ) {
|
620 |
+
var partySelect = $('#rtb-party'),
|
621 |
+
selected_location = jQuery( '#rtb-location' ).length ? jQuery( '#rtb-location' ).val() : '',
|
622 |
+
selected_date = new Date( rtb_booking_form.datepicker.get( 'select', 'yyyy/mm/dd' ) ),
|
623 |
+
selected_date_year = selected_date.getFullYear(),
|
624 |
+
selected_date_month = selected_date.getMonth(),
|
625 |
+
selected_date_date = selected_date.getDate(),
|
626 |
+
selected_time = rtb_booking_form.timepicker.get('value');
|
627 |
+
|
628 |
+
if ( ! selected_time ) { return; }
|
629 |
+
|
630 |
+
selected_date_month = ('0' + (selected_date_month + 1)).slice(-2);
|
631 |
+
selected_date_date = ('0' + selected_date_date).slice(-2);
|
632 |
+
|
633 |
+
//reset party size
|
634 |
+
partySelect.prop("selectedIndex", 0).change();
|
635 |
+
|
636 |
+
var params = {};
|
637 |
+
|
638 |
+
params.action = 'rtb_get_available_party_size';
|
639 |
+
params.nonce = rtb_booking_form_js_localize.nonce;
|
640 |
+
params.year = selected_date_year;
|
641 |
+
params.month = selected_date_month;
|
642 |
+
params.day = selected_date_date;
|
643 |
+
params.time = selected_time;
|
644 |
+
params.location = selected_location;
|
645 |
+
|
646 |
+
var data = jQuery.param( params );
|
647 |
+
jQuery.post( ajaxurl, data, function( response ) {
|
648 |
+
if ( ! response ) {
|
649 |
+
return;
|
650 |
+
}
|
651 |
+
|
652 |
+
response = jQuery.parseJSON(response);
|
653 |
+
|
654 |
+
var available_spots = response.available_spots;
|
655 |
+
|
656 |
+
partySelect.prop('disabled', false);
|
657 |
+
|
658 |
+
partySelect.find('> option').each(function() {
|
659 |
+
var that = $(this);
|
660 |
+
if (this.value > available_spots) {
|
661 |
+
that.prop('disabled', true);
|
662 |
+
} else {
|
663 |
+
that.prop('disabled', false);
|
664 |
+
}
|
665 |
+
});
|
666 |
+
});
|
667 |
+
}
|
668 |
+
}
|
669 |
+
|
670 |
+
rtb_booking_form.update_possible_tables = function() {
|
671 |
+
|
672 |
+
if ( rtb_pickadate.enable_tables ) {
|
673 |
+
|
674 |
+
var table_select = $('#rtb-table'),
|
675 |
+
party = $('#rtb-party').val(),
|
676 |
+
selected_date = new Date( rtb_booking_form.datepicker.get( 'select', 'yyyy/mm/dd' ) ),
|
677 |
+
selected_date_year = selected_date.getFullYear(),
|
678 |
+
selected_date_month = selected_date.getMonth(),
|
679 |
+
selected_date_date = selected_date.getDate(),
|
680 |
+
selected_time = rtb_booking_form.timepicker.get('value');
|
681 |
+
|
682 |
+
if ( ! selected_time || ! party ) { return; }
|
683 |
+
|
684 |
+
selected_date_month = ('0' + (selected_date_month + 1)).slice(-2);
|
685 |
+
selected_date_date = ('0' + selected_date_date).slice(-2);
|
686 |
+
|
687 |
+
//reset table selection
|
688 |
+
table_select.prop("selectedIndex", 0).change();
|
689 |
+
|
690 |
+
//remove table combinations
|
691 |
+
table_select.find('> option').each( function() {
|
692 |
+
if ( $( this ).val().indexOf( ',' ) !== -1 ) { $( this ).remove(); }
|
693 |
+
});
|
694 |
+
|
695 |
+
var booking_id = $( '.rtb-booking-form form input[name="ID"]').length ? $( '.rtb-booking-form form input[name="ID"]').val() : 0;
|
696 |
+
|
697 |
+
var params = {};
|
698 |
+
|
699 |
+
params.action = 'rtb_get_available_tables';
|
700 |
+
params.nonce = rtb_booking_form_js_localize.nonce;
|
701 |
+
params.year = selected_date_year;
|
702 |
+
params.month = selected_date_month;
|
703 |
+
params.day = selected_date_date;
|
704 |
+
params.time = selected_time;
|
705 |
+
params.party = party;
|
706 |
+
params.booking_id = booking_id
|
707 |
+
|
708 |
+
var data = jQuery.param( params );
|
709 |
+
jQuery.post( ajaxurl, data, function( response ) {
|
710 |
+
if ( ! response ) {
|
711 |
+
return;
|
712 |
+
}
|
713 |
+
|
714 |
+
response = jQuery.parseJSON(response);
|
715 |
+
|
716 |
+
var available_tables = response.available_tables;
|
717 |
+
|
718 |
+
table_select.prop('disabled', false);
|
719 |
+
|
720 |
+
table_select.find('> option').hide();
|
721 |
+
table_select.find('> option').attr('disabled', 'disabled');
|
722 |
+
|
723 |
+
if( 1 > available_tables.length ) {
|
724 |
+
displayFieldError( 'table', rtb_booking_form_js_localize.error['no-table-available'] );
|
725 |
+
}
|
726 |
+
else {
|
727 |
+
clearPrevFieldError( 'table' );
|
728 |
+
}
|
729 |
+
|
730 |
+
jQuery.each(available_tables, function(index, element) {
|
731 |
+
|
732 |
+
if ( index.indexOf( ',' ) === -1 ) {
|
733 |
+
table_select.find('> option[value="' + index + '"]').show();
|
734 |
+
table_select.find('> option[value="' + index + '"]').removeAttr('disabled', 'disabled');
|
735 |
+
}
|
736 |
+
else {
|
737 |
+
table_select.append( '<option value="' + index + '">' + element + '</option>' );
|
738 |
+
}
|
739 |
+
|
740 |
+
});
|
741 |
+
|
742 |
+
if ( response.selected_table != -1 ) {
|
743 |
+
table_select.val( response.selected_table );
|
744 |
+
}
|
745 |
+
else if( '' != table_select.data('selected') ) {
|
746 |
+
table_select.val( table_select.data('selected') );
|
747 |
+
}
|
748 |
+
|
749 |
+
// pre-select table if it was selected before and is available
|
750 |
+
|
751 |
+
|
752 |
+
});
|
753 |
+
}
|
754 |
+
|
755 |
+
}
|
756 |
+
|
757 |
+
rtb_booking_form.init();
|
758 |
+
});
|
759 |
+
|
760 |
+
//Handle reservation modification
|
761 |
+
jQuery(document).ready(function() {
|
762 |
+
jQuery('.rtb-modification-toggle').on('click', function() {
|
763 |
+
jQuery('.rtb-modification-form, .rtb-booking-form-form').toggleClass('rtb-hidden');
|
764 |
+
|
765 |
+
if (jQuery('.rtb-modification-form').hasClass('rtb-hidden')) {
|
766 |
+
jQuery('.rtb-modification-toggle').html(rtb_booking_form_js_localize.want_to_modify);
|
767 |
+
}
|
768 |
+
else {
|
769 |
+
jQuery('.rtb-modification-toggle').html(rtb_booking_form_js_localize.make);
|
770 |
+
}
|
771 |
+
});
|
772 |
+
|
773 |
+
var modify_booking = function(ev) {
|
774 |
+
var booking_email = jQuery('input[name="rtb_modification_email"]').val();
|
775 |
+
|
776 |
+
var params = {};
|
777 |
+
|
778 |
+
params.action = 'rtb_find_reservations';
|
779 |
+
params.nonce = rtb_booking_form_js_localize.nonce;
|
780 |
+
params.booking_email = booking_email;
|
781 |
+
|
782 |
+
var data = jQuery.param( params );
|
783 |
+
jQuery.post(ajaxurl, data, function(response) {
|
784 |
+
|
785 |
+
if (response.success) {
|
786 |
+
var booking_html = '';
|
787 |
+
var guest_txt = '';
|
788 |
+
var pay_btn = '';
|
789 |
+
|
790 |
+
jQuery(response.data.bookings).each(function( index, val) {
|
791 |
+
pay_btn = '';
|
792 |
+
guest_txt = val.party > 1 ? rtb_booking_form_js_localize.guests : rtb_booking_form_js_localize.guest;
|
793 |
+
|
794 |
+
if('payment_pending' == val.status || 'payment_failed' == val.status) {
|
795 |
+
pay_btn = `
|
796 |
+
<div class="rtb-deposit-booking" data-bookingid="${val.ID}" data-bookingemail="${val.email}">
|
797 |
+
${rtb_booking_form_js_localize.deposit}
|
798 |
+
</div>
|
799 |
+
`;
|
800 |
+
}
|
801 |
+
|
802 |
+
booking_html += `
|
803 |
+
<div class="rtb-cancel-booking-div">
|
804 |
+
<div class="rtb-cancel-booking" data-bookingid="${val.ID}" data-bookingemail="${val.email}">
|
805 |
+
${rtb_booking_form_js_localize.cancel}
|
806 |
+
</div>
|
807 |
+
${pay_btn}
|
808 |
+
<div class="rtb-booking-information">${val.datetime} - ${val.party} ${guest_txt} (${val.status_lbl})</div>
|
809 |
+
</div>
|
810 |
+
`;
|
811 |
+
});
|
812 |
+
|
813 |
+
jQuery('.rtb-bookings-results').html(booking_html);
|
814 |
+
|
815 |
+
cancellationHandler();
|
816 |
+
delayedPaymentHandler();
|
817 |
+
}
|
818 |
+
else {jQuery('.rtb-bookings-results').html(response.data.msg);}
|
819 |
+
});
|
820 |
+
};
|
821 |
+
|
822 |
+
jQuery(document).on('click', '.rtb-find-reservation-button', modify_booking);
|
823 |
+
jQuery(document).on('keypress', '.rtb-modification-form input', function (ev) {
|
824 |
+
// Capture enter key
|
825 |
+
if(13 == ev.which) {
|
826 |
+
ev.preventDefault();
|
827 |
+
modify_booking(ev);
|
828 |
+
}
|
829 |
+
});
|
830 |
+
});
|
831 |
+
|
832 |
+
function cancellationHandler() {
|
833 |
+
jQuery('.rtb-cancel-booking:not(.cancelled)').off('click');
|
834 |
+
jQuery('.rtb-cancel-booking:not(.cancelled)').on('click', function() {
|
835 |
+
var btn = jQuery(this);
|
836 |
+
|
837 |
+
if(btn.hasClass('processing')) {
|
838 |
+
return;
|
839 |
+
}
|
840 |
+
|
841 |
+
btn.addClass('processing');
|
842 |
+
|
843 |
+
var booking_id = btn.data('bookingid');
|
844 |
+
var booking_email = btn.data('bookingemail');
|
845 |
+
|
846 |
+
var params = {};
|
847 |
+
|
848 |
+
params.action = 'rtb_cancel_reservations';
|
849 |
+
params.nonce = rtb_booking_form_js_localize.nonce;
|
850 |
+
params.booking_id = booking_id;
|
851 |
+
params.booking_email = booking_email;
|
852 |
+
|
853 |
+
var data = jQuery.param( params );
|
854 |
+
jQuery.post(ajaxurl, data, function(response) {
|
855 |
+
if (response.success) {
|
856 |
+
if (response.data.hasOwnProperty('cancelled_redirect')) {
|
857 |
+
window.location.href = response.data.cancelled_redirect;
|
858 |
+
}
|
859 |
+
else {
|
860 |
+
btn.off('click');
|
861 |
+
btn.addClass('cancelled');
|
862 |
+
btn.text(rtb_booking_form_js_localize.cancelled);
|
863 |
+
}
|
864 |
+
}
|
865 |
+
else {
|
866 |
+
btn.parent().after(`<p class="alert error">${response.data.msg}</p>`);
|
867 |
+
}
|
868 |
+
|
869 |
+
btn.removeClass('processing');
|
870 |
+
});
|
871 |
+
});
|
872 |
+
}
|
873 |
+
|
874 |
+
function delayedPaymentHandler() {
|
875 |
+
jQuery('.rtb-deposit-booking').off('click');
|
876 |
+
jQuery('.rtb-deposit-booking').on('click', function() {
|
877 |
+
var btn = jQuery(this);
|
878 |
+
|
879 |
+
if(btn.hasClass('processing')) {
|
880 |
+
return;
|
881 |
+
}
|
882 |
+
|
883 |
+
btn.addClass('processing');
|
884 |
+
|
885 |
+
var booking_id = btn.data('bookingid');
|
886 |
+
var booking_email = btn.data('bookingemail');
|
887 |
+
|
888 |
+
var data = {
|
889 |
+
'booking_id': booking_id,
|
890 |
+
'booking_email': booking_email,
|
891 |
+
'payment': 'rtb-delayed-deposit'
|
892 |
+
};
|
893 |
+
|
894 |
+
let current_loc = window.location;
|
895 |
+
let params = new URLSearchParams();
|
896 |
+
Object.keys( data ).map( function( param ) { params.append( param, data[ param ] ) } );
|
897 |
+
|
898 |
+
window.location = current_loc.origin + current_loc.pathname + '?' + params.toString();
|
899 |
+
|
900 |
+
});
|
901 |
+
}
|
902 |
+
|
903 |
+
function displayFieldError( field, message ) {
|
904 |
+
|
905 |
+
const fieldSelector = '.'+field;
|
906 |
+
var fieldElm = jQuery('form.rtb-booking-form-form '+fieldSelector);
|
907 |
+
|
908 |
+
if( fieldElm.length ) {
|
909 |
+
|
910 |
+
clearPrevFieldError( field );
|
911 |
+
|
912 |
+
fieldElm.prepend(`
|
913 |
+
<div class="rtb-error">${message}</div>
|
914 |
+
`);
|
915 |
+
}
|
916 |
+
}
|
917 |
+
|
918 |
+
function clearPrevFieldError( field ) {
|
919 |
+
const fieldSelector = '.'+field;
|
920 |
+
var errorElms = jQuery('form.rtb-booking-form-form ' + fieldSelector + ' .rtb-error');
|
921 |
+
|
922 |
+
if( errorElms.length ) {
|
923 |
+
errorElms.each((idx, x) => x.remove());
|
924 |
+
}
|
925 |
+
}
|
926 |
+
|
927 |
+
// Functions for the 'View Bookings' shortcode
|
928 |
+
jQuery(document).ready(function ($) {
|
929 |
+
jQuery('.rtb-view-bookings-form-date-selector').on('change', function() {
|
930 |
+
window.location.href = replaceUrlParam(window.location.href, 'date', jQuery(this).val());
|
931 |
+
});
|
932 |
+
|
933 |
+
jQuery('.rtb-edit-view-booking').on('click', function() {
|
934 |
+
jQuery('.rtb-view-bookings-form-confirmation-div, .rtb-view-bookings-form-confirmation-background-div').removeClass('rtb-hidden');
|
935 |
+
|
936 |
+
jQuery('.rtb-view-bookings-form-confirmation-div').data('bookingid', jQuery(this).data('bookingid'));
|
937 |
+
|
938 |
+
jQuery(this).prop('checked', false);
|
939 |
+
});
|
940 |
+
|
941 |
+
jQuery('.rtb-view-bookings-form-confirmation-accept').on('click', function() {
|
942 |
+
var booking_id = jQuery('.rtb-view-bookings-form-confirmation-div').data('bookingid');
|
943 |
+
|
944 |
+
var params = {};
|
945 |
+
|
946 |
+
params.action = 'rtb_set_reservation_arrived';
|
947 |
+
params.nonce = rtb_booking_form_js_localize.nonce;
|
948 |
+
params.booking = {
|
949 |
+
'ID': booking_id
|
950 |
+
};
|
951 |
+
|
952 |
+
var data = $.param( params );
|
953 |
+
|
954 |
+
jQuery.post(ajaxurl, data, function(response) {
|
955 |
+
|
956 |
+
if (response.success) {window.location.href = window.location.href}
|
957 |
+
else {jQuery('.rtb-view-bookings-form-confirmation-div').html(response.data.msg);}
|
958 |
+
});
|
959 |
+
});
|
960 |
+
|
961 |
+
jQuery('.rtb-view-bookings-form-confirmation-decline').on('click', function() {
|
962 |
+
jQuery('.rtb-view-bookings-form-confirmation-div, .rtb-view-bookings-form-confirmation-background-div').addClass('rtb-hidden');
|
963 |
+
});
|
964 |
+
jQuery('.rtb-view-bookings-form-confirmation-background-div').on('click', function() {
|
965 |
+
jQuery('.rtb-view-bookings-form-confirmation-div, .rtb-view-bookings-form-confirmation-background-div').addClass('rtb-hidden');
|
966 |
+
});
|
967 |
+
jQuery('#rtb-view-bookings-form-close').on('click', function() {
|
968 |
+
jQuery('.rtb-view-bookings-form-confirmation-div, .rtb-view-bookings-form-confirmation-background-div').addClass('rtb-hidden');
|
969 |
+
});
|
970 |
+
});
|
971 |
+
|
972 |
+
function replaceUrlParam(url, paramName, paramValue)
|
973 |
+
{
|
974 |
+
if (paramValue == null) {
|
975 |
+
paramValue = '';
|
976 |
+
}
|
977 |
+
var pattern = new RegExp('\\b('+paramName+'=).*?(&|#|$)');
|
978 |
+
if (url.search(pattern)>=0) {
|
979 |
+
return url.replace(pattern,'$1' + paramValue + '$2');
|
980 |
+
}
|
981 |
+
url = url.replace(/[?#]$/,'');
|
982 |
+
return url + (url.indexOf('?')>0 ? '&' : '?') + paramName + '=' + paramValue;
|
983 |
}
|
assets/js/columns.js
CHANGED
@@ -1,11 +1,11 @@
|
|
1 |
-
jQuery(document).ready(function($){
|
2 |
-
$(function(){
|
3 |
-
$(window).resize(function(){
|
4 |
-
$('.rtb-booking-form form button').each(function(){
|
5 |
-
var thisButton = $(this);
|
6 |
-
var buttonHalfWidth = ( thisButton.outerWidth() / 2 );
|
7 |
-
thisButton.css('margin-left', 'calc(50% - '+buttonHalfWidth+'px');
|
8 |
-
});
|
9 |
-
}).resize();
|
10 |
-
});
|
11 |
});
|
1 |
+
jQuery(document).ready(function($){
|
2 |
+
$(function(){
|
3 |
+
$(window).resize(function(){
|
4 |
+
$('.rtb-booking-form form button').each(function(){
|
5 |
+
var thisButton = $(this);
|
6 |
+
var buttonHalfWidth = ( thisButton.outerWidth() / 2 );
|
7 |
+
thisButton.css('margin-left', 'calc(50% - '+buttonHalfWidth+'px');
|
8 |
+
});
|
9 |
+
}).resize();
|
10 |
+
});
|
11 |
});
|
assets/js/dashboard-review-ask.js
CHANGED
@@ -1,80 +1,80 @@
|
|
1 |
-
jQuery(document).ready(function($) {
|
2 |
-
jQuery('.rtb-main-dashboard-review-ask').css('display', 'block');
|
3 |
-
|
4 |
-
jQuery(document).on('click', '.rtb-main-dashboard-review-ask .notice-dismiss', function(event) {
|
5 |
-
var data = rtb_hide_review_ask_params( 7 );
|
6 |
-
jQuery.post(ajaxurl, data, function() {});
|
7 |
-
});
|
8 |
-
|
9 |
-
jQuery('.rtb-review-ask-yes').on('click', function() {
|
10 |
-
jQuery('.rtb-review-ask-feedback-text').removeClass('rtb-hidden');
|
11 |
-
jQuery('.rtb-review-ask-starting-text').addClass('rtb-hidden');
|
12 |
-
|
13 |
-
jQuery('.rtb-review-ask-no-thanks').removeClass('rtb-hidden');
|
14 |
-
jQuery('.rtb-review-ask-review').removeClass('rtb-hidden');
|
15 |
-
|
16 |
-
jQuery('.rtb-review-ask-not-really').addClass('rtb-hidden');
|
17 |
-
jQuery('.rtb-review-ask-yes').addClass('rtb-hidden');
|
18 |
-
|
19 |
-
var data = rtb_hide_review_ask_params( 7 );
|
20 |
-
jQuery.post(ajaxurl, data, function() {});
|
21 |
-
});
|
22 |
-
|
23 |
-
jQuery('.rtb-review-ask-not-really').on('click', function() {
|
24 |
-
jQuery('.rtb-review-ask-review-text').removeClass('rtb-hidden');
|
25 |
-
jQuery('.rtb-review-ask-starting-text').addClass('rtb-hidden');
|
26 |
-
|
27 |
-
jQuery('.rtb-review-ask-feedback-form').removeClass('rtb-hidden');
|
28 |
-
jQuery('.rtb-review-ask-actions').addClass('rtb-hidden');
|
29 |
-
|
30 |
-
var data = rtb_hide_review_ask_params( 1000 );
|
31 |
-
jQuery.post(ajaxurl, data, function() {});
|
32 |
-
});
|
33 |
-
|
34 |
-
jQuery('.rtb-review-ask-no-thanks').on('click', function() {
|
35 |
-
var data = rtb_hide_review_ask_params( 1000 );
|
36 |
-
jQuery.post(ajaxurl, data, function() {});
|
37 |
-
|
38 |
-
jQuery('.rtb-main-dashboard-review-ask').css('display', 'none');
|
39 |
-
});
|
40 |
-
|
41 |
-
jQuery('.rtb-review-ask-review').on('click', function() {
|
42 |
-
jQuery('.rtb-review-ask-feedback-text').addClass('rtb-hidden');
|
43 |
-
jQuery('.rtb-review-ask-thank-you-text').removeClass('rtb-hidden');
|
44 |
-
|
45 |
-
var data = rtb_hide_review_ask_params( 1000 );
|
46 |
-
jQuery.post(ajaxurl, data, function() {});
|
47 |
-
});
|
48 |
-
|
49 |
-
jQuery('.rtb-review-ask-send-feedback').on('click', function() {
|
50 |
-
var feedback = jQuery('.rtb-review-ask-feedback-explanation textarea').val();
|
51 |
-
var email_address = jQuery('.rtb-review-ask-feedback-explanation input[name="feedback_email_address"]').val();
|
52 |
-
|
53 |
-
var params = {};
|
54 |
-
|
55 |
-
params.action = 'rtb-send-feedback';
|
56 |
-
params.nonce = rtb_review_ask.nonce;
|
57 |
-
params.feedback = feedback;
|
58 |
-
params.email_address = email_address;
|
59 |
-
|
60 |
-
var data = jQuery.param( params );
|
61 |
-
jQuery.post(ajaxurl, data, function() {});
|
62 |
-
|
63 |
-
var data = rtb_hide_review_ask_params( 1000 );
|
64 |
-
jQuery.post(ajaxurl, data, function() {});
|
65 |
-
|
66 |
-
jQuery('.rtb-review-ask-feedback-form').addClass('rtb-hidden');
|
67 |
-
jQuery('.rtb-review-ask-review-text').addClass('rtb-hidden');
|
68 |
-
jQuery('.rtb-review-ask-thank-you-text').removeClass('rtb-hidden');
|
69 |
-
});
|
70 |
-
|
71 |
-
function rtb_hide_review_ask_params(ask_review_time = 7) {
|
72 |
-
var params = {};
|
73 |
-
|
74 |
-
params.action = 'rtb-hide-review-ask';
|
75 |
-
params.nonce = rtb_review_ask.nonce;
|
76 |
-
params.ask_review_time = ask_review_time;
|
77 |
-
|
78 |
-
return jQuery.param( params );
|
79 |
-
}
|
80 |
});
|
1 |
+
jQuery(document).ready(function($) {
|
2 |
+
jQuery('.rtb-main-dashboard-review-ask').css('display', 'block');
|
3 |
+
|
4 |
+
jQuery(document).on('click', '.rtb-main-dashboard-review-ask .notice-dismiss', function(event) {
|
5 |
+
var data = rtb_hide_review_ask_params( 7 );
|
6 |
+
jQuery.post(ajaxurl, data, function() {});
|
7 |
+
});
|
8 |
+
|
9 |
+
jQuery('.rtb-review-ask-yes').on('click', function() {
|
10 |
+
jQuery('.rtb-review-ask-feedback-text').removeClass('rtb-hidden');
|
11 |
+
jQuery('.rtb-review-ask-starting-text').addClass('rtb-hidden');
|
12 |
+
|
13 |
+
jQuery('.rtb-review-ask-no-thanks').removeClass('rtb-hidden');
|
14 |
+
jQuery('.rtb-review-ask-review').removeClass('rtb-hidden');
|
15 |
+
|
16 |
+
jQuery('.rtb-review-ask-not-really').addClass('rtb-hidden');
|
17 |
+
jQuery('.rtb-review-ask-yes').addClass('rtb-hidden');
|
18 |
+
|
19 |
+
var data = rtb_hide_review_ask_params( 7 );
|
20 |
+
jQuery.post(ajaxurl, data, function() {});
|
21 |
+
});
|
22 |
+
|
23 |
+
jQuery('.rtb-review-ask-not-really').on('click', function() {
|
24 |
+
jQuery('.rtb-review-ask-review-text').removeClass('rtb-hidden');
|
25 |
+
jQuery('.rtb-review-ask-starting-text').addClass('rtb-hidden');
|
26 |
+
|
27 |
+
jQuery('.rtb-review-ask-feedback-form').removeClass('rtb-hidden');
|
28 |
+
jQuery('.rtb-review-ask-actions').addClass('rtb-hidden');
|
29 |
+
|
30 |
+
var data = rtb_hide_review_ask_params( 1000 );
|
31 |
+
jQuery.post(ajaxurl, data, function() {});
|
32 |
+
});
|
33 |
+
|
34 |
+
jQuery('.rtb-review-ask-no-thanks').on('click', function() {
|
35 |
+
var data = rtb_hide_review_ask_params( 1000 );
|
36 |
+
jQuery.post(ajaxurl, data, function() {});
|
37 |
+
|
38 |
+
jQuery('.rtb-main-dashboard-review-ask').css('display', 'none');
|
39 |
+
});
|
40 |
+
|
41 |
+
jQuery('.rtb-review-ask-review').on('click', function() {
|
42 |
+
jQuery('.rtb-review-ask-feedback-text').addClass('rtb-hidden');
|
43 |
+
jQuery('.rtb-review-ask-thank-you-text').removeClass('rtb-hidden');
|
44 |
+
|
45 |
+
var data = rtb_hide_review_ask_params( 1000 );
|
46 |
+
jQuery.post(ajaxurl, data, function() {});
|
47 |
+
});
|
48 |
+
|
49 |
+
jQuery('.rtb-review-ask-send-feedback').on('click', function() {
|
50 |
+
var feedback = jQuery('.rtb-review-ask-feedback-explanation textarea').val();
|
51 |
+
var email_address = jQuery('.rtb-review-ask-feedback-explanation input[name="feedback_email_address"]').val();
|
52 |
+
|
53 |
+
var params = {};
|
54 |
+
|
55 |
+
params.action = 'rtb-send-feedback';
|
56 |
+
params.nonce = rtb_review_ask.nonce;
|
57 |
+
params.feedback = feedback;
|
58 |
+
params.email_address = email_address;
|
59 |
+
|
60 |
+
var data = jQuery.param( params );
|
61 |
+
jQuery.post(ajaxurl, data, function() {});
|
62 |
+
|
63 |
+
var data = rtb_hide_review_ask_params( 1000 );
|
64 |
+
jQuery.post(ajaxurl, data, function() {});
|
65 |
+
|
66 |
+
jQuery('.rtb-review-ask-feedback-form').addClass('rtb-hidden');
|
67 |
+
jQuery('.rtb-review-ask-review-text').addClass('rtb-hidden');
|
68 |
+
jQuery('.rtb-review-ask-thank-you-text').removeClass('rtb-hidden');
|
69 |
+
});
|
70 |
+
|
71 |
+
function rtb_hide_review_ask_params(ask_review_time = 7) {
|
72 |
+
var params = {};
|
73 |
+
|
74 |
+
params.action = 'rtb-hide-review-ask';
|
75 |
+
params.nonce = rtb_review_ask.nonce;
|
76 |
+
params.ask_review_time = ask_review_time;
|
77 |
+
|
78 |
+
return jQuery.param( params );
|
79 |
+
}
|
80 |
});
|
assets/js/editor.js
CHANGED
@@ -1,1236 +1,1236 @@
|
|
1 |
-
/**
|
2 |
-
* Custom fields editor for Custom Fields for Restaurant Reservations
|
3 |
-
*/
|
4 |
-
var cffrtb_editor = cffrtb_editor || {};
|
5 |
-
|
6 |
-
/**
|
7 |
-
* Initialize the editor object after jQuery has loaded
|
8 |
-
*/
|
9 |
-
jQuery(document).ready(function ($) {
|
10 |
-
|
11 |
-
/**
|
12 |
-
* jQuery reference for editor panel
|
13 |
-
*/
|
14 |
-
cffrtb_editor.el = $( '#cffrtb-editor' );
|
15 |
-
|
16 |
-
/**
|
17 |
-
* Show the error modal
|
18 |
-
*/
|
19 |
-
cffrtb_editor.show_error = function( msg ) {
|
20 |
-
|
21 |
-
var rtb_error_modal = $( '#rtb-error-modal ' );
|
22 |
-
|
23 |
-
rtb_error_modal.find( '.rtb-error-msg' ).html( msg );
|
24 |
-
rtb_error_modal.addClass( 'is-visible' );
|
25 |
-
|
26 |
-
$(document).keyup( function(e) {
|
27 |
-
if ( e.which == '27' ) {
|
28 |
-
cffrtb_editor.hide_error( rtb_error_modal );
|
29 |
-
}
|
30 |
-
});
|
31 |
-
|
32 |
-
rtb_error_modal.click( function(e) {
|
33 |
-
if ( $(e.target).is( rtb_error_modal ) || $(e.target).is( rtb_error_modal.find( 'a.button' ) ) ) {
|
34 |
-
|
35 |
-
e.stopPropagation();
|
36 |
-
e.preventDefault();
|
37 |
-
|
38 |
-
cffrtb_editor.hide_error( rtb_error_modal );
|
39 |
-
}
|
40 |
-
});
|
41 |
-
};
|
42 |
-
|
43 |
-
/**
|
44 |
-
* Hide the error modal
|
45 |
-
*/
|
46 |
-
cffrtb_editor.hide_error = function( el ) {
|
47 |
-
el.removeClass( 'is-visible' );
|
48 |
-
el.off();
|
49 |
-
};
|
50 |
-
|
51 |
-
});
|
52 |
-
|
53 |
-
/**
|
54 |
-
* Initialize the field editor after jQuery has loaded
|
55 |
-
*/
|
56 |
-
jQuery(document).ready(function ($) {
|
57 |
-
|
58 |
-
/**
|
59 |
-
* Manage the field editor
|
60 |
-
*/
|
61 |
-
cffrtb_editor.editor = {
|
62 |
-
|
63 |
-
el: $( '#cffrtb-field-editor' ),
|
64 |
-
|
65 |
-
option_el: $( '#cffrtb-field-editor-option' ),
|
66 |
-
|
67 |
-
init: function() {
|
68 |
-
|
69 |
-
// Store common form references
|
70 |
-
this.form = {
|
71 |
-
el: this.el.find( '#cffrtb-field-editor-form' ),
|
72 |
-
id: this.el.find( 'input[name="id"]' ),
|
73 |
-
type: this.el.find( 'input[name="type"]' ),
|
74 |
-
subtype: this.el.find( 'input[name="subtype"]' ),
|
75 |
-
};
|
76 |
-
|
77 |
-
// Show field editor option modal
|
78 |
-
$( '.add-field' ).click( function(e) {
|
79 |
-
e.stopPropagation();
|
80 |
-
e.preventDefault();
|
81 |
-
|
82 |
-
cffrtb_editor.editor.show_option();
|
83 |
-
});
|
84 |
-
|
85 |
-
// Register click events on options modal
|
86 |
-
this.option_el.click( function(e) {
|
87 |
-
e.stopPropagation();
|
88 |
-
e.preventDefault();
|
89 |
-
|
90 |
-
var target = $( e.target );
|
91 |
-
|
92 |
-
if ( target.hasClass( 'field' ) ) {
|
93 |
-
cffrtb_editor.editor.hide_option();
|
94 |
-
cffrtb_editor.editor.show_editor();
|
95 |
-
|
96 |
-
} else if ( target.hasClass( 'fieldset' ) ) {
|
97 |
-
cffrtb_editor.editor.hide_option();
|
98 |
-
cffrtb_editor.editor.show_editor( 'fieldset' );
|
99 |
-
|
100 |
-
} else if ( target.is( cffrtb_editor.editor.option_el ) ) {
|
101 |
-
cffrtb_editor.editor.hide_option();
|
102 |
-
}
|
103 |
-
});
|
104 |
-
|
105 |
-
// Close field editor modal when background is clicked
|
106 |
-
this.el.click( function(e) {
|
107 |
-
if ( $( e.target ).is( cffrtb_editor.editor.el ) ) {
|
108 |
-
cffrtb_editor.editor.hide_editor();
|
109 |
-
}
|
110 |
-
});
|
111 |
-
|
112 |
-
// Close field editor modal when ESC is keyed
|
113 |
-
$(document).keyup( function(e) {
|
114 |
-
if ( e.which == '27' ) {
|
115 |
-
cffrtb_editor.editor.hide_editor();
|
116 |
-
}
|
117 |
-
});
|
118 |
-
|
119 |
-
// Form actions
|
120 |
-
this.form.el.find( '> .actions' ).on( 'click', function(e) {
|
121 |
-
|
122 |
-
e.stopPropagation();
|
123 |
-
e.preventDefault();
|
124 |
-
|
125 |
-
var target = $( e.target );
|
126 |
-
|
127 |
-
// Exit early if the actions are disabled
|
128 |
-
if ( typeof target.attr( 'disabled' ) !== 'undefined' ) {
|
129 |
-
return;
|
130 |
-
}
|
131 |
-
|
132 |
-
// Save field
|
133 |
-
if ( target.hasClass( 'save' ) ) {
|
134 |
-
cffrtb_editor.editor.save_field();
|
135 |
-
|
136 |
-
// Cancel and close editor
|
137 |
-
} else if ( target.hasClass( 'cancel' ) ) {
|
138 |
-
cffrtb_editor.editor.hide_editor();
|
139 |
-
}
|
140 |
-
});
|
141 |
-
|
142 |
-
// Field type selections
|
143 |
-
this.form.el.find( '.type .selector' ).on( 'click', function(e) {
|
144 |
-
|
145 |
-
var target = $( e.target );
|
146 |
-
|
147 |
-
if ( target.get(0).tagName != 'A' ) {
|
148 |
-
return;
|
149 |
-
}
|
150 |
-
|
151 |
-
var level = target.parent().parent();
|
152 |
-
|
153 |
-
if ( target.parent().parent().hasClass( 'types' ) ) {
|
154 |
-
cffrtb_editor.editor.set_type( target.data( 'type' ), target );
|
155 |
-
|
156 |
-
} else {
|
157 |
-
cffrtb_editor.editor.set_subtype( target.data( 'subtype' ), target );
|
158 |
-
}
|
159 |
-
|
160 |
-
});
|
161 |
-
|
162 |
-
// Add an option
|
163 |
-
this.el.find( '.settings-panel.options .add a' ).on( 'click', function(e) {
|
164 |
-
e.stopPropagation();
|
165 |
-
e.preventDefault();
|
166 |
-
cffrtb_editor.editor.add_option();
|
167 |
-
});
|
168 |
-
|
169 |
-
// Add an option with ENTER key
|
170 |
-
this.get_add_option_el().keyup( function(e) {
|
171 |
-
if ( e.which == '13' ) {
|
172 |
-
e.stopPropagation();
|
173 |
-
e.preventDefault();
|
174 |
-
|
175 |
-
cffrtb_editor.editor.add_option();
|
176 |
-
}
|
177 |
-
});
|
178 |
-
|
179 |
-
// Remove an option
|
180 |
-
this.get_options_list_el().on( 'click', function(e) {
|
181 |
-
e.stopPropagation();
|
182 |
-
e.preventDefault();
|
183 |
-
|
184 |
-
var target = $( e.target );
|
185 |
-
|
186 |
-
if( target.is( 'a, a .dashicons' ) ) {
|
187 |
-
cffrtb_editor.editor.remove_option( target.closest( 'li' ) );
|
188 |
-
}
|
189 |
-
});
|
190 |
-
|
191 |
-
// Make the option list sortable
|
192 |
-
this.get_options_list_el().sortable({
|
193 |
-
placeholder: 'cffrtb-editor-options-placeholder',
|
194 |
-
delay: 250
|
195 |
-
});
|
196 |
-
|
197 |
-
},
|
198 |
-
|
199 |
-
/**
|
200 |
-
* Get the add option input element
|
201 |
-
*/
|
202 |
-
get_add_option_el: function() {
|
203 |
-
|
204 |
-
if ( typeof this.form.add_option == 'undefined' ) {
|
205 |
-
this.form.add_option = this.el.find( '.settings-panel.options .add input' );
|
206 |
-
}
|
207 |
-
|
208 |
-
return this.form.add_option;
|
209 |
-
},
|
210 |
-
|
211 |
-
/**
|
212 |
-
* Get the options list element
|
213 |
-
*/
|
214 |
-
get_options_list_el: function() {
|
215 |
-
|
216 |
-
if ( typeof this.form.options_list == 'undefined' ) {
|
217 |
-
this.form.options_list = this.el.find( '.settings-panel.options .options' );
|
218 |
-
}
|
219 |
-
|
220 |
-
return this.form.options_list;
|
221 |
-
},
|
222 |
-
|
223 |
-
/**
|
224 |
-
* Update the editor values with a new field object
|
225 |
-
*/
|
226 |
-
update_editor_values: function( field ) {
|
227 |
-
|
228 |
-
this.form.id.val( field.ID );
|
229 |
-
this.set_type( field.type );
|
230 |
-
this.set_subtype( field.subtype );
|
231 |
-
this.form.el.find( 'input[name="title"]' ).val( field.title );
|
232 |
-
|
233 |
-
if ( field.required ) {
|
234 |
-
this.form.el.find( '.required input' ).attr( 'checked', 'checked' );
|
235 |
-
}
|
236 |
-
|
237 |
-
if ( field.options && Object.keys( field.options ).length ) {
|
238 |
-
var options = '';
|
239 |
-
for( var i in field.options ) {
|
240 |
-
if ( field.options[i].disabled ) {
|
241 |
-
continue;
|
242 |
-
}
|
243 |
-
options += '<li data-id="' + field.options[i].id + '"><a href="#"><span class="dashicons dashicons-dismiss"></span></a> <span class="value">' + field.options[i].value + '</span></li>';
|
244 |
-
}
|
245 |
-
this.form.el.find( '.settings-panel.options .options' ).html( options );
|
246 |
-
}
|
247 |
-
|
248 |
-
this.el.trigger( 'cffrtb_update_editor_values', field );
|
249 |
-
},
|
250 |
-
|
251 |
-
/**
|
252 |
-
* Show the field/fieldset type selection before opening the editor
|
253 |
-
*/
|
254 |
-
show_option: function() {
|
255 |
-
|
256 |
-
this.option_el.addClass( 'is-visible' );
|
257 |
-
$( 'body' ).addClass( 'rtb-hide-body-scroll' );
|
258 |
-
|
259 |
-
},
|
260 |
-
|
261 |
-
/**
|
262 |
-
* Hide the field/fieldset type selection
|
263 |
-
*/
|
264 |
-
hide_option: function() {
|
265 |
-
|
266 |
-
this.option_el.removeClass( 'is-visible' );
|
267 |
-
$( 'body' ).removeClass( 'rtb-hide-body-scroll' );
|
268 |
-
|
269 |
-
},
|
270 |
-
|
271 |
-
/**
|
272 |
-
* Show the editor
|
273 |
-
*/
|
274 |
-
show_editor: function( mode ) {
|
275 |
-
|
276 |
-
if ( mode === 'edit' ) {
|
277 |
-
this.form.el.find( '> .title > h2' ).html( cffrtb_editor.strings.editor_edit_field );
|
278 |
-
this.form.el.find( '.actions a.save' ).html( cffrtb_editor.strings.editor_save_field );
|
279 |
-
} else if ( mode == 'fieldset' ) {
|
280 |
-
this.form.el.find( '> .title > h2' ).html( cffrtb_editor.strings.editor_add_fieldset );
|
281 |
-
this.form.el.find( '.actions a.save' ).addClass( 'fieldset' ).html( cffrtb_editor.strings.editor_save_fieldset );
|
282 |
-
this.form.el.addClass( mode );
|
283 |
-
this.form.type.val( 'fieldset' );
|
284 |
-
this.form.subtype.val( 'fieldset' );
|
285 |
-
} else {
|
286 |
-
this.form.el.find( '> .title > h2' ).html( cffrtb_editor.strings.editor_add_field );
|
287 |
-
this.form.el.find( '.actions a.save' ).html( cffrtb_editor.strings.editor_add_field );
|
288 |
-
}
|
289 |
-
|
290 |
-
this.el.addClass( 'is-visible' );
|
291 |
-
$( 'body' ).addClass( 'rtb-hide-body-scroll' );
|
292 |
-
},
|
293 |
-
|
294 |
-
/**
|
295 |
-
* Hide the editor
|
296 |
-
*/
|
297 |
-
hide_editor: function() {
|
298 |
-
this.el.removeClass( 'is-visible' );
|
299 |
-
this.option_el.removeClass( 'is-hidden' );
|
300 |
-
this.form.el.addClass( 'is-hidden' );
|
301 |
-
this.form.el.removeClass( 'fieldset' );
|
302 |
-
this.form.el.find( '.actions a.save' ).removeClass( 'fieldset' );
|
303 |
-
$( 'body' ).removeClass( 'rtb-hide-body-scroll' );
|
304 |
-
this.form.el.find( 'input, select, textarea' ).not( 'input[type="checkbox"]' ).val( '' );
|
305 |
-
this.form.el.find( 'input[type="checkbox"]' ).removeAttr( 'checked' );
|
306 |
-
this.get_options_list_el().empty();
|
307 |
-
this.set_type( cffrtb_editor.default_type );
|
308 |
-
this.set_subtype( cffrtb_editor.default_subtype );
|
309 |
-
},
|
310 |
-
|
311 |
-
/**
|
312 |
-
* Set the type and select the new subtype
|
313 |
-
*/
|
314 |
-
set_type: function( type, el ) {
|
315 |
-
|
316 |
-
if ( type == this.form.type.val() ) {
|
317 |
-
return;
|
318 |
-
}
|
319 |
-
|
320 |
-
// Set the value
|
321 |
-
this.form.type.val( type );
|
322 |
-
|
323 |
-
// Remove the `current` class from the selection
|
324 |
-
this.el.find( '.type .types a' ).removeClass( 'current' );
|
325 |
-
|
326 |
-
// Add the `current` class to this selection
|
327 |
-
if ( typeof el == 'undefined' ) {
|
328 |
-
el = this.el.find( '.type .types .' + type );
|
329 |
-
}
|
330 |
-
el.addClass( 'current' );
|
331 |
-
|
332 |
-
// Show the settings if they exist
|
333 |
-
this.el.find( '.settings-panel' ).each( function() {
|
334 |
-
if ( $(this).hasClass( type ) ) {
|
335 |
-
$(this).addClass( 'current' );
|
336 |
-
} else {
|
337 |
-
$(this).removeClass( 'current' );
|
338 |
-
}
|
339 |
-
});
|
340 |
-
|
341 |
-
// Show the subtype list
|
342 |
-
this.el.find( '.type .subtypes' ).each( function() {
|
343 |
-
if ( $(this).hasClass( type ) ) {
|
344 |
-
$(this).addClass( 'current' );
|
345 |
-
} else {
|
346 |
-
$(this).removeClass( 'current' );
|
347 |
-
}
|
348 |
-
});
|
349 |
-
|
350 |
-
// Trigger a click on the first subtype for this type
|
351 |
-
this.el.find( '.subtypes.' + type + ' li' ).first().find( 'a' ).trigger( 'click' );
|
352 |
-
},
|
353 |
-
|
354 |
-
/**
|
355 |
-
* Select a subtype
|
356 |
-
*/
|
357 |
-
set_subtype: function( subtype, el ) {
|
358 |
-
|
359 |
-
if ( subtype == this.form.subtype.val() ) {
|
360 |
-
return;
|
361 |
-
}
|
362 |
-
|
363 |
-
// Set the value
|
364 |
-
this.form.subtype.val( subtype );
|
365 |
-
|
366 |
-
// Remove the `current` class from the selection
|
367 |
-
this.el.find( '.type .subtypes a' ).removeClass( 'current' );
|
368 |
-
|
369 |
-
// Apply the `current` class to the right subtype
|
370 |
-
if ( typeof el == 'undefined' ) {
|
371 |
-
el = this.el.find( '.type .subtypes .' + subtype );
|
372 |
-
}
|
373 |
-
el.addClass( 'current' );
|
374 |
-
|
375 |
-
},
|
376 |
-
|
377 |
-
/**
|
378 |
-
* Add an option to the list
|
379 |
-
*/
|
380 |
-
add_option: function() {
|
381 |
-
var list = this.get_options_list_el();
|
382 |
-
var option = this.get_add_option_el();
|
383 |
-
|
384 |
-
this.get_options_list_el().append( '<li><a href="#"><span class="dashicons dashicons-dismiss"></span></a> <span class="value">' + this.get_add_option_el().val() + '</span></li>' );
|
385 |
-
this.get_add_option_el().val( '' );
|
386 |
-
|
387 |
-
// Scroll the options list if they have more than 10
|
388 |
-
if ( this.get_options_list_el().find( 'li' ).length > 10 ) {
|
389 |
-
this.get_options_list_el().addClass( 'scroll' );
|
390 |
-
}
|
391 |
-
},
|
392 |
-
|
393 |
-
/**
|
394 |
-
* Remove an option from the list
|
395 |
-
*/
|
396 |
-
remove_option: function( el ) {
|
397 |
-
|
398 |
-
el.fadeOut( '200', function() {
|
399 |
-
$(this).remove();
|
400 |
-
|
401 |
-
// Remove scrollbar if the options list is less than 10 options
|
402 |
-
if ( cffrtb_editor.editor.get_options_list_el().find( 'li' ).length <= 10 ) {
|
403 |
-
cffrtb_editor.editor.get_options_list_el().removeClass( 'scroll' );
|
404 |
-
}
|
405 |
-
});
|
406 |
-
},
|
407 |
-
|
408 |
-
/**
|
409 |
-
* Disable actions
|
410 |
-
*/
|
411 |
-
disable_actions: function() {
|
412 |
-
this.form.el.find( '.actions' ).addClass( 'working' ).find( '.save, .cancel' ).attr( 'disabled', 'disabled' );
|
413 |
-
},
|
414 |
-
|
415 |
-
/**
|
416 |
-
* Enable actions
|
417 |
-
*/
|
418 |
-
enable_actions: function() {
|
419 |
-
this.form.el.find( '.actions' ).removeClass( 'working' ).find( '.save, .cancel' ).removeAttr( 'disabled' );
|
420 |
-
},
|
421 |
-
|
422 |
-
/**
|
423 |
-
* Load field
|
424 |
-
*/
|
425 |
-
load_field: function( item_slug ) {
|
426 |
-
|
427 |
-
// Don't trigger if we're already saving
|
428 |
-
if ( cffrtb_editor.list.get_title_el( item_slug ).hasClass( 'saving' ) ) {
|
429 |
-
return;
|
430 |
-
}
|
431 |
-
|
432 |
-
var id = cffrtb_editor.list.items[item_slug].el.data( 'id' );
|
433 |
-
if ( typeof id == 'undefined' ) {
|
434 |
-
return;
|
435 |
-
}
|
436 |
-
|
437 |
-
cffrtb_editor.list.disable_sorting();
|
438 |
-
cffrtb_editor.list.get_title_el( item_slug ).addClass( 'saving' );
|
439 |
-
cffrtb_editor.list.get_title_el( item_slug ).find( '.view .controls' ).prepend( '<span class="load-spinner"></span>' );
|
440 |
-
|
441 |
-
var params = {};
|
442 |
-
|
443 |
-
params.action = 'cffrtb-load-field';
|
444 |
-
params.nonce = cffrtb_editor.ajax_nonce;
|
445 |
-
params.ID = id;
|
446 |
-
|
447 |
-
var data = $.param( params );
|
448 |
-
|
449 |
-
$.post( ajaxurl, data, function( r ) {
|
450 |
-
|
451 |
-
if ( r.success ) {
|
452 |
-
cffrtb_editor.editor.update_editor_values( r.data.field );
|
453 |
-
cffrtb_editor.editor.show_editor( 'edit' );
|
454 |
-
|
455 |
-
} else {
|
456 |
-
if ( typeof r.data === 'undefined' || typeof r.data.error === 'undefined' ) {
|
457 |
-
cffrtb_editor.show_error( cffrtb_editor.strings.unknown_error );
|
458 |
-
} else {
|
459 |
-
cffrtb_editor.show_error( r.data.msg );
|
460 |
-
}
|
461 |
-
}
|
462 |
-
|
463 |
-
// Reset status
|
464 |
-
cffrtb_editor.list.enable_sorting();
|
465 |
-
cffrtb_editor.list.get_title_el( item_slug ).removeClass( 'saving' );
|
466 |
-
cffrtb_editor.list.get_title_el( item_slug ).find( '.view .controls .load-spinner' ).fadeOut( 400, function() { $(this).remove(); });
|
467 |
-
});
|
468 |
-
|
469 |
-
},
|
470 |
-
|
471 |
-
/**
|
472 |
-
* Save a field
|
473 |
-
*/
|
474 |
-
save_field: function() {
|
475 |
-
|
476 |
-
this.disable_actions();
|
477 |
-
|
478 |
-
var params = {};
|
479 |
-
|
480 |
-
params.action = 'cffrtb-save-field';
|
481 |
-
params.nonce = cffrtb_editor.ajax_nonce;
|
482 |
-
params.request = 'save_field';
|
483 |
-
params.field = {
|
484 |
-
ID: this.form.id.val(),
|
485 |
-
type: this.form.type.val(),
|
486 |
-
subtype: this.form.subtype.val(),
|
487 |
-
title: this.form.el.find( 'input[name="title"]' ).val(),
|
488 |
-
};
|
489 |
-
|
490 |
-
if ( this.form.el.find( 'input[name="required"]' ).is( ':checked' ) ) {
|
491 |
-
params.field.required = 1;
|
492 |
-
} else {
|
493 |
-
params.field.required = 0;
|
494 |
-
}
|
495 |
-
|
496 |
-
if ( params.field.type == 'options' ) {
|
497 |
-
params.field.options = {};
|
498 |
-
this.get_options_list_el().find( 'li' ).each( function(i) {
|
499 |
-
|
500 |
-
var id = $(this).data( 'id' );
|
501 |
-
if ( typeof id == 'undefined' ) {
|
502 |
-
id = 'new-' + Math.random().toString(36).substring(7);
|
503 |
-
}
|
504 |
-
|
505 |
-
params.field.options[i] = {
|
506 |
-
id: id,
|
507 |
-
value: $(this).find( '.value' ).html(),
|
508 |
-
order: i
|
509 |
-
};
|
510 |
-
});
|
511 |
-
}
|
512 |
-
|
513 |
-
// Perform some basic validation checks here
|
514 |
-
var errors = this.validate_field( params.field );
|
515 |
-
if ( typeof errors !== 'undefined' ) {
|
516 |
-
cffrtb_editor.show_error( errors );
|
517 |
-
this.enable_actions();
|
518 |
-
|
519 |
-
return;
|
520 |
-
}
|
521 |
-
|
522 |
-
var data = $.param( params );
|
523 |
-
|
524 |
-
$.post( ajaxurl, data, function( r ) {
|
525 |
-
|
526 |
-
if ( r.success ) {
|
527 |
-
cffrtb_editor.editor.hide_editor();
|
528 |
-
cffrtb_editor.list.add_item( r.data.field, r.data.ID, r.data.is_new_field, r.data.type );
|
529 |
-
|
530 |
-
} else {
|
531 |
-
if ( typeof r.data === 'undefined' || typeof r.data.error === 'undefined' ) {
|
532 |
-
cffrtb_editor.show_error( cffrtb_editor.strings.unknown_error );
|
533 |
-
} else {
|
534 |
-
console.log( r.data );
|
535 |
-
cffrtb_editor.show_error( r.data.msg );
|
536 |
-
}
|
537 |
-
}
|
538 |
-
|
539 |
-
cffrtb_editor.editor.enable_actions();
|
540 |
-
});
|
541 |
-
},
|
542 |
-
|
543 |
-
/**
|
544 |
-
* Some quick validation on the field data before sending it off
|
545 |
-
*/
|
546 |
-
validate_field: function( field ) {
|
547 |
-
|
548 |
-
if ( field.title.length === 0 || !field.title.trim() ) {
|
549 |
-
return cffrtb_editor.strings.field_missing_title;
|
550 |
-
}
|
551 |
-
|
552 |
-
if ( field.type == 'options' && field.options.length === 0 ) {
|
553 |
-
return cffrtb_editor.strings.field_missing_options;
|
554 |
-
}
|
555 |
-
}
|
556 |
-
};
|
557 |
-
|
558 |
-
/**
|
559 |
-
* Initialize the editor
|
560 |
-
*/
|
561 |
-
cffrtb_editor.editor.init();
|
562 |
-
|
563 |
-
});
|
564 |
-
|
565 |
-
/**
|
566 |
-
* Initialize the fields list object after jQuery has loaded
|
567 |
-
*/
|
568 |
-
jQuery(document).ready(function ($) {
|
569 |
-
|
570 |
-
/**
|
571 |
-
* Manage the list of fields
|
572 |
-
*/
|
573 |
-
cffrtb_editor.list = {
|
574 |
-
|
575 |
-
// list element
|
576 |
-
el: cffrtb_editor.el.find( '#cffrtb-list' ),
|
577 |
-
|
578 |
-
// fieldsets and fields.
|
579 |
-
// just storing jQuery references to prevent duplicate lookups
|
580 |
-
items: {},
|
581 |
-
|
582 |
-
// flag to disable events during view->edit transition
|
583 |
-
in_transition: false,
|
584 |
-
|
585 |
-
// disabled fields list
|
586 |
-
disabled_el: cffrtb_editor.el.find( '#cffrtb-disabled' ),
|
587 |
-
|
588 |
-
init: function() {
|
589 |
-
|
590 |
-
// Store jQuery references to prevent duplicate lookups
|
591 |
-
this.el.find( '.fieldset, .field' ).each( function() {
|
592 |
-
cffrtb_editor.list.items[ $(this).data( 'slug' ) ] = {
|
593 |
-
el: $(this)
|
594 |
-
};
|
595 |
-
});
|
596 |
-
|
597 |
-
// Clear pre-existing listeners
|
598 |
-
this.el.off( 'click keyup' );
|
599 |
-
|
600 |
-
// Register click listeners
|
601 |
-
this.el.on( 'click', function( e ) {
|
602 |
-
|
603 |
-
e.stopPropagation();
|
604 |
-
e.preventDefault();
|
605 |
-
|
606 |
-
var target = $( e.target );
|
607 |
-
|
608 |
-
// Any click outside one of the field titles
|
609 |
-
if ( !target.hasClass( 'title' ) && !target.parents().hasClass( 'title' ) ) {
|
610 |
-
cffrtb_editor.list.save_all();
|
611 |
-
return;
|
612 |
-
}
|
613 |
-
|
614 |
-
var item_slug = target.parents( '.fieldset, .field' ).first().data( 'slug' );
|
615 |
-
|
616 |
-
// Open options panel
|
617 |
-
if ( cffrtb_editor.list.is_target( target, 'options' ) ) {
|
618 |
-
cffrtb_editor.list.save_all();
|
619 |
-
cffrtb_editor.editor.load_field( item_slug );
|
620 |
-
|
621 |
-
// Delete field
|
622 |
-
} else if ( cffrtb_editor.list.is_target( target, 'delete' ) ) {
|
623 |
-
cffrtb_editor.list.save_all();
|
624 |
-
cffrtb_editor.list.delete_item( item_slug );
|
625 |
-
|
626 |
-
// Open label editing panel
|
627 |
-
} else if ( !cffrtb_editor.list.is_editing( item_slug ) ) {
|
628 |
-
cffrtb_editor.list.show_edit( item_slug );
|
629 |
-
|
630 |
-
// Save label
|
631 |
-
} else if ( cffrtb_editor.list.is_target( target, 'save' ) ) {
|
632 |
-
cffrtb_editor.list.save_label( item_slug );
|
633 |
-
|
634 |
-
// Give focus to input when editing panel is active
|
635 |
-
} else if ( target.hasClass( 'edit' ) ) {
|
636 |
-
cffrtb_editor.list.set_focus( item_slug );
|
637 |
-
}
|
638 |
-
|
639 |
-
});
|
640 |
-
|
641 |
-
// Save label with ENTER key
|
642 |
-
this.el.keyup( function(e) {
|
643 |
-
|
644 |
-
if ( !cffrtb_editor.list.in_transition && e.which == '13' ) {
|
645 |
-
|
646 |
-
var target = $( e.target );
|
647 |
-
|
648 |
-
if ( target.is( 'input:focus' ) ) {
|
649 |
-
|
650 |
-
e.stopPropagation();
|
651 |
-
e.preventDefault();
|
652 |
-
|
653 |
-
cffrtb_editor.list.save_label( target.parents( '.fieldset, .field' ).first().data( 'slug' ) );
|
654 |
-
}
|
655 |
-
}
|
656 |
-
});
|
657 |
-
|
658 |
-
// Make the list sortable
|
659 |
-
this.el.sortable({
|
660 |
-
placeholder: 'cffrtb-list-placeholder',
|
661 |
-
delay: 250,
|
662 |
-
update: this.sorting_complete
|
663 |
-
});
|
664 |
-
this.el.find( '.fieldset ul' ).sortable({
|
665 |
-
placeholder: 'cffrtb-list-placeholder',
|
666 |
-
connectWith: '#cffrtb-list .fieldset ul',
|
667 |
-
delay: 250,
|
668 |
-
update: this.sorting_complete
|
669 |
-
});
|
670 |
-
|
671 |
-
// Clear pre-existing listeners
|
672 |
-
this.disabled_el.off( 'click' );
|
673 |
-
|
674 |
-
// Register click events on disabled fields
|
675 |
-
this.disabled_el.on( 'click', function(e) {
|
676 |
-
|
677 |
-
e.stopPropagation();
|
678 |
-
e.preventDefault();
|
679 |
-
|
680 |
-
var target = $( e.target );
|
681 |
-
|
682 |
-
// Restore field
|
683 |
-
if ( cffrtb_editor.list.is_target( target, 'enable' ) ) {
|
684 |
-
cffrtb_editor.list.enable_item( target.parents( '.fieldset, .field' ).first() );
|
685 |
-
|
686 |
-
// Open learn more text
|
687 |
-
} else if ( cffrtb_editor.list.is_target( target, 'learn-more' ) ) {
|
688 |
-
cffrtb_editor.list.disabled_el.find( '.reset .description' ).addClass( 'is-visible' );
|
689 |
-
|
690 |
-
// Revert to default
|
691 |
-
} else if ( cffrtb_editor.list.is_target( target, 'reset-all' ) ) {
|
692 |
-
cffrtb_editor.list.reset_all();
|
693 |
-
}
|
694 |
-
});
|
695 |
-
|
696 |
-
},
|
697 |
-
|
698 |
-
/**
|
699 |
-
* Is the field being edited?
|
700 |
-
*/
|
701 |
-
is_editing: function( item_slug ) {
|
702 |
-
return this.get_title_el( item_slug ).hasClass( 'editing' );
|
703 |
-
},
|
704 |
-
|
705 |
-
/**
|
706 |
-
* Is the click target opening the field options?
|
707 |
-
*/
|
708 |
-
is_target: function( target, match ) {
|
709 |
-
return target.hasClass( match ) || target.parents().hasClass( match );
|
710 |
-
},
|
711 |
-
|
712 |
-
/**
|
713 |
-
* Get the title element for an item
|
714 |
-
*/
|
715 |
-
get_title_el: function( item_slug ) {
|
716 |
-
|
717 |
-
if ( typeof this.items[item_slug].title == 'undefined' ) {
|
718 |
-
this.items[item_slug].title = this.items[item_slug].el.find( '> .title' );
|
719 |
-
}
|
720 |
-
|
721 |
-
return this.items[item_slug].title;
|
722 |
-
},
|
723 |
-
|
724 |
-
/**
|
725 |
-
* Get the edit element for an item
|
726 |
-
*/
|
727 |
-
get_edit_el: function( item_slug ) {
|
728 |
-
|
729 |
-
if ( typeof this.items[item_slug].title == 'undefined' ) {
|
730 |
-
this.items[item_slug].edit = this.items[item_slug].el.find( '> .title .edit' );
|
731 |
-
}
|
732 |
-
|
733 |
-
return this.items[item_slug].edit;
|
734 |
-
},
|
735 |
-
|
736 |
-
/**
|
737 |
-
* Get the value of the input field
|
738 |
-
*/
|
739 |
-
get_input: function( item_slug ) {
|
740 |
-
return this.get_input_el( item_slug ).val();
|
741 |
-
},
|
742 |
-
|
743 |
-
/**
|
744 |
-
* Get the input element of a field
|
745 |
-
*/
|
746 |
-
get_input_el: function( item_slug ) {
|
747 |
-
|
748 |
-
if ( typeof this.items[item_slug].input == 'undefined' ) {
|
749 |
-
this.items[item_slug].input = this.items[item_slug].el.find( '> .title .edit input' );
|
750 |
-
}
|
751 |
-
|
752 |
-
return this.items[item_slug].input;
|
753 |
-
},
|
754 |
-
|
755 |
-
/**
|
756 |
-
* Get the fieldset slug an item is attached to
|
757 |
-
*/
|
758 |
-
get_fieldset: function( item_slug ) {
|
759 |
-
|
760 |
-
if ( this.items[item_slug].el.hasClass( 'fieldset' ) ) {
|
761 |
-
return item_slug;
|
762 |
-
} else {
|
763 |
-
return this.items[item_slug].el.parents( '.fieldset' ).first().data( 'slug' );
|
764 |
-
}
|
765 |
-
},
|
766 |
-
|
767 |
-
/**
|
768 |
-
* Update the value of the label in the view
|
769 |
-
*/
|
770 |
-
update_view: function( item_slug ) {
|
771 |
-
|
772 |
-
if ( typeof this.items[item_slug].view_value == 'undefined' ) {
|
773 |
-
this.items[item_slug].view_value = this.items[item_slug].el.find( '> .title .view .value' );
|
774 |
-
}
|
775 |
-
|
776 |
-
this.items[item_slug].view_value.html( this.get_input( item_slug ) );
|
777 |
-
},
|
778 |
-
|
779 |
-
/**
|
780 |
-
* Open an item's edit mode
|
781 |
-
*/
|
782 |
-
show_edit: function( item_slug ) {
|
783 |
-
|
784 |
-
// Save and close any other labels being edited
|
785 |
-
this.save_all();
|
786 |
-
|
787 |
-
// Set transition flag and timer
|
788 |
-
this.in_transition = true;
|
789 |
-
setTimeout( this.clear_transition_flag, 600 );
|
790 |
-
|
791 |
-
// Open edit mode for this item
|
792 |
-
this.enable_tabbing( item_slug );
|
793 |
-
this.get_title_el( item_slug ).addClass( 'editing' );
|
794 |
-
this.set_focus( item_slug );
|
795 |
-
},
|
796 |
-
|
797 |
-
/**
|
798 |
-
* Focus and select the input field
|
799 |
-
*/
|
800 |
-
set_focus: function( item_slug ) {
|
801 |
-
this.get_input_el( item_slug ).focus().select();
|
802 |
-
},
|
803 |
-
|
804 |
-
/**
|
805 |
-
* Clear the transition flag used when opening the editing panel
|
806 |
-
*/
|
807 |
-
clear_transition_flag: function() {
|
808 |
-
cffrtb_editor.list.in_transition = false;
|
809 |
-
},
|
810 |
-
|
811 |
-
/**
|
812 |
-
* Return an item to view mode
|
813 |
-
*/
|
814 |
-
show_view: function( item_slug ) {
|
815 |
-
this.get_title_el( item_slug ).removeClass( 'editing' );
|
816 |
-
this.disable_tabbing( item_slug );
|
817 |
-
this.set_focus( item_slug );
|
818 |
-
},
|
819 |
-
|
820 |
-
/**
|
821 |
-
* Disable tabbing through a hidden edit interface
|
822 |
-
*/
|
823 |
-
disable_tabbing: function( item_slug ) {
|
824 |
-
this.get_title_el( item_slug ).find( '> .edit input, > .edit .save' ).attr( 'tabindex', '-1' );
|
825 |
-
},
|
826 |
-
|
827 |
-
/**
|
828 |
-
* Enable tabbing through a hidden edit interface
|
829 |
-
*/
|
830 |
-
enable_tabbing: function( item_slug ) {
|
831 |
-
this.get_title_el( item_slug ).find( '> .edit input, > .edit .save' ).removeAttr( 'tabindex' );
|
832 |
-
},
|
833 |
-
|
834 |
-
/**
|
835 |
-
* Disable drag and drop sorting
|
836 |
-
*/
|
837 |
-
disable_sorting: function() {
|
838 |
-
this.el.sortable( 'option', 'disabled', true );
|
839 |
-
this.el.find( '.fieldset ul' ).sortable( 'option', 'disabled', true );
|
840 |
-
},
|
841 |
-
|
842 |
-
/**
|
843 |
-
* Enable drag and drop sorting
|
844 |
-
*/
|
845 |
-
enable_sorting: function() {
|
846 |
-
this.el.sortable( 'option', 'disabled', false );
|
847 |
-
this.el.find( '.fieldset ul' ).sortable( 'option', 'disabled', false );
|
848 |
-
},
|
849 |
-
|
850 |
-
/**
|
851 |
-
* Save and close any fields being edited
|
852 |
-
*/
|
853 |
-
save_all: function() {
|
854 |
-
this.el.find( '.title.editing' ).each( function() {
|
855 |
-
cffrtb_editor.list.save_label( $(this ).parent().data( 'slug' ) );
|
856 |
-
});
|
857 |
-
},
|
858 |
-
|
859 |
-
/**
|
860 |
-
* Save an item's label
|
861 |
-
*/
|
862 |
-
save_label: function( item_slug ) {
|
863 |
-
|
864 |
-
// Don't trigger if we're already saving
|
865 |
-
if ( this.get_title_el( item_slug ).hasClass( 'saving' ) ) {
|
866 |
-
return;
|
867 |
-
}
|
868 |
-
|
869 |
-
// Indicate status
|
870 |
-
this.get_title_el( item_slug ).addClass( 'saving' );
|
871 |
-
this.get_input_el( item_slug ).attr( 'disabled', 'disabled' );
|
872 |
-
|
873 |
-
var params = {};
|
874 |
-
|
875 |
-
params.action = 'cffrtb-save-field';
|
876 |
-
params.nonce = cffrtb_editor.ajax_nonce;
|
877 |
-
params.request = 'save_label';
|
878 |
-
params.field = {
|
879 |
-
slug: item_slug,
|
880 |
-
title: this.get_input( item_slug ),
|
881 |
-
fieldset: this.get_fieldset( item_slug )
|
882 |
-
};
|
883 |
-
|
884 |
-
if( this.items[item_slug].el.data( 'id' ) ) {
|
885 |
-
params.field.ID = this.items[item_slug].el.data( 'id' );
|
886 |
-
}
|
887 |
-
|
888 |
-
var data = $.param( params );
|
889 |
-
|
890 |
-
$.post( ajaxurl, data, function( r ) {
|
891 |
-
|
892 |
-
if ( r.success ) {
|
893 |
-
cffrtb_editor.list.update_view( item_slug );
|
894 |
-
cffrtb_editor.list.show_view( item_slug );
|
895 |
-
|
896 |
-
} else {
|
897 |
-
if ( typeof r.data === 'undefined' || typeof r.data.error === 'undefined' ) {
|
898 |
-
cffrtb_editor.show_error( cffrtb_editor.strings.unknown_error );
|
899 |
-
} else {
|
900 |
-
console.log( r.data );
|
901 |
-
cffrtb_editor.show_error( r.data.msg );
|
902 |
-
}
|
903 |
-
}
|
904 |
-
|
905 |
-
// Reset status
|
906 |
-
cffrtb_editor.list.get_title_el( item_slug ).removeClass( 'saving' );
|
907 |
-
cffrtb_editor.list.get_input_el( item_slug ).removeAttr( 'disabled' );
|
908 |
-
});
|
909 |
-
},
|
910 |
-
|
911 |
-
/**
|
912 |
-
* Sorting complete
|
913 |
-
*/
|
914 |
-
sorting_complete: function( event, ui ) {
|
915 |
-
cffrtb_editor.list.save_sort( $( ui.item.context ) );
|
916 |
-
},
|
917 |
-
|
918 |
-
/**
|
919 |
-
* Save the sort order after it's been changed
|
920 |
-
*/
|
921 |
-
save_sort: function( target ) {
|
922 |
-
|
923 |
-
if ( cffrtb_editor.el.hasClass( 'saving-order' ) ) {
|
924 |
-
return;
|
925 |
-
}
|
926 |
-
|
927 |
-
// Indicate status
|
928 |
-
cffrtb_editor.list.disable_sorting();
|
929 |
-
cffrtb_editor.el.addClass( 'saving-order' );
|
930 |
-
target.find( '> .title .view .controls' ).prepend( '<span class="load-spinner"></span>' );
|
931 |
-
|
932 |
-
var params = {};
|
933 |
-
|
934 |
-
params.action = 'cffrtb-save-order';
|
935 |
-
params.nonce = cffrtb_editor.ajax_nonce;
|
936 |
-
params.order = [];
|
937 |
-
|
938 |
-
var i = 0;
|
939 |
-
cffrtb_editor.list.el.find( '> li' ).each( function() {
|
940 |
-
params.order.push( cffrtb_editor.list.get_item_order_obj( $(this), i ) );
|
941 |
-
i++;
|
942 |
-
|
943 |
-
$(this).find( '> ul > li' ).each( function() {
|
944 |
-
params.order.push( cffrtb_editor.list.get_item_order_obj( $(this), i ) );
|
945 |
-
i++;
|
946 |
-
});
|
947 |
-
});
|
948 |
-
|
949 |
-
var data = $.param( params );
|
950 |
-
|
951 |
-
$.post( ajaxurl, data, function( r ) {
|
952 |
-
|
953 |
-
if ( r.success ) {
|
954 |
-
|
955 |
-
} else {
|
956 |
-
if ( typeof r.data === 'undefined' || typeof r.data.error === 'undefined' ) {
|
957 |
-
cffrtb_editor.show_error( cffrtb_editor.strings.unknown_error );
|
958 |
-
} else {
|
959 |
-
console.log( r.data );
|
960 |
-
cffrtb_editor.show_error( r.data.msg );
|
961 |
-
}
|
962 |
-
}
|
963 |
-
|
964 |
-
// Reset status
|
965 |
-
cffrtb_editor.el.removeClass( 'saving-order' );
|
966 |
-
target.find( '> .title .view .controls .load-spinner' ).fadeOut( 400, function() { $(this).remove(); });
|
967 |
-
cffrtb_editor.list.enable_sorting();
|
968 |
-
});
|
969 |
-
},
|
970 |
-
|
971 |
-
/**
|
972 |
-
* Get an object with an elements order and slug/id data
|
973 |
-
*/
|
974 |
-
get_item_order_obj: function( el, i ) {
|
975 |
-
|
976 |
-
var item = {};
|
977 |
-
|
978 |
-
if ( el.data( 'slug' ) ) {
|
979 |
-
item.slug = el.data( 'slug' );
|
980 |
-
}
|
981 |
-
|
982 |
-
if ( el.data( 'id' ) ) {
|
983 |
-
item.ID = el.data( 'id' );
|
984 |
-
}
|
985 |
-
|
986 |
-
item.fieldset = cffrtb_editor.list.get_fieldset( item.slug );
|
987 |
-
|
988 |
-
item.order = i;
|
989 |
-
|
990 |
-
return item;
|
991 |
-
},
|
992 |
-
|
993 |
-
/**
|
994 |
-
* Add an item
|
995 |
-
*
|
996 |
-
* Expects to receive an HTML string for the new <li> element
|
997 |
-
*/
|
998 |
-
add_item: function( html, ID, is_new_field, type ) {
|
999 |
-
|
1000 |
-
if ( is_new_field ) {
|
1001 |
-
if ( type == 'fieldset' ) {
|
1002 |
-
this.el.find( '> .fieldset' ).last().after( html );
|
1003 |
-
} else {
|
1004 |
-
this.el.find( '> .fieldset' ).last().find( '> .fields' ).append( html );
|
1005 |
-
}
|
1006 |
-
} else {
|
1007 |
-
this.el.find( '.fieldset, .field' ).each( function() {
|
1008 |
-
if ( $(this).data( 'id' ) == ID ) {
|
1009 |
-
$(this).html( html ).hide().fadeIn();
|
1010 |
-
}
|
1011 |
-
});
|
1012 |
-
}
|
1013 |
-
|
1014 |
-
this.init();
|
1015 |
-
this.save_sort( this.el.find( '.fieldset, .field' ).last() );
|
1016 |
-
},
|
1017 |
-
|
1018 |
-
/**
|
1019 |
-
* Delete or disable an item from the list of fields
|
1020 |
-
*/
|
1021 |
-
delete_item: function( item_slug ) {
|
1022 |
-
|
1023 |
-
if ( cffrtb_editor.el.hasClass( 'deleting' ) ) {
|
1024 |
-
return;
|
1025 |
-
}
|
1026 |
-
|
1027 |
-
var item_el = this.items[item_slug].el;
|
1028 |
-
var is_fieldset = item_el.hasClass( 'fieldset' );
|
1029 |
-
|
1030 |
-
// Indicate status
|
1031 |
-
cffrtb_editor.list.disable_sorting();
|
1032 |
-
cffrtb_editor.el.addClass( 'deleting' );
|
1033 |
-
item_el.find( '> .title .view .controls' ).prepend( '<span class="load-spinner"></span>' );
|
1034 |
-
|
1035 |
-
// Can't remove a fieldset with fields
|
1036 |
-
if ( is_fieldset && item_el.find( '.field' ).length ) {
|
1037 |
-
cffrtb_editor.show_error( cffrtb_editor.strings.fieldset_not_empty );
|
1038 |
-
item_el.find( '> .title .view .controls .load-spinner' ).fadeOut( 400, function() { $(this).remove(); });
|
1039 |
-
cffrtb_editor.el.removeClass( 'deleting' );
|
1040 |
-
cffrtb_editor.list.enable_sorting();
|
1041 |
-
return;
|
1042 |
-
}
|
1043 |
-
|
1044 |
-
var id = item_el.data( 'id' );
|
1045 |
-
|
1046 |
-
var params = {};
|
1047 |
-
|
1048 |
-
params.action = 'cffrtb-delete-field';
|
1049 |
-
params.nonce = cffrtb_editor.ajax_nonce;
|
1050 |
-
|
1051 |
-
if ( id ) {
|
1052 |
-
params.ID = id;
|
1053 |
-
} else {
|
1054 |
-
params.slug = item_slug;
|
1055 |
-
params.fieldset = this.get_fieldset( item_slug );
|
1056 |
-
}
|
1057 |
-
|
1058 |
-
var data = $.param( params );
|
1059 |
-
|
1060 |
-
$.post( ajaxurl, data, function( r ) {
|
1061 |
-
|
1062 |
-
if ( r.success ) {
|
1063 |
-
|
1064 |
-
item_el.fadeOut( 400, function() { $(this).remove(); });
|
1065 |
-
|
1066 |
-
if ( typeof r.data !== 'undefined' && typeof r.data.field !== 'undefined' ) {
|
1067 |
-
|
1068 |
-
if ( !cffrtb_editor.list.disabled_el.find( '.fields' ).length ) {
|
1069 |
-
cffrtb_editor.list.disabled_el.find( '.no-disabled-fields' ).remove();
|
1070 |
-
cffrtb_editor.list.disabled_el.find( '.reset' ).before( '<ul class="fields"></ul>' );
|
1071 |
-
}
|
1072 |
-
|
1073 |
-
cffrtb_editor.list.disabled_el.find( '.fields' ).append( r.data.field );
|
1074 |
-
|
1075 |
-
cffrtb_editor.list.disabled_el.find( '.reset' ).addClass( 'is-visible' );
|
1076 |
-
}
|
1077 |
-
|
1078 |
-
} else {
|
1079 |
-
|
1080 |
-
if ( typeof r.data === 'undefined' || typeof r.data.error === 'undefined' ) {
|
1081 |
-
cffrtb_editor.show_error( cffrtb_editor.strings.unknown_error );
|
1082 |
-
} else {
|
1083 |
-
console.log( r.data );
|
1084 |
-
cffrtb_editor.show_error( r.data.msg );
|
1085 |
-
}
|
1086 |
-
|
1087 |
-
item_el.find( '> .title .view .controls .load-spinner' ).fadeOut( 400, function() { $(this).remove(); });
|
1088 |
-
}
|
1089 |
-
|
1090 |
-
// Reset status
|
1091 |
-
cffrtb_editor.el.removeClass( 'deleting' );
|
1092 |
-
cffrtb_editor.list.enable_sorting();
|
1093 |
-
});
|
1094 |
-
},
|
1095 |
-
|
1096 |
-
/**
|
1097 |
-
* Enable a field that has been disabled
|
1098 |
-
*/
|
1099 |
-
enable_item: function( field ) {
|
1100 |
-
|
1101 |
-
if ( field.hasClass( 'enabling' ) ) {
|
1102 |
-
return;
|
1103 |
-
}
|
1104 |
-
|
1105 |
-
// Indicate status
|
1106 |
-
field.addClass( 'enabling' );
|
1107 |
-
field.find( '> .title .view .controls' ).prepend( '<span class="load-spinner"></span>' );
|
1108 |
-
|
1109 |
-
var params = {};
|
1110 |
-
|
1111 |
-
params.action = 'cffrtb-enable-field';
|
1112 |
-
params.nonce = cffrtb_editor.ajax_nonce;
|
1113 |
-
|
1114 |
-
params.slug = field.data( 'slug' );
|
1115 |
-
|
1116 |
-
if ( field.hasClass( 'fieldset' ) ) {
|
1117 |
-
params.type = 'fieldset';
|
1118 |
-
} else {
|
1119 |
-
params.type = 'field';
|
1120 |
-
}
|
1121 |
-
|
1122 |
-
var data = $.param( params );
|
1123 |
-
|
1124 |
-
$.post( ajaxurl, data, function( r ) {
|
1125 |
-
|
1126 |
-
if ( r.success ) {
|
1127 |
-
cffrtb_editor.list.add_item( r.data.field, 0, true );
|
1128 |
-
field.fadeOut( 400, function() { $(this).remove(); });
|
1129 |
-
|
1130 |
-
} else {
|
1131 |
-
|
1132 |
-
if ( typeof r.data === 'undefined' || typeof r.data.error === 'undefined' ) {
|
1133 |
-
cffrtb_editor.show_error( cffrtb_editor.strings.unknown_error );
|
1134 |
-
} else {
|
1135 |
-
console.log( r.data );
|
1136 |
-
cffrtb_editor.show_error( r.data.msg );
|
1137 |
-
}
|
1138 |
-
|
1139 |
-
field.find( '> .title .view .controls .load-spinner' ).fadeOut( 400, function() { $(this).remove(); });
|
1140 |
-
field.removeClass( 'enabling' );
|
1141 |
-
}
|
1142 |
-
|
1143 |
-
cffrtb_editor.list.enable_sorting();
|
1144 |
-
});
|
1145 |
-
},
|
1146 |
-
|
1147 |
-
/**
|
1148 |
-
* Reset all modifications and custom fields created by the
|
1149 |
-
* plugin
|
1150 |
-
*/
|
1151 |
-
reset_all: function() {
|
1152 |
-
|
1153 |
-
this.disabled_el.find( '.reset-all' ).attr( 'disabled', 'disabled' );
|
1154 |
-
|
1155 |
-
if ( !window.confirm( cffrtb_editor.strings.confirm_reset_all ) ) {
|
1156 |
-
this.disabled_el.find( '.reset-all' ).removeAttr( 'disabled' );
|
1157 |
-
return;
|
1158 |
-
}
|
1159 |
-
|
1160 |
-
var params = {};
|
1161 |
-
|
1162 |
-
params.action = 'cffrtb-reset-all';
|
1163 |
-
params.nonce = cffrtb_editor.ajax_nonce;
|
1164 |
-
|
1165 |
-
var data = $.param( params );
|
1166 |
-
|
1167 |
-
$.post( ajaxurl, data, function( r ) {
|
1168 |
-
|
1169 |
-
if ( r.success ) {
|
1170 |
-
|
1171 |
-
// Refresh the page so that the new details are visible
|
1172 |
-
window.location.reload();
|
1173 |
-
|
1174 |
-
} else {
|
1175 |
-
|
1176 |
-
if ( typeof r.data === 'undefined' || typeof r.data.error === 'undefined' ) {
|
1177 |
-
cffrtb_editor.show_error( cffrtb_editor.strings.unknown_error );
|
1178 |
-
} else {
|
1179 |
-
console.log( r.data );
|
1180 |
-
cffrtb_editor.show_error( r.data.msg );
|
1181 |
-
}
|
1182 |
-
|
1183 |
-
cffrtb_editor.list.disabled_el.find( '.reset-all' ).removeAttr( 'disabled' );
|
1184 |
-
}
|
1185 |
-
});
|
1186 |
-
}
|
1187 |
-
};
|
1188 |
-
|
1189 |
-
/**
|
1190 |
-
* Initialize the list
|
1191 |
-
*/
|
1192 |
-
cffrtb_editor.list.init();
|
1193 |
-
|
1194 |
-
});
|
1195 |
-
|
1196 |
-
/**
|
1197 |
-
* Display the pointer admin help tips
|
1198 |
-
*/
|
1199 |
-
jQuery(document).ready(function ($) {
|
1200 |
-
|
1201 |
-
if ( typeof cffrtb_editor.pointers == 'undefined' || !cffrtb_editor.pointers.length ) {
|
1202 |
-
return;
|
1203 |
-
}
|
1204 |
-
|
1205 |
-
function cffrtb_pointer_show( pointers ) {
|
1206 |
-
|
1207 |
-
var pointer = pointers.splice( 0, 1 )[0];
|
1208 |
-
var options = $.extend( pointer.options, {
|
1209 |
-
close: function() {
|
1210 |
-
$.post( ajaxurl, {
|
1211 |
-
pointer: pointer.id,
|
1212 |
-
action: 'dismiss-wp-pointer'
|
1213 |
-
}, function() {
|
1214 |
-
if ( pointers.length ) {
|
1215 |
-
cffrtb_pointer_show( pointers );
|
1216 |
-
}
|
1217 |
-
});
|
1218 |
-
}
|
1219 |
-
});
|
1220 |
-
|
1221 |
-
var target = $( pointer.target );
|
1222 |
-
|
1223 |
-
if ( !target ) {
|
1224 |
-
return;
|
1225 |
-
}
|
1226 |
-
|
1227 |
-
target.first().pointer( options ).pointer( 'open' );
|
1228 |
-
|
1229 |
-
$( 'html, body' ).animate({
|
1230 |
-
scrollTop: target.offset().top - 100
|
1231 |
-
}, 500);
|
1232 |
-
}
|
1233 |
-
|
1234 |
-
cffrtb_pointer_show( cffrtb_editor.pointers );
|
1235 |
-
|
1236 |
-
});
|
1 |
+
/**
|
2 |
+
* Custom fields editor for Custom Fields for Restaurant Reservations
|
3 |
+
*/
|
4 |
+
var cffrtb_editor = cffrtb_editor || {};
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Initialize the editor object after jQuery has loaded
|
8 |
+
*/
|
9 |
+
jQuery(document).ready(function ($) {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* jQuery reference for editor panel
|
13 |
+
*/
|
14 |
+
cffrtb_editor.el = $( '#cffrtb-editor' );
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Show the error modal
|
18 |
+
*/
|
19 |
+
cffrtb_editor.show_error = function( msg ) {
|
20 |
+
|
21 |
+
var rtb_error_modal = $( '#rtb-error-modal ' );
|
22 |
+
|
23 |
+
rtb_error_modal.find( '.rtb-error-msg' ).html( msg );
|
24 |
+
rtb_error_modal.addClass( 'is-visible' );
|
25 |
+
|
26 |
+
$(document).keyup( function(e) {
|
27 |
+
if ( e.which == '27' ) {
|
28 |
+
cffrtb_editor.hide_error( rtb_error_modal );
|
29 |
+
}
|
30 |
+
});
|
31 |
+
|
32 |
+
rtb_error_modal.click( function(e) {
|
33 |
+
if ( $(e.target).is( rtb_error_modal ) || $(e.target).is( rtb_error_modal.find( 'a.button' ) ) ) {
|
34 |
+
|
35 |
+
e.stopPropagation();
|
36 |
+
e.preventDefault();
|
37 |
+
|
38 |
+
cffrtb_editor.hide_error( rtb_error_modal );
|
39 |
+
}
|
40 |
+
});
|
41 |
+
};
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Hide the error modal
|
45 |
+
*/
|
46 |
+
cffrtb_editor.hide_error = function( el ) {
|
47 |
+
el.removeClass( 'is-visible' );
|
48 |
+
el.off();
|
49 |
+
};
|
50 |
+
|
51 |
+
});
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Initialize the field editor after jQuery has loaded
|
55 |
+
*/
|
56 |
+
jQuery(document).ready(function ($) {
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Manage the field editor
|
60 |
+
*/
|
61 |
+
cffrtb_editor.editor = {
|
62 |
+
|
63 |
+
el: $( '#cffrtb-field-editor' ),
|
64 |
+
|
65 |
+
option_el: $( '#cffrtb-field-editor-option' ),
|
66 |
+
|
67 |
+
init: function() {
|
68 |
+
|
69 |
+
// Store common form references
|
70 |
+
this.form = {
|
71 |
+
el: this.el.find( '#cffrtb-field-editor-form' ),
|
72 |
+
id: this.el.find( 'input[name="id"]' ),
|
73 |
+
type: this.el.find( 'input[name="type"]' ),
|
74 |
+
subtype: this.el.find( 'input[name="subtype"]' ),
|
75 |
+
};
|
76 |
+
|
77 |
+
// Show field editor option modal
|
78 |
+
$( '.add-field' ).click( function(e) {
|
79 |
+
e.stopPropagation();
|
80 |
+
e.preventDefault();
|
81 |
+
|
82 |
+
cffrtb_editor.editor.show_option();
|
83 |
+
});
|
84 |
+
|
85 |
+
// Register click events on options modal
|
86 |
+
this.option_el.click( function(e) {
|
87 |
+
e.stopPropagation();
|
88 |
+
e.preventDefault();
|
89 |
+
|
90 |
+
var target = $( e.target );
|
91 |
+
|
92 |
+
if ( target.hasClass( 'field' ) ) {
|
93 |
+
cffrtb_editor.editor.hide_option();
|
94 |
+
cffrtb_editor.editor.show_editor();
|
95 |
+
|
96 |
+
} else if ( target.hasClass( 'fieldset' ) ) {
|
97 |
+
cffrtb_editor.editor.hide_option();
|
98 |
+
cffrtb_editor.editor.show_editor( 'fieldset' );
|
99 |
+
|
100 |
+
} else if ( target.is( cffrtb_editor.editor.option_el ) ) {
|
101 |
+
cffrtb_editor.editor.hide_option();
|
102 |
+
}
|
103 |
+
});
|
104 |
+
|
105 |
+
// Close field editor modal when background is clicked
|
106 |
+
this.el.click( function(e) {
|
107 |
+
if ( $( e.target ).is( cffrtb_editor.editor.el ) ) {
|
108 |
+
cffrtb_editor.editor.hide_editor();
|
109 |
+
}
|
110 |
+
});
|
111 |
+
|
112 |
+
// Close field editor modal when ESC is keyed
|
113 |
+
$(document).keyup( function(e) {
|
114 |
+
if ( e.which == '27' ) {
|
115 |
+
cffrtb_editor.editor.hide_editor();
|
116 |
+
}
|
117 |
+
});
|
118 |
+
|
119 |
+
// Form actions
|
120 |
+
this.form.el.find( '> .actions' ).on( 'click', function(e) {
|
121 |
+
|
122 |
+
e.stopPropagation();
|
123 |
+
e.preventDefault();
|
124 |
+
|
125 |
+
var target = $( e.target );
|
126 |
+
|
127 |
+
// Exit early if the actions are disabled
|
128 |
+
if ( typeof target.attr( 'disabled' ) !== 'undefined' ) {
|
129 |
+
return;
|
130 |
+
}
|
131 |
+
|
132 |
+
// Save field
|
133 |
+
if ( target.hasClass( 'save' ) ) {
|
134 |
+
cffrtb_editor.editor.save_field();
|
135 |
+
|
136 |
+
// Cancel and close editor
|
137 |
+
} else if ( target.hasClass( 'cancel' ) ) {
|
138 |
+
cffrtb_editor.editor.hide_editor();
|
139 |
+
}
|
140 |
+
});
|
141 |
+
|
142 |
+
// Field type selections
|
143 |
+
this.form.el.find( '.type .selector' ).on( 'click', function(e) {
|
144 |
+
|
145 |
+
var target = $( e.target );
|
146 |
+
|
147 |
+
if ( target.get(0).tagName != 'A' ) {
|
148 |
+
return;
|
149 |
+
}
|
150 |
+
|
151 |
+
var level = target.parent().parent();
|
152 |
+
|
153 |
+
if ( target.parent().parent().hasClass( 'types' ) ) {
|
154 |
+
cffrtb_editor.editor.set_type( target.data( 'type' ), target );
|
155 |
+
|
156 |
+
} else {
|
157 |
+
cffrtb_editor.editor.set_subtype( target.data( 'subtype' ), target );
|
158 |
+
}
|
159 |
+
|
160 |
+
});
|
161 |
+
|
162 |
+
// Add an option
|
163 |
+
this.el.find( '.settings-panel.options .add a' ).on( 'click', function(e) {
|
164 |
+
e.stopPropagation();
|
165 |
+
e.preventDefault();
|
166 |
+
cffrtb_editor.editor.add_option();
|
167 |
+
});
|
168 |
+
|
169 |
+
// Add an option with ENTER key
|
170 |
+
this.get_add_option_el().keyup( function(e) {
|
171 |
+
if ( e.which == '13' ) {
|
172 |
+
e.stopPropagation();
|
173 |
+
e.preventDefault();
|
174 |
+
|
175 |
+
cffrtb_editor.editor.add_option();
|
176 |
+
}
|
177 |
+
});
|
178 |
+
|
179 |
+
// Remove an option
|
180 |
+
this.get_options_list_el().on( 'click', function(e) {
|
181 |
+
e.stopPropagation();
|
182 |
+
e.preventDefault();
|
183 |
+
|
184 |
+
var target = $( e.target );
|
185 |
+
|
186 |
+
if( target.is( 'a, a .dashicons' ) ) {
|
187 |
+
cffrtb_editor.editor.remove_option( target.closest( 'li' ) );
|
188 |
+
}
|
189 |
+
});
|
190 |
+
|
191 |
+
// Make the option list sortable
|
192 |
+
this.get_options_list_el().sortable({
|
193 |
+
placeholder: 'cffrtb-editor-options-placeholder',
|
194 |
+
delay: 250
|
195 |
+
});
|
196 |
+
|
197 |
+
},
|
198 |
+
|
199 |
+
/**
|
200 |
+
* Get the add option input element
|
201 |
+
*/
|
202 |
+
get_add_option_el: function() {
|
203 |
+
|
204 |
+
if ( typeof this.form.add_option == 'undefined' ) {
|
205 |
+
this.form.add_option = this.el.find( '.settings-panel.options .add input' );
|
206 |
+
}
|
207 |
+
|
208 |
+
return this.form.add_option;
|
209 |
+
},
|
210 |
+
|
211 |
+
/**
|
212 |
+
* Get the options list element
|
213 |
+
*/
|
214 |
+
get_options_list_el: function() {
|
215 |
+
|
216 |
+
if ( typeof this.form.options_list == 'undefined' ) {
|
217 |
+
this.form.options_list = this.el.find( '.settings-panel.options .options' );
|
218 |
+
}
|
219 |
+
|
220 |
+
return this.form.options_list;
|
221 |
+
},
|
222 |
+
|
223 |
+
/**
|
224 |
+
* Update the editor values with a new field object
|
225 |
+
*/
|
226 |
+
update_editor_values: function( field ) {
|
227 |
+
|
228 |
+
this.form.id.val( field.ID );
|
229 |
+
this.set_type( field.type );
|
230 |
+
this.set_subtype( field.subtype );
|
231 |
+
this.form.el.find( 'input[name="title"]' ).val( field.title );
|
232 |
+
|
233 |
+
if ( field.required ) {
|
234 |
+
this.form.el.find( '.required input' ).attr( 'checked', 'checked' );
|
235 |
+
}
|
236 |
+
|
237 |
+
if ( field.options && Object.keys( field.options ).length ) {
|
238 |
+
var options = '';
|
239 |
+
for( var i in field.options ) {
|
240 |
+
if ( field.options[i].disabled ) {
|
241 |
+
continue;
|
242 |
+
}
|
243 |
+
options += '<li data-id="' + field.options[i].id + '"><a href="#"><span class="dashicons dashicons-dismiss"></span></a> <span class="value">' + field.options[i].value + '</span></li>';
|
244 |
+
}
|
245 |
+
this.form.el.find( '.settings-panel.options .options' ).html( options );
|
246 |
+
}
|
247 |
+
|
248 |
+
this.el.trigger( 'cffrtb_update_editor_values', field );
|
249 |
+
},
|
250 |
+
|
251 |
+
/**
|
252 |
+
* Show the field/fieldset type selection before opening the editor
|
253 |
+
*/
|
254 |
+
show_option: function() {
|
255 |
+
|
256 |
+
this.option_el.addClass( 'is-visible' );
|
257 |
+
$( 'body' ).addClass( 'rtb-hide-body-scroll' );
|
258 |
+
|
259 |
+
},
|
260 |
+
|
261 |
+
/**
|
262 |
+
* Hide the field/fieldset type selection
|
263 |
+
*/
|
264 |
+
hide_option: function() {
|
265 |
+
|
266 |
+
this.option_el.removeClass( 'is-visible' );
|
267 |
+
$( 'body' ).removeClass( 'rtb-hide-body-scroll' );
|
268 |
+
|
269 |
+
},
|
270 |
+
|
271 |
+
/**
|
272 |
+
* Show the editor
|
273 |
+
*/
|
274 |
+
show_editor: function( mode ) {
|
275 |
+
|
276 |
+
if ( mode === 'edit' ) {
|
277 |
+
this.form.el.find( '> .title > h2' ).html( cffrtb_editor.strings.editor_edit_field );
|
278 |
+
this.form.el.find( '.actions a.save' ).html( cffrtb_editor.strings.editor_save_field );
|
279 |
+
} else if ( mode == 'fieldset' ) {
|
280 |
+
this.form.el.find( '> .title > h2' ).html( cffrtb_editor.strings.editor_add_fieldset );
|
281 |
+
this.form.el.find( '.actions a.save' ).addClass( 'fieldset' ).html( cffrtb_editor.strings.editor_save_fieldset );
|
282 |
+
this.form.el.addClass( mode );
|
283 |
+
this.form.type.val( 'fieldset' );
|
284 |
+
this.form.subtype.val( 'fieldset' );
|
285 |
+
} else {
|
286 |
+
this.form.el.find( '> .title > h2' ).html( cffrtb_editor.strings.editor_add_field );
|
287 |
+
this.form.el.find( '.actions a.save' ).html( cffrtb_editor.strings.editor_add_field );
|
288 |
+
}
|
289 |
+
|
290 |
+
this.el.addClass( 'is-visible' );
|
291 |
+
$( 'body' ).addClass( 'rtb-hide-body-scroll' );
|
292 |
+
},
|
293 |
+
|
294 |
+
/**
|
295 |
+
* Hide the editor
|
296 |
+
*/
|
297 |
+
hide_editor: function() {
|
298 |
+
this.el.removeClass( 'is-visible' );
|
299 |
+
this.option_el.removeClass( 'is-hidden' );
|
300 |
+
this.form.el.addClass( 'is-hidden' );
|
301 |
+
this.form.el.removeClass( 'fieldset' );
|
302 |
+
this.form.el.find( '.actions a.save' ).removeClass( 'fieldset' );
|
303 |
+
$( 'body' ).removeClass( 'rtb-hide-body-scroll' );
|
304 |
+
this.form.el.find( 'input, select, textarea' ).not( 'input[type="checkbox"]' ).val( '' );
|
305 |
+
this.form.el.find( 'input[type="checkbox"]' ).removeAttr( 'checked' );
|
306 |
+
this.get_options_list_el().empty();
|
307 |
+
this.set_type( cffrtb_editor.default_type );
|
308 |
+
this.set_subtype( cffrtb_editor.default_subtype );
|
309 |
+
},
|
310 |
+
|
311 |
+
/**
|
312 |
+
* Set the type and select the new subtype
|
313 |
+
*/
|
314 |
+
set_type: function( type, el ) {
|
315 |
+
|
316 |
+
if ( type == this.form.type.val() ) {
|
317 |
+
return;
|
318 |
+
}
|
319 |
+
|
320 |
+
// Set the value
|
321 |
+
this.form.type.val( type );
|
322 |
+
|
323 |
+
// Remove the `current` class from the selection
|
324 |
+
this.el.find( '.type .types a' ).removeClass( 'current' );
|
325 |
+
|
326 |
+
// Add the `current` class to this selection
|
327 |
+
if ( typeof el == 'undefined' ) {
|
328 |
+
el = this.el.find( '.type .types .' + type );
|
329 |
+
}
|
330 |
+
el.addClass( 'current' );
|
331 |
+
|
332 |
+
// Show the settings if they exist
|
333 |
+
this.el.find( '.settings-panel' ).each( function() {
|
334 |
+
if ( $(this).hasClass( type ) ) {
|
335 |
+
$(this).addClass( 'current' );
|
336 |
+
} else {
|
337 |
+
$(this).removeClass( 'current' );
|
338 |
+
}
|
339 |
+
});
|
340 |
+
|
341 |
+
// Show the subtype list
|
342 |
+
this.el.find( '.type .subtypes' ).each( function() {
|
343 |
+
if ( $(this).hasClass( type ) ) {
|
344 |
+
$(this).addClass( 'current' );
|
345 |
+
} else {
|
346 |
+
$(this).removeClass( 'current' );
|
347 |
+
}
|
348 |
+
});
|
349 |
+
|
350 |
+
// Trigger a click on the first subtype for this type
|
351 |
+
this.el.find( '.subtypes.' + type + ' li' ).first().find( 'a' ).trigger( 'click' );
|
352 |
+
},
|
353 |
+
|
354 |
+
/**
|
355 |
+
* Select a subtype
|
356 |
+
*/
|
357 |
+
set_subtype: function( subtype, el ) {
|
358 |
+
|
359 |
+
if ( subtype == this.form.subtype.val() ) {
|
360 |
+
return;
|
361 |
+
}
|
362 |
+
|
363 |
+
// Set the value
|
364 |
+
this.form.subtype.val( subtype );
|
365 |
+
|
366 |
+
// Remove the `current` class from the selection
|
367 |
+
this.el.find( '.type .subtypes a' ).removeClass( 'current' );
|
368 |
+
|
369 |
+
// Apply the `current` class to the right subtype
|
370 |
+
if ( typeof el == 'undefined' ) {
|
371 |
+
el = this.el.find( '.type .subtypes .' + subtype );
|
372 |
+
}
|
373 |
+
el.addClass( 'current' );
|
374 |
+
|
375 |
+
},
|
376 |
+
|
377 |
+
/**
|
378 |
+
* Add an option to the list
|
379 |
+
*/
|
380 |
+
add_option: function() {
|
381 |
+
var list = this.get_options_list_el();
|
382 |
+
var option = this.get_add_option_el();
|
383 |
+
|
384 |
+
this.get_options_list_el().append( '<li><a href="#"><span class="dashicons dashicons-dismiss"></span></a> <span class="value">' + this.get_add_option_el().val() + '</span></li>' );
|
385 |
+
this.get_add_option_el().val( '' );
|
386 |
+
|
387 |
+
// Scroll the options list if they have more than 10
|
388 |
+
if ( this.get_options_list_el().find( 'li' ).length > 10 ) {
|
389 |
+
this.get_options_list_el().addClass( 'scroll' );
|
390 |
+
}
|
391 |
+
},
|
392 |
+
|
393 |
+
/**
|
394 |
+
* Remove an option from the list
|
395 |
+
*/
|
396 |
+
remove_option: function( el ) {
|
397 |
+
|
398 |
+
el.fadeOut( '200', function() {
|
399 |
+
$(this).remove();
|
400 |
+
|
401 |
+
// Remove scrollbar if the options list is less than 10 options
|
402 |
+
if ( cffrtb_editor.editor.get_options_list_el().find( 'li' ).length <= 10 ) {
|
403 |
+
cffrtb_editor.editor.get_options_list_el().removeClass( 'scroll' );
|
404 |
+
}
|
405 |
+
});
|
406 |
+
},
|
407 |
+
|
408 |
+
/**
|
409 |
+
* Disable actions
|
410 |
+
*/
|
411 |
+
disable_actions: function() {
|
412 |
+
this.form.el.find( '.actions' ).addClass( 'working' ).find( '.save, .cancel' ).attr( 'disabled', 'disabled' );
|
413 |
+
},
|
414 |
+
|
415 |
+
/**
|
416 |
+
* Enable actions
|
417 |
+
*/
|
418 |
+
enable_actions: function() {
|
419 |
+
this.form.el.find( '.actions' ).removeClass( 'working' ).find( '.save, .cancel' ).removeAttr( 'disabled' );
|
420 |
+
},
|
421 |
+
|
422 |
+
/**
|
423 |
+
* Load field
|
424 |
+
*/
|
425 |
+
load_field: function( item_slug ) {
|
426 |
+
|
427 |
+
// Don't trigger if we're already saving
|
428 |
+
if ( cffrtb_editor.list.get_title_el( item_slug ).hasClass( 'saving' ) ) {
|
429 |
+
return;
|
430 |
+
}
|
431 |
+
|
432 |
+
var id = cffrtb_editor.list.items[item_slug].el.data( 'id' );
|
433 |
+
if ( typeof id == 'undefined' ) {
|
434 |
+
return;
|
435 |
+
}
|
436 |
+
|
437 |
+
cffrtb_editor.list.disable_sorting();
|
438 |
+
cffrtb_editor.list.get_title_el( item_slug ).addClass( 'saving' );
|
439 |
+
cffrtb_editor.list.get_title_el( item_slug ).find( '.view .controls' ).prepend( '<span class="load-spinner"></span>' );
|
440 |
+
|
441 |
+
var params = {};
|
442 |
+
|
443 |
+
params.action = 'cffrtb-load-field';
|
444 |
+
params.nonce = cffrtb_editor.ajax_nonce;
|
445 |
+
params.ID = id;
|
446 |
+
|
447 |
+
var data = $.param( params );
|
448 |
+
|
449 |
+
$.post( ajaxurl, data, function( r ) {
|
450 |
+
|
451 |
+
if ( r.success ) {
|
452 |
+
cffrtb_editor.editor.update_editor_values( r.data.field );
|
453 |
+
cffrtb_editor.editor.show_editor( 'edit' );
|
454 |
+
|
455 |
+
} else {
|
456 |
+
if ( typeof r.data === 'undefined' || typeof r.data.error === 'undefined' ) {
|
457 |
+
cffrtb_editor.show_error( cffrtb_editor.strings.unknown_error );
|
458 |
+
} else {
|
459 |
+
cffrtb_editor.show_error( r.data.msg );
|
460 |
+
}
|
461 |
+
}
|
462 |
+
|
463 |
+
// Reset status
|
464 |
+
cffrtb_editor.list.enable_sorting();
|
465 |
+
cffrtb_editor.list.get_title_el( item_slug ).removeClass( 'saving' );
|
466 |
+
cffrtb_editor.list.get_title_el( item_slug ).find( '.view .controls .load-spinner' ).fadeOut( 400, function() { $(this).remove(); });
|
467 |
+
});
|
468 |
+
|
469 |
+
},
|
470 |
+
|
471 |
+
/**
|
472 |
+
* Save a field
|
473 |
+
*/
|
474 |
+
save_field: function() {
|
475 |
+
|
476 |
+
this.disable_actions();
|
477 |
+
|
478 |
+
var params = {};
|
479 |
+
|
480 |
+
params.action = 'cffrtb-save-field';
|
481 |
+
params.nonce = cffrtb_editor.ajax_nonce;
|
482 |
+
params.request = 'save_field';
|
483 |
+
params.field = {
|
484 |
+
ID: this.form.id.val(),
|
485 |
+
type: this.form.type.val(),
|
486 |
+
subtype: this.form.subtype.val(),
|
487 |
+
title: this.form.el.find( 'input[name="title"]' ).val(),
|
488 |
+
};
|
489 |
+
|
490 |
+
if ( this.form.el.find( 'input[name="required"]' ).is( ':checked' ) ) {
|
491 |
+
params.field.required = 1;
|
492 |
+
} else {
|
493 |
+
params.field.required = 0;
|
494 |
+
}
|
495 |
+
|
496 |
+
if ( params.field.type == 'options' ) {
|
497 |
+
params.field.options = {};
|
498 |
+
this.get_options_list_el().find( 'li' ).each( function(i) {
|
499 |
+
|
500 |
+
var id = $(this).data( 'id' );
|
501 |
+
if ( typeof id == 'undefined' ) {
|
502 |
+
id = 'new-' + Math.random().toString(36).substring(7);
|
503 |
+
}
|
504 |
+
|
505 |
+
params.field.options[i] = {
|
506 |
+
id: id,
|
507 |
+
value: $(this).find( '.value' ).html(),
|
508 |
+
order: i
|
509 |
+
};
|
510 |
+
});
|
511 |
+
}
|
512 |
+
|
513 |
+
// Perform some basic validation checks here
|
514 |
+
var errors = this.validate_field( params.field );
|
515 |
+
if ( typeof errors !== 'undefined' ) {
|
516 |
+
cffrtb_editor.show_error( errors );
|
517 |
+
this.enable_actions();
|
518 |
+
|
519 |
+
return;
|
520 |
+
}
|
521 |
+
|
522 |
+
var data = $.param( params );
|
523 |
+
|
524 |
+
$.post( ajaxurl, data, function( r ) {
|
525 |
+
|
526 |
+
if ( r.success ) {
|
527 |
+
cffrtb_editor.editor.hide_editor();
|
528 |
+
cffrtb_editor.list.add_item( r.data.field, r.data.ID, r.data.is_new_field, r.data.type );
|
529 |
+
|
530 |
+
} else {
|
531 |
+
if ( typeof r.data === 'undefined' || typeof r.data.error === 'undefined' ) {
|
532 |
+
cffrtb_editor.show_error( cffrtb_editor.strings.unknown_error );
|
533 |
+
} else {
|
534 |
+
console.log( r.data );
|
535 |
+
cffrtb_editor.show_error( r.data.msg );
|
536 |
+
}
|
537 |
+
}
|
538 |
+
|
539 |
+
cffrtb_editor.editor.enable_actions();
|
540 |
+
});
|
541 |
+
},
|
542 |
+
|
543 |
+
/**
|
544 |
+
* Some quick validation on the field data before sending it off
|
545 |
+
*/
|
546 |
+
validate_field: function( field ) {
|
547 |
+
|
548 |
+
if ( field.title.length === 0 || !field.title.trim() ) {
|
549 |
+
return cffrtb_editor.strings.field_missing_title;
|
550 |
+
}
|
551 |
+
|
552 |
+
if ( field.type == 'options' && field.options.length === 0 ) {
|
553 |
+
return cffrtb_editor.strings.field_missing_options;
|
554 |
+
}
|
555 |
+
}
|
556 |
+
};
|
557 |
+
|
558 |
+
/**
|
559 |
+
* Initialize the editor
|
560 |
+
*/
|
561 |
+
cffrtb_editor.editor.init();
|
562 |
+
|
563 |
+
});
|
564 |
+
|
565 |
+
/**
|
566 |
+
* Initialize the fields list object after jQuery has loaded
|
567 |
+
*/
|
568 |
+
jQuery(document).ready(function ($) {
|
569 |
+
|
570 |
+
/**
|
571 |
+
* Manage the list of fields
|
572 |
+
*/
|
573 |
+
cffrtb_editor.list = {
|
574 |
+
|
575 |
+
// list element
|
576 |
+
el: cffrtb_editor.el.find( '#cffrtb-list' ),
|
577 |
+
|
578 |
+
// fieldsets and fields.
|
579 |
+
// just storing jQuery references to prevent duplicate lookups
|
580 |
+
items: {},
|
581 |
+
|
582 |
+
// flag to disable events during view->edit transition
|
583 |
+
in_transition: false,
|
584 |
+
|
585 |
+
// disabled fields list
|
586 |
+
disabled_el: cffrtb_editor.el.find( '#cffrtb-disabled' ),
|
587 |
+
|
588 |
+
init: function() {
|
589 |
+
|
590 |
+
// Store jQuery references to prevent duplicate lookups
|
591 |
+
this.el.find( '.fieldset, .field' ).each( function() {
|
592 |
+
cffrtb_editor.list.items[ $(this).data( 'slug' ) ] = {
|
593 |
+
el: $(this)
|
594 |
+
};
|
595 |
+
});
|
596 |
+
|
597 |
+
// Clear pre-existing listeners
|
598 |
+
this.el.off( 'click keyup' );
|
599 |
+
|
600 |
+
// Register click listeners
|
601 |
+
this.el.on( 'click', function( e ) {
|
602 |
+
|
603 |
+
e.stopPropagation();
|
604 |
+
e.preventDefault();
|
605 |
+
|
606 |
+
var target = $( e.target );
|
607 |
+
|
608 |
+
// Any click outside one of the field titles
|
609 |
+
if ( !target.hasClass( 'title' ) && !target.parents().hasClass( 'title' ) ) {
|
610 |
+
cffrtb_editor.list.save_all();
|
611 |
+
return;
|
612 |
+
}
|
613 |
+
|
614 |
+
var item_slug = target.parents( '.fieldset, .field' ).first().data( 'slug' );
|
615 |
+
|
616 |
+
// Open options panel
|
617 |
+
if ( cffrtb_editor.list.is_target( target, 'options' ) ) {
|
618 |
+
cffrtb_editor.list.save_all();
|
619 |
+
cffrtb_editor.editor.load_field( item_slug );
|
620 |
+
|
621 |
+
// Delete field
|
622 |
+
} else if ( cffrtb_editor.list.is_target( target, 'delete' ) ) {
|
623 |
+
cffrtb_editor.list.save_all();
|
624 |
+
cffrtb_editor.list.delete_item( item_slug );
|
625 |
+
|
626 |
+
// Open label editing panel
|
627 |
+
} else if ( !cffrtb_editor.list.is_editing( item_slug ) ) {
|
628 |
+
cffrtb_editor.list.show_edit( item_slug );
|
629 |
+
|
630 |
+
// Save label
|
631 |
+
} else if ( cffrtb_editor.list.is_target( target, 'save' ) ) {
|
632 |
+
cffrtb_editor.list.save_label( item_slug );
|
633 |
+
|
634 |
+
// Give focus to input when editing panel is active
|
635 |
+
} else if ( target.hasClass( 'edit' ) ) {
|
636 |
+
cffrtb_editor.list.set_focus( item_slug );
|
637 |
+
}
|
638 |
+
|
639 |
+
});
|
640 |
+
|
641 |
+
// Save label with ENTER key
|
642 |
+
this.el.keyup( function(e) {
|
643 |
+
|
644 |
+
if ( !cffrtb_editor.list.in_transition && e.which == '13' ) {
|
645 |
+
|
646 |
+
var target = $( e.target );
|
647 |
+
|
648 |
+
if ( target.is( 'input:focus' ) ) {
|
649 |
+
|
650 |
+
e.stopPropagation();
|
651 |
+
e.preventDefault();
|
652 |
+
|
653 |
+
cffrtb_editor.list.save_label( target.parents( '.fieldset, .field' ).first().data( 'slug' ) );
|
654 |
+
}
|
655 |
+
}
|
656 |
+
});
|
657 |
+
|
658 |
+
// Make the list sortable
|
659 |
+
this.el.sortable({
|
660 |
+
placeholder: 'cffrtb-list-placeholder',
|
661 |
+
delay: 250,
|
662 |
+
update: this.sorting_complete
|
663 |
+
});
|
664 |
+
this.el.find( '.fieldset ul' ).sortable({
|
665 |
+
placeholder: 'cffrtb-list-placeholder',
|
666 |
+
connectWith: '#cffrtb-list .fieldset ul',
|
667 |
+
delay: 250,
|
668 |
+
update: this.sorting_complete
|
669 |
+
});
|
670 |
+
|
671 |
+
// Clear pre-existing listeners
|
672 |
+
this.disabled_el.off( 'click' );
|
673 |
+
|
674 |
+
// Register click events on disabled fields
|
675 |
+
this.disabled_el.on( 'click', function(e) {
|
676 |
+
|
677 |
+
e.stopPropagation();
|
678 |
+
e.preventDefault();
|
679 |
+
|
680 |
+
var target = $( e.target );
|
681 |
+
|
682 |
+
// Restore field
|
683 |
+
if ( cffrtb_editor.list.is_target( target, 'enable' ) ) {
|
684 |
+
cffrtb_editor.list.enable_item( target.parents( '.fieldset, .field' ).first() );
|
685 |
+
|
686 |
+
// Open learn more text
|
687 |
+
} else if ( cffrtb_editor.list.is_target( target, 'learn-more' ) ) {
|
688 |
+
cffrtb_editor.list.disabled_el.find( '.reset .description' ).addClass( 'is-visible' );
|
689 |
+
|
690 |
+
// Revert to default
|
691 |
+
} else if ( cffrtb_editor.list.is_target( target, 'reset-all' ) ) {
|
692 |
+
cffrtb_editor.list.reset_all();
|
693 |
+
}
|
694 |
+
});
|
695 |
+
|
696 |
+
},
|
697 |
+
|
698 |
+
/**
|
699 |
+
* Is the field being edited?
|
700 |
+
*/
|
701 |
+
is_editing: function( item_slug ) {
|
702 |
+
return this.get_title_el( item_slug ).hasClass( 'editing' );
|
703 |
+
},
|
704 |
+
|
705 |
+
/**
|
706 |
+
* Is the click target opening the field options?
|
707 |
+
*/
|
708 |
+
is_target: function( target, match ) {
|
709 |
+
return target.hasClass( match ) || target.parents().hasClass( match );
|
710 |
+
},
|
711 |
+
|
712 |
+
/**
|
713 |
+
* Get the title element for an item
|
714 |
+
*/
|
715 |
+
get_title_el: function( item_slug ) {
|
716 |
+
|
717 |
+
if ( typeof this.items[item_slug].title == 'undefined' ) {
|
718 |
+
this.items[item_slug].title = this.items[item_slug].el.find( '> .title' );
|
719 |
+
}
|
720 |
+
|
721 |
+
return this.items[item_slug].title;
|
722 |
+
},
|
723 |
+
|
724 |
+
/**
|
725 |
+
* Get the edit element for an item
|
726 |
+
*/
|
727 |
+
get_edit_el: function( item_slug ) {
|
728 |
+
|
729 |
+
if ( typeof this.items[item_slug].title == 'undefined' ) {
|
730 |
+
this.items[item_slug].edit = this.items[item_slug].el.find( '> .title .edit' );
|
731 |
+
}
|
732 |
+
|
733 |
+
return this.items[item_slug].edit;
|
734 |
+
},
|
735 |
+
|
736 |
+
/**
|
737 |
+
* Get the value of the input field
|
738 |
+
*/
|
739 |
+
get_input: function( item_slug ) {
|
740 |
+
return this.get_input_el( item_slug ).val();
|
741 |
+
},
|
742 |
+
|
743 |
+
/**
|
744 |
+
* Get the input element of a field
|
745 |
+
*/
|
746 |
+
get_input_el: function( item_slug ) {
|
747 |
+
|
748 |
+
if ( typeof this.items[item_slug].input == 'undefined' ) {
|
749 |
+
this.items[item_slug].input = this.items[item_slug].el.find( '> .title .edit input' );
|
750 |
+
}
|
751 |
+
|
752 |
+
return this.items[item_slug].input;
|
753 |
+
},
|
754 |
+
|
755 |
+
/**
|
756 |
+
* Get the fieldset slug an item is attached to
|
757 |
+
*/
|
758 |
+
get_fieldset: function( item_slug ) {
|
759 |
+
|
760 |
+
if ( this.items[item_slug].el.hasClass( 'fieldset' ) ) {
|
761 |
+
return item_slug;
|
762 |
+
} else {
|
763 |
+
return this.items[item_slug].el.parents( '.fieldset' ).first().data( 'slug' );
|
764 |
+
}
|
765 |
+
},
|
766 |
+
|
767 |
+
/**
|
768 |
+
* Update the value of the label in the view
|
769 |
+
*/
|
770 |
+
update_view: function( item_slug ) {
|
771 |
+
|
772 |
+
if ( typeof this.items[item_slug].view_value == 'undefined' ) {
|
773 |
+
this.items[item_slug].view_value = this.items[item_slug].el.find( '> .title .view .value' );
|
774 |
+
}
|
775 |
+
|
776 |
+
this.items[item_slug].view_value.html( this.get_input( item_slug ) );
|
777 |
+
},
|
778 |
+
|
779 |
+
/**
|
780 |
+
* Open an item's edit mode
|
781 |
+
*/
|
782 |
+
show_edit: function( item_slug ) {
|
783 |
+
|
784 |
+
// Save and close any other labels being edited
|
785 |
+
this.save_all();
|
786 |
+
|
787 |
+
// Set transition flag and timer
|
788 |
+
this.in_transition = true;
|
789 |
+
setTimeout( this.clear_transition_flag, 600 );
|
790 |
+
|
791 |
+
// Open edit mode for this item
|
792 |
+
this.enable_tabbing( item_slug );
|
793 |
+
this.get_title_el( item_slug ).addClass( 'editing' );
|
794 |
+
this.set_focus( item_slug );
|
795 |
+
},
|
796 |
+
|
797 |
+
/**
|
798 |
+
* Focus and select the input field
|
799 |
+
*/
|
800 |
+
set_focus: function( item_slug ) {
|
801 |
+
this.get_input_el( item_slug ).focus().select();
|
802 |
+
},
|
803 |
+
|
804 |
+
/**
|
805 |
+
* Clear the transition flag used when opening the editing panel
|
806 |
+
*/
|
807 |
+
clear_transition_flag: function() {
|
808 |
+
cffrtb_editor.list.in_transition = false;
|
809 |
+
},
|
810 |
+
|
811 |
+
/**
|
812 |
+
* Return an item to view mode
|
813 |
+
*/
|
814 |
+
show_view: function( item_slug ) {
|
815 |
+
this.get_title_el( item_slug ).removeClass( 'editing' );
|
816 |
+
this.disable_tabbing( item_slug );
|
817 |
+
this.set_focus( item_slug );
|
818 |
+
},
|
819 |
+
|
820 |
+
/**
|
821 |
+
* Disable tabbing through a hidden edit interface
|
822 |
+
*/
|
823 |
+
disable_tabbing: function( item_slug ) {
|
824 |
+
this.get_title_el( item_slug ).find( '> .edit input, > .edit .save' ).attr( 'tabindex', '-1' );
|
825 |
+
},
|
826 |
+
|
827 |
+
/**
|
828 |
+
* Enable tabbing through a hidden edit interface
|
829 |
+
*/
|
830 |
+
enable_tabbing: function( item_slug ) {
|
831 |
+
this.get_title_el( item_slug ).find( '> .edit input, > .edit .save' ).removeAttr( 'tabindex' );
|
832 |
+
},
|
833 |
+
|
834 |
+
/**
|
835 |
+
* Disable drag and drop sorting
|
836 |
+
*/
|
837 |
+
disable_sorting: function() {
|
838 |
+
this.el.sortable( 'option', 'disabled', true );
|
839 |
+
this.el.find( '.fieldset ul' ).sortable( 'option', 'disabled', true );
|
840 |
+
},
|
841 |
+
|
842 |
+
/**
|
843 |
+
* Enable drag and drop sorting
|
844 |
+
*/
|
845 |
+
enable_sorting: function() {
|
846 |
+
this.el.sortable( 'option', 'disabled', false );
|
847 |
+
this.el.find( '.fieldset ul' ).sortable( 'option', 'disabled', false );
|
848 |
+
},
|
849 |
+
|
850 |
+
/**
|
851 |
+
* Save and close any fields being edited
|
852 |
+
*/
|
853 |
+
save_all: function() {
|
854 |
+
this.el.find( '.title.editing' ).each( function() {
|
855 |
+
cffrtb_editor.list.save_label( $(this ).parent().data( 'slug' ) );
|
856 |
+
});
|
857 |
+
},
|
858 |
+
|
859 |
+
/**
|
860 |
+
* Save an item's label
|
861 |
+
*/
|
862 |
+
save_label: function( item_slug ) {
|
863 |
+
|
864 |
+
// Don't trigger if we're already saving
|
865 |
+
if ( this.get_title_el( item_slug ).hasClass( 'saving' ) ) {
|
866 |
+
return;
|
867 |
+
}
|
868 |
+
|
869 |
+
// Indicate status
|
870 |
+
this.get_title_el( item_slug ).addClass( 'saving' );
|
871 |
+
this.get_input_el( item_slug ).attr( 'disabled', 'disabled' );
|
872 |
+
|
873 |
+
var params = {};
|
874 |
+
|
875 |
+
params.action = 'cffrtb-save-field';
|
876 |
+
params.nonce = cffrtb_editor.ajax_nonce;
|
877 |
+
params.request = 'save_label';
|
878 |
+
params.field = {
|
879 |
+
slug: item_slug,
|
880 |
+
title: this.get_input( item_slug ),
|
881 |
+
fieldset: this.get_fieldset( item_slug )
|
882 |
+
};
|
883 |
+
|
884 |
+
if( this.items[item_slug].el.data( 'id' ) ) {
|
885 |
+
params.field.ID = this.items[item_slug].el.data( 'id' );
|
886 |
+
}
|
887 |
+
|
888 |
+
var data = $.param( params );
|
889 |
+
|
890 |
+
$.post( ajaxurl, data, function( r ) {
|
891 |
+
|
892 |
+
if ( r.success ) {
|
893 |
+
cffrtb_editor.list.update_view( item_slug );
|
894 |
+
cffrtb_editor.list.show_view( item_slug );
|
895 |
+
|
896 |
+
} else {
|
897 |
+
if ( typeof r.data === 'undefined' || typeof r.data.error === 'undefined' ) {
|
898 |
+
cffrtb_editor.show_error( cffrtb_editor.strings.unknown_error );
|
899 |
+
} else {
|
900 |
+
console.log( r.data );
|
901 |
+
cffrtb_editor.show_error( r.data.msg );
|
902 |
+
}
|
903 |
+
}
|
904 |
+
|
905 |
+
// Reset status
|
906 |
+
cffrtb_editor.list.get_title_el( item_slug ).removeClass( 'saving' );
|
907 |
+
cffrtb_editor.list.get_input_el( item_slug ).removeAttr( 'disabled' );
|
908 |
+
});
|
909 |
+
},
|
910 |
+
|
911 |
+
/**
|
912 |
+
* Sorting complete
|
913 |
+
*/
|
914 |
+
sorting_complete: function( event, ui ) {
|
915 |
+
cffrtb_editor.list.save_sort( $( ui.item.context ) );
|
916 |
+
},
|
917 |
+
|
918 |
+
/**
|
919 |
+
* Save the sort order after it's been changed
|
920 |
+
*/
|
921 |
+
save_sort: function( target ) {
|
922 |
+
|
923 |
+
if ( cffrtb_editor.el.hasClass( 'saving-order' ) ) {
|
924 |
+
return;
|
925 |
+
}
|
926 |
+
|
927 |
+
// Indicate status
|
928 |
+
cffrtb_editor.list.disable_sorting();
|
929 |
+
cffrtb_editor.el.addClass( 'saving-order' );
|
930 |
+
target.find( '> .title .view .controls' ).prepend( '<span class="load-spinner"></span>' );
|
931 |
+
|
932 |
+
var params = {};
|
933 |
+
|
934 |
+
params.action = 'cffrtb-save-order';
|
935 |
+
params.nonce = cffrtb_editor.ajax_nonce;
|
936 |
+
params.order = [];
|
937 |
+
|
938 |
+
var i = 0;
|
939 |
+
cffrtb_editor.list.el.find( '> li' ).each( function() {
|
940 |
+
params.order.push( cffrtb_editor.list.get_item_order_obj( $(this), i ) );
|
941 |
+
i++;
|
942 |
+
|
943 |
+
$(this).find( '> ul > li' ).each( function() {
|
944 |
+
params.order.push( cffrtb_editor.list.get_item_order_obj( $(this), i ) );
|
945 |
+
i++;
|
946 |
+
});
|
947 |
+
});
|
948 |
+
|
949 |
+
var data = $.param( params );
|
950 |
+
|
951 |
+
$.post( ajaxurl, data, function( r ) {
|
952 |
+
|
953 |
+
if ( r.success ) {
|
954 |
+
|
955 |
+
} else {
|
956 |
+
if ( typeof r.data === 'undefined' || typeof r.data.error === 'undefined' ) {
|
957 |
+
cffrtb_editor.show_error( cffrtb_editor.strings.unknown_error );
|
958 |
+
} else {
|
959 |
+
console.log( r.data );
|
960 |
+
cffrtb_editor.show_error( r.data.msg );
|
961 |
+
}
|
962 |
+
}
|
963 |
+
|
964 |
+
// Reset status
|
965 |
+
cffrtb_editor.el.removeClass( 'saving-order' );
|
966 |
+
target.find( '> .title .view .controls .load-spinner' ).fadeOut( 400, function() { $(this).remove(); });
|
967 |
+
cffrtb_editor.list.enable_sorting();
|
968 |
+
});
|
969 |
+
},
|
970 |
+
|
971 |
+
/**
|
972 |
+
* Get an object with an elements order and slug/id data
|
973 |
+
*/
|
974 |
+
get_item_order_obj: function( el, i ) {
|
975 |
+
|
976 |
+
var item = {};
|
977 |
+
|
978 |
+
if ( el.data( 'slug' ) ) {
|
979 |
+
item.slug = el.data( 'slug' );
|
980 |
+
}
|
981 |
+
|
982 |
+
if ( el.data( 'id' ) ) {
|
983 |
+
item.ID = el.data( 'id' );
|
984 |
+
}
|
985 |
+
|
986 |
+
item.fieldset = cffrtb_editor.list.get_fieldset( item.slug );
|
987 |
+
|
988 |
+
item.order = i;
|
989 |
+
|
990 |
+
return item;
|
991 |
+
},
|
992 |
+
|
993 |
+
/**
|
994 |
+
* Add an item
|
995 |
+
*
|
996 |
+
* Expects to receive an HTML string for the new <li> element
|
997 |
+
*/
|
998 |
+
add_item: function( html, ID, is_new_field, type ) {
|
999 |
+
|
1000 |
+
if ( is_new_field ) {
|
1001 |
+
if ( type == 'fieldset' ) {
|
1002 |
+
this.el.find( '> .fieldset' ).last().after( html );
|
1003 |
+
} else {
|
1004 |
+
this.el.find( '> .fieldset' ).last().find( '> .fields' ).append( html );
|
1005 |
+
}
|
1006 |
+
} else {
|
1007 |
+
this.el.find( '.fieldset, .field' ).each( function() {
|
1008 |
+
if ( $(this).data( 'id' ) == ID ) {
|
1009 |
+
$(this).html( html ).hide().fadeIn();
|
1010 |
+
}
|
1011 |
+
});
|
1012 |
+
}
|
1013 |
+
|
1014 |
+
this.init();
|
1015 |
+
this.save_sort( this.el.find( '.fieldset, .field' ).last() );
|
1016 |
+
},
|
1017 |
+
|
1018 |
+
/**
|
1019 |
+
* Delete or disable an item from the list of fields
|
1020 |
+
*/
|
1021 |
+
delete_item: function( item_slug ) {
|
1022 |
+
|
1023 |
+
if ( cffrtb_editor.el.hasClass( 'deleting' ) ) {
|
1024 |
+
return;
|
1025 |
+
}
|
1026 |
+
|
1027 |
+
var item_el = this.items[item_slug].el;
|
1028 |
+
var is_fieldset = item_el.hasClass( 'fieldset' );
|
1029 |
+
|
1030 |
+
// Indicate status
|
1031 |
+
cffrtb_editor.list.disable_sorting();
|
1032 |
+
cffrtb_editor.el.addClass( 'deleting' );
|
1033 |
+
item_el.find( '> .title .view .controls' ).prepend( '<span class="load-spinner"></span>' );
|
1034 |
+
|
1035 |
+
// Can't remove a fieldset with fields
|
1036 |
+
if ( is_fieldset && item_el.find( '.field' ).length ) {
|
1037 |
+
cffrtb_editor.show_error( cffrtb_editor.strings.fieldset_not_empty );
|
1038 |
+
item_el.find( '> .title .view .controls .load-spinner' ).fadeOut( 400, function() { $(this).remove(); });
|
1039 |
+
cffrtb_editor.el.removeClass( 'deleting' );
|
1040 |
+
cffrtb_editor.list.enable_sorting();
|
1041 |
+
return;
|
1042 |
+
}
|
1043 |
+
|
1044 |
+
var id = item_el.data( 'id' );
|
1045 |
+
|
1046 |
+
var params = {};
|
1047 |
+
|
1048 |
+
params.action = 'cffrtb-delete-field';
|
1049 |
+
params.nonce = cffrtb_editor.ajax_nonce;
|
1050 |
+
|
1051 |
+
if ( id ) {
|
1052 |
+
params.ID = id;
|
1053 |
+
} else {
|
1054 |
+
params.slug = item_slug;
|
1055 |
+
params.fieldset = this.get_fieldset( item_slug );
|
1056 |
+
}
|
1057 |
+
|
1058 |
+
var data = $.param( params );
|
1059 |
+
|
1060 |
+
$.post( ajaxurl, data, function( r ) {
|
1061 |
+
|
1062 |
+
if ( r.success ) {
|
1063 |
+
|
1064 |
+
item_el.fadeOut( 400, function() { $(this).remove(); });
|
1065 |
+
|
1066 |
+
if ( typeof r.data !== 'undefined' && typeof r.data.field !== 'undefined' ) {
|
1067 |
+
|
1068 |
+
if ( !cffrtb_editor.list.disabled_el.find( '.fields' ).length ) {
|
1069 |
+
cffrtb_editor.list.disabled_el.find( '.no-disabled-fields' ).remove();
|
1070 |
+
cffrtb_editor.list.disabled_el.find( '.reset' ).before( '<ul class="fields"></ul>' );
|
1071 |
+
}
|
1072 |
+
|
1073 |
+
cffrtb_editor.list.disabled_el.find( '.fields' ).append( r.data.field );
|
1074 |
+
|
1075 |
+
cffrtb_editor.list.disabled_el.find( '.reset' ).addClass( 'is-visible' );
|
1076 |
+
}
|
1077 |
+
|
1078 |
+
} else {
|
1079 |
+
|
1080 |
+
if ( typeof r.data === 'undefined' || typeof r.data.error === 'undefined' ) {
|
1081 |
+
cffrtb_editor.show_error( cffrtb_editor.strings.unknown_error );
|
1082 |
+
} else {
|
1083 |
+
console.log( r.data );
|
1084 |
+
cffrtb_editor.show_error( r.data.msg );
|
1085 |
+
}
|
1086 |
+
|
1087 |
+
item_el.find( '> .title .view .controls .load-spinner' ).fadeOut( 400, function() { $(this).remove(); });
|
1088 |
+
}
|
1089 |
+
|
1090 |
+
// Reset status
|
1091 |
+
cffrtb_editor.el.removeClass( 'deleting' );
|
1092 |
+
cffrtb_editor.list.enable_sorting();
|
1093 |
+
});
|
1094 |
+
},
|
1095 |
+
|
1096 |
+
/**
|
1097 |
+
* Enable a field that has been disabled
|
1098 |
+
*/
|
1099 |
+
enable_item: function( field ) {
|
1100 |
+
|
1101 |
+
if ( field.hasClass( 'enabling' ) ) {
|
1102 |
+
return;
|
1103 |
+
}
|
1104 |
+
|
1105 |
+
// Indicate status
|
1106 |
+
field.addClass( 'enabling' );
|
1107 |
+
field.find( '> .title .view .controls' ).prepend( '<span class="load-spinner"></span>' );
|
1108 |
+
|
1109 |
+
var params = {};
|
1110 |
+
|
1111 |
+
params.action = 'cffrtb-enable-field';
|
1112 |
+
params.nonce = cffrtb_editor.ajax_nonce;
|
1113 |
+
|
1114 |
+
params.slug = field.data( 'slug' );
|
1115 |
+
|
1116 |
+
if ( field.hasClass( 'fieldset' ) ) {
|
1117 |
+
params.type = 'fieldset';
|
1118 |
+
} else {
|
1119 |
+
params.type = 'field';
|
1120 |
+
}
|
1121 |
+
|
1122 |
+
var data = $.param( params );
|
1123 |
+
|
1124 |
+
$.post( ajaxurl, data, function( r ) {
|
1125 |
+
|
1126 |
+
if ( r.success ) {
|
1127 |
+
cffrtb_editor.list.add_item( r.data.field, 0, true );
|
1128 |
+
field.fadeOut( 400, function() { $(this).remove(); });
|
1129 |
+
|
1130 |
+
} else {
|
1131 |
+
|
1132 |
+
if ( typeof r.data === 'undefined' || typeof r.data.error === 'undefined' ) {
|
1133 |
+
cffrtb_editor.show_error( cffrtb_editor.strings.unknown_error );
|
1134 |
+
} else {
|
1135 |
+
console.log( r.data );
|
1136 |
+
cffrtb_editor.show_error( r.data.msg );
|
1137 |
+
}
|
1138 |
+
|
1139 |
+
field.find( '> .title .view .controls .load-spinner' ).fadeOut( 400, function() { $(this).remove(); });
|
1140 |
+
field.removeClass( 'enabling' );
|
1141 |
+
}
|
1142 |
+
|
1143 |
+
cffrtb_editor.list.enable_sorting();
|
1144 |
+
});
|
1145 |
+
},
|
1146 |
+
|
1147 |
+
/**
|
1148 |
+
* Reset all modifications and custom fields created by the
|
1149 |
+
* plugin
|
1150 |
+
*/
|
1151 |
+
reset_all: function() {
|
1152 |
+
|
1153 |
+
this.disabled_el.find( '.reset-all' ).attr( 'disabled', 'disabled' );
|
1154 |
+
|
1155 |
+
if ( !window.confirm( cffrtb_editor.strings.confirm_reset_all ) ) {
|
1156 |
+
this.disabled_el.find( '.reset-all' ).removeAttr( 'disabled' );
|
1157 |
+
return;
|
1158 |
+
}
|
1159 |
+
|
1160 |
+
var params = {};
|
1161 |
+
|
1162 |
+
params.action = 'cffrtb-reset-all';
|
1163 |
+
params.nonce = cffrtb_editor.ajax_nonce;
|
1164 |
+
|
1165 |
+
var data = $.param( params );
|
1166 |
+
|
1167 |
+
$.post( ajaxurl, data, function( r ) {
|
1168 |
+
|
1169 |
+
if ( r.success ) {
|
1170 |
+
|
1171 |
+
// Refresh the page so that the new details are visible
|
1172 |
+
window.location.reload();
|
1173 |
+
|
1174 |
+
} else {
|
1175 |
+
|
1176 |
+
if ( typeof r.data === 'undefined' || typeof r.data.error === 'undefined' ) {
|
1177 |
+
cffrtb_editor.show_error( cffrtb_editor.strings.unknown_error );
|
1178 |
+
} else {
|
1179 |
+
console.log( r.data );
|
1180 |
+
cffrtb_editor.show_error( r.data.msg );
|
1181 |
+
}
|
1182 |
+
|
1183 |
+
cffrtb_editor.list.disabled_el.find( '.reset-all' ).removeAttr( 'disabled' );
|
1184 |
+
}
|
1185 |
+
});
|
1186 |
+
}
|
1187 |
+
};
|
1188 |
+
|
1189 |
+
/**
|
1190 |
+
* Initialize the list
|
1191 |
+
*/
|
1192 |
+
cffrtb_editor.list.init();
|
1193 |
+
|
1194 |
+
});
|
1195 |
+
|
1196 |
+
/**
|
1197 |
+
* Display the pointer admin help tips
|
1198 |
+
*/
|
1199 |
+
jQuery(document).ready(function ($) {
|
1200 |
+
|
1201 |
+
if ( typeof cffrtb_editor.pointers == 'undefined' || !cffrtb_editor.pointers.length ) {
|
1202 |
+
return;
|
1203 |
+
}
|
1204 |
+
|
1205 |
+
function cffrtb_pointer_show( pointers ) {
|
1206 |
+
|
1207 |
+
var pointer = pointers.splice( 0, 1 )[0];
|
1208 |
+
var options = $.extend( pointer.options, {
|
1209 |
+
close: function() {
|
1210 |
+
$.post( ajaxurl, {
|
1211 |
+
pointer: pointer.id,
|
1212 |
+
action: 'dismiss-wp-pointer'
|
1213 |
+
}, function() {
|
1214 |
+
if ( pointers.length ) {
|
1215 |
+
cffrtb_pointer_show( pointers );
|
1216 |
+
}
|
1217 |
+
});
|
1218 |
+
}
|
1219 |
+
});
|
1220 |
+
|
1221 |
+
var target = $( pointer.target );
|
1222 |
+
|
1223 |
+
if ( !target ) {
|
1224 |
+
return;
|
1225 |
+
}
|
1226 |
+
|
1227 |
+
target.first().pointer( options ).pointer( 'open' );
|
1228 |
+
|
1229 |
+
$( 'html, body' ).animate({
|
1230 |
+
scrollTop: target.offset().top - 100
|
1231 |
+
}, 500);
|
1232 |
+
}
|
1233 |
+
|
1234 |
+
cffrtb_pointer_show( cffrtb_editor.pointers );
|
1235 |
+
|
1236 |
+
});
|
assets/js/helper-install-notice.js
CHANGED
@@ -1,11 +1,11 @@
|
|
1 |
-
jQuery( document ).ready( function( $ ) {
|
2 |
-
|
3 |
-
jQuery(document).on( 'click', '.rtb-helper-install-notice .notice-dismiss', function( event ) {
|
4 |
-
var data = jQuery.param({
|
5 |
-
action: 'rtb_hide_helper_notice',
|
6 |
-
nonce: rtb_helper_notice.nonce
|
7 |
-
});
|
8 |
-
|
9 |
-
jQuery.post( ajaxurl, data, function() {} );
|
10 |
-
});
|
11 |
});
|
1 |
+
jQuery( document ).ready( function( $ ) {
|
2 |
+
|
3 |
+
jQuery(document).on( 'click', '.rtb-helper-install-notice .notice-dismiss', function( event ) {
|
4 |
+
var data = jQuery.param({
|
5 |
+
action: 'rtb_hide_helper_notice',
|
6 |
+
nonce: rtb_helper_notice.nonce
|
7 |
+
});
|
8 |
+
|
9 |
+
jQuery.post( ajaxurl, data, function() {} );
|
10 |
+
});
|
11 |
});
|
assets/js/mailchimp-admin.js
CHANGED
@@ -1,2 +1,2 @@
|
|
1 |
-
/*! mailchimp-for-rtb 2018-10-05 */
|
2 |
jQuery(document).ready(function(a){function b(){var b=a(".mcf-sap_loading");b.animate({opacity:1},500,function(){});var d={action:"mcfrtb-get-lists",nonce:rtb_admin_mc.ajax_nonce},e=a.param(d);a.post(ajaxurl,e,function(d){if(d.success){"undefined"==typeof d.data&&(console.log("no data returned from ajax request"),console.log(d)),b.stop(),b.css("opacity","0");var e=a("#mcfrtb-merge-controls").data("input-name")+"[list]";a(".mcf-list-select").append('<select name="'+e+'" id="'+e+'"><option></option></select>');var f=a(".mcf-list-select select");for(var g in d.data.lists){var h=a("<option></option>"),i=d.data.lists[g];h.attr("value",i.id).text(i.name),f.append(h)}a(f).change(function(b){c(a("option:selected",this).val())}),null!==rtb_admin_mc.lists&&"undefined"!=typeof rtb_admin_mc.lists.list&&""!==rtb_admin_mc.lists.list&&f.find("option[value="+rtb_admin_mc.lists.list+"]").attr("selected","selected").trigger("change")}else"undefined"==typeof d.data||"undefined"==typeof d.data.msg?a(".mcf-list-select").html('<span class="error">'+rtb_admin_mc.strings.api_unknown_error+"</span>"):a(".mcf-list-select").html('<span class="error">'+d.data.msg+"</span>"),b.animate({opacity:0},500,function(){})})}function c(b){var c=a(".mcf-sap_loading"),e=a("#mcfrtb-merge-controls");if(c.animate({opacity:1},500,function(){}),e.empty(),e.data("list-id",b),e.removeClass("active"),""===b)return void c.animate({opacity:0},500,function(){});var f={list:b,action:"mcfrtb-load-merge-fields",nonce:rtb_admin_mc.ajax_nonce},g=a.param(f);a.post(ajaxurl,g,function(f){if(f.success){if("undefined"==typeof f.data&&(console.log("no data returned from ajax request"),console.log(f)),f.data.list_id!=e.data("list-id"))return;if(e.empty(),"undefined"!=typeof rtb_admin_mc.merge_fields){var g=e.data("input-name")+"[fields]",h=a("<select><option></option></select>");for(var i in f.data.merge_fields){var j=a("<option></option>"),k=f.data.merge_fields[i];j.attr("value",k.tag).text(k.name),h.append(j)}e.hide(),e.append("<table></table>");var l=e.find("table");l.append("<tr><th>"+rtb_admin_mc.strings.merge_booking_data+"</th><th>"+rtb_admin_mc.strings.merge_list_field+"</th></tr>"),l.append("<tr><td>"+rtb_admin_mc.strings.merge_email_label+'</td><td><span class="description">'+rtb_admin_mc.strings.merge_email_description+"</span></td></tr>");for(var m in rtb_admin_mc.merge_fields)l.append('<tr><td><label for="mcfrtb-'+m+'">'+rtb_admin_mc.merge_fields[m]+'</label></td><td><select name="'+g+"["+m+']" id="mcfrtb-'+m+'">'+h.html()+"</select></td></tr>"),null!==rtb_admin_mc.lists&&b===rtb_admin_mc.lists.list&&"undefined"!=typeof rtb_admin_mc.lists.fields[m]&&""!==rtb_admin_mc.lists.fields[m]&&l.find("#mcfrtb-"+m+" option[value="+rtb_admin_mc.lists.fields[m]+"]").attr("selected","selected");e.find("select").change(function(){d(a(this),a("option:selected",this).val(),e)}),e.append('<p class="description">'+rtb_admin_mc.strings.merge_description+"</p>"),e.addClass("active"),e.fadeIn()}}else"undefined"==typeof f.data||"undefined"==typeof f.data.msg?e.html('<p class="error">'+rtb_admin_mc.strings.api_unknown_error+"</p>"):e.html('<p class="error">'+f.data.msg+"</p>");c.animate({opacity:0},500,function(){})})}function d(b,c,d){if(""!==c){var e=0,f=[];if(a("select option:selected",d).each(function(){a(this).val()===c&&(f.push(a(this)),e++)}),e>1){b.find("option[value="+c+"]").prop("selected","");for(var g in f)f[g].parent().after('<span class="merge-duplicate-warning dashicons dashicons-lock"></span>');a(".merge-duplicate-warning").fadeOut(2e3,function(){a(this).remove()})}}}a(".mcf-list-select").length&&b()});
|
1 |
+
/*! mailchimp-for-rtb 2018-10-05 */
|
2 |
jQuery(document).ready(function(a){function b(){var b=a(".mcf-sap_loading");b.animate({opacity:1},500,function(){});var d={action:"mcfrtb-get-lists",nonce:rtb_admin_mc.ajax_nonce},e=a.param(d);a.post(ajaxurl,e,function(d){if(d.success){"undefined"==typeof d.data&&(console.log("no data returned from ajax request"),console.log(d)),b.stop(),b.css("opacity","0");var e=a("#mcfrtb-merge-controls").data("input-name")+"[list]";a(".mcf-list-select").append('<select name="'+e+'" id="'+e+'"><option></option></select>');var f=a(".mcf-list-select select");for(var g in d.data.lists){var h=a("<option></option>"),i=d.data.lists[g];h.attr("value",i.id).text(i.name),f.append(h)}a(f).change(function(b){c(a("option:selected",this).val())}),null!==rtb_admin_mc.lists&&"undefined"!=typeof rtb_admin_mc.lists.list&&""!==rtb_admin_mc.lists.list&&f.find("option[value="+rtb_admin_mc.lists.list+"]").attr("selected","selected").trigger("change")}else"undefined"==typeof d.data||"undefined"==typeof d.data.msg?a(".mcf-list-select").html('<span class="error">'+rtb_admin_mc.strings.api_unknown_error+"</span>"):a(".mcf-list-select").html('<span class="error">'+d.data.msg+"</span>"),b.animate({opacity:0},500,function(){})})}function c(b){var c=a(".mcf-sap_loading"),e=a("#mcfrtb-merge-controls");if(c.animate({opacity:1},500,function(){}),e.empty(),e.data("list-id",b),e.removeClass("active"),""===b)return void c.animate({opacity:0},500,function(){});var f={list:b,action:"mcfrtb-load-merge-fields",nonce:rtb_admin_mc.ajax_nonce},g=a.param(f);a.post(ajaxurl,g,function(f){if(f.success){if("undefined"==typeof f.data&&(console.log("no data returned from ajax request"),console.log(f)),f.data.list_id!=e.data("list-id"))return;if(e.empty(),"undefined"!=typeof rtb_admin_mc.merge_fields){var g=e.data("input-name")+"[fields]",h=a("<select><option></option></select>");for(var i in f.data.merge_fields){var j=a("<option></option>"),k=f.data.merge_fields[i];j.attr("value",k.tag).text(k.name),h.append(j)}e.hide(),e.append("<table></table>");var l=e.find("table");l.append("<tr><th>"+rtb_admin_mc.strings.merge_booking_data+"</th><th>"+rtb_admin_mc.strings.merge_list_field+"</th></tr>"),l.append("<tr><td>"+rtb_admin_mc.strings.merge_email_label+'</td><td><span class="description">'+rtb_admin_mc.strings.merge_email_description+"</span></td></tr>");for(var m in rtb_admin_mc.merge_fields)l.append('<tr><td><label for="mcfrtb-'+m+'">'+rtb_admin_mc.merge_fields[m]+'</label></td><td><select name="'+g+"["+m+']" id="mcfrtb-'+m+'">'+h.html()+"</select></td></tr>"),null!==rtb_admin_mc.lists&&b===rtb_admin_mc.lists.list&&"undefined"!=typeof rtb_admin_mc.lists.fields[m]&&""!==rtb_admin_mc.lists.fields[m]&&l.find("#mcfrtb-"+m+" option[value="+rtb_admin_mc.lists.fields[m]+"]").attr("selected","selected");e.find("select").change(function(){d(a(this),a("option:selected",this).val(),e)}),e.append('<p class="description">'+rtb_admin_mc.strings.merge_description+"</p>"),e.addClass("active"),e.fadeIn()}}else"undefined"==typeof f.data||"undefined"==typeof f.data.msg?e.html('<p class="error">'+rtb_admin_mc.strings.api_unknown_error+"</p>"):e.html('<p class="error">'+f.data.msg+"</p>");c.animate({opacity:0},500,function(){})})}function d(b,c,d){if(""!==c){var e=0,f=[];if(a("select option:selected",d).each(function(){a(this).val()===c&&(f.push(a(this)),e++)}),e>1){b.find("option[value="+c+"]").prop("selected","");for(var g in f)f[g].parent().after('<span class="merge-duplicate-warning dashicons dashicons-lock"></span>');a(".merge-duplicate-warning").fadeOut(2e3,function(){a(this).remove()})}}}a(".mcf-list-select").length&&b()});
|
assets/js/plugin-deactivation.js
CHANGED
@@ -1,53 +1,53 @@
|
|
1 |
-
jQuery(function($){
|
2 |
-
var $deactivateLink = $('#the-list').find('[data-slug="restaurant-reservations"] span.deactivate a'),
|
3 |
-
$overlay = $('#rtb-deactivate-survey-restaurant-reservations'),
|
4 |
-
$form = $overlay.find('form'),
|
5 |
-
formOpen = false;
|
6 |
-
// Plugin listing table deactivate link.
|
7 |
-
$deactivateLink.on('click', function(event) {
|
8 |
-
event.preventDefault();
|
9 |
-
$overlay.css('display', 'table');
|
10 |
-
formOpen = true;
|
11 |
-
$form.find('.rtb-deactivate-survey-option:first-of-type input[type=radio]').focus();
|
12 |
-
});
|
13 |
-
// Survey radio option selected.
|
14 |
-
$form.on('change', 'input[type=radio]', function(event) {
|
15 |
-
event.preventDefault();
|
16 |
-
$form.find('input[type=text], .error').hide();
|
17 |
-
$form.find('.rtb-deactivate-survey-option').removeClass('selected');
|
18 |
-
$(this).closest('.rtb-deactivate-survey-option').addClass('selected').find('input[type=text]').show();
|
19 |
-
});
|
20 |
-
// Survey Skip & Deactivate.
|
21 |
-
$form.on('click', '.rtb-deactivate-survey-deactivate', function(event) {
|
22 |
-
event.preventDefault();
|
23 |
-
location.href = $deactivateLink.attr('href');
|
24 |
-
});
|
25 |
-
// Survey submit.
|
26 |
-
$form.submit(function(event) {
|
27 |
-
event.preventDefault();
|
28 |
-
if (! $form.find('input[type=radio]:checked').val()) {
|
29 |
-
$form.find('.rtb-deactivate-survey-footer').prepend('<span class="error">Please select an option below</span>');
|
30 |
-
return;
|
31 |
-
}
|
32 |
-
var data = {
|
33 |
-
code: $form.find('.selected input[type=radio]').val(),
|
34 |
-
install_time: $form.data('installtime'),
|
35 |
-
reason: $form.find('.selected .rtb-deactivate-survey-option-reason').text(),
|
36 |
-
details: $form.find('.selected input[type=text]').val(),
|
37 |
-
site: rtb_deactivation_data.site_url,
|
38 |
-
plugin: 'Five-Star Restaurant Reservations'
|
39 |
-
}
|
40 |
-
var submitSurvey = $.post('https://www.fivestarplugins.com/key-check/Deactivation_Surveys.php', data);
|
41 |
-
submitSurvey.always(function() {
|
42 |
-
location.href = $deactivateLink.attr('href');
|
43 |
-
});
|
44 |
-
});
|
45 |
-
// Exit key closes survey when open.
|
46 |
-
$(document).keyup(function(event) {
|
47 |
-
if (27 === event.keyCode && formOpen) {
|
48 |
-
$overlay.hide();
|
49 |
-
formOpen = false;
|
50 |
-
$deactivateLink.focus();
|
51 |
-
}
|
52 |
-
});
|
53 |
});
|
1 |
+
jQuery(function($){
|
2 |
+
var $deactivateLink = $('#the-list').find('[data-slug="restaurant-reservations"] span.deactivate a'),
|
3 |
+
$overlay = $('#rtb-deactivate-survey-restaurant-reservations'),
|
4 |
+
$form = $overlay.find('form'),
|
5 |
+
formOpen = false;
|
6 |
+
// Plugin listing table deactivate link.
|
7 |
+
$deactivateLink.on('click', function(event) {
|
8 |
+
event.preventDefault();
|
9 |
+
$overlay.css('display', 'table');
|
10 |
+
formOpen = true;
|
11 |
+
$form.find('.rtb-deactivate-survey-option:first-of-type input[type=radio]').focus();
|
12 |
+
});
|
13 |
+
// Survey radio option selected.
|
14 |
+
$form.on('change', 'input[type=radio]', function(event) {
|
15 |
+
event.preventDefault();
|
16 |
+
$form.find('input[type=text], .error').hide();
|
17 |
+
$form.find('.rtb-deactivate-survey-option').removeClass('selected');
|
18 |
+
$(this).closest('.rtb-deactivate-survey-option').addClass('selected').find('input[type=text]').show();
|
19 |
+
});
|
20 |
+
// Survey Skip & Deactivate.
|
21 |
+
$form.on('click', '.rtb-deactivate-survey-deactivate', function(event) {
|
22 |
+
event.preventDefault();
|
23 |
+
location.href = $deactivateLink.attr('href');
|
24 |
+
});
|
25 |
+
// Survey submit.
|
26 |
+
$form.submit(function(event) {
|
27 |
+
event.preventDefault();
|
28 |
+
if (! $form.find('input[type=radio]:checked').val()) {
|
29 |
+
$form.find('.rtb-deactivate-survey-footer').prepend('<span class="error">Please select an option below</span>');
|
30 |
+
return;
|
31 |
+
}
|
32 |
+
var data = {
|
33 |
+
code: $form.find('.selected input[type=radio]').val(),
|
34 |
+
install_time: $form.data('installtime'),
|
35 |
+
reason: $form.find('.selected .rtb-deactivate-survey-option-reason').text(),
|
36 |
+
details: $form.find('.selected input[type=text]').val(),
|
37 |
+
site: rtb_deactivation_data.site_url,
|
38 |
+
plugin: 'Five-Star Restaurant Reservations'
|
39 |
+
}
|
40 |
+
var submitSurvey = $.post('https://www.fivestarplugins.com/key-check/Deactivation_Surveys.php', data);
|
41 |
+
submitSurvey.always(function() {
|
42 |
+
location.href = $deactivateLink.attr('href');
|
43 |
+
});
|
44 |
+
});
|
45 |
+
// Exit key closes survey when open.
|
46 |
+
$(document).keyup(function(event) {
|
47 |
+
if (27 === event.keyCode && formOpen) {
|
48 |
+
$overlay.hide();
|
49 |
+
formOpen = false;
|
50 |
+
$deactivateLink.focus();
|
51 |
+
}
|
52 |
+
});
|
53 |
});
|
assets/js/rtb-recaptcha.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
-
var rtbLoadRecaptcha = function() {
|
2 |
-
grecaptcha.render('rtb_recaptcha', {
|
3 |
-
'sitekey' : rtb_recaptcha.site_key
|
4 |
-
});
|
5 |
}
|
1 |
+
var rtbLoadRecaptcha = function() {
|
2 |
+
grecaptcha.render('rtb_recaptcha', {
|
3 |
+
'sitekey' : rtb_recaptcha.site_key
|
4 |
+
});
|
5 |
}
|
assets/js/stripe-payment.js
CHANGED
@@ -1,197 +1,197 @@
|
|
1 |
-
var key = null;
|
2 |
-
|
3 |
-
if ( rtb_stripe_payment.stripe_mode == 'test' ) {
|
4 |
-
key = rtb_stripe_payment.test_publishable_key;
|
5 |
-
}
|
6 |
-
else {
|
7 |
-
key = rtb_stripe_payment.live_publishable_key;
|
8 |
-
}
|
9 |
-
|
10 |
-
if( rtb_stripe_payment.stripe_sca ) {
|
11 |
-
var _stripe = Stripe(key);
|
12 |
-
}
|
13 |
-
else {
|
14 |
-
Stripe.setPublishableKey(key);
|
15 |
-
}
|
16 |
-
|
17 |
-
function stripeResponseHandler(status, response) {
|
18 |
-
if (response.error) {
|
19 |
-
// show errors returned by Stripe
|
20 |
-
jQuery(".payment-errors").html(response.error.message);
|
21 |
-
// re-enable the submit button
|
22 |
-
jQuery('#stripe-submit').attr("disabled", false);
|
23 |
-
}
|
24 |
-
else {
|
25 |
-
var form$ = jQuery("#stripe-payment-form");
|
26 |
-
// token contains id, last4, and card type
|
27 |
-
var token = response['id'];
|
28 |
-
// insert the token into the form so it gets submitted to the server
|
29 |
-
form$.append("<input type='hidden' name='stripeToken' value='" + token + "'/>");
|
30 |
-
// and submit
|
31 |
-
form$.get(0).submit();
|
32 |
-
}
|
33 |
-
}
|
34 |
-
|
35 |
-
function error_handler(msg = '') {
|
36 |
-
jQuery('.payment-errors').html(msg);
|
37 |
-
enable_payment_form();
|
38 |
-
}
|
39 |
-
|
40 |
-
function disable_payment_form() {
|
41 |
-
jQuery('.payment-errors').html('');
|
42 |
-
rtb_stripe_payment.stripe_sca && jQuery('.stripe-payment-help-text').slideDown();
|
43 |
-
jQuery('#stripe-submit').prop('disabled', true);
|
44 |
-
}
|
45 |
-
function enable_payment_form() {
|
46 |
-
rtb_stripe_payment.stripe_sca && jQuery('.stripe-payment-help-text').slideUp();
|
47 |
-
jQuery('#stripe-submit').prop('disabled', false);
|
48 |
-
}
|
49 |
-
|
50 |
-
jQuery(document).ready(function($) {
|
51 |
-
|
52 |
-
var cardElement = null;
|
53 |
-
|
54 |
-
// setup card element
|
55 |
-
if( rtb_stripe_payment.stripe_sca ) {
|
56 |
-
var stripeElement = _stripe.elements();
|
57 |
-
cardElement = stripeElement.create('card', { hidePostalCode: true });
|
58 |
-
cardElement.mount('#cardElement');
|
59 |
-
|
60 |
-
cardElement.on('change', function(ev) {
|
61 |
-
if (ev.complete) {
|
62 |
-
// enable payment button
|
63 |
-
enable_payment_form();
|
64 |
-
}
|
65 |
-
else {
|
66 |
-
if (ev.error) {
|
67 |
-
error_handler(ev.error.message);
|
68 |
-
}
|
69 |
-
}
|
70 |
-
});
|
71 |
-
}
|
72 |
-
|
73 |
-
$('#stripe-payment-form .single-masked').on('keyup', function (ev) {
|
74 |
-
let value = $(this).val();
|
75 |
-
|
76 |
-
if ( /\//.test(value) ) {
|
77 |
-
value = value.replace( /(\/)+/, '/' );
|
78 |
-
}
|
79 |
-
|
80 |
-
if(value.length > 2 && !/\//.test(value)) {
|
81 |
-
value = value.split('');
|
82 |
-
value.splice(2, 0, '/');
|
83 |
-
value = value.join('');
|
84 |
-
}
|
85 |
-
|
86 |
-
$(this).val(value);
|
87 |
-
});
|
88 |
-
|
89 |
-
$("#stripe-payment-form").submit(function(event) {
|
90 |
-
// disable the submit button to prevent repeated clicks
|
91 |
-
disable_payment_form();
|
92 |
-
|
93 |
-
// send the card details to Stripe
|
94 |
-
if( rtb_stripe_payment.stripe_sca ) {
|
95 |
-
|
96 |
-
var booking_id = $(this).data( 'booking_id' );
|
97 |
-
// Call your backend to create the Checkout Session
|
98 |
-
var params = {
|
99 |
-
'nonce': rtb_stripe_payment.nonce,
|
100 |
-
'action': 'rtb_stripe_get_intent',
|
101 |
-
'booking_id': booking_id
|
102 |
-
};
|
103 |
-
|
104 |
-
$.post(ajaxurl, params, function(result) {
|
105 |
-
result = JSON.parse(result);
|
106 |
-
if( result.success ) {
|
107 |
-
|
108 |
-
_stripe.confirmCardPayment(result.clientSecret, {
|
109 |
-
payment_method: {
|
110 |
-
card: cardElement,
|
111 |
-
billing_details: {
|
112 |
-
name: result.name,
|
113 |
-
email: result.email
|
114 |
-
}
|
115 |
-
}
|
116 |
-
}).then(function(result) {
|
117 |
-
params = {
|
118 |
-
nonce: rtb_stripe_payment.nonce,
|
119 |
-
action: 'rtb_stripe_pmt_succeed',
|
120 |
-
booking_id: booking_id
|
121 |
-
};
|
122 |
-
|
123 |
-
if (result.error) {
|
124 |
-
// Show error to your customer (e.g., insufficient funds)
|
125 |
-
params['success'] = false;
|
126 |
-
params['message'] = result.error.message;
|
127 |
-
error_handler(result.error.message);
|
128 |
-
}
|
129 |
-
else {
|
130 |
-
var pi = result.paymentIntent;
|
131 |
-
|
132 |
-
// The payment has been processed!
|
133 |
-
if (pi.status === 'succeeded' || pi.status === 'requires_capture') {
|
134 |
-
params['success'] = true;
|
135 |
-
params['payment_amount'] = pi.amount;
|
136 |
-
params['payment_id'] = pi.id;
|
137 |
-
// params['payment_intent'] = pi;
|
138 |
-
}
|
139 |
-
else {
|
140 |
-
params['success'] = false;
|
141 |
-
params['message'] = 'Unknown error';
|
142 |
-
}
|
143 |
-
}
|
144 |
-
|
145 |
-
$.post(ajaxurl, params, function (result) {
|
146 |
-
result = JSON.parse(result);
|
147 |
-
|
148 |
-
if(true == result.success) {
|
149 |
-
var url = new URL(window.location.pathname, window.location.origin);
|
150 |
-
|
151 |
-
for(const [key, value] of Object.entries(result.urlParams)) {
|
152 |
-
url.searchParams.append(key, value);
|
153 |
-
}
|
154 |
-
|
155 |
-
window.location = url.href;
|
156 |
-
}
|
157 |
-
else {
|
158 |
-
error_handler(result.message);
|
159 |
-
console.log('RTB-Stripe error: ', result.message);
|
160 |
-
}
|
161 |
-
});
|
162 |
-
});
|
163 |
-
}
|
164 |
-
else {
|
165 |
-
error_handler(result.message);
|
166 |
-
console.log('RTB-Stripe error: ', result.message);
|
167 |
-
}
|
168 |
-
});
|
169 |
-
}
|
170 |
-
else {
|
171 |
-
|
172 |
-
let exp_month, exp_year;
|
173 |
-
|
174 |
-
let single_field = $('#stripe-payment-form .single-masked').length;
|
175 |
-
if(single_field) {
|
176 |
-
let data = $('#stripe-payment-form .single-masked').val().split('/');
|
177 |
-
exp_month = data[0];
|
178 |
-
exp_year = data[1];
|
179 |
-
}
|
180 |
-
else {
|
181 |
-
exp_month = $('input[data-stripe="exp_month"]').val();
|
182 |
-
exp_year = $('input[data-stripe="exp_year"]').val();
|
183 |
-
}
|
184 |
-
|
185 |
-
Stripe.createToken({
|
186 |
-
number: $('input[data-stripe="card_number"]').val(),
|
187 |
-
cvc: $('input[data-stripe="card_cvc"]').val(),
|
188 |
-
exp_month: exp_month,
|
189 |
-
exp_year: exp_year,
|
190 |
-
currency: $('input[data-stripe="currency"]').val()
|
191 |
-
}, stripeResponseHandler);
|
192 |
-
}
|
193 |
-
|
194 |
-
// prevent the form from submitting with the default action
|
195 |
-
return false;
|
196 |
-
});
|
197 |
});
|
1 |
+
var key = null;
|
2 |
+
|
3 |
+
if ( rtb_stripe_payment.stripe_mode == 'test' ) {
|
4 |
+
key = rtb_stripe_payment.test_publishable_key;
|
5 |
+
}
|
6 |
+
else {
|
7 |
+
key = rtb_stripe_payment.live_publishable_key;
|
8 |
+
}
|
9 |
+
|
10 |
+
if( rtb_stripe_payment.stripe_sca ) {
|
11 |
+
var _stripe = Stripe(key);
|
12 |
+
}
|
13 |
+
else {
|
14 |
+
Stripe.setPublishableKey(key);
|
15 |
+
}
|
16 |
+
|
17 |
+
function stripeResponseHandler(status, response) {
|
18 |
+
if (response.error) {
|
19 |
+
// show errors returned by Stripe
|
20 |
+
jQuery(".payment-errors").html(response.error.message);
|
21 |
+
// re-enable the submit button
|
22 |
+
jQuery('#stripe-submit').attr("disabled", false);
|
23 |
+
}
|
24 |
+
else {
|
25 |
+
var form$ = jQuery("#stripe-payment-form");
|
26 |
+
// token contains id, last4, and card type
|
27 |
+
var token = response['id'];
|
28 |
+
// insert the token into the form so it gets submitted to the server
|
29 |
+
form$.append("<input type='hidden' name='stripeToken' value='" + token + "'/>");
|
30 |
+
// and submit
|
31 |
+
form$.get(0).submit();
|
32 |
+
}
|
33 |
+
}
|
34 |
+
|
35 |
+
function error_handler(msg = '') {
|
36 |
+
jQuery('.payment-errors').html(msg);
|
37 |
+
enable_payment_form();
|
38 |
+
}
|
39 |
+
|
40 |
+
function disable_payment_form() {
|
41 |
+
jQuery('.payment-errors').html('');
|
42 |
+
rtb_stripe_payment.stripe_sca && jQuery('.stripe-payment-help-text').slideDown();
|
43 |
+
jQuery('#stripe-submit').prop('disabled', true);
|
44 |
+
}
|
45 |
+
function enable_payment_form() {
|
46 |
+
rtb_stripe_payment.stripe_sca && jQuery('.stripe-payment-help-text').slideUp();
|
47 |
+
jQuery('#stripe-submit').prop('disabled', false);
|
48 |
+
}
|
49 |
+
|
50 |
+
jQuery(document).ready(function($) {
|
51 |
+
|
52 |
+
var cardElement = null;
|
53 |
+
|
54 |
+
// setup card element
|
55 |
+
if( rtb_stripe_payment.stripe_sca ) {
|
56 |
+
var stripeElement = _stripe.elements();
|
57 |
+
cardElement = stripeElement.create('card', { hidePostalCode: true });
|
58 |
+
cardElement.mount('#cardElement');
|
59 |
+
|
60 |
+
cardElement.on('change', function(ev) {
|
61 |
+
if (ev.complete) {
|
62 |
+
// enable payment button
|
63 |
+
enable_payment_form();
|
64 |
+
}
|
65 |
+
else {
|
66 |
+
if (ev.error) {
|
67 |
+
error_handler(ev.error.message);
|
68 |
+
}
|
69 |
+
}
|
70 |
+
});
|
71 |
+
}
|
72 |
+
|
73 |
+
$('#stripe-payment-form .single-masked').on('keyup', function (ev) {
|
74 |
+
let value = $(this).val();
|
75 |
+
|
76 |
+
if ( /\//.test(value) ) {
|
77 |
+
value = value.replace( /(\/)+/, '/' );
|
78 |
+
}
|
79 |
+
|
80 |
+
if(value.length > 2 && !/\//.test(value)) {
|
81 |
+
value = value.split('');
|
82 |
+
value.splice(2, 0, '/');
|
83 |
+
value = value.join('');
|
84 |
+
}
|
85 |
+
|
86 |
+
$(this).val(value);
|
87 |
+
});
|
88 |
+
|
89 |
+
$("#stripe-payment-form").submit(function(event) {
|
90 |
+
// disable the submit button to prevent repeated clicks
|
91 |
+
disable_payment_form();
|
92 |
+
|
93 |
+
// send the card details to Stripe
|
94 |
+
if( rtb_stripe_payment.stripe_sca ) {
|
95 |
+
|
96 |
+
var booking_id = $(this).data( 'booking_id' );
|
97 |
+
// Call your backend to create the Checkout Session
|
98 |
+
var params = {
|
99 |
+
'nonce': rtb_stripe_payment.nonce,
|
100 |
+
'action': 'rtb_stripe_get_intent',
|
101 |
+
'booking_id': booking_id
|
102 |
+
};
|
103 |
+
|
104 |
+
$.post(ajaxurl, params, function(result) {
|
105 |
+
result = JSON.parse(result);
|
106 |
+
if( result.success ) {
|
107 |
+
|
108 |
+
_stripe.confirmCardPayment(result.clientSecret, {
|
109 |
+
payment_method: {
|
110 |
+
card: cardElement,
|
111 |
+
billing_details: {
|
112 |
+
name: result.name,
|
113 |
+
email: result.email
|
114 |
+
}
|
115 |
+
}
|
116 |
+
}).then(function(result) {
|
117 |
+
params = {
|
118 |
+
nonce: rtb_stripe_payment.nonce,
|
119 |
+
action: 'rtb_stripe_pmt_succeed',
|
120 |
+
booking_id: booking_id
|
121 |
+
};
|
122 |
+
|
123 |
+
if (result.error) {
|
124 |
+
// Show error to your customer (e.g., insufficient funds)
|
125 |
+
params['success'] = false;
|
126 |
+
params['message'] = result.error.message;
|
127 |
+
error_handler(result.error.message);
|
128 |
+
}
|
129 |
+
else {
|
130 |
+
var pi = result.paymentIntent;
|
131 |
+
|
132 |
+
// The payment has been processed!
|
133 |
+
if (pi.status === 'succeeded' || pi.status === 'requires_capture') {
|
134 |
+
params['success'] = true;
|
135 |
+
params['payment_amount'] = pi.amount;
|
136 |
+
params['payment_id'] = pi.id;
|
137 |
+
// params['payment_intent'] = pi;
|
138 |
+
}
|
139 |
+
else {
|
140 |
+
params['success'] = false;
|
141 |
+
params['message'] = 'Unknown error';
|
142 |
+
}
|
143 |
+
}
|
144 |
+
|
145 |
+
$.post(ajaxurl, params, function (result) {
|
146 |
+
result = JSON.parse(result);
|
147 |
+
|
148 |
+
if(true == result.success) {
|
149 |
+
var url = new URL(window.location.pathname, window.location.origin);
|
150 |
+
|
151 |
+
for(const [key, value] of Object.entries(result.urlParams)) {
|
152 |
+
url.searchParams.append(key, value);
|
153 |
+
}
|
154 |
+
|
155 |
+
window.location = url.href;
|
156 |
+
}
|
157 |
+
else {
|
158 |
+
error_handler(result.message);
|
159 |
+
console.log('RTB-Stripe error: ', result.message);
|
160 |
+
}
|
161 |
+
});
|
162 |
+
});
|
163 |
+
}
|
164 |
+
else {
|
165 |
+
error_handler(result.message);
|
166 |
+
console.log('RTB-Stripe error: ', result.message);
|
167 |
+
}
|
168 |
+
});
|
169 |
+
}
|
170 |
+
else {
|
171 |
+
|
172 |
+
let exp_month, exp_year;
|
173 |
+
|
174 |
+
let single_field = $('#stripe-payment-form .single-masked').length;
|
175 |
+
if(single_field) {
|
176 |
+
let data = $('#stripe-payment-form .single-masked').val().split('/');
|
177 |
+
exp_month = data[0];
|
178 |
+
exp_year = data[1];
|
179 |
+
}
|
180 |
+
else {
|
181 |
+
exp_month = $('input[data-stripe="exp_month"]').val();
|
182 |
+
exp_year = $('input[data-stripe="exp_year"]').val();
|
183 |
+
}
|
184 |
+
|
185 |
+
Stripe.createToken({
|
186 |
+
number: $('input[data-stripe="card_number"]').val(),
|
187 |
+
cvc: $('input[data-stripe="card_cvc"]').val(),
|
188 |
+
exp_month: exp_month,
|
189 |
+
exp_year: exp_year,
|
190 |
+
currency: $('input[data-stripe="currency"]').val()
|
191 |
+
}, stripeResponseHandler);
|
192 |
+
}
|
193 |
+
|
194 |
+
// prevent the form from submitting with the default action
|
195 |
+
return false;
|
196 |
+
});
|
197 |
});
|
includes/Addons.class.php
CHANGED
@@ -1,362 +1,362 @@
|
|
1 |
-
<?php
|
2 |
-
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
-
|
4 |
-
if ( !class_exists( 'rtbAddons' ) ) {
|
5 |
-
/**
|
6 |
-
* Class to handle the addons page for Restaurant Reservations
|
7 |
-
*
|
8 |
-
* @since 1.3
|
9 |
-
*/
|
10 |
-
class rtbAddons {
|
11 |
-
|
12 |
-
public function __construct( ) {
|
13 |
-
|
14 |
-
// Add the admin menu
|
15 |
-
add_action( 'admin_menu', array( $this, 'add_menu_page' ), 100 );
|
16 |
-
|
17 |
-
// Add a newsletter subscription prompt above the addons
|
18 |
-
add_action( 'rtb_addons_pre', array( $this, 'add_subscribe_pompt' ) );
|
19 |
-
}
|
20 |
-
|
21 |
-
/**
|
22 |
-
* Add the addons page to the admin menu
|
23 |
-
*/
|
24 |
-
public function add_menu_page() {
|
25 |
-
|
26 |
-
add_submenu_page(
|
27 |
-
'rtb-bookings',
|
28 |
-
_x( 'Addons', 'Title of addons page', 'restaurant-reservations' ),
|
29 |
-
_x( 'Addons', 'Title of addons page in the admin menu', 'restaurant-reservations' ),
|
30 |
-
'manage_options',
|
31 |
-
'rtb-addons',
|
32 |
-
array( $this, 'show_admin_addons_page' )
|
33 |
-
);
|
34 |
-
|
35 |
-
}
|
36 |
-
|
37 |
-
/**
|
38 |
-
* Display the addons page
|
39 |
-
*/
|
40 |
-
public function show_admin_addons_page() {
|
41 |
-
|
42 |
-
// Set campaign parameters for addon URLs
|
43 |
-
$url_params = '?utm_source=Plugin&utm_medium=Addon%20List&utm_campaign=Restaurant%20Reservations';
|
44 |
-
?>
|
45 |
-
|
46 |
-
<div class="wrap">
|
47 |
-
<h1><?php _e( 'Addons for Restaurant Reservations', 'restaurant-reservations' ); ?></h1>
|
48 |
-
<?php do_action( 'rtb_addons_pre' ); ?>
|
49 |
-
<div class="rtb-addons">
|
50 |
-
<div class="addon addon-custom-fields">
|
51 |
-
<a href="https://www.fivestarplugins.com/plugins/five-star-restaurant-reservations/">
|
52 |
-
<img src="<?php echo RTB_PLUGIN_URL . '/assets/img/custom-fields.png'; ?>">
|
53 |
-
</a>
|
54 |
-
<h3><?php esc_html_e( 'Custom Fields', 'restaurant-reservations' ); ?></h3>
|
55 |
-
<div class="details">
|
56 |
-
<div class="description">
|
57 |
-
<?php esc_html_e( 'Plan your dinner service better by asking for special seating requests, dietary needs and more when customers book online.', 'restaurant-reservations' ); ?>
|
58 |
-
</div>
|
59 |
-
<div class="action">
|
60 |
-
<a href="https://www.fivestarplugins.com/plugins/five-star-restaurant-reservations/" class="button button-primary" target="_blank">
|
61 |
-
<?php esc_html_e( 'Learn More', 'restaurant-reservations' ); ?>
|
62 |
-
</a>
|
63 |
-
</div>
|
64 |
-
</div>
|
65 |
-
</div>
|
66 |
-
<div class="addon addon-export-bookings">
|
67 |
-
<a href="https://www.fivestarplugins.com/plugins/five-star-restaurant-reservations/">
|
68 |
-
<img src="<?php echo RTB_PLUGIN_URL . '/assets/img/export-bookings.png'; ?>">
|
69 |
-
</a>
|
70 |
-
<h3><?php esc_html_e( 'Export Bookings', 'restaurant-reservations' ); ?></h3>
|
71 |
-
<div class="details">
|
72 |
-
<div class="description">
|
73 |
-
<?php esc_html_e( 'Easily print your bookings in a PDF or export them to an Excel/CSV file so you can analyze patterns, cull customer data and import bookings into other services.', 'restaurant-reservations' ); ?>
|
74 |
-
</div>
|
75 |
-
<div class="action">
|
76 |
-
<a href="https://www.fivestarplugins.com/plugins/five-star-restaurant-reservations/" class="button button-primary" target="_blank">
|
77 |
-
<?php esc_html_e( 'Learn More', 'restaurant-reservations' ); ?>
|
78 |
-
</a>
|
79 |
-
</div>
|
80 |
-
</div>
|
81 |
-
</div>
|
82 |
-
<div class="addon addon-email-templates">
|
83 |
-
<a href="https://www.fivestarplugins.com/plugins/five-star-restaurant-reservations/">
|
84 |
-
<img src="<?php echo RTB_PLUGIN_URL . '/assets/img/email-templates.png'; ?>">
|
85 |
-
</a>
|
86 |
-
<h3><?php esc_html_e( 'Email Templates', 'restaurant-reservations' ); ?></h3>
|
87 |
-
<div class="details">
|
88 |
-
<div class="description">
|
89 |
-
<?php esc_html_e( 'Send beautiful email notifications with your own logo and brand colors when your customers make a reservation.', 'restaurant-reservations' ); ?>
|
90 |
-
</div>
|
91 |
-
<div class="action">
|
92 |
-
<a href="https://www.fivestarplugins.com/plugins/five-star-restaurant-reservations/" class="button button-primary" target="_blank">
|
93 |
-
<?php esc_html_e( 'Learn More', 'restaurant-reservations' ); ?>
|
94 |
-
</a>
|
95 |
-
</div>
|
96 |
-
</div>
|
97 |
-
</div>
|
98 |
-
<div class="addon addon-mailchimp">
|
99 |
-
<a href="https://www.fivestarplugins.com/plugins/five-star-restaurant-reservations/">
|
100 |
-
<img src="<?php echo RTB_PLUGIN_URL . '/assets/img/mailchimp.png'; ?>">
|
101 |
-
</a>
|
102 |
-
<h3><?php esc_html_e( 'MailChimp', 'restaurant-reservations' ); ?></h3>
|
103 |
-
<div class="details">
|
104 |
-
<div class="description">
|
105 |
-
<?php esc_html_e( 'Subscribe requests to your MailChimp mailing list and watch your subscription rates grow effortlessly.', 'restaurant-reservations' ); ?>
|
106 |
-
</div>
|
107 |
-
<div class="action">
|
108 |
-
<a href="https://www.fivestarplugins.com/plugins/five-star-restaurant-reservations/" class="button button-primary" target="_blank">
|
109 |
-
<?php esc_html_e( 'Learn More', 'restaurant-reservations' ); ?>
|
110 |
-
</a>
|
111 |
-
</div>
|
112 |
-
</div>
|
113 |
-
</div>
|
114 |
-
</div><?php /*
|
115 |
-
<h2>Recommended Themes</h2>
|
116 |
-
<p>The following restaurant themes integrate beautifully with Restaurant Reservations, providing a clean, stylized booking form that matches your site's design.</p>
|
117 |
-
<div class="rtb-addons">
|
118 |
-
<div class="addon addon-themes">
|
119 |
-
<a href="https://www.fivestarplugins.com/">
|
120 |
-
<img src="<?php echo RTB_PLUGIN_URL . '/assets/img/theme-augustan.jpg'; ?>">
|
121 |
-
</a>
|
122 |
-
<h3><?php esc_html_e( 'Augustan', 'restaurant-reservations' ); ?></h3>
|
123 |
-
<div class="details">
|
124 |
-
<div class="description">
|
125 |
-
<?php esc_html_e( 'A traditionally elegant theme for high-class restaurants, with simple setup and powerful features.', 'restaurant-reservations' ); ?>
|
126 |
-
</div>
|
127 |
-
<div class="action">
|
128 |
-
<a href="https://www.fivestarplugins.com/" class="button" target="_blank">
|
129 |
-
<?php esc_html_e( 'View Theme', 'restaurant-reservations' ); ?>
|
130 |
-
</a>
|
131 |
-
<span class="rtb-by">
|
132 |
-
by <a href="https://www.fivestarplugins.com/<?php echo esc_url( $url_params ); ?>">Five Star Plugins</a>
|
133 |
-
</span>
|
134 |
-
</div>
|
135 |
-
</div>
|
136 |
-
</div>
|
137 |
-
<div class="addon addon-themes">
|
138 |
-
<a href="https://www.fivestarplugins.com/">
|
139 |
-
<img src="<?php echo RTB_PLUGIN_URL . '/assets/img/theme-luigi.jpg'; ?>">
|
140 |
-
</a>
|
141 |
-
<h3><?php esc_html_e( 'Luigi', 'restaurant-reservations' ); ?></h3>
|
142 |
-
<div class="details">
|
143 |
-
<div class="description">
|
144 |
-
<?php esc_html_e( 'A smart theme for upscale bistros and fine Italian restaurants. Get up and running quickly.', 'restaurant-reservations' ); ?>
|
145 |
-
</div>
|
146 |
-
<div class="action">
|
147 |
-
<a href="https://www.fivestarplugins.com/" class="button" target="_blank">
|
148 |
-
<?php esc_html_e( 'View Theme', 'restaurant-reservations' ); ?>
|
149 |
-
</a>
|
150 |
-
<span class="rtb-by">
|
151 |
-
by <a href="https://www.fivestarplugins.com/">Five Star Plugins</a>
|
152 |
-
</span>
|
153 |
-
</div>
|
154 |
-
</div>
|
155 |
-
</div>
|
156 |
-
<div class="addon addon-themes">
|
157 |
-
<a href="https://www.fivestarplugins.com/">
|
158 |
-
<img src="<?php echo RTB_PLUGIN_URL . '/assets/img/theme-the-spot.jpg'; ?>">
|
159 |
-
</a>
|
160 |
-
<h3><?php esc_html_e( 'The Spot', 'restaurant-reservations' ); ?></h3>
|
161 |
-
<div class="details">
|
162 |
-
<div class="description">
|
163 |
-
<?php esc_html_e( 'A vibrant theme for bars, pubs and destination restaurants with an attention-grabbing homepage.', 'restaurant-reservations' ); ?>
|
164 |
-
</div>
|
165 |
-
<div class="action">
|
166 |
-
<a href="https://www.fivestarplugins.com/" class="button" target="_blank">
|
167 |
-
<?php esc_html_e( 'View Theme', 'restaurant-reservations' ); ?>
|
168 |
-
</a>
|
169 |
-
<span class="rtb-by">
|
170 |
-
by <a href="https://www.fivestarplugins.com/">Five Star Plugins</a>
|
171 |
-
</span>
|
172 |
-
</div>
|
173 |
-
</div>
|
174 |
-
</div>
|
175 |
-
<div class="addon addon-themes">
|
176 |
-
<a href="https://www.fivestarplugins.com/">
|
177 |
-
<img src="<?php echo RTB_PLUGIN_URL . '/assets/img/theme-plate-up.jpg'; ?>">
|
178 |
-
</a>
|
179 |
-
<h3><?php esc_html_e( 'Plate Up', 'restaurant-reservations' ); ?></h3>
|
180 |
-
<div class="details">
|
181 |
-
<div class="description">
|
182 |
-
<?php esc_html_e( 'A refined theme for sophisticated, modern restaurants to drive customers to your booking form.', 'restaurant-reservations' ); ?>
|
183 |
-
</div>
|
184 |
-
<div class="action">
|
185 |
-
<a href="https://www.fivestarplugins.com/" class="button" target="_blank">
|
186 |
-
<?php esc_html_e( 'View Theme', 'restaurant-reservations' ); ?>
|
187 |
-
</a>
|
188 |
-
<span class="rtb-by">
|
189 |
-
by <a href="https://www.fivestarplugins.com/">Five Star Plugins</a>
|
190 |
-
</span>
|
191 |
-
</div>
|
192 |
-
</div>
|
193 |
-
</div>
|
194 |
-
<div class="addon addon-themes">
|
195 |
-
<a href="https://themebeans.com/themes/plate?utm_source=totc_addons_plate&utm_medium=banner&utm_campaign=TOTC%20Addons%20Link%2C%20Plate">
|
196 |
-
<img src="<?php echo RTB_PLUGIN_URL . '/assets/img/theme-plate.jpg'; ?>">
|
197 |
-
</a>
|
198 |
-
<h3><?php esc_html_e( 'Plate', 'restaurant-reservations' ); ?></h3>
|
199 |
-
<div class="details">
|
200 |
-
<div class="description">
|
201 |
-
<?php esc_html_e( 'A delightfully beautiful WordPress theme designed to help you build a stunning restaurant website.', 'restaurant-reservations' ); ?>
|
202 |
-
</div>
|
203 |
-
<div class="action">
|
204 |
-
<a href="https://themebeans.com/themes/plate?utm_source=totc_addons_plate&utm_medium=banner&utm_campaign=TOTC%20Addons%20Link%2C%20Plate" class="button" target="_blank">
|
205 |
-
<?php esc_html_e( 'View Theme', 'restaurant-reservations' ); ?>
|
206 |
-
</a>
|
207 |
-
<span class="rtb-by">
|
208 |
-
by <a href="https://themebeans.com?utm_source=totc_addons_plate&utm_medium=banner&utm_campaign=TOTC%20Addons%20Link%2C%20Plate">ThemeBeans</a>
|
209 |
-
</span>
|
210 |
-
</div>
|
211 |
-
</div>
|
212 |
-
</div>
|
213 |
-
<div class="addon addon-themes">
|
214 |
-
<a href="https://wordpress.org/themes/auberge/">
|
215 |
-
<img src="<?php echo RTB_PLUGIN_URL . '/assets/img/theme-auberge.jpg'; ?>">
|
216 |
-
</a>
|
217 |
-
<h3><?php esc_html_e( 'Auberge', 'restaurant-reservations' ); ?></h3>
|
218 |
-
<div class="details">
|
219 |
-
<div class="description">
|
220 |
-
<?php esc_html_e( 'Display a menu of your restaurant, café or bar stylishly with this free mobile-friendly WordPress theme.', 'restaurant-reservations' ); ?>
|
221 |
-
</div>
|
222 |
-
<div class="action">
|
223 |
-
<a href="https://wordpress.org/themes/auberge/" class="button" target="_blank">
|
224 |
-
<?php esc_html_e( 'View Theme', 'restaurant-reservations' ); ?>
|
225 |
-
</a>
|
226 |
-
<span class="rtb-by">
|
227 |
-
by <a href="https://www.webmandesign.eu/">Webman Design</a>
|
228 |
-
</span>
|
229 |
-
</div>
|
230 |
-
</div>
|
231 |
-
</div>
|
232 |
-
<div class="addon addon-themes">
|
233 |
-
<a href="http://www.anarieldesign.com/themes/restaurant-bar-wordpress-theme/?utm_source=Theme%20of%20the%20Crop&utm_medium=Addon%20List&utm_campaign=Restaurant%20Reservations">
|
234 |
-
<img src="<?php echo RTB_PLUGIN_URL . '/assets/img/theme-liber.jpg'; ?>">
|
235 |
-
</a>
|
236 |
-
<h3><?php esc_html_e( 'Liber', 'restaurant-reservations' ); ?></h3>
|
237 |
-
<div class="details">
|
238 |
-
<div class="description">
|
239 |
-
<?php esc_html_e( 'A responsive theme optimized for restaurants and bars supporting features these websites need.', 'restaurant-reservations' ); ?>
|
240 |
-
</div>
|
241 |
-
<div class="action">
|
242 |
-
<a href="http://www.anarieldesign.com/themes/restaurant-bar-wordpress-theme/?utm_source=Theme%20of%20the%20Crop&utm_medium=Addon%20List&utm_campaign=Restaurant%20Reservations" class="button" target="_blank">
|
243 |
-
<?php esc_html_e( 'View Theme', 'restaurant-reservations' ); ?>
|
244 |
-
</a>
|
245 |
-
<span class="rtb-by">
|
246 |
-
by <a href="http://www.anarieldesign.com/">Anariel Design</a>
|
247 |
-
</span>
|
248 |
-
</div>
|
249 |
-
</div>
|
250 |
-
</div>
|
251 |
-
<div class="addon addon-themes">
|
252 |
-
<a href="https://wordpress.org/themes/brasserie/">
|
253 |
-
<img src="<?php echo RTB_PLUGIN_URL . '/assets/img/theme-brasserie.jpg'; ?>">
|
254 |
-
</a>
|
255 |
-
<h3><?php esc_html_e( 'Brasserie', 'restaurant-reservations' ); ?></h3>
|
256 |
-
<div class="details">
|
257 |
-
<div class="description">
|
258 |
-
<?php esc_html_e( 'A delightfully simple to use and beautifully crafted free theme for any food establishment.', 'restaurant-reservations' ); ?>
|
259 |
-
</div>
|
260 |
-
<div class="action">
|
261 |
-
<a href="https://wordpress.org/themes/brasserie/" class="button" target="_blank">
|
262 |
-
<?php esc_html_e( 'View Theme', 'restaurant-reservations' ); ?>
|
263 |
-
</a>
|
264 |
-
<span class="rtb-by">
|
265 |
-
by <a href="https://www.templateexpress.com/">Template Express</a>
|
266 |
-
</span>
|
267 |
-
</div>
|
268 |
-
</div>
|
269 |
-
</div>
|
270 |
-
<div class="addon addon-themes">
|
271 |
-
<a href="http://www.anarieldesign.com/themes/food-blog-wordpress-theme/?utm_source=Theme%20of%20the%20Crop&utm_medium=Addon%20List&utm_campaign=Restaurant%20Reservations">
|
272 |
-
<img src="<?php echo RTB_PLUGIN_URL . '/assets/img/theme-veggie.jpg'; ?>">
|
273 |
-
</a>
|
274 |
-
<h3><?php esc_html_e( 'Veggie', 'restaurant-reservations' ); ?></h3>
|
275 |
-
<div class="details">
|
276 |
-
<div class="description">
|
277 |
-
<?php esc_html_e( 'A food blogging and restaurant theme with modern, easy-to-read typography and minimalist design.', 'restaurant-reservations' ); ?>
|
278 |
-
</div>
|
279 |
-
<div class="action">
|
280 |
-
<a href="http://www.anarieldesign.com/themes/food-blog-wordpress-theme/?utm_source=Theme%20of%20the%20Crop&utm_medium=Addon%20List&utm_campaign=Restaurant%20Reservations" class="button" target="_blank">
|
281 |
-
<?php esc_html_e( 'View Theme', 'restaurant-reservations' ); ?>
|
282 |
-
</a>
|
283 |
-
<span class="rtb-by">
|
284 |
-
by <a href="http://www.anarieldesign.com/">Anariel Design</a>
|
285 |
-
</span>
|
286 |
-
</div>
|
287 |
-
</div>
|
288 |
-
</div>
|
289 |
-
<div class="addon addon-themes">
|
290 |
-
<a href="http://www.anarieldesign.com/themes/wine-and-winery-wordpress-theme/?utm_source=Theme%20of%20the%20Crop&utm_medium=Addon%20List&utm_campaign=Restaurant%20Reservations">
|
291 |
-
<img src="<?php echo RTB_PLUGIN_URL . '/assets/img/theme-good-ol-wine.jpg'; ?>">
|
292 |
-
</a>
|
293 |
-
<h3><?php esc_html_e( "Good Ol' Wine", 'restaurant-reservations' ); ?></h3>
|
294 |
-
<div class="details">
|
295 |
-
<div class="description">
|
296 |
-
<?php esc_html_e( 'A beautiful responsive theme that is suitable for wine enthusiasts, wineries and wine bars.', 'restaurant-reservations' ); ?>
|
297 |
-
</div>
|
298 |
-
<div class="action">
|
299 |
-
<a href="http://www.anarieldesign.com/themes/wine-and-winery-wordpress-theme/?utm_source=Theme%20of%20the%20Crop&utm_medium=Addon%20List&utm_campaign=Restaurant%20Reservations" class="button" target="_blank">
|
300 |
-
<?php esc_html_e( 'View Theme', 'restaurant-reservations' ); ?>
|
301 |
-
</a>
|
302 |
-
<span class="rtb-by">
|
303 |
-
by <a href="http://www.anarieldesign.com/">Anariel Design</a>
|
304 |
-
</span>
|
305 |
-
</div>
|
306 |
-
</div>
|
307 |
-
</div>
|
308 |
-
<div class="addon addon-themes">
|
309 |
-
<a href="http://www.anarieldesign.com/themes/simple-and-fresh-blogging-theme/?utm_source=Theme%20of%20the%20Crop&utm_medium=Addon%20List&utm_campaign=Restaurant%20Reservations">
|
310 |
-
<img src="<?php echo RTB_PLUGIN_URL . '/assets/img/theme-healthy-living.jpg'; ?>">
|
311 |
-
</a>
|
312 |
-
<h3><?php esc_html_e( 'Healthy Living', 'restaurant-reservations' ); ?></h3>
|
313 |
-
<div class="details">
|
314 |
-
<div class="description">
|
315 |
-
<?php esc_html_e( 'A modern, clean healthy food blogging theme that can be used for a restaurant as well.', 'restaurant-reservations' ); ?>
|
316 |
-
</div>
|
317 |
-
<div class="action">
|
318 |
-
<a href="http://www.anarieldesign.com/themes/simple-and-fresh-blogging-theme/?utm_source=Theme%20of%20the%20Crop&utm_medium=Addon%20List&utm_campaign=Restaurant%20Reservations" class="button" target="_blank">
|
319 |
-
<?php esc_html_e( 'View Theme', 'restaurant-reservations' ); ?>
|
320 |
-
</a>
|
321 |
-
<span class="rtb-by">
|
322 |
-
by <a href="http://www.anarieldesign.com/">Anariel Design</a>
|
323 |
-
</span>
|
324 |
-
</div>
|
325 |
-
</div>
|
326 |
-
</div>
|
327 |
-
</div>*/ ?>
|
328 |
-
<?php do_action( 'rtb_addons_post' ); ?>
|
329 |
-
</div>
|
330 |
-
|
331 |
-
<?php
|
332 |
-
}
|
333 |
-
|
334 |
-
/**
|
335 |
-
* Add a prompt for users to subscribe to the Five Star Plugins mailing list
|
336 |
-
* below the addons list.
|
337 |
-
*
|
338 |
-
* @since 0.1
|
339 |
-
*/
|
340 |
-
public function add_subscribe_pompt() {
|
341 |
-
|
342 |
-
?>
|
343 |
-
|
344 |
-
<p>
|
345 |
-
<?php
|
346 |
-
echo sprintf(
|
347 |
-
esc_html_x( 'Find out when new addons are available by subscribing to the %smonthly newsletter%s, liking %sFive Star Plugins%s on Facebook, or following %sFive Star Plugins%s on Twitter.', 'restaurant-reservations' ),
|
348 |
-
'<a target="_blank" href="https://www.fivestarplugins.com/">',
|
349 |
-
'</a>',
|
350 |
-
'<a target="_blank" href="https://www.facebook.com/fivestarplugins/">',
|
351 |
-
'</a>',
|
352 |
-
'<a target="_blank" href="http://twitter.com/fivestarplugins">',
|
353 |
-
'</a>'
|
354 |
-
);
|
355 |
-
?>
|
356 |
-
</p>
|
357 |
-
|
358 |
-
<?php
|
359 |
-
}
|
360 |
-
|
361 |
-
}
|
362 |
-
} // endif;
|
1 |
+
<?php
|
2 |
+
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
+
|
4 |
+
if ( !class_exists( 'rtbAddons' ) ) {
|
5 |
+
/**
|
6 |
+
* Class to handle the addons page for Restaurant Reservations
|
7 |
+
*
|
8 |
+
* @since 1.3
|
9 |
+
*/
|
10 |
+
class rtbAddons {
|
11 |
+
|
12 |
+
public function __construct( ) {
|
13 |
+
|
14 |
+
// Add the admin menu
|
15 |
+
add_action( 'admin_menu', array( $this, 'add_menu_page' ), 100 );
|
16 |
+
|
17 |
+
// Add a newsletter subscription prompt above the addons
|
18 |
+
add_action( 'rtb_addons_pre', array( $this, 'add_subscribe_pompt' ) );
|
19 |
+
}
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Add the addons page to the admin menu
|
23 |
+
*/
|
24 |
+
public function add_menu_page() {
|
25 |
+
|
26 |
+
add_submenu_page(
|
27 |
+
'rtb-bookings',
|
28 |
+
_x( 'Addons', 'Title of addons page', 'restaurant-reservations' ),
|
29 |
+
_x( 'Addons', 'Title of addons page in the admin menu', 'restaurant-reservations' ),
|
30 |
+
'manage_options',
|
31 |
+
'rtb-addons',
|
32 |
+
array( $this, 'show_admin_addons_page' )
|
33 |
+
);
|
34 |
+
|
35 |
+
}
|
36 |
+
|
37 |
+
/**
|
38 |
+
* Display the addons page
|
39 |
+
*/
|
40 |
+
public function show_admin_addons_page() {
|
41 |
+
|
42 |
+
// Set campaign parameters for addon URLs
|
43 |
+
$url_params = '?utm_source=Plugin&utm_medium=Addon%20List&utm_campaign=Restaurant%20Reservations';
|
44 |
+
?>
|
45 |
+
|
46 |
+
<div class="wrap">
|
47 |
+
<h1><?php _e( 'Addons for Restaurant Reservations', 'restaurant-reservations' ); ?></h1>
|
48 |
+
<?php do_action( 'rtb_addons_pre' ); ?>
|
49 |
+
<div class="rtb-addons">
|
50 |
+
<div class="addon addon-custom-fields">
|
51 |
+
<a href="https://www.fivestarplugins.com/plugins/five-star-restaurant-reservations/">
|
52 |
+
<img src="<?php echo RTB_PLUGIN_URL . '/assets/img/custom-fields.png'; ?>">
|
53 |
+
</a>
|
54 |
+
<h3><?php esc_html_e( 'Custom Fields', 'restaurant-reservations' ); ?></h3>
|
55 |
+
<div class="details">
|
56 |
+
<div class="description">
|
57 |
+
<?php esc_html_e( 'Plan your dinner service better by asking for special seating requests, dietary needs and more when customers book online.', 'restaurant-reservations' ); ?>
|
58 |
+
</div>
|
59 |
+
<div class="action">
|
60 |
+
<a href="https://www.fivestarplugins.com/plugins/five-star-restaurant-reservations/" class="button button-primary" target="_blank">
|
61 |
+
<?php esc_html_e( 'Learn More', 'restaurant-reservations' ); ?>
|
62 |
+
</a>
|
63 |
+
</div>
|
64 |
+
</div>
|
65 |
+
</div>
|
66 |
+
<div class="addon addon-export-bookings">
|
67 |
+
<a href="https://www.fivestarplugins.com/plugins/five-star-restaurant-reservations/">
|
68 |
+
<img src="<?php echo RTB_PLUGIN_URL . '/assets/img/export-bookings.png'; ?>">
|
69 |
+
</a>
|
70 |
+
<h3><?php esc_html_e( 'Export Bookings', 'restaurant-reservations' ); ?></h3>
|
71 |
+
<div class="details">
|
72 |
+
<div class="description">
|
73 |
+
<?php esc_html_e( 'Easily print your bookings in a PDF or export them to an Excel/CSV file so you can analyze patterns, cull customer data and import bookings into other services.', 'restaurant-reservations' ); ?>
|
74 |
+
</div>
|
75 |
+
<div class="action">
|
76 |
+
<a href="https://www.fivestarplugins.com/plugins/five-star-restaurant-reservations/" class="button button-primary" target="_blank">
|
77 |
+
<?php esc_html_e( 'Learn More', 'restaurant-reservations' ); ?>
|
78 |
+
</a>
|
79 |
+
</div>
|
80 |
+
</div>
|
81 |
+
</div>
|
82 |
+
<div class="addon addon-email-templates">
|
83 |
+
<a href="https://www.fivestarplugins.com/plugins/five-star-restaurant-reservations/">
|
84 |
+
<img src="<?php echo RTB_PLUGIN_URL . '/assets/img/email-templates.png'; ?>">
|
85 |
+
</a>
|
86 |
+
<h3><?php esc_html_e( 'Email Templates', 'restaurant-reservations' ); ?></h3>
|
87 |
+
<div class="details">
|
88 |
+
<div class="description">
|
89 |
+
<?php esc_html_e( 'Send beautiful email notifications with your own logo and brand colors when your customers make a reservation.', 'restaurant-reservations' ); ?>
|
90 |
+
</div>
|
91 |
+
<div class="action">
|
92 |
+
<a href="https://www.fivestarplugins.com/plugins/five-star-restaurant-reservations/" class="button button-primary" target="_blank">
|
93 |
+
<?php esc_html_e( 'Learn More', 'restaurant-reservations' ); ?>
|
94 |
+
</a>
|
95 |
+
</div>
|
96 |
+
</div>
|
97 |
+
</div>
|
98 |
+
<div class="addon addon-mailchimp">
|
99 |
+
<a href="https://www.fivestarplugins.com/plugins/five-star-restaurant-reservations/">
|
100 |
+
<img src="<?php echo RTB_PLUGIN_URL . '/assets/img/mailchimp.png'; ?>">
|
101 |
+
</a>
|
102 |
+
<h3><?php esc_html_e( 'MailChimp', 'restaurant-reservations' ); ?></h3>
|
103 |
+
<div class="details">
|
104 |
+
<div class="description">
|
105 |
+
<?php esc_html_e( 'Subscribe requests to your MailChimp mailing list and watch your subscription rates grow effortlessly.', 'restaurant-reservations' ); ?>
|
106 |
+
</div>
|
107 |
+
<div class="action">
|
108 |
+
<a href="https://www.fivestarplugins.com/plugins/five-star-restaurant-reservations/" class="button button-primary" target="_blank">
|
109 |
+
<?php esc_html_e( 'Learn More', 'restaurant-reservations' ); ?>
|
110 |
+
</a>
|
111 |
+
</div>
|
112 |
+
</div>
|
113 |
+
</div>
|
114 |
+
</div><?php /*
|
115 |
+
<h2>Recommended Themes</h2>
|
116 |
+
<p>The following restaurant themes integrate beautifully with Restaurant Reservations, providing a clean, stylized booking form that matches your site's design.</p>
|
117 |
+
<div class="rtb-addons">
|
118 |
+
<div class="addon addon-themes">
|
119 |
+
<a href="https://www.fivestarplugins.com/">
|
120 |
+
<img src="<?php echo RTB_PLUGIN_URL . '/assets/img/theme-augustan.jpg'; ?>">
|
121 |
+
</a>
|
122 |
+
<h3><?php esc_html_e( 'Augustan', 'restaurant-reservations' ); ?></h3>
|
123 |
+
<div class="details">
|
124 |
+
<div class="description">
|
125 |
+
<?php esc_html_e( 'A traditionally elegant theme for high-class restaurants, with simple setup and powerful features.', 'restaurant-reservations' ); ?>
|
126 |
+
</div>
|
127 |
+
<div class="action">
|
128 |
+
<a href="https://www.fivestarplugins.com/" class="button" target="_blank">
|
129 |
+
<?php esc_html_e( 'View Theme', 'restaurant-reservations' ); ?>
|
130 |
+
</a>
|
131 |
+
<span class="rtb-by">
|
132 |
+
by <a href="https://www.fivestarplugins.com/<?php echo esc_url( $url_params ); ?>">Five Star Plugins</a>
|
133 |
+
</span>
|
134 |
+
</div>
|
135 |
+
</div>
|
136 |
+
</div>
|
137 |
+
<div class="addon addon-themes">
|
138 |
+
<a href="https://www.fivestarplugins.com/">
|
139 |
+
<img src="<?php echo RTB_PLUGIN_URL . '/assets/img/theme-luigi.jpg'; ?>">
|
140 |
+
</a>
|
141 |
+
<h3><?php esc_html_e( 'Luigi', 'restaurant-reservations' ); ?></h3>
|
142 |
+
<div class="details">
|
143 |
+
<div class="description">
|
144 |
+
<?php esc_html_e( 'A smart theme for upscale bistros and fine Italian restaurants. Get up and running quickly.', 'restaurant-reservations' ); ?>
|
145 |
+
</div>
|
146 |
+
<div class="action">
|
147 |
+
<a href="https://www.fivestarplugins.com/" class="button" target="_blank">
|
148 |
+
<?php esc_html_e( 'View Theme', 'restaurant-reservations' ); ?>
|
149 |
+
</a>
|
150 |
+
<span class="rtb-by">
|
151 |
+
by <a href="https://www.fivestarplugins.com/">Five Star Plugins</a>
|
152 |
+
</span>
|
153 |
+
</div>
|
154 |
+
</div>
|
155 |
+
</div>
|
156 |
+
<div class="addon addon-themes">
|
157 |
+
<a href="https://www.fivestarplugins.com/">
|
158 |
+
<img src="<?php echo RTB_PLUGIN_URL . '/assets/img/theme-the-spot.jpg'; ?>">
|
159 |
+
</a>
|
160 |
+
<h3><?php esc_html_e( 'The Spot', 'restaurant-reservations' ); ?></h3>
|
161 |
+
<div class="details">
|
162 |
+
<div class="description">
|
163 |
+
<?php esc_html_e( 'A vibrant theme for bars, pubs and destination restaurants with an attention-grabbing homepage.', 'restaurant-reservations' ); ?>
|
164 |
+
</div>
|
165 |
+
<div class="action">
|
166 |
+
<a href="https://www.fivestarplugins.com/" class="button" target="_blank">
|
167 |
+
<?php esc_html_e( 'View Theme', 'restaurant-reservations' ); ?>
|
168 |
+
</a>
|
169 |
+
<span class="rtb-by">
|
170 |
+
by <a href="https://www.fivestarplugins.com/">Five Star Plugins</a>
|
171 |
+
</span>
|
172 |
+
</div>
|
173 |
+
</div>
|
174 |
+
</div>
|
175 |
+
<div class="addon addon-themes">
|
176 |
+
<a href="https://www.fivestarplugins.com/">
|
177 |
+
<img src="<?php echo RTB_PLUGIN_URL . '/assets/img/theme-plate-up.jpg'; ?>">
|
178 |
+
</a>
|
179 |
+
<h3><?php esc_html_e( 'Plate Up', 'restaurant-reservations' ); ?></h3>
|
180 |
+
<div class="details">
|
181 |
+
<div class="description">
|
182 |
+
<?php esc_html_e( 'A refined theme for sophisticated, modern restaurants to drive customers to your booking form.', 'restaurant-reservations' ); ?>
|
183 |
+
</div>
|
184 |
+
<div class="action">
|
185 |
+
<a href="https://www.fivestarplugins.com/" class="button" target="_blank">
|
186 |
+
<?php esc_html_e( 'View Theme', 'restaurant-reservations' ); ?>
|
187 |
+
</a>
|
188 |
+
<span class="rtb-by">
|
189 |
+
by <a href="https://www.fivestarplugins.com/">Five Star Plugins</a>
|
190 |
+
</span>
|
191 |
+
</div>
|
192 |
+
</div>
|
193 |
+
</div>
|
194 |
+
<div class="addon addon-themes">
|
195 |
+
<a href="https://themebeans.com/themes/plate?utm_source=totc_addons_plate&utm_medium=banner&utm_campaign=TOTC%20Addons%20Link%2C%20Plate">
|
196 |
+
<img src="<?php echo RTB_PLUGIN_URL . '/assets/img/theme-plate.jpg'; ?>">
|
197 |
+
</a>
|
198 |
+
<h3><?php esc_html_e( 'Plate', 'restaurant-reservations' ); ?></h3>
|
199 |
+
<div class="details">
|
200 |
+
<div class="description">
|
201 |
+
<?php esc_html_e( 'A delightfully beautiful WordPress theme designed to help you build a stunning restaurant website.', 'restaurant-reservations' ); ?>
|
202 |
+
</div>
|
203 |
+
<div class="action">
|
204 |
+
<a href="https://themebeans.com/themes/plate?utm_source=totc_addons_plate&utm_medium=banner&utm_campaign=TOTC%20Addons%20Link%2C%20Plate" class="button" target="_blank">
|
205 |
+
<?php esc_html_e( 'View Theme', 'restaurant-reservations' ); ?>
|
206 |
+
</a>
|
207 |
+
<span class="rtb-by">
|
208 |
+
by <a href="https://themebeans.com?utm_source=totc_addons_plate&utm_medium=banner&utm_campaign=TOTC%20Addons%20Link%2C%20Plate">ThemeBeans</a>
|
209 |
+
</span>
|
210 |
+
</div>
|
211 |
+
</div>
|
212 |
+
</div>
|
213 |
+
<div class="addon addon-themes">
|
214 |
+
<a href="https://wordpress.org/themes/auberge/">
|
215 |
+
<img src="<?php echo RTB_PLUGIN_URL . '/assets/img/theme-auberge.jpg'; ?>">
|
216 |
+
</a>
|
217 |
+
<h3><?php esc_html_e( 'Auberge', 'restaurant-reservations' ); ?></h3>
|
218 |
+
<div class="details">
|
219 |
+
<div class="description">
|
220 |
+
<?php esc_html_e( 'Display a menu of your restaurant, café or bar stylishly with this free mobile-friendly WordPress theme.', 'restaurant-reservations' ); ?>
|
221 |
+
</div>
|
222 |
+
<div class="action">
|
223 |
+
<a href="https://wordpress.org/themes/auberge/" class="button" target="_blank">
|
224 |
+
<?php esc_html_e( 'View Theme', 'restaurant-reservations' ); ?>
|
225 |
+
</a>
|
226 |
+
<span class="rtb-by">
|
227 |
+
by <a href="https://www.webmandesign.eu/">Webman Design</a>
|
228 |
+
</span>
|
229 |
+
</div>
|
230 |
+
</div>
|
231 |
+
</div>
|
232 |
+
<div class="addon addon-themes">
|
233 |
+
<a href="http://www.anarieldesign.com/themes/restaurant-bar-wordpress-theme/?utm_source=Theme%20of%20the%20Crop&utm_medium=Addon%20List&utm_campaign=Restaurant%20Reservations">
|
234 |
+
<img src="<?php echo RTB_PLUGIN_URL . '/assets/img/theme-liber.jpg'; ?>">
|
235 |
+
</a>
|
236 |
+
<h3><?php esc_html_e( 'Liber', 'restaurant-reservations' ); ?></h3>
|
237 |
+
<div class="details">
|
238 |
+
<div class="description">
|
239 |
+
<?php esc_html_e( 'A responsive theme optimized for restaurants and bars supporting features these websites need.', 'restaurant-reservations' ); ?>
|
240 |
+
</div>
|
241 |
+
<div class="action">
|
242 |
+
<a href="http://www.anarieldesign.com/themes/restaurant-bar-wordpress-theme/?utm_source=Theme%20of%20the%20Crop&utm_medium=Addon%20List&utm_campaign=Restaurant%20Reservations" class="button" target="_blank">
|
243 |
+
<?php esc_html_e( 'View Theme', 'restaurant-reservations' ); ?>
|
244 |
+
</a>
|
245 |
+
<span class="rtb-by">
|
246 |
+
by <a href="http://www.anarieldesign.com/">Anariel Design</a>
|
247 |
+
</span>
|
248 |
+
</div>
|
249 |
+
</div>
|
250 |
+
</div>
|
251 |
+
<div class="addon addon-themes">
|
252 |
+
<a href="https://wordpress.org/themes/brasserie/">
|
253 |
+
<img src="<?php echo RTB_PLUGIN_URL . '/assets/img/theme-brasserie.jpg'; ?>">
|
254 |
+
</a>
|
255 |
+
<h3><?php esc_html_e( 'Brasserie', 'restaurant-reservations' ); ?></h3>
|
256 |
+
<div class="details">
|
257 |
+
<div class="description">
|
258 |
+
<?php esc_html_e( 'A delightfully simple to use and beautifully crafted free theme for any food establishment.', 'restaurant-reservations' ); ?>
|
259 |
+
</div>
|
260 |
+
<div class="action">
|
261 |
+
<a href="https://wordpress.org/themes/brasserie/" class="button" target="_blank">
|
262 |
+
<?php esc_html_e( 'View Theme', 'restaurant-reservations' ); ?>
|
263 |
+
</a>
|
264 |
+
<span class="rtb-by">
|
265 |
+
by <a href="https://www.templateexpress.com/">Template Express</a>
|
266 |
+
</span>
|
267 |
+
</div>
|
268 |
+
</div>
|
269 |
+
</div>
|
270 |
+
<div class="addon addon-themes">
|
271 |
+
<a href="http://www.anarieldesign.com/themes/food-blog-wordpress-theme/?utm_source=Theme%20of%20the%20Crop&utm_medium=Addon%20List&utm_campaign=Restaurant%20Reservations">
|
272 |
+
<img src="<?php echo RTB_PLUGIN_URL . '/assets/img/theme-veggie.jpg'; ?>">
|
273 |
+
</a>
|
274 |
+
<h3><?php esc_html_e( 'Veggie', 'restaurant-reservations' ); ?></h3>
|
275 |
+
<div class="details">
|
276 |
+
<div class="description">
|
277 |
+
<?php esc_html_e( 'A food blogging and restaurant theme with modern, easy-to-read typography and minimalist design.', 'restaurant-reservations' ); ?>
|
278 |
+
</div>
|
279 |
+
<div class="action">
|
280 |
+
<a href="http://www.anarieldesign.com/themes/food-blog-wordpress-theme/?utm_source=Theme%20of%20the%20Crop&utm_medium=Addon%20List&utm_campaign=Restaurant%20Reservations" class="button" target="_blank">
|
281 |
+
<?php esc_html_e( 'View Theme', 'restaurant-reservations' ); ?>
|
282 |
+
</a>
|
283 |
+
<span class="rtb-by">
|
284 |
+
by <a href="http://www.anarieldesign.com/">Anariel Design</a>
|
285 |
+
</span>
|
286 |
+
</div>
|
287 |
+
</div>
|
288 |
+
</div>
|
289 |
+
<div class="addon addon-themes">
|
290 |
+
<a href="http://www.anarieldesign.com/themes/wine-and-winery-wordpress-theme/?utm_source=Theme%20of%20the%20Crop&utm_medium=Addon%20List&utm_campaign=Restaurant%20Reservations">
|
291 |
+
<img src="<?php echo RTB_PLUGIN_URL . '/assets/img/theme-good-ol-wine.jpg'; ?>">
|
292 |
+
</a>
|
293 |
+
<h3><?php esc_html_e( "Good Ol' Wine", 'restaurant-reservations' ); ?></h3>
|
294 |
+
<div class="details">
|
295 |
+
<div class="description">
|
296 |
+
<?php esc_html_e( 'A beautiful responsive theme that is suitable for wine enthusiasts, wineries and wine bars.', 'restaurant-reservations' ); ?>
|
297 |
+
</div>
|
298 |
+
<div class="action">
|
299 |
+
<a href="http://www.anarieldesign.com/themes/wine-and-winery-wordpress-theme/?utm_source=Theme%20of%20the%20Crop&utm_medium=Addon%20List&utm_campaign=Restaurant%20Reservations" class="button" target="_blank">
|
300 |
+
<?php esc_html_e( 'View Theme', 'restaurant-reservations' ); ?>
|
301 |
+
</a>
|
302 |
+
<span class="rtb-by">
|
303 |
+
by <a href="http://www.anarieldesign.com/">Anariel Design</a>
|
304 |
+
</span>
|
305 |
+
</div>
|
306 |
+
</div>
|
307 |
+
</div>
|
308 |
+
<div class="addon addon-themes">
|
309 |
+
<a href="http://www.anarieldesign.com/themes/simple-and-fresh-blogging-theme/?utm_source=Theme%20of%20the%20Crop&utm_medium=Addon%20List&utm_campaign=Restaurant%20Reservations">
|
310 |
+
<img src="<?php echo RTB_PLUGIN_URL . '/assets/img/theme-healthy-living.jpg'; ?>">
|
311 |
+
</a>
|
312 |
+
<h3><?php esc_html_e( 'Healthy Living', 'restaurant-reservations' ); ?></h3>
|
313 |
+
<div class="details">
|
314 |
+
<div class="description">
|
315 |
+
<?php esc_html_e( 'A modern, clean healthy food blogging theme that can be used for a restaurant as well.', 'restaurant-reservations' ); ?>
|
316 |
+
</div>
|
317 |
+
<div class="action">
|
318 |
+
<a href="http://www.anarieldesign.com/themes/simple-and-fresh-blogging-theme/?utm_source=Theme%20of%20the%20Crop&utm_medium=Addon%20List&utm_campaign=Restaurant%20Reservations" class="button" target="_blank">
|
319 |
+
<?php esc_html_e( 'View Theme', 'restaurant-reservations' ); ?>
|
320 |
+
</a>
|
321 |
+
<span class="rtb-by">
|
322 |
+
by <a href="http://www.anarieldesign.com/">Anariel Design</a>
|
323 |
+
</span>
|
324 |
+
</div>
|
325 |
+
</div>
|
326 |
+
</div>
|
327 |
+
</div>*/ ?>
|
328 |
+
<?php do_action( 'rtb_addons_post' ); ?>
|
329 |
+
</div>
|
330 |
+
|
331 |
+
<?php
|
332 |
+
}
|
333 |
+
|
334 |
+
/**
|
335 |
+
* Add a prompt for users to subscribe to the Five Star Plugins mailing list
|
336 |
+
* below the addons list.
|
337 |
+
*
|
338 |
+
* @since 0.1
|
339 |
+
*/
|
340 |
+
public function add_subscribe_pompt() {
|
341 |
+
|
342 |
+
?>
|
343 |
+
|
344 |
+
<p>
|
345 |
+
<?php
|
346 |
+
echo sprintf(
|
347 |
+
esc_html_x( 'Find out when new addons are available by subscribing to the %smonthly newsletter%s, liking %sFive Star Plugins%s on Facebook, or following %sFive Star Plugins%s on Twitter.', 'restaurant-reservations' ),
|
348 |
+
'<a target="_blank" href="https://www.fivestarplugins.com/">',
|
349 |
+
'</a>',
|
350 |
+
'<a target="_blank" href="https://www.facebook.com/fivestarplugins/">',
|
351 |
+
'</a>',
|
352 |
+
'<a target="_blank" href="http://twitter.com/fivestarplugins">',
|
353 |
+
'</a>'
|
354 |
+
);
|
355 |
+
?>
|
356 |
+
</p>
|
357 |
+
|
358 |
+
<?php
|
359 |
+
}
|
360 |
+
|
361 |
+
}
|
362 |
+
} // endif;
|
includes/AdminBookings.class.php
CHANGED
@@ -1,928 +1,928 @@
|
|
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 |
-
/**
|
13 |
-
* The bookings table
|
14 |
-
*
|
15 |
-
* This is only instantiated on the bookings admin page at the moment when
|
16 |
-
* it is generated.
|
17 |
-
*
|
18 |
-
* @see self::show_admin_bookings_page()
|
19 |
-
* @see WP_List_table.BookingsTable.class.php
|
20 |
-
* @since 1.6
|
21 |
-
*/
|
22 |
-
public $bookings_table;
|
23 |
-
|
24 |
-
public function __construct() {
|
25 |
-
|
26 |
-
// Add the admin menu
|
27 |
-
add_action( 'admin_menu', array( $this, 'add_menu_page' ) );
|
28 |
-
|
29 |
-
// Print the modals
|
30 |
-
add_action( 'admin_footer-toplevel_page_rtb-bookings', array( $this, 'print_modals' ) );
|
31 |
-
|
32 |
-
// Receive Ajax requests
|
33 |
-
add_action( 'wp_ajax_nopriv_rtb-admin-booking-modal' , array( $this , 'nopriv_ajax' ) );
|
34 |
-
add_action( 'wp_ajax_rtb-admin-booking-modal', array( $this, 'booking_modal_ajax' ) );
|
35 |
-
add_action( 'wp_ajax_nopriv_rtb-admin-trash-booking' , array( $this , 'nopriv_ajax' ) );
|
36 |
-
add_action( 'wp_ajax_rtb-admin-trash-booking', array( $this, 'trash_booking_ajax' ) );
|
37 |
-
add_action( 'wp_ajax_nopriv_rtb-admin-email-modal' , array( $this , 'nopriv_ajax' ) );
|
38 |
-
add_action( 'wp_ajax_rtb-admin-email-modal', array( $this, 'email_modal_ajax' ) );
|
39 |
-
add_action( 'wp_ajax_nopriv_rtb-admin-column-modal' , array( $this , 'nopriv_ajax' ) );
|
40 |
-
add_action( 'wp_ajax_rtb-admin-column-modal', array( $this, 'column_modal_ajax' ) );
|
41 |
-
add_action( 'wp_ajax_nopriv_rtb-admin-ban-modal' , array( $this , 'nopriv_ajax' ) );
|
42 |
-
add_action( 'wp_ajax_rtb-admin-ban-modal', array( $this, 'ban_modal_ajax' ) );
|
43 |
-
add_action( 'wp_ajax_nopriv_rtb-admin-delete-modal' , array( $this , 'nopriv_ajax' ) );
|
44 |
-
add_action( 'wp_ajax_rtb-admin-delete-modal', array( $this, 'delete_modal_ajax' ) );
|
45 |
-
|
46 |
-
add_action( 'wp_ajax_nopriv_rtb_set_reservation_arrived' , array( $this , 'nopriv_ajax' ) );
|
47 |
-
add_action( 'wp_ajax_rtb_set_reservation_arrived', array( $this, 'set_booking_arrived' ) );
|
48 |
-
|
49 |
-
// Validate post status and notification fields
|
50 |
-
add_action( 'rtb_validate_booking_submission', array( $this, 'validate_admin_fields' ) );
|
51 |
-
|
52 |
-
// Set post status when adding to the database
|
53 |
-
add_filter( 'rtb_insert_booking_data', array( $this, 'insert_booking_data' ), 10, 2 );
|
54 |
-
|
55 |
-
// Add the columns configuration button to the table
|
56 |
-
add_action( 'rtb_bookings_table_actions', array( $this, 'print_columns_config_button' ), 9 );
|
57 |
-
|
58 |
-
}
|
59 |
-
|
60 |
-
/**
|
61 |
-
* Add the top-level admin menu page
|
62 |
-
* @since 0.0.1
|
63 |
-
*/
|
64 |
-
public function add_menu_page() {
|
65 |
-
|
66 |
-
add_menu_page(
|
67 |
-
_x( 'Bookings', 'Title of admin page that lists bookings', 'restaurant-reservations' ),
|
68 |
-
_x( 'Bookings', 'Title of bookings admin menu item', 'restaurant-reservations' ),
|
69 |
-
'manage_bookings',
|
70 |
-
'rtb-bookings',
|
71 |
-
array( $this, 'show_admin_bookings_page' ),
|
72 |
-
'dashicons-calendar',
|
73 |
-
'26.2987'
|
74 |
-
);
|
75 |
-
|
76 |
-
}
|
77 |
-
|
78 |
-
/**
|
79 |
-
* Display the admin bookings page
|
80 |
-
* @since 0.0.1
|
81 |
-
*/
|
82 |
-
public function show_admin_bookings_page() {
|
83 |
-
|
84 |
-
require_once( RTB_PLUGIN_DIR . '/includes/WP_List_Table.BookingsTable.class.php' );
|
85 |
-
$this->bookings_table = new rtbBookingsTable();
|
86 |
-
$this->bookings_table->prepare_items();
|
87 |
-
?>
|
88 |
-
|
89 |
-
<div class="wrap">
|
90 |
-
<h1>
|
91 |
-
<?php _e( 'Restaurant Bookings', 'restaurant-reservations' ); ?>
|
92 |
-
<a href="#" class="add-new-h2 page-title-action add-booking"><?php _e( 'Add New', 'restaurant-reservations' ); ?></a>
|
93 |
-
</h1>
|
94 |
-
|
95 |
-
<?php do_action( 'rtb_bookings_table_top' ); ?>
|
96 |
-
<form id="rtb-bookings-table" method="POST" action="">
|
97 |
-
<input type="hidden" name="post_type" value="<?php echo RTB_BOOKING_POST_TYPE; ?>" />
|
98 |
-
<input type="hidden" name="page" value="rtb-bookings">
|
99 |
-
|
100 |
-
<div class="rtb-primary-controls clearfix">
|
101 |
-
<div class="rtb-views">
|
102 |
-
<?php $this->bookings_table->views(); ?>
|
103 |
-
</div>
|
104 |
-
<?php $this->bookings_table->advanced_filters(); ?>
|
105 |
-
</div>
|
106 |
-
|
107 |
-
<?php $this->bookings_table->display(); ?>
|
108 |
-
</form>
|
109 |
-
<?php do_action( 'rtb_bookings_table_btm' ); ?>
|
110 |
-
</div>
|
111 |
-
|
112 |
-
<?php
|
113 |
-
}
|
114 |
-
|
115 |
-
/**
|
116 |
-
* Print button for configuring columns
|
117 |
-
*
|
118 |
-
* @param string pos Position of this tablenav: top|btm
|
119 |
-
* @since 0.1
|
120 |
-
*/
|
121 |
-
public function print_columns_config_button( $pos ) {
|
122 |
-
if ( $pos != 'top' ) {
|
123 |
-
return;
|
124 |
-
}
|
125 |
-
?>
|
126 |
-
|
127 |
-
<div class="alignleft actions rtb-actions">
|
128 |
-
<a href="#" class="button rtb-columns-button">
|
129 |
-
<span class="dashicons dashicons-admin-settings"></span>
|
130 |
-
<?php esc_html_e( 'Columns', 'restaurant-reservations' ); ?>
|
131 |
-
</a>
|
132 |
-
</div>
|
133 |
-
|
134 |
-
<?php
|
135 |
-
}
|
136 |
-
|
137 |
-
/**
|
138 |
-
* Print the modal containers
|
139 |
-
*
|
140 |
-
* New/edit bookings, send email, configure columns, errors.
|
141 |
-
*
|
142 |
-
* @since 0.0.1
|
143 |
-
*/
|
144 |
-
public function print_modals() {
|
145 |
-
|
146 |
-
global $rtb_controller;
|
147 |
-
?>
|
148 |
-
|
149 |
-
<!-- Restaurant Reservations add/edit booking modal -->
|
150 |
-
<div id="rtb-booking-modal" class="rtb-admin-modal">
|
151 |
-
<div class="rtb-booking-form rtb-container">
|
152 |
-
<form method="POST">
|
153 |
-
<input type="hidden" name="action" value="admin_booking_request">
|
154 |
-
<input type="hidden" name="ID" value="">
|
155 |
-
|
156 |
-
<?php
|
157 |
-
/**
|
158 |
-
* The generated fields are wrapped in a div so we can
|
159 |
-
* replace its contents with an HTML blob passed back from
|
160 |
-
* an Ajax request. This way the field data and error
|
161 |
-
* messages are always populated from the same server-side
|
162 |
-
* code.
|
163 |
-
*/
|
164 |
-
?>
|
165 |
-
<div id="rtb-booking-form-fields">
|
166 |
-
<?php echo $this->print_booking_form_fields(); ?>
|
167 |
-
</div>
|
168 |
-
|
169 |
-
<button type="submit" class="button button-primary">
|
170 |
-
<?php _e( 'Add Booking', 'restaurant-reservations' ); ?>
|
171 |
-
</button>
|
172 |
-
<a href="#" class="button" id="rtb-cancel-booking-modal">
|
173 |
-
<?php _e( 'Cancel', 'restaurant-reservations' ); ?>
|
174 |
-
</a>
|
175 |
-
<div class="action-status">
|
176 |
-
<span class="spinner loading"></span>
|
177 |
-
<span class="dashicons dashicons-no-alt error"></span>
|
178 |
-
<span class="dashicons dashicons-yes success"></span>
|
179 |
-
</div>
|
180 |
-
</form>
|
181 |
-
</div>
|
182 |
-
</div>
|
183 |
-
|
184 |
-
<!-- Restaurant Reservations send email modal -->
|
185 |
-
<div id="rtb-email-modal" class="rtb-admin-modal">
|
186 |
-
<div class="rtb-email-form rtb-container">
|
187 |
-
<form method="POST">
|
188 |
-
<input type="hidden" name="action" value="admin_send_email">
|
189 |
-
<input type="hidden" name="ID" value="">
|
190 |
-
<input type="hidden" name="name" value="">
|
191 |
-
<input type="hidden" name="email" value="">
|
192 |
-
|
193 |
-
<fieldset>
|
194 |
-
<legend><?php _e( 'Send Email', 'restaurant-reservations' ); ?></legend>
|
195 |
-
|
196 |
-
<div class="to">
|
197 |
-
<label for="rtb-email-to"><?php _ex( 'To', 'Label next to the email address to which an email will be sent', 'restaurant-reservations' ); ?></label>
|
198 |
-
<span class="rtb-email-to"></span>
|
199 |
-
</div>
|
200 |
-
<div class="subject">
|
201 |
-
<label for="rtb-email-subject"><?php _e( 'Subject', 'restaurant-reservations' ); ?></label>
|
202 |
-
<input type="text" name="rtb-email-subject" placeholder="<?php echo $rtb_controller->settings->get_setting( 'subject-admin-notice'); ?>">
|
203 |
-
</div>
|
204 |
-
<div class="message">
|
205 |
-
<label for="rtb-email-message"><?php _e( 'Message', 'restaurant-reservations' ); ?></label>
|
206 |
-
<textarea name="rtb-email-message" id="rtb-email-message"></textarea>
|
207 |
-
</div>
|
208 |
-
</fieldset>
|
209 |
-
|
210 |
-
<button type="submit" class="button button-primary">
|
211 |
-
<?php _e( 'Send Email', 'restaurant-reservations' ); ?>
|
212 |
-
</button>
|
213 |
-
<a href="#" class="button" id="rtb-cancel-email-modal">
|
214 |
-
<?php _e( 'Cancel', 'restaurant-reservations' ); ?>
|
215 |
-
</a>
|
216 |
-
<div class="action-status">
|
217 |
-
<span class="spinner loading"></span>
|
218 |
-
<span class="dashicons dashicons-no-alt error"></span>
|
219 |
-
<span class="dashicons dashicons-yes success"></span>
|
220 |
-
</div>
|
221 |
-
</form>
|
222 |
-
</div>
|
223 |
-
</div>
|
224 |
-
|
225 |
-
<!-- Restaurant Reservations column configuration modal -->
|
226 |
-
<div id="rtb-column-modal" class="rtb-admin-modal">
|
227 |
-
<div class="rtb-column-form rtb-container">
|
228 |
-
<form method="POST">
|
229 |
-
<input type="hidden" name="action" value="admin_column_config">
|
230 |
-
|
231 |
-
<fieldset>
|
232 |
-
<legend><?php esc_html_e( 'Columns', 'restaurant-reservations' ); ?></legend>
|
233 |
-
<ul>
|
234 |
-
<?php
|
235 |
-
$columns = $this->bookings_table->get_all_columns();
|
236 |
-
$visible = $this->bookings_table->get_columns();
|
237 |
-
foreach( $columns as $column => $label ) :
|
238 |
-
// Don't allow these columns to be hidden
|
239 |
-
if ( $column == 'cb' || $column == 'details' || $column == 'date' ) {
|
240 |
-
continue;
|
241 |
-
}
|
242 |
-
?>
|
243 |
-
<li>
|
244 |
-
<label>
|
245 |
-
<input type="checkbox" name="rtb-columns-config" value="<?php esc_attr_e( $column ); ?>"<?php if ( array_key_exists( $column, $visible ) ) : ?> checked<?php endif; ?>>
|
246 |
-
<?php esc_html_e( $label ); ?>
|
247 |
-
</label>
|
248 |
-
</li>
|
249 |
-
<?php
|
250 |
-
endforeach;
|
251 |
-
?>
|
252 |
-
</ul>
|
253 |
-
</fieldset>
|
254 |
-
|
255 |
-
<button type="submit" class="button button-primary">
|
256 |
-
<?php _e( 'Update', 'restaurant-reservations' ); ?>
|
257 |
-
</button>
|
258 |
-
<a href="#" class="button" id="rtb-cancel-column-modal">
|
259 |
-
<?php _e( 'Cancel', 'restaurant-reservations' ); ?>
|
260 |
-
</a>
|
261 |
-
<div class="action-status">
|
262 |
-
<span class="spinner loading"></span>
|
263 |
-
<span class="dashicons dashicons-no-alt error"></span>
|
264 |
-
<span class="dashicons dashicons-yes success"></span>
|
265 |
-
</div>
|
266 |
-
</form>
|
267 |
-
</div>
|
268 |
-
</div>
|
269 |
-
|
270 |
-
<!-- Restaurant Reservations details modal -->
|
271 |
-
<div id="rtb-details-modal" class="rtb-admin-modal">
|
272 |
-
<div class="rtb-details-form rtb-container">
|
273 |
-
<div class="rtb-details-data"></div>
|
274 |
-
<a href="#" class="button" id="rtb-cancel-details-modal">
|
275 |
-
<?php _e( 'Close', 'restaurant-reservations' ); ?>
|
276 |
-
</a>
|
277 |
-
</div>
|
278 |
-
</div>
|
279 |
-
|
280 |
-
<!-- Restaurant Reservations ban email/ip modal -->
|
281 |
-
<div id="rtb-ban-modal" class="rtb-admin-modal">
|
282 |
-
<div class="rtb-ban-form rtb-container">
|
283 |
-
<div class="rtb-ban-msg">
|
284 |
-
<p class="intro">
|
285 |
-
<?php
|
286 |
-
printf(
|
287 |
-
__( 'Ban future bookings from the email address %s or the IP address %s?', 'restaurant-reservations' ),
|
288 |
-
'<span id="rtb-ban-modal-email"></span>',
|
289 |
-
'<span id="rtb-ban-modal-ip"></span>'
|
290 |
-
);
|
291 |
-
?>
|
292 |
-
</p>
|
293 |
-
<p>
|
294 |
-
<?php
|
295 |
-
esc_html_e( 'It is recommended to ban by email address instead of IP. Only ban by IP address to block a malicious user who is using different email addresses to avoid a previous ban.', 'restaurant-reservations' );
|
296 |
-
?>
|
297 |
-
</p>
|
298 |
-
</div>
|
299 |
-
<button class="button button-primary" id="rtb-ban-modal-email-btn">Ban Email</button>
|
300 |
-
<button class="button button-primary" id="rtb-ban-modal-ip-btn">Ban IP</button>
|
301 |
-
<a href="#" id="rtb-cancel-ban-modal" class="button"><?php _e( 'Close', 'restaurant-reservations' ); ?></a>
|
302 |
-
<a class="button-link" href="<?php echo esc_url( admin_url( '/admin.php?page=rtb-settings' ) ); ?>" target="_blank">
|
303 |
-
<?php esc_html_e( 'View all bans', 'restaurant-reservations' ); ?>
|
304 |
-
</a>
|
305 |
-
<div class="action-status">
|
306 |
-
<span class="spinner loading"></span>
|
307 |
-
<span class="dashicons dashicons-no-alt error"></span>
|
308 |
-
<span class="dashicons dashicons-yes success"></span>
|
309 |
-
</div>
|
310 |
-
</div>
|
311 |
-
</div>
|
312 |
-
|
313 |
-
<!-- Restaurant Reservations delete customer modal -->
|
314 |
-
<div id="rtb-delete-modal" class="rtb-admin-modal">
|
315 |
-
<div class="rtb-delete-form rtb-container">
|
316 |
-
<div class="rtb-delete-msg">
|
317 |
-
<?php
|
318 |
-
printf(
|
319 |
-
__( 'Delete all booking records related to email address %s? This action can not be undone.', 'restaurant-reservations' ),
|
320 |
-
'<span id="rtb-delete-modal-email"></span>'
|
321 |
-
);
|
322 |
-
?>
|
323 |
-
</div>
|
324 |
-
<div id="rtb-delete-status">
|
325 |
-
<span class="rtb-delete-status-total">
|
326 |
-
<span id="rtb-delete-status-progress" class="rtb-delete-status-progress"></span>
|
327 |
-
</span>
|
328 |
-
<div id="rtb-delete-status-deleted"></div>
|
329 |
-
</div>
|
330 |
-
<button class="button button-primary" id="rtb-delete-modal-btn">Delete Bookings</button>
|
331 |
-
<button id="rtb-cancel-delete-modal" class="button"><?php _e( 'Close', 'restaurant-reservations' ); ?></button>
|
332 |
-
<div class="action-status">
|
333 |
-
<span class="spinner loading"></span>
|
334 |
-
<span class="dashicons dashicons-no-alt error"></span>
|
335 |
-
<span class="dashicons dashicons-yes success"></span>
|
336 |
-
</div>
|
337 |
-
</div>
|
338 |
-
</div>
|
339 |
-
|
340 |
-
<!-- Restaurant Reservations error message modal -->
|
341 |
-
<div id="rtb-error-modal" class="rtb-admin-modal">
|
342 |
-
<div class="rtb-error rtb-container">
|
343 |
-
<div class="rtb-error-msg"></div>
|
344 |
-
<a href="#" class="button"><?php _e( 'Close', 'restaurant-reservations' ); ?></a>
|
345 |
-
</div>
|
346 |
-
</div>
|
347 |
-
|
348 |
-
<?php
|
349 |
-
}
|
350 |
-
|
351 |
-
/**
|
352 |
-
* Retrieve booking form fields used in the admin booking modal. These
|
353 |
-
* fields are also passed back with ajax requests since they render error
|
354 |
-
* messages and populate fields with validated data.
|
355 |
-
* @since 0.0.1
|
356 |
-
*/
|
357 |
-
public function print_booking_form_fields() {
|
358 |
-
|
359 |
-
global $rtb_controller;
|
360 |
-
|
361 |
-
// Add post status and notification fields to admin booking form
|
362 |
-
add_filter( 'rtb_booking_form_fields', array( $this, 'add_admin_fields' ), 20, 2 );
|
363 |
-
|
364 |
-
// Retrieve the form fields
|
365 |
-
$fields = $rtb_controller->settings->get_booking_form_fields( $rtb_controller->request );
|
366 |
-
|
367 |
-
ob_start();
|
368 |
-
?>
|
369 |
-
|
370 |
-
<?php foreach( $fields as $fieldset => $contents ) : ?>
|
371 |
-
<fieldset class="<?php echo esc_attr( $fieldset ); ?>">
|
372 |
-
<?php
|
373 |
-
foreach( $contents['fields'] as $slug => $field ) {
|
374 |
-
|
375 |
-
$args = empty( $field['callback_args'] ) ? null : $field['callback_args'];
|
376 |
-
|
377 |
-
call_user_func( $field['callback'], $slug, $field['title'], $field['request_input'], $args );
|
378 |
-
}
|
379 |
-
?>
|
380 |
-
</fieldset>
|
381 |
-
<?php endforeach;
|
382 |
-
|
383 |
-
// Remove the admin fields filter
|
384 |
-
remove_filter( 'rtb_booking_form_fields', array( $this, 'add_admin_fields' ) );
|
385 |
-
|
386 |
-
return ob_get_clean();
|
387 |
-
}
|
388 |
-
|
389 |
-
/**
|
390 |
-
* Add the post status and notifications fields to the booking form fields
|
391 |
-
* @since 1.3
|
392 |
-
*/
|
393 |
-
public function add_admin_fields( $fields, $request ) {
|
394 |
-
|
395 |
-
if ( !is_admin() || !current_user_can( 'manage_bookings' ) ) {
|
396 |
-
return $fields;
|
397 |
-
}
|
398 |
-
|
399 |
-
global $rtb_controller;
|
400 |
-
|
401 |
-
// Get all valid booking statuses
|
402 |
-
$booking_statuses = array();
|
403 |
-
foreach( $rtb_controller->cpts->booking_statuses as $status => $args ) {
|
404 |
-
$booking_statuses[ $status ] = $args['label'];
|
405 |
-
}
|
406 |
-
|
407 |
-
$fields['admin'] = array(
|
408 |
-
'fields' => array(
|
409 |
-
'post-status' => array(
|
410 |
-
'title' => __( 'Booking Status', 'restaurant-reservations' ),
|
411 |
-
'request_input' => empty( $request->post_status ) ? '' : $request->post_status,
|
412 |
-
'callback' => 'rtb_print_form_select_field',
|
413 |
-
'callback_args' => array(
|
414 |
-
'options' => $booking_statuses,
|
415 |
-
)
|
416 |
-
),
|
417 |
-
'notifications' => array(
|
418 |
-
'title' => __( 'Send notifications', 'restaurant-reservations' ),
|
419 |
-
'request_input' => empty( $request->send_notifications ) ? false : $request->send_notifications,
|
420 |
-
'callback' => array( $this, 'print_form_send_notifications_field' ),
|
421 |
-
'callback_args' => array(
|
422 |
-
'description' => array(
|
423 |
-
'prompt' => __( 'Learn more', 'restaurant-reservations' ),
|
424 |
-
'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' ),
|
425 |
-
),
|
426 |
-
),
|
427 |
-
),
|
428 |
-
),
|
429 |
-
);
|
430 |
-
|
431 |
-
return $fields;
|
432 |
-
}
|
433 |
-
|
434 |
-
|
435 |
-
/**
|
436 |
-
* Print a field to toggle notifications when adding/editing a booking from
|
437 |
-
* the admin
|
438 |
-
* @since 1.3
|
439 |
-
*/
|
440 |
-
function print_form_send_notifications_field( $slug, $title, $value, $args ) {
|
441 |
-
|
442 |
-
$slug = esc_attr( $slug );
|
443 |
-
$title = esc_attr( $title );
|
444 |
-
$value = (bool) $value;
|
445 |
-
$description = empty( $args['description'] ) || empty( $args['description']['prompt'] ) || empty( $args['description']['text'] ) ? null : $args['description'];
|
446 |
-
?>
|
447 |
-
|
448 |
-
<div class="<?php echo esc_attr( $slug ); ?>">
|
449 |
-
<?php echo rtb_print_form_error( $slug ); ?>
|
450 |
-
<label>
|
451 |
-
<input type="checkbox" name="rtb-<?php echo esc_attr( $slug ); ?>" value="1"<?php checked( $value ); ?>>
|
452 |
-
<?php echo esc_html( $title ); ?>
|
453 |
-
<?php if ( !empty( $description ) ) : ?>
|
454 |
-
<a href="#" class="rtb-description-prompt">
|
455 |
-
<?php echo esc_html( $description['prompt'] ); ?>
|
456 |
-
</a>
|
457 |
-
<?php endif; ?>
|
458 |
-
</label>
|
459 |
-
<?php if ( !empty( $description ) ) : ?>
|
460 |
-
<div class="rtb-description">
|
461 |
-
<?php echo esc_html( $description['text'] ); ?>
|
462 |
-
</div>
|
463 |
-
<?php endif; ?>
|
464 |
-
</div>
|
465 |
-
|
466 |
-
<?php
|
467 |
-
}
|
468 |
-
|
469 |
-
/**
|
470 |
-
* Handle ajax requests from the admin bookings area from logged out users
|
471 |
-
* @since 1.3
|
472 |
-
*/
|
473 |
-
public function nopriv_ajax() {
|
474 |
-
|
475 |
-
wp_send_json_error(
|
476 |
-
array(
|
477 |
-
'error' => 'loggedout',
|
478 |
-
'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>' ),
|
479 |
-
)
|
480 |
-
);
|
481 |
-
}
|
482 |
-
|
483 |
-
/**
|
484 |
-
* Handle ajax requests from the admin bookings area
|
485 |
-
* @since 1.3
|
486 |
-
*/
|
487 |
-
public function booking_modal_ajax() {
|
488 |
-
|
489 |
-
global $rtb_controller;
|
490 |
-
|
491 |
-
// Authenticate request
|
492 |
-
if ( !check_ajax_referer( 'rtb-admin', 'nonce' ) || !current_user_can( 'manage_bookings' ) ) {
|
493 |
-
$this->nopriv_ajax();
|
494 |
-
}
|
495 |
-
|
496 |
-
// Retrieve a booking with a GET request
|
497 |
-
if ( !empty( $_GET['booking'] ) && !empty( $_GET['booking']['ID'] ) ) {
|
498 |
-
|
499 |
-
$id = (int) $_GET['booking']['ID'];
|
500 |
-
|
501 |
-
require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
|
502 |
-
$rtb_controller->request = new rtbBooking();
|
503 |
-
$result = $rtb_controller->request->load_post( $id );
|
504 |
-
|
505 |
-
if ( $result ) {
|
506 |
-
|
507 |
-
// Don't allow editing of trashed bookings. This wil force
|
508 |
-
// appropriate use of the trash status and (hopefully) prevent
|
509 |
-
// mistakes in booking management.
|
510 |
-
if ( $rtb_controller->request->post_status == 'trash' ) {
|
511 |
-
wp_send_json_error(
|
512 |
-
array(
|
513 |
-
'error' => 'booking_trashed',
|
514 |
-
'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>' ),
|
515 |
-
)
|
516 |
-
);
|
517 |
-
}
|
518 |
-
|
519 |
-
$rtb_controller->request->prepare_request_data();
|
520 |
-
wp_send_json_success(
|
521 |
-
array(
|
522 |
-
'booking' => $rtb_controller->request,
|
523 |
-
'fields' => $this->print_booking_form_fields(),
|
524 |
-
)
|
525 |
-
);
|
526 |
-
|
527 |
-
} else {
|
528 |
-
wp_send_json_error(
|
529 |
-
array(
|
530 |
-
'error' => 'booking_not_found',
|
531 |
-
'msg' => __( 'The booking could not be retrieved. Please reload the page and try again.', 'restaurant-reservations' ),
|
532 |
-
)
|
533 |
-
);
|
534 |
-
}
|
535 |
-
|
536 |
-
// Insert or update a booking with a POST request
|
537 |
-
} elseif ( !empty( $_POST['booking'] ) ) {
|
538 |
-
|
539 |
-
// Set up $_POST object for validation
|
540 |
-
foreach( $_POST['booking'] as $field ) {
|
541 |
-
|
542 |
-
// $field is setup by jQuery's serializeArray(), which will preserve
|
543 |
-
// array indicators in field names. So name[] is passed as "name[]"
|
544 |
-
// instead of "name". Let's strip out any trailing brackets to match
|
545 |
-
// the normal behaviour when receiving $_POST data on the server.
|
546 |
-
if ( substr( $field['name'], -2 ) === '[]' ) {
|
547 |
-
$name = substr( $field['name'], 0, -2 );
|
548 |
-
if ( !isset( $_POST[ $name ] ) ) {
|
549 |
-
$_POST[ $name ] = array();
|
550 |
-
}
|
551 |
-
$_POST[ $name ][] = $field['value'];
|
552 |
-
} else {
|
553 |
-
$_POST[ $field['name'] ] = $field['value'];
|
554 |
-
}
|
555 |
-
|
556 |
-
}
|
557 |
-
|
558 |
-
require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
|
559 |
-
$rtb_controller->request = new rtbBooking();
|
560 |
-
|
561 |
-
// Add an ID if we're updating the post
|
562 |
-
if ( !empty( $_POST['ID'] ) ) {
|
563 |
-
$result = $rtb_controller->request->load_post((int) $_POST['ID']);
|
564 |
-
if (!$result) {
|
565 |
-
wp_send_json_error(
|
566 |
-
array(
|
567 |
-
'error' => 'no_booking_found',
|
568 |
-
'booking' => $rtb_controller->request,
|
569 |
-
'fields' => $this->print_booking_form_fields(),
|
570 |
-
)
|
571 |
-
);
|
572 |
-
}
|
573 |
-
}
|
574 |
-
|
575 |
-
// Disable notifications
|
576 |
-
$this->maybe_disable_notifications();
|
577 |
-
|
578 |
-
$result = $rtb_controller->request->insert_booking($by_admin = true);
|
579 |
-
|
580 |
-
if ( $result ) {
|
581 |
-
wp_send_json_success(
|
582 |
-
array(
|
583 |
-
'booking' => $rtb_controller->request,
|
584 |
-
)
|
585 |
-
);
|
586 |
-
} else {
|
587 |
-
wp_send_json_error(
|
588 |
-
array(
|
589 |
-
'error' => 'invalid_booking_data',
|
590 |
-
'booking' => $rtb_controller->request,
|
591 |
-
'fields' => $this->print_booking_form_fields(),
|
592 |
-
)
|
593 |
-
);
|
594 |
-
}
|
595 |
-
}
|
596 |
-
|
597 |
-
// Fallback to a valid error
|
598 |
-
wp_send_json_error();
|
599 |
-
}
|
600 |
-
|
601 |
-
/**
|
602 |
-
* Set booking status to trash
|
603 |
-
* @since 1.3
|
604 |
-
*/
|
605 |
-
public function trash_booking_ajax() {
|
606 |
-
|
607 |
-
global $rtb_controller;
|
608 |
-
|
609 |
-
// Authenticate request
|
610 |
-
if ( !check_ajax_referer( 'rtb-admin', 'nonce' ) || !current_user_can( 'manage_bookings' ) || empty( $_POST['booking'] ) ) {
|
611 |
-
$this->nopriv_ajax();
|
612 |
-
}
|
613 |
-
|
614 |
-
$id = (int) $_POST['booking'];
|
615 |
-
|
616 |
-
$result = wp_trash_post( $id );
|
617 |
-
|
618 |
-
if ( $result === false ) {
|
619 |
-
wp_send_json_error(
|
620 |
-
array(
|
621 |
-
'error' => 'trash_failed',
|
622 |
-
'msg' => __( 'Unable to trash this post. Please try again. If you continue to have trouble, please refresh the page.', 'restaurant-reservations' ),
|
623 |
-
)
|
624 |
-
);
|
625 |
-
|
626 |
-
} else {
|
627 |
-
wp_send_json_success(
|
628 |
-
array(
|
629 |
-
'booking' => $id,
|
630 |
-
)
|
631 |
-
);
|
632 |
-
}
|
633 |
-
}
|
634 |
-
|
635 |
-
/**
|
636 |
-
* Handle ajax requests to send an email
|
637 |
-
*
|
638 |
-
* @since 1.3.1
|
639 |
-
*/
|
640 |
-
public function email_modal_ajax() {
|
641 |
-
|
642 |
-
global $rtb_controller;
|
643 |
-
|
644 |
-
// Authenticate request
|
645 |
-
if ( !check_ajax_referer( 'rtb-admin', 'nonce' ) || !current_user_can( 'manage_bookings' ) ) {
|
646 |
-
$this->nopriv_ajax();
|
647 |
-
}
|
648 |
-
|
649 |
-
// Set up $_POST object for validation
|
650 |
-
foreach( $_POST['email'] as $field ) {
|
651 |
-
$_POST[ $field['name'] ] = $field['value'];
|
652 |
-
}
|
653 |
-
|
654 |
-
$ids = ( isset( $_POST['ID'] ) and $_POST['ID'] != '' ) ? array_map( 'intval', explode( ',', $_POST['ID'] ) ) : array();
|
655 |
-
$subject = stripcslashes( sanitize_text_field( $_POST['rtb-email-subject'] ) );
|
656 |
-
$message = stripcslashes( wp_kses_post( $_POST['rtb-email-message'] ) );
|
657 |
-
|
658 |
-
if ( empty( $message ) ) {
|
659 |
-
wp_send_json_error(
|
660 |
-
array(
|
661 |
-
'error' => 'email_missing_message',
|
662 |
-
'msg' => __( 'Please enter a message before sending the email.', 'restaurant-reservations' ),
|
663 |
-
)
|
664 |
-
);
|
665 |
-
}
|
666 |
-
|
667 |
-
if ( empty( $ids ) ) {
|
668 |
-
wp_send_json_error(
|
669 |
-
array(
|
670 |
-
'error' => 'email_missing_data',
|
671 |
-
'msg' => __( 'The email could not be sent because some critical information was missing.', 'restaurant-reservations' ),
|
672 |
-
)
|
673 |
-
);
|
674 |
-
}
|
675 |
-
|
676 |
-
require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
|
677 |
-
|
678 |
-
foreach ( $ids as $id ) {
|
679 |
-
|
680 |
-
$booking = new rtbBooking();
|
681 |
-
$booking->load_post( $id );
|
682 |
-
|
683 |
-
$email = new rtbNotificationEmail( 'admin_email_notice', 'user' );
|
684 |
-
$email->subject = empty( $subject ) ? $rtb_controller->settings->get_setting( 'subject-admin-notice' ) : $subject;
|
685 |
-
$email->message = $message;
|
686 |
-
$email->set_booking( $booking );
|
687 |
-
if ( $email->prepare_notification() ) {
|
688 |
-
do_action( 'rtb_send_notification_before', $email );
|
689 |
-
$email->send_notification();
|
690 |
-
do_action( 'rtb_send_notification_after', $email );
|
691 |
-
}
|
692 |
-
|
693 |
-
// Store email in postmeta for log
|
694 |
-
$booking->add_log( 'email', $email->subject, $email->message );
|
695 |
-
$booking->insert_post_data();
|
696 |
-
}
|
697 |
-
|
698 |
-
wp_send_json_success();
|
699 |
-
}
|
700 |
-
|
701 |
-
/**
|
702 |
-
* Handle ajax requests to configure columns
|
703 |
-
*
|
704 |
-
* @since 1.3.1
|
705 |
-
*/
|
706 |
-
public function column_modal_ajax() {
|
707 |
-
|
708 |
-
global $rtb_controller;
|
709 |
-
|
710 |
-
// Authenticate request
|
711 |
-
if ( !check_ajax_referer( 'rtb-admin', 'nonce' ) || !current_user_can( 'manage_bookings' ) ) {
|
712 |
-
$this->nopriv_ajax();
|
713 |
-
}
|
714 |
-
|
715 |
-
if ( !isset( $_POST['columns'] ) || !is_array( $_POST['columns'] ) || empty( $_POST['columns'] ) ) {
|
716 |
-
wp_send_json_error(
|
717 |
-
array(
|
718 |
-
'error' => 'no_columns',
|
719 |
-
'msg' => __( 'You must select at least one column to display.', 'restaurant-reservations' ),
|
720 |
-
)
|
721 |
-
);
|
722 |
-
}
|
723 |
-
|
724 |
-
$settings = get_option( 'rtb-settings' );
|
725 |
-
$settings['bookings-table-columns'] = array_map( 'sanitize_key', $_POST['columns'] );
|
726 |
-
update_option( 'rtb-settings', $settings );
|
727 |
-
|
728 |
-
wp_send_json_success();
|
729 |
-
}
|
730 |
-
|
731 |
-
/**
|
732 |
-
* Handle ajax requests to ban by IP or email address
|
733 |
-
*
|
734 |
-
* @since 1.3.1
|
735 |
-
*/
|
736 |
-
public function ban_modal_ajax() {
|
737 |
-
|
738 |
-
global $rtb_controller;
|
739 |
-
|
740 |
-
// Authenticate request
|
741 |
-
if ( !check_ajax_referer( 'rtb-admin', 'nonce' ) || !current_user_can( 'manage_bookings' ) ) {
|
742 |
-
$this->nopriv_ajax();
|
743 |
-
}
|
744 |
-
|
745 |
-
// Ban an email address
|
746 |
-
if ( isset( $_POST['email'] ) && !empty( $_POST['email'] ) ) {
|
747 |
-
$email = trim( sanitize_email( $_POST['email'] ) );
|
748 |
-
$banned_emails = preg_split( '/\r\n|\r|\n/', (string) $rtb_controller->settings->get_setting( 'ban-emails' ) );
|
749 |
-
|
750 |
-
if ( !in_array( $email, $banned_emails ) ) {
|
751 |
-
$banned_emails[] = $email;
|
752 |
-
$rtb_controller->settings->settings['ban-emails'] = join( "\n", $banned_emails );
|
753 |
-
update_option( 'rtb-settings', $rtb_controller->settings->settings );
|
754 |
-
}
|
755 |
-
|
756 |
-
wp_send_json_success();
|
757 |
-
|
758 |
-
// Ban an IP address
|
759 |
-
} elseif ( isset( $_POST['ip'] ) && !empty( $_POST['ip'] ) ) {
|
760 |
-
$ip = trim( sanitize_text_field( $_POST['ip'] ) );
|
761 |
-
$banned_ips = preg_split( '/\r\n|\r|\n/', (string) $rtb_controller->settings->get_setting( 'ban-ips' ) );
|
762 |
-
|
763 |
-
if ( !in_array( $ip, $banned_ips ) ) {
|
764 |
-
$banned_ips[] = $ip;
|
765 |
-
$rtb_controller->settings->settings['ban-ips'] = join( "\n", $banned_ips );
|
766 |
-
update_option( 'rtb-settings', $rtb_controller->settings->settings );
|
767 |
-
}
|
768 |
-
|
769 |
-
wp_send_json_success();
|
770 |
-
}
|
771 |
-
|
772 |
-
wp_send_json_error(
|
773 |
-
array(
|
774 |
-
'error' => 'no_data',
|
775 |
-
'msg' => __( 'No IP or email address could be found for this ban request.', 'restaurant-reservations' ),
|
776 |
-
)
|
777 |
-
);
|
778 |
-
}
|
779 |
-
|
780 |
-
/**
|
781 |
-
* Handle ajax requests to delete bookings by email
|
782 |
-
*
|
783 |
-
* @since 1.7.7
|
784 |
-
*/
|
785 |
-
public function delete_modal_ajax() {
|
786 |
-
|
787 |
-
global $rtb_controller;
|
788 |
-
|
789 |
-
// Authenticate request
|
790 |
-
if ( !check_ajax_referer( 'rtb-admin', 'nonce' ) || !current_user_can( 'manage_bookings' ) ) {
|
791 |
-
$this->nopriv_ajax();
|
792 |
-
}
|
793 |
-
|
794 |
-
if ( !empty( $_POST['email'] ) ) {
|
795 |
-
$email = sanitize_email( $_POST['email'] );
|
796 |
-
|
797 |
-
$args = array(
|
798 |
-
'date_range' => null,
|
799 |
-
'posts_per_page' => 100,
|
800 |
-
'paged' => !empty( $_POST['page'] ) ? (int) $_POST['page'] : 1,
|
801 |
-
);
|
802 |
-
|
803 |
-
$query = new rtbQuery( $args, 'delete-by-email' );
|
804 |
-
$query->prepare_args();
|
805 |
-
|
806 |
-
$bookings = $query->get_bookings();
|
807 |
-
$deleted = 0;
|
808 |
-
foreach( $bookings as $booking ) {
|
809 |
-
if ( isset( $booking->email ) && $booking->email === $email) {
|
810 |
-
wp_delete_post( $booking->ID, true );
|
811 |
-
$deleted++;
|
812 |
-
}
|
813 |
-
}
|
814 |
-
|
815 |
-
// Get count of all bookings
|
816 |
-
global $wpdb;
|
817 |
-
$where = "WHERE p.post_type = '" . RTB_BOOKING_POST_TYPE . "'";
|
818 |
-
$query = "SELECT count( * ) AS num_posts
|
819 |
-
FROM $wpdb->posts p
|
820 |
-
$where
|
821 |
-
";
|
822 |
-
|
823 |
-
$count = $wpdb->get_results( $query );
|
824 |
-
$count = (int) $count[0]->num_posts;
|
825 |
-
|
826 |
-
wp_send_json_success(array(
|
827 |
-
'processed' => count($bookings),
|
828 |
-
'deleted' => $deleted,
|
829 |
-
'total' => $count,
|
830 |
-
));
|
831 |
-
}
|
832 |
-
|
833 |
-
wp_send_json_error(
|
834 |
-
array(
|
835 |
-
'error' => 'no_data',
|
836 |
-
'msg' => __( 'No email address could be found for this delete request.', 'restaurant-reservations' ),
|
837 |
-
)
|
838 |
-
);
|
839 |
-
}
|
840 |
-
|
841 |
-
/**
|
842 |
-
* Register a party as having arrived
|
843 |
-
* @since 2.0.0
|
844 |
-
*/
|
845 |
-
public function set_booking_arrived() {
|
846 |
-
|
847 |
-
// Authenticate request
|
848 |
-
if ( !check_ajax_referer( 'rtb-booking-form', 'nonce' ) || !current_user_can( 'manage_bookings' ) ) {
|
849 |
-
$this->nopriv_ajax();
|
850 |
-
}
|
851 |
-
|
852 |
-
$booking_id = isset($_POST['booking']) ? intval( $_POST['booking']['ID'] ) : 0;
|
853 |
-
|
854 |
-
$booking_id = wp_update_post(array(
|
855 |
-
'ID' => $booking_id,
|
856 |
-
'post_status' => 'arrived'
|
857 |
-
) );
|
858 |
-
|
859 |
-
if ( $booking_id ) {
|
860 |
-
wp_send_json_success();
|
861 |
-
}
|
862 |
-
else {
|
863 |
-
wp_send_json_error(
|
864 |
-
array(
|
865 |
-
'error' => 'loggedout',
|
866 |
-
'msg' => sprintf( __( 'You have been logged out. Please %slogin again%s.', 'restaurant-reservations' ), '<a href="' . wp_login_url( ) . '">', '</a>' ),
|
867 |
-
)
|
868 |
-
);
|
869 |
-
}
|
870 |
-
}
|
871 |
-
|
872 |
-
/**
|
873 |
-
* Validate post status and notification fields
|
874 |
-
* @since 1.3
|
875 |
-
*/
|
876 |
-
public function validate_admin_fields( $booking ) {
|
877 |
-
|
878 |
-
// Only validate in the admin
|
879 |
-
if ( !$_POST['action'] || $_POST['action'] !== 'admin_booking_request' ) {
|
880 |
-
return;
|
881 |
-
}
|
882 |
-
|
883 |
-
global $rtb_controller;
|
884 |
-
|
885 |
-
// Disable Notifications
|
886 |
-
$booking->send_notifications = empty( $_POST['rtb-notifications'] ) ? false : true;
|
887 |
-
}
|
888 |
-
|
889 |
-
/**
|
890 |
-
* Adjust post status when adding/editing a booking from the admin area
|
891 |
-
* @since 1.3
|
892 |
-
*/
|
893 |
-
public function insert_booking_data( $args, $booking ) {
|
894 |
-
|
895 |
-
// Validate user request
|
896 |
-
if ( empty( $_POST['action'] ) || $_POST['action'] !== 'admin_booking_request' || !current_user_can( 'manage_bookings' ) ) {
|
897 |
-
return $args;
|
898 |
-
}
|
899 |
-
|
900 |
-
if ( !empty( $booking->post_status ) ) {
|
901 |
-
$args['post_status'] = $booking->post_status;
|
902 |
-
}
|
903 |
-
|
904 |
-
return $args;
|
905 |
-
}
|
906 |
-
|
907 |
-
/**
|
908 |
-
* Maybe disable notifications when adding/editing bookings from the
|
909 |
-
* admin booking modal
|
910 |
-
* @since 1.3
|
911 |
-
*/
|
912 |
-
public function maybe_disable_notifications() {
|
913 |
-
|
914 |
-
// Don't disable notifications if they have opted to send them
|
915 |
-
if ( !empty( $_POST['rtb-notifications'] ) ) {
|
916 |
-
return;
|
917 |
-
}
|
918 |
-
|
919 |
-
// Disable all notifications. This filter is here in case a
|
920 |
-
// third-party sets up a notification that they don't want to be
|
921 |
-
// disabled even if the user has opted not to send notifications
|
922 |
-
// To exempt a notification, hook into the filter and copy it
|
923 |
-
// from $rtb_notifications to the empty array.
|
924 |
-
global $rtb_controller;
|
925 |
-
$rtb_controller->notifications->notifications = apply_filters( 'rtb_admin_disabled_notifications_exemption', array(), $rtb_controller->notifications->notifications );
|
926 |
-
}
|
927 |
-
}
|
928 |
-
} // endif;
|
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 |
+
/**
|
13 |
+
* The bookings table
|
14 |
+
*
|
15 |
+
* This is only instantiated on the bookings admin page at the moment when
|
16 |
+
* it is generated.
|
17 |
+
*
|
18 |
+
* @see self::show_admin_bookings_page()
|
19 |
+
* @see WP_List_table.BookingsTable.class.php
|
20 |
+
* @since 1.6
|
21 |
+
*/
|
22 |
+
public $bookings_table;
|
23 |
+
|
24 |
+
public function __construct() {
|
25 |
+
|
26 |
+
// Add the admin menu
|
27 |
+
add_action( 'admin_menu', array( $this, 'add_menu_page' ) );
|
28 |
+
|
29 |
+
// Print the modals
|
30 |
+
add_action( 'admin_footer-toplevel_page_rtb-bookings', array( $this, 'print_modals' ) );
|
31 |
+
|
32 |
+
// Receive Ajax requests
|
33 |
+
add_action( 'wp_ajax_nopriv_rtb-admin-booking-modal' , array( $this , 'nopriv_ajax' ) );
|
34 |
+
add_action( 'wp_ajax_rtb-admin-booking-modal', array( $this, 'booking_modal_ajax' ) );
|
35 |
+
add_action( 'wp_ajax_nopriv_rtb-admin-trash-booking' , array( $this , 'nopriv_ajax' ) );
|
36 |
+
add_action( 'wp_ajax_rtb-admin-trash-booking', array( $this, 'trash_booking_ajax' ) );
|
37 |
+
add_action( 'wp_ajax_nopriv_rtb-admin-email-modal' , array( $this , 'nopriv_ajax' ) );
|
38 |
+
add_action( 'wp_ajax_rtb-admin-email-modal', array( $this, 'email_modal_ajax' ) );
|
39 |
+
add_action( 'wp_ajax_nopriv_rtb-admin-column-modal' , array( $this , 'nopriv_ajax' ) );
|
40 |
+
add_action( 'wp_ajax_rtb-admin-column-modal', array( $this, 'column_modal_ajax' ) );
|
41 |
+
add_action( 'wp_ajax_nopriv_rtb-admin-ban-modal' , array( $this , 'nopriv_ajax' ) );
|
42 |
+
add_action( 'wp_ajax_rtb-admin-ban-modal', array( $this, 'ban_modal_ajax' ) );
|
43 |
+
add_action( 'wp_ajax_nopriv_rtb-admin-delete-modal' , array( $this , 'nopriv_ajax' ) );
|
44 |
+
add_action( 'wp_ajax_rtb-admin-delete-modal', array( $this, 'delete_modal_ajax' ) );
|
45 |
+
|
46 |
+
add_action( 'wp_ajax_nopriv_rtb_set_reservation_arrived' , array( $this , 'nopriv_ajax' ) );
|
47 |
+
add_action( 'wp_ajax_rtb_set_reservation_arrived', array( $this, 'set_booking_arrived' ) );
|
48 |
+
|
49 |
+
// Validate post status and notification fields
|
50 |
+
add_action( 'rtb_validate_booking_submission', array( $this, 'validate_admin_fields' ) );
|
51 |
+
|
52 |
+
// Set post status when adding to the database
|
53 |
+
add_filter( 'rtb_insert_booking_data', array( $this, 'insert_booking_data' ), 10, 2 );
|
54 |
+
|
55 |
+
// Add the columns configuration button to the table
|
56 |
+
add_action( 'rtb_bookings_table_actions', array( $this, 'print_columns_config_button' ), 9 );
|
57 |
+
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Add the top-level admin menu page
|
62 |
+
* @since 0.0.1
|
63 |
+
*/
|
64 |
+
public function add_menu_page() {
|
65 |
+
|
66 |
+
add_menu_page(
|
67 |
+
_x( 'Bookings', 'Title of admin page that lists bookings', 'restaurant-reservations' ),
|
68 |
+
_x( 'Bookings', 'Title of bookings admin menu item', 'restaurant-reservations' ),
|
69 |
+
'manage_bookings',
|
70 |
+
'rtb-bookings',
|
71 |
+
array( $this, 'show_admin_bookings_page' ),
|
72 |
+
'dashicons-calendar',
|
73 |
+
'26.2987'
|
74 |
+
);
|
75 |
+
|
76 |
+
}
|
77 |
+
|
78 |
+
/**
|
79 |
+
* Display the admin bookings page
|
80 |
+
* @since 0.0.1
|
81 |
+
*/
|
82 |
+
public function show_admin_bookings_page() {
|
83 |
+
|
84 |
+
require_once( RTB_PLUGIN_DIR . '/includes/WP_List_Table.BookingsTable.class.php' );
|
85 |
+
$this->bookings_table = new rtbBookingsTable();
|
86 |
+
$this->bookings_table->prepare_items();
|
87 |
+
?>
|
88 |
+
|
89 |
+
<div class="wrap">
|
90 |
+
<h1>
|
91 |
+
<?php _e( 'Restaurant Bookings', 'restaurant-reservations' ); ?>
|
92 |
+
<a href="#" class="add-new-h2 page-title-action add-booking"><?php _e( 'Add New', 'restaurant-reservations' ); ?></a>
|
93 |
+
</h1>
|
94 |
+
|
95 |
+
<?php do_action( 'rtb_bookings_table_top' ); ?>
|
96 |
+
<form id="rtb-bookings-table" method="POST" action="">
|
97 |
+
<input type="hidden" name="post_type" value="<?php echo RTB_BOOKING_POST_TYPE; ?>" />
|
98 |
+
<input type="hidden" name="page" value="rtb-bookings">
|
99 |
+
|
100 |
+
<div class="rtb-primary-controls clearfix">
|
101 |
+
<div class="rtb-views">
|
102 |
+
<?php $this->bookings_table->views(); ?>
|
103 |
+
</div>
|
104 |
+
<?php $this->bookings_table->advanced_filters(); ?>
|
105 |
+
</div>
|
106 |
+
|
107 |
+
<?php $this->bookings_table->display(); ?>
|
108 |
+
</form>
|
109 |
+
<?php do_action( 'rtb_bookings_table_btm' ); ?>
|
110 |
+
</div>
|
111 |
+
|
112 |
+
<?php
|
113 |
+
}
|
114 |
+
|
115 |
+
/**
|
116 |
+
* Print button for configuring columns
|
117 |
+
*
|
118 |
+
* @param string pos Position of this tablenav: top|btm
|
119 |
+
* @since 0.1
|
120 |
+
*/
|
121 |
+
public function print_columns_config_button( $pos ) {
|
122 |
+
if ( $pos != 'top' ) {
|
123 |
+
return;
|
124 |
+
}
|
125 |
+
?>
|
126 |
+
|
127 |
+
<div class="alignleft actions rtb-actions">
|
128 |
+
<a href="#" class="button rtb-columns-button">
|
129 |
+
<span class="dashicons dashicons-admin-settings"></span>
|
130 |
+
<?php esc_html_e( 'Columns', 'restaurant-reservations' ); ?>
|
131 |
+
</a>
|
132 |
+
</div>
|
133 |
+
|
134 |
+
<?php
|
135 |
+
}
|
136 |
+
|
137 |
+
/**
|
138 |
+
* Print the modal containers
|
139 |
+
*
|
140 |
+
* New/edit bookings, send email, configure columns, errors.
|
141 |
+
*
|
142 |
+
* @since 0.0.1
|
143 |
+
*/
|
144 |
+
public function print_modals() {
|
145 |
+
|
146 |
+
global $rtb_controller;
|
147 |
+
?>
|
148 |
+
|
149 |
+
<!-- Restaurant Reservations add/edit booking modal -->
|
150 |
+
<div id="rtb-booking-modal" class="rtb-admin-modal">
|
151 |
+
<div class="rtb-booking-form rtb-container">
|
152 |
+
<form method="POST">
|
153 |
+
<input type="hidden" name="action" value="admin_booking_request">
|
154 |
+
<input type="hidden" name="ID" value="">
|
155 |
+
|
156 |
+
<?php
|
157 |
+
/**
|
158 |
+
* The generated fields are wrapped in a div so we can
|
159 |
+
* replace its contents with an HTML blob passed back from
|
160 |
+
* an Ajax request. This way the field data and error
|
161 |
+
* messages are always populated from the same server-side
|
162 |
+
* code.
|
163 |
+
*/
|
164 |
+
?>
|
165 |
+
<div id="rtb-booking-form-fields">
|
166 |
+
<?php echo $this->print_booking_form_fields(); ?>
|
167 |
+
</div>
|
168 |
+
|
169 |
+
<button type="submit" class="button button-primary">
|
170 |
+
<?php _e( 'Add Booking', 'restaurant-reservations' ); ?>
|
171 |
+
</button>
|
172 |
+
<a href="#" class="button" id="rtb-cancel-booking-modal">
|
173 |
+
<?php _e( 'Cancel', 'restaurant-reservations' ); ?>
|
174 |
+
</a>
|
175 |
+
<div class="action-status">
|
176 |
+
<span class="spinner loading"></span>
|
177 |
+
<span class="dashicons dashicons-no-alt error"></span>
|
178 |
+
<span class="dashicons dashicons-yes success"></span>
|
179 |
+
</div>
|
180 |
+
</form>
|
181 |
+
</div>
|
182 |
+
</div>
|
183 |
+
|
184 |
+
<!-- Restaurant Reservations send email modal -->
|
185 |
+
<div id="rtb-email-modal" class="rtb-admin-modal">
|
186 |
+
<div class="rtb-email-form rtb-container">
|
187 |
+
<form method="POST">
|
188 |
+
<input type="hidden" name="action" value="admin_send_email">
|
189 |
+
<input type="hidden" name="ID" value="">
|
190 |
+
<input type="hidden" name="name" value="">
|
191 |
+
<input type="hidden" name="email" value="">
|
192 |
+
|
193 |
+
<fieldset>
|
194 |
+
<legend><?php _e( 'Send Email', 'restaurant-reservations' ); ?></legend>
|
195 |
+
|
196 |
+
<div class="to">
|
197 |
+
<label for="rtb-email-to"><?php _ex( 'To', 'Label next to the email address to which an email will be sent', 'restaurant-reservations' ); ?></label>
|
198 |
+
<span class="rtb-email-to"></span>
|
199 |
+
</div>
|
200 |
+
<div class="subject">
|
201 |
+
<label for="rtb-email-subject"><?php _e( 'Subject', 'restaurant-reservations' ); ?></label>
|
202 |
+
<input type="text" name="rtb-email-subject" placeholder="<?php echo $rtb_controller->settings->get_setting( 'subject-admin-notice'); ?>">
|
203 |
+
</div>
|
204 |
+
<div class="message">
|
205 |
+
<label for="rtb-email-message"><?php _e( 'Message', 'restaurant-reservations' ); ?></label>
|
206 |
+
<textarea name="rtb-email-message" id="rtb-email-message"></textarea>
|
207 |
+
</div>
|
208 |
+
</fieldset>
|
209 |
+
|
210 |
+
<button type="submit" class="button button-primary">
|
211 |
+
<?php _e( 'Send Email', 'restaurant-reservations' ); ?>
|
212 |
+
</button>
|
213 |
+
<a href="#" class="button" id="rtb-cancel-email-modal">
|
214 |
+
<?php _e( 'Cancel', 'restaurant-reservations' ); ?>
|
215 |
+
</a>
|
216 |
+
<div class="action-status">
|
217 |
+
<span class="spinner loading"></span>
|
218 |
+
<span class="dashicons dashicons-no-alt error"></span>
|
219 |
+
<span class="dashicons dashicons-yes success"></span>
|
220 |
+
</div>
|
221 |
+
</form>
|
222 |
+
</div>
|
223 |
+
</div>
|
224 |
+
|
225 |
+
<!-- Restaurant Reservations column configuration modal -->
|
226 |
+
<div id="rtb-column-modal" class="rtb-admin-modal">
|
227 |
+
<div class="rtb-column-form rtb-container">
|
228 |
+
<form method="POST">
|
229 |
+
<input type="hidden" name="action" value="admin_column_config">
|
230 |
+
|
231 |
+
<fieldset>
|
232 |
+
<legend><?php esc_html_e( 'Columns', 'restaurant-reservations' ); ?></legend>
|
233 |
+
<ul>
|
234 |
+
<?php
|
235 |
+
$columns = $this->bookings_table->get_all_columns();
|
236 |
+
$visible = $this->bookings_table->get_columns();
|
237 |
+
foreach( $columns as $column => $label ) :
|
238 |
+
// Don't allow these columns to be hidden
|
239 |
+
if ( $column == 'cb' || $column == 'details' || $column == 'date' ) {
|
240 |
+
continue;
|
241 |
+
}
|
242 |
+
?>
|
243 |
+
<li>
|
244 |
+
<label>
|
245 |
+
<input type="checkbox" name="rtb-columns-config" value="<?php esc_attr_e( $column ); ?>"<?php if ( array_key_exists( $column, $visible ) ) : ?> checked<?php endif; ?>>
|
246 |
+
<?php esc_html_e( $label ); ?>
|
247 |
+
</label>
|
248 |
+
</li>
|
249 |
+
<?php
|
250 |
+
endforeach;
|
251 |
+
?>
|
252 |
+
</ul>
|
253 |
+
</fieldset>
|
254 |
+
|
255 |
+
<button type="submit" class="button button-primary">
|
256 |
+
<?php _e( 'Update', 'restaurant-reservations' ); ?>
|
257 |
+
</button>
|
258 |
+
<a href="#" class="button" id="rtb-cancel-column-modal">
|
259 |
+
<?php _e( 'Cancel', 'restaurant-reservations' ); ?>
|
260 |
+
</a>
|
261 |
+
<div class="action-status">
|
262 |
+
<span class="spinner loading"></span>
|
263 |
+
<span class="dashicons dashicons-no-alt error"></span>
|
264 |
+
<span class="dashicons dashicons-yes success"></span>
|
265 |
+
</div>
|
266 |
+
</form>
|
267 |
+
</div>
|
268 |
+
</div>
|
269 |
+
|
270 |
+
<!-- Restaurant Reservations details modal -->
|
271 |
+
<div id="rtb-details-modal" class="rtb-admin-modal">
|
272 |
+
<div class="rtb-details-form rtb-container">
|
273 |
+
<div class="rtb-details-data"></div>
|
274 |
+
<a href="#" class="button" id="rtb-cancel-details-modal">
|
275 |
+
<?php _e( 'Close', 'restaurant-reservations' ); ?>
|
276 |
+
</a>
|
277 |
+
</div>
|
278 |
+
</div>
|
279 |
+
|
280 |
+
<!-- Restaurant Reservations ban email/ip modal -->
|
281 |
+
<div id="rtb-ban-modal" class="rtb-admin-modal">
|
282 |
+
<div class="rtb-ban-form rtb-container">
|
283 |
+
<div class="rtb-ban-msg">
|
284 |
+
<p class="intro">
|
285 |
+
<?php
|
286 |
+
printf(
|
287 |
+
__( 'Ban future bookings from the email address %s or the IP address %s?', 'restaurant-reservations' ),
|
288 |
+
'<span id="rtb-ban-modal-email"></span>',
|
289 |
+
'<span id="rtb-ban-modal-ip"></span>'
|
290 |
+
);
|
291 |
+
?>
|
292 |
+
</p>
|
293 |
+
<p>
|
294 |
+
<?php
|
295 |
+
esc_html_e( 'It is recommended to ban by email address instead of IP. Only ban by IP address to block a malicious user who is using different email addresses to avoid a previous ban.', 'restaurant-reservations' );
|
296 |
+
?>
|
297 |
+
</p>
|
298 |
+
</div>
|
299 |
+
<button class="button button-primary" id="rtb-ban-modal-email-btn">Ban Email</button>
|
300 |
+
<button class="button button-primary" id="rtb-ban-modal-ip-btn">Ban IP</button>
|
301 |
+
<a href="#" id="rtb-cancel-ban-modal" class="button"><?php _e( 'Close', 'restaurant-reservations' ); ?></a>
|
302 |
+
<a class="button-link" href="<?php echo esc_url( admin_url( '/admin.php?page=rtb-settings' ) ); ?>" target="_blank">
|
303 |
+
<?php esc_html_e( 'View all bans', 'restaurant-reservations' ); ?>
|
304 |
+
</a>
|
305 |
+
<div class="action-status">
|
306 |
+
<span class="spinner loading"></span>
|
307 |
+
<span class="dashicons dashicons-no-alt error"></span>
|
308 |
+
<span class="dashicons dashicons-yes success"></span>
|
309 |
+
</div>
|
310 |
+
</div>
|
311 |
+
</div>
|
312 |
+
|
313 |
+
<!-- Restaurant Reservations delete customer modal -->
|
314 |
+
<div id="rtb-delete-modal" class="rtb-admin-modal">
|
315 |
+
<div class="rtb-delete-form rtb-container">
|
316 |
+
<div class="rtb-delete-msg">
|
317 |
+
<?php
|
318 |
+
printf(
|
319 |
+
__( 'Delete all booking records related to email address %s? This action can not be undone.', 'restaurant-reservations' ),
|
320 |
+
'<span id="rtb-delete-modal-email"></span>'
|
321 |
+
);
|
322 |
+
?>
|
323 |
+
</div>
|
324 |
+
<div id="rtb-delete-status">
|
325 |
+
<span class="rtb-delete-status-total">
|
326 |
+
<span id="rtb-delete-status-progress" class="rtb-delete-status-progress"></span>
|
327 |
+
</span>
|
328 |
+
<div id="rtb-delete-status-deleted"></div>
|
329 |
+
</div>
|
330 |
+
<button class="button button-primary" id="rtb-delete-modal-btn">Delete Bookings</button>
|
331 |
+
<button id="rtb-cancel-delete-modal" class="button"><?php _e( 'Close', 'restaurant-reservations' ); ?></button>
|
332 |
+
<div class="action-status">
|
333 |
+
<span class="spinner loading"></span>
|
334 |
+
<span class="dashicons dashicons-no-alt error"></span>
|
335 |
+
<span class="dashicons dashicons-yes success"></span>
|
336 |
+
</div>
|
337 |
+
</div>
|
338 |
+
</div>
|
339 |
+
|
340 |
+
<!-- Restaurant Reservations error message modal -->
|
341 |
+
<div id="rtb-error-modal" class="rtb-admin-modal">
|
342 |
+
<div class="rtb-error rtb-container">
|
343 |
+
<div class="rtb-error-msg"></div>
|
344 |
+
<a href="#" class="button"><?php _e( 'Close', 'restaurant-reservations' ); ?></a>
|
345 |
+
</div>
|
346 |
+
</div>
|
347 |
+
|
348 |
+
<?php
|
349 |
+
}
|
350 |
+
|
351 |
+
/**
|
352 |
+
* Retrieve booking form fields used in the admin booking modal. These
|
353 |
+
* fields are also passed back with ajax requests since they render error
|
354 |
+
* messages and populate fields with validated data.
|
355 |
+
* @since 0.0.1
|
356 |
+
*/
|
357 |
+
public function print_booking_form_fields() {
|
358 |
+
|
359 |
+
global $rtb_controller;
|
360 |
+
|
361 |
+
// Add post status and notification fields to admin booking form
|
362 |
+
add_filter( 'rtb_booking_form_fields', array( $this, 'add_admin_fields' ), 20, 2 );
|
363 |
+
|
364 |
+
// Retrieve the form fields
|
365 |
+
$fields = $rtb_controller->settings->get_booking_form_fields( $rtb_controller->request );
|
366 |
+
|
367 |
+
ob_start();
|
368 |
+
?>
|
369 |
+
|
370 |
+
<?php foreach( $fields as $fieldset => $contents ) : ?>
|
371 |
+
<fieldset class="<?php echo esc_attr( $fieldset ); ?>">
|
372 |
+
<?php
|
373 |
+
foreach( $contents['fields'] as $slug => $field ) {
|
374 |
+
|
375 |
+
$args = empty( $field['callback_args'] ) ? null : $field['callback_args'];
|
376 |
+
|
377 |
+
call_user_func( $field['callback'], $slug, $field['title'], $field['request_input'], $args );
|
378 |
+
}
|
379 |
+
?>
|
380 |
+
</fieldset>
|
381 |
+
<?php endforeach;
|
382 |
+
|
383 |
+
// Remove the admin fields filter
|
384 |
+
remove_filter( 'rtb_booking_form_fields', array( $this, 'add_admin_fields' ) );
|
385 |
+
|
386 |
+
return ob_get_clean();
|
387 |
+
}
|
388 |
+
|
389 |
+
/**
|
390 |
+
* Add the post status and notifications fields to the booking form fields
|
391 |
+
* @since 1.3
|
392 |
+
*/
|
393 |
+
public function add_admin_fields( $fields, $request ) {
|
394 |
+
|
395 |
+
if ( !is_admin() || !current_user_can( 'manage_bookings' ) ) {
|
396 |
+
return $fields;
|
397 |
+
}
|
398 |
+
|
399 |
+
global $rtb_controller;
|
400 |
+
|
401 |
+
// Get all valid booking statuses
|
402 |
+
$booking_statuses = array();
|
403 |
+
foreach( $rtb_controller->cpts->booking_statuses as $status => $args ) {
|
404 |
+
$booking_statuses[ $status ] = $args['label'];
|
405 |
+
}
|
406 |
+
|
407 |
+
$fields['admin'] = array(
|
408 |
+
'fields' => array(
|
409 |
+
'post-status' => array(
|
410 |
+
'title' => __( 'Booking Status', 'restaurant-reservations' ),
|
411 |
+
'request_input' => empty( $request->post_status ) ? '' : $request->post_status,
|
412 |
+
'callback' => 'rtb_print_form_select_field',
|
413 |
+
'callback_args' => array(
|
414 |
+
'options' => $booking_statuses,
|
415 |
+
)
|
416 |
+
),
|
417 |
+
'notifications' => array(
|
418 |
+
'title' => __( 'Send notifications', 'restaurant-reservations' ),
|
419 |
+
'request_input' => empty( $request->send_notifications ) ? false : $request->send_notifications,
|
420 |
+
'callback' => array( $this, 'print_form_send_notifications_field' ),
|
421 |
+
'callback_args' => array(
|
422 |
+
'description' => array(
|
423 |
+
'prompt' => __( 'Learn more', 'restaurant-reservations' ),
|
424 |
+
'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' ),
|
425 |
+
),
|
426 |
+
),
|
427 |
+
),
|
428 |
+
),
|
429 |
+
);
|
430 |
+
|
431 |
+
return $fields;
|
432 |
+
}
|
433 |
+
|
434 |
+
|
435 |
+
/**
|
436 |
+
* Print a field to toggle notifications when adding/editing a booking from
|
437 |
+
* the admin
|
438 |
+
* @since 1.3
|
439 |
+
*/
|
440 |
+
function print_form_send_notifications_field( $slug, $title, $value, $args ) {
|
441 |
+
|
442 |
+
$slug = esc_attr( $slug );
|
443 |
+
$title = esc_attr( $title );
|
444 |
+
$value = (bool) $value;
|
445 |
+
$description = empty( $args['description'] ) || empty( $args['description']['prompt'] ) || empty( $args['description']['text'] ) ? null : $args['description'];
|
446 |
+
?>
|
447 |
+
|
448 |
+
<div class="<?php echo esc_attr( $slug ); ?>">
|
449 |
+
<?php echo rtb_print_form_error( $slug ); ?>
|
450 |
+
<label>
|
451 |
+
<input type="checkbox" name="rtb-<?php echo esc_attr( $slug ); ?>" value="1"<?php checked( $value ); ?>>
|
452 |
+
<?php echo esc_html( $title ); ?>
|
453 |
+
<?php if ( !empty( $description ) ) : ?>
|
454 |
+
<a href="#" class="rtb-description-prompt">
|
455 |
+
<?php echo esc_html( $description['prompt'] ); ?>
|
456 |
+
</a>
|
457 |
+
<?php endif; ?>
|
458 |
+
</label>
|
459 |
+
<?php if ( !empty( $description ) ) : ?>
|
460 |
+
<div class="rtb-description">
|
461 |
+
<?php echo esc_html( $description['text'] ); ?>
|
462 |
+
</div>
|
463 |
+
<?php endif; ?>
|
464 |
+
</div>
|
465 |
+
|
466 |
+
<?php
|
467 |
+
}
|
468 |
+
|
469 |
+
/**
|
470 |
+
* Handle ajax requests from the admin bookings area from logged out users
|
471 |
+
* @since 1.3
|
472 |
+
*/
|
473 |
+
public function nopriv_ajax() {
|
474 |
+
|
475 |
+
wp_send_json_error(
|
476 |
+
array(
|
477 |
+
'error' => 'loggedout',
|
478 |
+
'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>' ),
|
479 |
+
)
|
480 |
+
);
|
481 |
+
}
|
482 |
+
|
483 |
+
/**
|
484 |
+
* Handle ajax requests from the admin bookings area
|
485 |
+
* @since 1.3
|
486 |
+
*/
|
487 |
+
public function booking_modal_ajax() {
|
488 |
+
|
489 |
+
global $rtb_controller;
|
490 |
+
|
491 |
+
// Authenticate request
|
492 |
+
if ( !check_ajax_referer( 'rtb-admin', 'nonce' ) || !current_user_can( 'manage_bookings' ) ) {
|
493 |
+
$this->nopriv_ajax();
|
494 |
+
}
|
495 |
+
|
496 |
+
// Retrieve a booking with a GET request
|
497 |
+
if ( !empty( $_GET['booking'] ) && !empty( $_GET['booking']['ID'] ) ) {
|
498 |
+
|
499 |
+
$id = (int) $_GET['booking']['ID'];
|
500 |
+
|
501 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
|
502 |
+
$rtb_controller->request = new rtbBooking();
|
503 |
+
$result = $rtb_controller->request->load_post( $id );
|
504 |
+
|
505 |
+
if ( $result ) {
|
506 |
+
|
507 |
+
// Don't allow editing of trashed bookings. This wil force
|
508 |
+
// appropriate use of the trash status and (hopefully) prevent
|
509 |
+
// mistakes in booking management.
|
510 |
+
if ( $rtb_controller->request->post_status == 'trash' ) {
|
511 |
+
wp_send_json_error(
|
512 |
+
array(
|
513 |
+
'error' => 'booking_trashed',
|
514 |
+
'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>' ),
|
515 |
+
)
|
516 |
+
);
|
517 |
+
}
|
518 |
+
|
519 |
+
$rtb_controller->request->prepare_request_data();
|
520 |
+
wp_send_json_success(
|
521 |
+
array(
|
522 |
+
'booking' => $rtb_controller->request,
|
523 |
+
'fields' => $this->print_booking_form_fields(),
|
524 |
+
)
|
525 |
+
);
|
526 |
+
|
527 |
+
} else {
|
528 |
+
wp_send_json_error(
|
529 |
+
array(
|
530 |
+
'error' => 'booking_not_found',
|
531 |
+
'msg' => __( 'The booking could not be retrieved. Please reload the page and try again.', 'restaurant-reservations' ),
|
532 |
+
)
|
533 |
+
);
|
534 |
+
}
|
535 |
+
|
536 |
+
// Insert or update a booking with a POST request
|
537 |
+
} elseif ( !empty( $_POST['booking'] ) ) {
|
538 |
+
|
539 |
+
// Set up $_POST object for validation
|
540 |
+
foreach( $_POST['booking'] as $field ) {
|
541 |
+
|
542 |
+
// $field is setup by jQuery's serializeArray(), which will preserve
|
543 |
+
// array indicators in field names. So name[] is passed as "name[]"
|
544 |
+
// instead of "name". Let's strip out any trailing brackets to match
|
545 |
+
// the normal behaviour when receiving $_POST data on the server.
|
546 |
+
if ( substr( $field['name'], -2 ) === '[]' ) {
|
547 |
+
$name = substr( $field['name'], 0, -2 );
|
548 |
+
if ( !isset( $_POST[ $name ] ) ) {
|
549 |
+
$_POST[ $name ] = array();
|
550 |
+
}
|
551 |
+
$_POST[ $name ][] = $field['value'];
|
552 |
+
} else {
|
553 |
+
$_POST[ $field['name'] ] = $field['value'];
|
554 |
+
}
|
555 |
+
|
556 |
+
}
|
557 |
+
|
558 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
|
559 |
+
$rtb_controller->request = new rtbBooking();
|
560 |
+
|
561 |
+
// Add an ID if we're updating the post
|
562 |
+
if ( !empty( $_POST['ID'] ) ) {
|
563 |
+
$result = $rtb_controller->request->load_post((int) $_POST['ID']);
|
564 |
+
if (!$result) {
|
565 |
+
wp_send_json_error(
|
566 |
+
array(
|
567 |
+
'error' => 'no_booking_found',
|
568 |
+
'booking' => $rtb_controller->request,
|
569 |
+
'fields' => $this->print_booking_form_fields(),
|
570 |
+
)
|
571 |
+
);
|
572 |
+
}
|
573 |
+
}
|
574 |
+
|
575 |
+
// Disable notifications
|
576 |
+
$this->maybe_disable_notifications();
|
577 |
+
|
578 |
+
$result = $rtb_controller->request->insert_booking($by_admin = true);
|
579 |
+
|
580 |
+
if ( $result ) {
|
581 |
+
wp_send_json_success(
|
582 |
+
array(
|
583 |
+
'booking' => $rtb_controller->request,
|
584 |
+
)
|
585 |
+
);
|
586 |
+
} else {
|
587 |
+
wp_send_json_error(
|
588 |
+
array(
|
589 |
+
'error' => 'invalid_booking_data',
|
590 |
+
'booking' => $rtb_controller->request,
|
591 |
+
'fields' => $this->print_booking_form_fields(),
|
592 |
+
)
|
593 |
+
);
|
594 |
+
}
|
595 |
+
}
|
596 |
+
|
597 |
+
// Fallback to a valid error
|
598 |
+
wp_send_json_error();
|
599 |
+
}
|
600 |
+
|
601 |
+
/**
|
602 |
+
* Set booking status to trash
|
603 |
+
* @since 1.3
|
604 |
+
*/
|
605 |
+
public function trash_booking_ajax() {
|
606 |
+
|
607 |
+
global $rtb_controller;
|
608 |
+
|
609 |
+
// Authenticate request
|
610 |
+
if ( !check_ajax_referer( 'rtb-admin', 'nonce' ) || !current_user_can( 'manage_bookings' ) || empty( $_POST['booking'] ) ) {
|
611 |
+
$this->nopriv_ajax();
|
612 |
+
}
|
613 |
+
|
614 |
+
$id = (int) $_POST['booking'];
|
615 |
+
|
616 |
+
$result = wp_trash_post( $id );
|
617 |
+
|
618 |
+
if ( $result === false ) {
|
619 |
+
wp_send_json_error(
|
620 |
+
array(
|
621 |
+
'error' => 'trash_failed',
|
622 |
+
'msg' => __( 'Unable to trash this post. Please try again. If you continue to have trouble, please refresh the page.', 'restaurant-reservations' ),
|
623 |
+
)
|
624 |
+
);
|
625 |
+
|
626 |
+
} else {
|
627 |
+
wp_send_json_success(
|
628 |
+
array(
|
629 |
+
'booking' => $id,
|
630 |
+
)
|
631 |
+
);
|
632 |
+
}
|
633 |
+
}
|
634 |
+
|
635 |
+
/**
|
636 |
+
* Handle ajax requests to send an email
|
637 |
+
*
|
638 |
+
* @since 1.3.1
|
639 |
+
*/
|
640 |
+
public function email_modal_ajax() {
|
641 |
+
|
642 |
+
global $rtb_controller;
|
643 |
+
|
644 |
+
// Authenticate request
|
645 |
+
if ( !check_ajax_referer( 'rtb-admin', 'nonce' ) || !current_user_can( 'manage_bookings' ) ) {
|
646 |
+
$this->nopriv_ajax();
|
647 |
+
}
|
648 |
+
|
649 |
+
// Set up $_POST object for validation
|
650 |
+
foreach( $_POST['email'] as $field ) {
|
651 |
+
$_POST[ $field['name'] ] = $field['value'];
|
652 |
+
}
|
653 |
+
|
654 |
+
$ids = ( isset( $_POST['ID'] ) and $_POST['ID'] != '' ) ? array_map( 'intval', explode( ',', $_POST['ID'] ) ) : array();
|
655 |
+
$subject = stripcslashes( sanitize_text_field( $_POST['rtb-email-subject'] ) );
|
656 |
+
$message = stripcslashes( wp_kses_post( $_POST['rtb-email-message'] ) );
|
657 |
+
|
658 |
+
if ( empty( $message ) ) {
|
659 |
+
wp_send_json_error(
|
660 |
+
array(
|
661 |
+
'error' => 'email_missing_message',
|
662 |
+
'msg' => __( 'Please enter a message before sending the email.', 'restaurant-reservations' ),
|
663 |
+
)
|
664 |
+
);
|
665 |
+
}
|
666 |
+
|
667 |
+
if ( empty( $ids ) ) {
|
668 |
+
wp_send_json_error(
|
669 |
+
array(
|
670 |
+
'error' => 'email_missing_data',
|
671 |
+
'msg' => __( 'The email could not be sent because some critical information was missing.', 'restaurant-reservations' ),
|
672 |
+
)
|
673 |
+
);
|
674 |
+
}
|
675 |
+
|
676 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
|
677 |
+
|
678 |
+
foreach ( $ids as $id ) {
|
679 |
+
|
680 |
+
$booking = new rtbBooking();
|
681 |
+
$booking->load_post( $id );
|
682 |
+
|
683 |
+
$email = new rtbNotificationEmail( 'admin_email_notice', 'user' );
|
684 |
+
$email->subject = empty( $subject ) ? $rtb_controller->settings->get_setting( 'subject-admin-notice' ) : $subject;
|
685 |
+
$email->message = $message;
|
686 |
+
$email->set_booking( $booking );
|
687 |
+
if ( $email->prepare_notification() ) {
|
688 |
+
do_action( 'rtb_send_notification_before', $email );
|
689 |
+
$email->send_notification();
|
690 |
+
do_action( 'rtb_send_notification_after', $email );
|
691 |
+
}
|
692 |
+
|
693 |
+
// Store email in postmeta for log
|
694 |
+
$booking->add_log( 'email', $email->subject, $email->message );
|
695 |
+
$booking->insert_post_data();
|
696 |
+
}
|
697 |
+
|
698 |
+
wp_send_json_success();
|
699 |
+
}
|
700 |
+
|
701 |
+
/**
|
702 |
+
* Handle ajax requests to configure columns
|
703 |
+
*
|
704 |
+
* @since 1.3.1
|
705 |
+
*/
|
706 |
+
public function column_modal_ajax() {
|
707 |
+
|
708 |
+
global $rtb_controller;
|
709 |
+
|
710 |
+
// Authenticate request
|
711 |
+
if ( !check_ajax_referer( 'rtb-admin', 'nonce' ) || !current_user_can( 'manage_bookings' ) ) {
|
712 |
+
$this->nopriv_ajax();
|
713 |
+
}
|
714 |
+
|
715 |
+
if ( !isset( $_POST['columns'] ) || !is_array( $_POST['columns'] ) || empty( $_POST['columns'] ) ) {
|
716 |
+
wp_send_json_error(
|
717 |
+
array(
|
718 |
+
'error' => 'no_columns',
|
719 |
+
'msg' => __( 'You must select at least one column to display.', 'restaurant-reservations' ),
|
720 |
+
)
|
721 |
+
);
|
722 |
+
}
|
723 |
+
|
724 |
+
$settings = get_option( 'rtb-settings' );
|
725 |
+
$settings['bookings-table-columns'] = array_map( 'sanitize_key', $_POST['columns'] );
|
726 |
+
update_option( 'rtb-settings', $settings );
|
727 |
+
|
728 |
+
wp_send_json_success();
|
729 |
+
}
|
730 |
+
|
731 |
+
/**
|
732 |
+
* Handle ajax requests to ban by IP or email address
|
733 |
+
*
|
734 |
+
* @since 1.3.1
|
735 |
+
*/
|
736 |
+
public function ban_modal_ajax() {
|
737 |
+
|
738 |
+
global $rtb_controller;
|
739 |
+
|
740 |
+
// Authenticate request
|
741 |
+
if ( !check_ajax_referer( 'rtb-admin', 'nonce' ) || !current_user_can( 'manage_bookings' ) ) {
|
742 |
+
$this->nopriv_ajax();
|
743 |
+
}
|
744 |
+
|
745 |
+
// Ban an email address
|
746 |
+
if ( isset( $_POST['email'] ) && !empty( $_POST['email'] ) ) {
|
747 |
+
$email = trim( sanitize_email( $_POST['email'] ) );
|
748 |
+
$banned_emails = preg_split( '/\r\n|\r|\n/', (string) $rtb_controller->settings->get_setting( 'ban-emails' ) );
|
749 |
+
|
750 |
+
if ( !in_array( $email, $banned_emails ) ) {
|
751 |
+
$banned_emails[] = $email;
|
752 |
+
$rtb_controller->settings->settings['ban-emails'] = join( "\n", $banned_emails );
|
753 |
+
update_option( 'rtb-settings', $rtb_controller->settings->settings );
|
754 |
+
}
|
755 |
+
|
756 |
+
wp_send_json_success();
|
757 |
+
|
758 |
+
// Ban an IP address
|
759 |
+
} elseif ( isset( $_POST['ip'] ) && !empty( $_POST['ip'] ) ) {
|
760 |
+
$ip = trim( sanitize_text_field( $_POST['ip'] ) );
|
761 |
+
$banned_ips = preg_split( '/\r\n|\r|\n/', (string) $rtb_controller->settings->get_setting( 'ban-ips' ) );
|
762 |
+
|
763 |
+
if ( !in_array( $ip, $banned_ips ) ) {
|
764 |
+
$banned_ips[] = $ip;
|
765 |
+
$rtb_controller->settings->settings['ban-ips'] = join( "\n", $banned_ips );
|
766 |
+
update_option( 'rtb-settings', $rtb_controller->settings->settings );
|
767 |
+
}
|
768 |
+
|
769 |
+
wp_send_json_success();
|
770 |
+
}
|
771 |
+
|
772 |
+
wp_send_json_error(
|
773 |
+
array(
|
774 |
+
'error' => 'no_data',
|
775 |
+
'msg' => __( 'No IP or email address could be found for this ban request.', 'restaurant-reservations' ),
|
776 |
+
)
|
777 |
+
);
|
778 |
+
}
|
779 |
+
|
780 |
+
/**
|
781 |
+
* Handle ajax requests to delete bookings by email
|
782 |
+
*
|
783 |
+
* @since 1.7.7
|
784 |
+
*/
|
785 |
+
public function delete_modal_ajax() {
|
786 |
+
|
787 |
+
global $rtb_controller;
|
788 |
+
|
789 |
+
// Authenticate request
|
790 |
+
if ( !check_ajax_referer( 'rtb-admin', 'nonce' ) || !current_user_can( 'manage_bookings' ) ) {
|
791 |
+
$this->nopriv_ajax();
|
792 |
+
}
|
793 |
+
|
794 |
+
if ( !empty( $_POST['email'] ) ) {
|
795 |
+
$email = sanitize_email( $_POST['email'] );
|
796 |
+
|
797 |
+
$args = array(
|
798 |
+
'date_range' => null,
|
799 |
+
'posts_per_page' => 100,
|
800 |
+
'paged' => !empty( $_POST['page'] ) ? (int) $_POST['page'] : 1,
|
801 |
+
);
|
802 |
+
|
803 |
+
$query = new rtbQuery( $args, 'delete-by-email' );
|
804 |
+
$query->prepare_args();
|
805 |
+
|
806 |
+
$bookings = $query->get_bookings();
|
807 |
+
$deleted = 0;
|
808 |
+
foreach( $bookings as $booking ) {
|
809 |
+
if ( isset( $booking->email ) && $booking->email === $email) {
|
810 |
+
wp_delete_post( $booking->ID, true );
|
811 |
+
$deleted++;
|
812 |
+
}
|
813 |
+
}
|
814 |
+
|
815 |
+
// Get count of all bookings
|
816 |
+
global $wpdb;
|
817 |
+
$where = "WHERE p.post_type = '" . RTB_BOOKING_POST_TYPE . "'";
|
818 |
+
$query = "SELECT count( * ) AS num_posts
|
819 |
+
FROM $wpdb->posts p
|
820 |
+
$where
|
821 |
+
";
|
822 |
+
|
823 |
+
$count = $wpdb->get_results( $query );
|
824 |
+
$count = (int) $count[0]->num_posts;
|
825 |
+
|
826 |
+
wp_send_json_success(array(
|
827 |
+
'processed' => count($bookings),
|
828 |
+
'deleted' => $deleted,
|
829 |
+
'total' => $count,
|
830 |
+
));
|
831 |
+
}
|
832 |
+
|
833 |
+
wp_send_json_error(
|
834 |
+
array(
|
835 |
+
'error' => 'no_data',
|
836 |
+
'msg' => __( 'No email address could be found for this delete request.', 'restaurant-reservations' ),
|
837 |
+
)
|
838 |
+
);
|
839 |
+
}
|
840 |
+
|
841 |
+
/**
|
842 |
+
* Register a party as having arrived
|
843 |
+
* @since 2.0.0
|
844 |
+
*/
|
845 |
+
public function set_booking_arrived() {
|
846 |
+
|
847 |
+
// Authenticate request
|
848 |
+
if ( !check_ajax_referer( 'rtb-booking-form', 'nonce' ) || !current_user_can( 'manage_bookings' ) ) {
|
849 |
+
$this->nopriv_ajax();
|
850 |
+
}
|
851 |
+
|
852 |
+
$booking_id = isset($_POST['booking']) ? intval( $_POST['booking']['ID'] ) : 0;
|
853 |
+
|
854 |
+
$booking_id = wp_update_post(array(
|
855 |
+
'ID' => $booking_id,
|
856 |
+
'post_status' => 'arrived'
|
857 |
+
) );
|
858 |
+
|
859 |
+
if ( $booking_id ) {
|
860 |
+
wp_send_json_success();
|
861 |
+
}
|
862 |
+
else {
|
863 |
+
wp_send_json_error(
|
864 |
+
array(
|
865 |
+
'error' => 'loggedout',
|
866 |
+
'msg' => sprintf( __( 'You have been logged out. Please %slogin again%s.', 'restaurant-reservations' ), '<a href="' . wp_login_url( ) . '">', '</a>' ),
|
867 |
+
)
|
868 |
+
);
|
869 |
+
}
|
870 |
+
}
|
871 |
+
|
872 |
+
/**
|
873 |
+
* Validate post status and notification fields
|
874 |
+
* @since 1.3
|
875 |
+
*/
|
876 |
+
public function validate_admin_fields( $booking ) {
|
877 |
+
|
878 |
+
// Only validate in the admin
|
879 |
+
if ( !$_POST['action'] || $_POST['action'] !== 'admin_booking_request' ) {
|
880 |
+
return;
|
881 |
+
}
|
882 |
+
|
883 |
+
global $rtb_controller;
|
884 |
+
|
885 |
+
// Disable Notifications
|
886 |
+
$booking->send_notifications = empty( $_POST['rtb-notifications'] ) ? false : true;
|
887 |
+
}
|
888 |
+
|
889 |
+
/**
|
890 |
+
* Adjust post status when adding/editing a booking from the admin area
|
891 |
+
* @since 1.3
|
892 |
+
*/
|
893 |
+
public function insert_booking_data( $args, $booking ) {
|
894 |
+
|
895 |
+
// Validate user request
|
896 |
+
if ( empty( $_POST['action'] ) || $_POST['action'] !== 'admin_booking_request' || !current_user_can( 'manage_bookings' ) ) {
|
897 |
+
return $args;
|
898 |
+
}
|
899 |
+
|
900 |
+
if ( !empty( $booking->post_status ) ) {
|
901 |
+
$args['post_status'] = $booking->post_status;
|
902 |
+
}
|
903 |
+
|
904 |
+
return $args;
|
905 |
+
}
|
906 |
+
|
907 |
+
/**
|
908 |
+
* Maybe disable notifications when adding/editing bookings from the
|
909 |
+
* admin booking modal
|
910 |
+
* @since 1.3
|
911 |
+
*/
|
912 |
+
public function maybe_disable_notifications() {
|
913 |
+
|
914 |
+
// Don't disable notifications if they have opted to send them
|
915 |
+
if ( !empty( $_POST['rtb-notifications'] ) ) {
|
916 |
+
return;
|
917 |
+
}
|
918 |
+
|
919 |
+
// Disable all notifications. This filter is here in case a
|
920 |
+
// third-party sets up a notification that they don't want to be
|
921 |
+
// disabled even if the user has opted not to send notifications
|
922 |
+
// To exempt a notification, hook into the filter and copy it
|
923 |
+
// from $rtb_notifications to the empty array.
|
924 |
+
global $rtb_controller;
|
925 |
+
$rtb_controller->notifications->notifications = apply_filters( 'rtb_admin_disabled_notifications_exemption', array(), $rtb_controller->notifications->notifications );
|
926 |
+
}
|
927 |
+
}
|
928 |
+
} // endif;
|
includes/AdminPageSettingLicenseKey.class.php
CHANGED
@@ -1,429 +1,429 @@
|
|
1 |
-
<?php
|
2 |
-
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
-
|
4 |
-
if ( !class_exists( 'rtbAdminPageSettingLicenseKey' ) ) {
|
5 |
-
/**
|
6 |
-
* Add a setting to Simple Admin Pages to register and verify an
|
7 |
-
* EDD Software Licensing key.
|
8 |
-
*
|
9 |
-
* This class is modelled on AdminPageSetting.class.php in the
|
10 |
-
* Simple Admin Pages Library. But it doesn't extend that class
|
11 |
-
* due to rules within the library about how versions are
|
12 |
-
* managed.
|
13 |
-
*
|
14 |
-
* See: https://github.com/NateWr/simple-admin-pages
|
15 |
-
*
|
16 |
-
* @since 1.4.1
|
17 |
-
*/
|
18 |
-
class rtbAdminPageSettingLicenseKey {
|
19 |
-
|
20 |
-
/**
|
21 |
-
* Scripts to load for this component
|
22 |
-
*
|
23 |
-
* @since 1.4.1
|
24 |
-
*/
|
25 |
-
public $scripts = array();
|
26 |
-
|
27 |
-
/**
|
28 |
-
* Styles to load for this component
|
29 |
-
*
|
30 |
-
* @since 1.4.1
|
31 |
-
*/
|
32 |
-
public $styles = array();
|
33 |
-
|
34 |
-
/**
|
35 |
-
* Product slug on the store
|
36 |
-
*
|
37 |
-
* @since 1.4.1
|
38 |
-
*/
|
39 |
-
public $product;
|
40 |
-
|
41 |
-
/**
|
42 |
-
* Store URL which manages license data
|
43 |
-
*
|
44 |
-
* @since 1.4.1
|
45 |
-
*/
|
46 |
-
public $store_url;
|
47 |
-
|
48 |
-
/**
|
49 |
-
* Translateable strings required for this component
|
50 |
-
*
|
51 |
-
* @since 1.4.1
|
52 |
-
*/
|
53 |
-
public $strings = array(
|
54 |
-
'active' => null, // __( 'Active', 'textdomain' ),
|
55 |
-
'expired' => null, // __( 'Expired', 'textdomain' ),
|
56 |
-
'inactive' => null, // __( 'Inactive', 'textdomain' ),
|
57 |
-
'expiry' => null, // _x( 'Expiry', 'Label before the expiration date of the license key', textdomain' ),
|
58 |
-
'deactivate' => null, // __( 'Deactivate License', 'textdomain' ),
|
59 |
-
);
|
60 |
-
|
61 |
-
/**
|
62 |
-
* Initialize the setting
|
63 |
-
*
|
64 |
-
* @since 1.4.1
|
65 |
-
*/
|
66 |
-
public function __construct( $args ) {
|
67 |
-
|
68 |
-
// Parse the values passed
|
69 |
-
$this->parse_args( $args );
|
70 |
-
|
71 |
-
// Get any existing value
|
72 |
-
$this->set_value();
|
73 |
-
|
74 |
-
// Set an error if the object is missing necessary data
|
75 |
-
if ( $this->missing_data() ) {
|
76 |
-
$this->set_error();
|
77 |
-
}
|
78 |
-
|
79 |
-
// Process a license activation/deactivation
|
80 |
-
add_filter( 'admin_init', array( $this, 'process_action' ), 100 );
|
81 |
-
}
|
82 |
-
|
83 |
-
/**
|
84 |
-
* Parse the arguments passed in the construction and assign them to
|
85 |
-
* internal variables.
|
86 |
-
*
|
87 |
-
* @since 1.4.1
|
88 |
-
*/
|
89 |
-
private function parse_args( $args ) {
|
90 |
-
foreach ( $args as $key => $val ) {
|
91 |
-
switch ( $key ) {
|
92 |
-
|
93 |
-
case 'id' :
|
94 |
-
$this->{$key} = esc_attr( $val );
|
95 |
-
|
96 |
-
case 'title' :
|
97 |
-
$this->{$key} = esc_attr( $val );
|
98 |
-
|
99 |
-
case 'product' :
|
100 |
-
$this->{$key} = sanitize_key( $val );
|
101 |
-
|
102 |
-
case 'url' :
|
103 |
-
$this->{$key} = sanitize_text_field( $val );
|
104 |
-
|
105 |
-
default :
|
106 |
-
$this->{$key} = $val;
|
107 |
-
|
108 |
-
}
|
109 |
-
}
|
110 |
-
}
|
111 |
-
|
112 |
-
/**
|
113 |
-
* Check for missing data when setup.
|
114 |
-
*
|
115 |
-
* @since 1.4.1
|
116 |
-
*/
|
117 |
-
private function missing_data() {
|
118 |
-
|
119 |
-
// Required fields
|
120 |
-
if ( empty( $this->id ) ) {
|
121 |
-
$this->set_error(
|
122 |
-
array(
|
123 |
-
'type' => 'missing_data',
|
124 |
-
'data' => 'id'
|
125 |
-
)
|
126 |
-
);
|
127 |
-
}
|
128 |
-
if ( empty( $this->title ) ) {
|
129 |
-
$this->set_error(
|
130 |
-
array(
|
131 |
-
'type' => 'missing_data',
|
132 |
-
'data' => 'title'
|
133 |
-
)
|
134 |
-
);
|
135 |
-
}
|
136 |
-
}
|
137 |
-
|
138 |
-
/**
|
139 |
-
* Set a value
|
140 |
-
*
|
141 |
-
* @since 1.4.1
|
142 |
-
*/
|
143 |
-
public function set_value( $val = null ) {
|
144 |
-
|
145 |
-
if ( $val === null ) {
|
146 |
-
$option_group_value = get_option( $this->page );
|
147 |
-
$val = isset( $option_group_value[ $this->id ] ) ? $option_group_value[ $this->id ] : '';
|
148 |
-
}
|
149 |
-
|
150 |
-
$this->value = $this->esc_value( $val );
|
151 |
-
}
|
152 |
-
|
153 |
-
/**
|
154 |
-
* Escape the value to display it in text fields and other input fields
|
155 |
-
*
|
156 |
-
* @since 1.4.1
|
157 |
-
*/
|
158 |
-
public function esc_value( $val ) {
|
159 |
-
|
160 |
-
$value = array(
|
161 |
-
'api_key' => '',
|
162 |
-
'status' => false,
|
163 |
-
'expiry' => false,
|
164 |
-
);
|
165 |
-
|
166 |
-
if ( empty( $val ) || empty( $val['api_key'] ) ) {
|
167 |
-
return $value;
|
168 |
-
}
|
169 |
-
|
170 |
-
$value['api_key'] = esc_attr( $val['api_key'] );
|
171 |
-
|
172 |
-
if ( !empty( $val['status'] ) ) {
|
173 |
-
$value['status'] = esc_attr( $val['status'] );
|
174 |
-
}
|
175 |
-
|
176 |
-
if ( !empty( $val['expiry'] ) ) {
|
177 |
-
$value['expiry'] = esc_attr( $val['expiry'] );
|
178 |
-
}
|
179 |
-
|
180 |
-
return $value;
|
181 |
-
}
|
182 |
-
|
183 |
-
/**
|
184 |
-
* Display this setting
|
185 |
-
*
|
186 |
-
* @since 1.4.1
|
187 |
-
*/
|
188 |
-
public function display_setting() {
|
189 |
-
|
190 |
-
// Set a flag for the output
|
191 |
-
$is_active = $this->value['status'] == 'valid' ? true : false;
|
192 |
-
$status = empty( $this->value['status'] ) ? 'inactive' : $this->value['status'];
|
193 |
-
$status_string = !empty( $this->strings[ $status] ) ? $this->strings[ $status ] : __( 'Invalid', 'restaurant-reservations' );
|
194 |
-
|
195 |
-
// Compile activation/deactivation URL
|
196 |
-
if ( !empty( $this->value['api_key'] ) ) {
|
197 |
-
$url = add_query_arg(
|
198 |
-
array(
|
199 |
-
'id' => $this->id,
|
200 |
-
)
|
201 |
-
);
|
202 |
-
if ( $is_active ) {
|
203 |
-
$url = add_query_arg( 'action', 'deactivate', $url );
|
204 |
-
} else {
|
205 |
-
$url = add_query_arg( 'action', 'activate', $url );
|
206 |
-
}
|
207 |
-
}
|
208 |
-
?>
|
209 |
-
|
210 |
-
<div class="rtb-license-setting" data-id="<?php echo esc_attr( $this->id ); ?>">
|
211 |
-
|
212 |
-
<input
|
213 |
-
name="<?php echo esc_attr( $this->get_input_name().'[api_key]' ); ?>"
|
214 |
-
type="text"
|
215 |
-
id="<?php echo esc_attr( $this->get_input_name().'[api_key]' ); ?>"
|
216 |
-
value="<?php echo esc_attr( $this->value['api_key'] ); ?>"
|
217 |
-
<?php echo !empty( $this->placeholder ) ? ' placeholder="' . esc_attr( $this->placeholder ) . '"' : ''; ?>
|
218 |
-
class="regular-text">
|
219 |
-
|
220 |
-
<?php if ( !empty( $this->value['api_key'] ) ) : ?>
|
221 |
-
<span class="status <?php echo $is_active ? 'valid' : 'inactive'; ?>">
|
222 |
-
<?php echo esc_html( $status_string ); ?>
|
223 |
-
</span>
|
224 |
-
|
225 |
-
<a href="<?php echo esc_url( $url ); ?>" class="button">
|
226 |
-
<?php echo $is_active ? $this->strings['deactivate'] : $this->strings['activate']; ?>
|
227 |
-
</a>
|
228 |
-
|
229 |
-
<span class="spinner"></span>
|
230 |
-
|
231 |
-
<?php endif;
|
232 |
-
|
233 |
-
$this->display_description();
|
234 |
-
|
235 |
-
?>
|
236 |
-
|
237 |
-
</div>
|
238 |
-
|
239 |
-
<?php
|
240 |
-
}
|
241 |
-
|
242 |
-
/**
|
243 |
-
* Display a description for this setting
|
244 |
-
*
|
245 |
-
* @since 1.4.1
|
246 |
-
*/
|
247 |
-
public function display_description() {
|
248 |
-
|
249 |
-
if ( !empty( $this->description ) ) : ?>
|
250 |
-
|
251 |
-
<p class="description"><?php echo $this->description; ?></p>
|
252 |
-
|
253 |
-
<?php endif;
|
254 |
-
}
|
255 |
-
|
256 |
-
/**
|
257 |
-
* Generate an option input field name, using the grouped schema.
|
258 |
-
*
|
259 |
-
* @since 1.4.1
|
260 |
-
*/
|
261 |
-
public function get_input_name() {
|
262 |
-
return esc_attr( $this->page ) . '[' . esc_attr( $this->id ) . ']';
|
263 |
-
}
|
264 |
-
|
265 |
-
|
266 |
-
/**
|
267 |
-
* Sanitize the array of text inputs for this setting
|
268 |
-
*
|
269 |
-
* @since 1.4.1
|
270 |
-
*/
|
271 |
-
public function sanitize_callback_wrapper( $values ) {
|
272 |
-
|
273 |
-
$output = array(
|
274 |
-
'api_key' => '',
|
275 |
-
'status' => false,
|
276 |
-
'expiry' => false,
|
277 |
-
);
|
278 |
-
|
279 |
-
if ( empty( $values ) || empty( $values['api_key'] ) ) {
|
280 |
-
return $output;
|
281 |
-
}
|
282 |
-
|
283 |
-
$output['api_key'] = trim( sanitize_text_field( $values['api_key'] ) );
|
284 |
-
|
285 |
-
// Clear status and expiry when a license key has changed
|
286 |
-
global $rtb_controller;
|
287 |
-
$old = $rtb_controller->settings->get_setting( $this->id );
|
288 |
-
if ( empty( $old['api_key'] ) || $old['api_key'] !== $output['api_key'] ) {
|
289 |
-
return $output;
|
290 |
-
}
|
291 |
-
|
292 |
-
// Preserve old status values
|
293 |
-
$output = array_merge( $old, $output );
|
294 |
-
|
295 |
-
return $output;
|
296 |
-
}
|
297 |
-
|
298 |
-
/**
|
299 |
-
* Add and register this setting
|
300 |
-
*
|
301 |
-
* @since 1.4.1
|
302 |
-
*/
|
303 |
-
public function add_settings_field( $section_id ) {
|
304 |
-
|
305 |
-
add_settings_field(
|
306 |
-
$this->id,
|
307 |
-
$this->title,
|
308 |
-
array( $this, 'display_setting' ),
|
309 |
-
$this->tab,
|
310 |
-
$section_id
|
311 |
-
);
|
312 |
-
|
313 |
-
}
|
314 |
-
|
315 |
-
/**
|
316 |
-
* Set an error
|
317 |
-
*
|
318 |
-
* @since 1.4.1
|
319 |
-
*/
|
320 |
-
public function set_error( $error ) {
|
321 |
-
$this->errors[] = array_merge(
|
322 |
-
$error,
|
323 |
-
array(
|
324 |
-
'class' => get_class( $this ),
|
325 |
-
'id' => $this->id,
|
326 |
-
'backtrace' => debug_backtrace()
|
327 |
-
)
|
328 |
-
);
|
329 |
-
}
|
330 |
-
|
331 |
-
/**
|
332 |
-
* Process a license activation if requested
|
333 |
-
*
|
334 |
-
* @since 1.4.1
|
335 |
-
*/
|
336 |
-
public function process_action() {
|
337 |
-
|
338 |
-
if ( !current_user_can( 'manage_options' ) || empty( $_GET['tab'] ) || $_GET['tab'] !== 'rtb-licenses' || empty( $_GET['action'] ) || empty( $_GET['id'] ) || $_GET['id'] !== $this->id ) {
|
339 |
-
return;
|
340 |
-
}
|
341 |
-
|
342 |
-
$params = array();
|
343 |
-
$params['edd_action'] = $_GET['action'] === 'activate' ? 'activate_license' : 'deactivate_license';
|
344 |
-
$params['license'] = sanitize_text_field( $this->value['api_key'] );
|
345 |
-
$params['item_name'] = urlencode( $this->product );
|
346 |
-
|
347 |
-
$response = wp_remote_get( add_query_arg( $params, $this->store_url ), array( 'timeout' => 15, 'sslverify' => false ) );
|
348 |
-
|
349 |
-
if ( is_wp_error( $response ) ) {
|
350 |
-
$url = remove_query_arg( array( 'id', 'action' ) );
|
351 |
-
$url = add_query_arg( 'license_result', 'response_wp_error', $url );
|
352 |
-
header( 'Location: ' . esc_url_raw( $url ) );
|
353 |
-
}
|
354 |
-
|
355 |
-
$license_data = json_decode( wp_remote_retrieve_body( $response ) );
|
356 |
-
|
357 |
-
|
358 |
-
if ( $params['edd_action'] == 'activate' ) {
|
359 |
-
$result = $this->process_activation_response( $license_data );
|
360 |
-
} else {
|
361 |
-
$result = $this->process_activation_response( $license_data );
|
362 |
-
}
|
363 |
-
|
364 |
-
// Construct a URL to redirect back to the tab
|
365 |
-
$url = remove_query_arg( array( 'id', 'action' ) );
|
366 |
-
$url = add_query_arg(
|
367 |
-
array(
|
368 |
-
'license_result' => $result ? 1 : 0,
|
369 |
-
'action' => $_GET['action'] == 'activate' ? 'activate' : 'deactivate',
|
370 |
-
),
|
371 |
-
$url
|
372 |
-
);
|
373 |
-
|
374 |
-
// If the result failed maybe add note on why
|
375 |
-
if ( !$result && !empty( $license_data->error ) ) {
|
376 |
-
$url = add_query_arg( 'result_error', $license_data->error, $url );
|
377 |
-
}
|
378 |
-
|
379 |
-
header( 'Location: ' . esc_url_raw( $url ) );
|
380 |
-
|
381 |
-
}
|
382 |
-
|
383 |
-
/**
|
384 |
-
* Process the response to an activation request
|
385 |
-
*
|
386 |
-
* @since 1.4.1
|
387 |
-
*/
|
388 |
-
public function process_activation_response( $license_data ) {
|
389 |
-
|
390 |
-
if ( ( !empty( $license_data->error ) && ( $license_data->error == 'missing' || $license_data->error == 'item_name_mismatch' ) ) || $license_data->license == 'invalid' ) {
|
391 |
-
$this->value['status'] = 'invalid';
|
392 |
-
$this->value['expiry'] = false;
|
393 |
-
} else {
|
394 |
-
$this->value['status'] = $license_data->license;
|
395 |
-
$this->value['expiry'] = $license_data->expires;
|
396 |
-
}
|
397 |
-
|
398 |
-
$rtb_settings = get_option( $this->page );
|
399 |
-
$rtb_settings[ $this->id ] = $this->value;
|
400 |
-
|
401 |
-
update_option( $this->page, $rtb_settings );
|
402 |
-
|
403 |
-
return $license_data->license == 'valid' || $license_data->license == 'deactivated';
|
404 |
-
}
|
405 |
-
|
406 |
-
/**
|
407 |
-
* Process the response to an deactivation request
|
408 |
-
*
|
409 |
-
* @since 1.4.1
|
410 |
-
*/
|
411 |
-
public function process_deactivation_response( $license_data ) {
|
412 |
-
|
413 |
-
if ( $license_data->license !== 'deactivated' ) {
|
414 |
-
return false;
|
415 |
-
} else {
|
416 |
-
$this->value['status'] = false;
|
417 |
-
$this->value['expiry'] = false;
|
418 |
-
}
|
419 |
-
|
420 |
-
$rtb_settings = get_option( $this->page );
|
421 |
-
$rtb_settings[ $this->id ] = $this->value;
|
422 |
-
|
423 |
-
update_option( $this->page, $rtb_settings );
|
424 |
-
|
425 |
-
return true;
|
426 |
-
}
|
427 |
-
|
428 |
-
}
|
429 |
-
} // endif;
|
1 |
+
<?php
|
2 |
+
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
+
|
4 |
+
if ( !class_exists( 'rtbAdminPageSettingLicenseKey' ) ) {
|
5 |
+
/**
|
6 |
+
* Add a setting to Simple Admin Pages to register and verify an
|
7 |
+
* EDD Software Licensing key.
|
8 |
+
*
|
9 |
+
* This class is modelled on AdminPageSetting.class.php in the
|
10 |
+
* Simple Admin Pages Library. But it doesn't extend that class
|
11 |
+
* due to rules within the library about how versions are
|
12 |
+
* managed.
|
13 |
+
*
|
14 |
+
* See: https://github.com/NateWr/simple-admin-pages
|
15 |
+
*
|
16 |
+
* @since 1.4.1
|
17 |
+
*/
|
18 |
+
class rtbAdminPageSettingLicenseKey {
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Scripts to load for this component
|
22 |
+
*
|
23 |
+
* @since 1.4.1
|
24 |
+
*/
|
25 |
+
public $scripts = array();
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Styles to load for this component
|
29 |
+
*
|
30 |
+
* @since 1.4.1
|
31 |
+
*/
|
32 |
+
public $styles = array();
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Product slug on the store
|
36 |
+
*
|
37 |
+
* @since 1.4.1
|
38 |
+
*/
|
39 |
+
public $product;
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Store URL which manages license data
|
43 |
+
*
|
44 |
+
* @since 1.4.1
|
45 |
+
*/
|
46 |
+
public $store_url;
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Translateable strings required for this component
|
50 |
+
*
|
51 |
+
* @since 1.4.1
|
52 |
+
*/
|
53 |
+
public $strings = array(
|
54 |
+
'active' => null, // __( 'Active', 'textdomain' ),
|
55 |
+
'expired' => null, // __( 'Expired', 'textdomain' ),
|
56 |
+
'inactive' => null, // __( 'Inactive', 'textdomain' ),
|
57 |
+
'expiry' => null, // _x( 'Expiry', 'Label before the expiration date of the license key', textdomain' ),
|
58 |
+
'deactivate' => null, // __( 'Deactivate License', 'textdomain' ),
|
59 |
+
);
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Initialize the setting
|
63 |
+
*
|
64 |
+
* @since 1.4.1
|
65 |
+
*/
|
66 |
+
public function __construct( $args ) {
|
67 |
+
|
68 |
+
// Parse the values passed
|
69 |
+
$this->parse_args( $args );
|
70 |
+
|
71 |
+
// Get any existing value
|
72 |
+
$this->set_value();
|
73 |
+
|
74 |
+
// Set an error if the object is missing necessary data
|
75 |
+
if ( $this->missing_data() ) {
|
76 |
+
$this->set_error();
|
77 |
+
}
|
78 |
+
|
79 |
+
// Process a license activation/deactivation
|
80 |
+
add_filter( 'admin_init', array( $this, 'process_action' ), 100 );
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* Parse the arguments passed in the construction and assign them to
|
85 |
+
* internal variables.
|
86 |
+
*
|
87 |
+
* @since 1.4.1
|
88 |
+
*/
|
89 |
+
private function parse_args( $args ) {
|
90 |
+
foreach ( $args as $key => $val ) {
|
91 |
+
switch ( $key ) {
|
92 |
+
|
93 |
+
case 'id' :
|
94 |
+
$this->{$key} = esc_attr( $val );
|
95 |
+
|
96 |
+
case 'title' :
|
97 |
+
$this->{$key} = esc_attr( $val );
|
98 |
+
|
99 |
+
case 'product' :
|
100 |
+
$this->{$key} = sanitize_key( $val );
|
101 |
+
|
102 |
+
case 'url' :
|
103 |
+
$this->{$key} = sanitize_text_field( $val );
|
104 |
+
|
105 |
+
default :
|
106 |
+
$this->{$key} = $val;
|
107 |
+
|
108 |
+
}
|
109 |
+
}
|
110 |
+
}
|
111 |
+
|
112 |
+
/**
|
113 |
+
* Check for missing data when setup.
|
114 |
+
*
|
115 |
+
* @since 1.4.1
|
116 |
+
*/
|
117 |
+
private function missing_data() {
|
118 |
+
|
119 |
+
// Required fields
|
120 |
+
if ( empty( $this->id ) ) {
|
121 |
+
$this->set_error(
|
122 |
+
array(
|
123 |
+
'type' => 'missing_data',
|
124 |
+
'data' => 'id'
|
125 |
+
)
|
126 |
+
);
|
127 |
+
}
|
128 |
+
if ( empty( $this->title ) ) {
|
129 |
+
$this->set_error(
|
130 |
+
array(
|
131 |
+
'type' => 'missing_data',
|
132 |
+
'data' => 'title'
|
133 |
+
)
|
134 |
+
);
|
135 |
+
}
|
136 |
+
}
|
137 |
+
|
138 |
+
/**
|
139 |
+
* Set a value
|
140 |
+
*
|
141 |
+
* @since 1.4.1
|
142 |
+
*/
|
143 |
+
public function set_value( $val = null ) {
|
144 |
+
|
145 |
+
if ( $val === null ) {
|
146 |
+
$option_group_value = get_option( $this->page );
|
147 |
+
$val = isset( $option_group_value[ $this->id ] ) ? $option_group_value[ $this->id ] : '';
|
148 |
+
}
|
149 |
+
|
150 |
+
$this->value = $this->esc_value( $val );
|
151 |
+
}
|
152 |
+
|
153 |
+
/**
|
154 |
+
* Escape the value to display it in text fields and other input fields
|
155 |
+
*
|
156 |
+
* @since 1.4.1
|
157 |
+
*/
|
158 |
+
public function esc_value( $val ) {
|
159 |
+
|
160 |
+
$value = array(
|
161 |
+
'api_key' => '',
|
162 |
+
'status' => false,
|
163 |
+
'expiry' => false,
|
164 |
+
);
|
165 |
+
|
166 |
+
if ( empty( $val ) || empty( $val['api_key'] ) ) {
|
167 |
+
return $value;
|
168 |
+
}
|
169 |
+
|
170 |
+
$value['api_key'] = esc_attr( $val['api_key'] );
|
171 |
+
|
172 |
+
if ( !empty( $val['status'] ) ) {
|
173 |
+
$value['status'] = esc_attr( $val['status'] );
|
174 |
+
}
|
175 |
+
|
176 |
+
if ( !empty( $val['expiry'] ) ) {
|
177 |
+
$value['expiry'] = esc_attr( $val['expiry'] );
|
178 |
+
}
|
179 |
+
|
180 |
+
return $value;
|
181 |
+
}
|
182 |
+
|
183 |
+
/**
|
184 |
+
* Display this setting
|
185 |
+
*
|
186 |
+
* @since 1.4.1
|
187 |
+
*/
|
188 |
+
public function display_setting() {
|
189 |
+
|
190 |
+
// Set a flag for the output
|
191 |
+
$is_active = $this->value['status'] == 'valid' ? true : false;
|
192 |
+
$status = empty( $this->value['status'] ) ? 'inactive' : $this->value['status'];
|
193 |
+
$status_string = !empty( $this->strings[ $status] ) ? $this->strings[ $status ] : __( 'Invalid', 'restaurant-reservations' );
|
194 |
+
|
195 |
+
// Compile activation/deactivation URL
|
196 |
+
if ( !empty( $this->value['api_key'] ) ) {
|
197 |
+
$url = add_query_arg(
|
198 |
+
array(
|
199 |
+
'id' => $this->id,
|
200 |
+
)
|
201 |
+
);
|
202 |
+
if ( $is_active ) {
|
203 |
+
$url = add_query_arg( 'action', 'deactivate', $url );
|
204 |
+
} else {
|
205 |
+
$url = add_query_arg( 'action', 'activate', $url );
|
206 |
+
}
|
207 |
+
}
|
208 |
+
?>
|
209 |
+
|
210 |
+
<div class="rtb-license-setting" data-id="<?php echo esc_attr( $this->id ); ?>">
|
211 |
+
|
212 |
+
<input
|
213 |
+
name="<?php echo esc_attr( $this->get_input_name().'[api_key]' ); ?>"
|
214 |
+
type="text"
|
215 |
+
id="<?php echo esc_attr( $this->get_input_name().'[api_key]' ); ?>"
|
216 |
+
value="<?php echo esc_attr( $this->value['api_key'] ); ?>"
|
217 |
+
<?php echo !empty( $this->placeholder ) ? ' placeholder="' . esc_attr( $this->placeholder ) . '"' : ''; ?>
|
218 |
+
class="regular-text">
|
219 |
+
|
220 |
+
<?php if ( !empty( $this->value['api_key'] ) ) : ?>
|
221 |
+
<span class="status <?php echo $is_active ? 'valid' : 'inactive'; ?>">
|
222 |
+
<?php echo esc_html( $status_string ); ?>
|
223 |
+
</span>
|
224 |
+
|
225 |
+
<a href="<?php echo esc_url( $url ); ?>" class="button">
|
226 |
+
<?php echo $is_active ? $this->strings['deactivate'] : $this->strings['activate']; ?>
|
227 |
+
</a>
|
228 |
+
|
229 |
+
<span class="spinner"></span>
|
230 |
+
|
231 |
+
<?php endif;
|
232 |
+
|
233 |
+
$this->display_description();
|
234 |
+
|
235 |
+
?>
|
236 |
+
|
237 |
+
</div>
|
238 |
+
|
239 |
+
<?php
|
240 |
+
}
|
241 |
+
|
242 |
+
/**
|
243 |
+
* Display a description for this setting
|
244 |
+
*
|
245 |
+
* @since 1.4.1
|
246 |
+
*/
|
247 |
+
public function display_description() {
|
248 |
+
|
249 |
+
if ( !empty( $this->description ) ) : ?>
|
250 |
+
|
251 |
+
<p class="description"><?php echo $this->description; ?></p>
|
252 |
+
|
253 |
+
<?php endif;
|
254 |
+
}
|
255 |
+
|
256 |
+
/**
|
257 |
+
* Generate an option input field name, using the grouped schema.
|
258 |
+
*
|
259 |
+
* @since 1.4.1
|
260 |
+
*/
|
261 |
+
public function get_input_name() {
|
262 |
+
return esc_attr( $this->page ) . '[' . esc_attr( $this->id ) . ']';
|
263 |
+
}
|
264 |
+
|
265 |
+
|
266 |
+
/**
|
267 |
+
* Sanitize the array of text inputs for this setting
|
268 |
+
*
|
269 |
+
* @since 1.4.1
|
270 |
+
*/
|
271 |
+
public function sanitize_callback_wrapper( $values ) {
|
272 |
+
|
273 |
+
$output = array(
|
274 |
+
'api_key' => '',
|
275 |
+
'status' => false,
|
276 |
+
'expiry' => false,
|
277 |
+
);
|
278 |
+
|
279 |
+
if ( empty( $values ) || empty( $values['api_key'] ) ) {
|
280 |
+
return $output;
|
281 |
+
}
|
282 |
+
|
283 |
+
$output['api_key'] = trim( sanitize_text_field( $values['api_key'] ) );
|
284 |
+
|
285 |
+
// Clear status and expiry when a license key has changed
|
286 |
+
global $rtb_controller;
|
287 |
+
$old = $rtb_controller->settings->get_setting( $this->id );
|
288 |
+
if ( empty( $old['api_key'] ) || $old['api_key'] !== $output['api_key'] ) {
|
289 |
+
return $output;
|
290 |
+
}
|
291 |
+
|
292 |
+
// Preserve old status values
|
293 |
+
$output = array_merge( $old, $output );
|
294 |
+
|
295 |
+
return $output;
|
296 |
+
}
|
297 |
+
|
298 |
+
/**
|
299 |
+
* Add and register this setting
|
300 |
+
*
|
301 |
+
* @since 1.4.1
|
302 |
+
*/
|
303 |
+
public function add_settings_field( $section_id ) {
|
304 |
+
|
305 |
+
add_settings_field(
|
306 |
+
$this->id,
|
307 |
+
$this->title,
|
308 |
+
array( $this, 'display_setting' ),
|
309 |
+
$this->tab,
|
310 |
+
$section_id
|
311 |
+
);
|
312 |
+
|
313 |
+
}
|
314 |
+
|
315 |
+
/**
|
316 |
+
* Set an error
|
317 |
+
*
|
318 |
+
* @since 1.4.1
|
319 |
+
*/
|
320 |
+
public function set_error( $error ) {
|
321 |
+
$this->errors[] = array_merge(
|
322 |
+
$error,
|
323 |
+
array(
|
324 |
+
'class' => get_class( $this ),
|
325 |
+
'id' => $this->id,
|
326 |
+
'backtrace' => debug_backtrace()
|
327 |
+
)
|
328 |
+
);
|
329 |
+
}
|
330 |
+
|
331 |
+
/**
|
332 |
+
* Process a license activation if requested
|
333 |
+
*
|
334 |
+
* @since 1.4.1
|
335 |
+
*/
|
336 |
+
public function process_action() {
|
337 |
+
|
338 |
+
if ( !current_user_can( 'manage_options' ) || empty( $_GET['tab'] ) || $_GET['tab'] !== 'rtb-licenses' || empty( $_GET['action'] ) || empty( $_GET['id'] ) || $_GET['id'] !== $this->id ) {
|
339 |
+
return;
|
340 |
+
}
|
341 |
+
|
342 |
+
$params = array();
|
343 |
+
$params['edd_action'] = $_GET['action'] === 'activate' ? 'activate_license' : 'deactivate_license';
|
344 |
+
$params['license'] = sanitize_text_field( $this->value['api_key'] );
|
345 |
+
$params['item_name'] = urlencode( $this->product );
|
346 |
+
|
347 |
+
$response = wp_remote_get( add_query_arg( $params, $this->store_url ), array( 'timeout' => 15, 'sslverify' => false ) );
|
348 |
+
|
349 |
+
if ( is_wp_error( $response ) ) {
|
350 |
+
$url = remove_query_arg( array( 'id', 'action' ) );
|
351 |
+
$url = add_query_arg( 'license_result', 'response_wp_error', $url );
|
352 |
+
header( 'Location: ' . esc_url_raw( $url ) );
|
353 |
+
}
|
354 |
+
|
355 |
+
$license_data = json_decode( wp_remote_retrieve_body( $response ) );
|
356 |
+
|
357 |
+
|
358 |
+
if ( $params['edd_action'] == 'activate' ) {
|
359 |
+
$result = $this->process_activation_response( $license_data );
|
360 |
+
} else {
|
361 |
+
$result = $this->process_activation_response( $license_data );
|
362 |
+
}
|
363 |
+
|
364 |
+
// Construct a URL to redirect back to the tab
|
365 |
+
$url = remove_query_arg( array( 'id', 'action' ) );
|
366 |
+
$url = add_query_arg(
|
367 |
+
array(
|
368 |
+
'license_result' => $result ? 1 : 0,
|
369 |
+
'action' => $_GET['action'] == 'activate' ? 'activate' : 'deactivate',
|
370 |
+
),
|
371 |
+
$url
|
372 |
+
);
|
373 |
+
|
374 |
+
// If the result failed maybe add note on why
|
375 |
+
if ( !$result && !empty( $license_data->error ) ) {
|
376 |
+
$url = add_query_arg( 'result_error', $license_data->error, $url );
|
377 |
+
}
|
378 |
+
|
379 |
+
header( 'Location: ' . esc_url_raw( $url ) );
|
380 |
+
|
381 |
+
}
|
382 |
+
|
383 |
+
/**
|
384 |
+
* Process the response to an activation request
|
385 |
+
*
|
386 |
+
* @since 1.4.1
|
387 |
+
*/
|
388 |
+
public function process_activation_response( $license_data ) {
|
389 |
+
|
390 |
+
if ( ( !empty( $license_data->error ) && ( $license_data->error == 'missing' || $license_data->error == 'item_name_mismatch' ) ) || $license_data->license == 'invalid' ) {
|
391 |
+
$this->value['status'] = 'invalid';
|
392 |
+
$this->value['expiry'] = false;
|
393 |
+
} else {
|
394 |
+
$this->value['status'] = $license_data->license;
|
395 |
+
$this->value['expiry'] = $license_data->expires;
|
396 |
+
}
|
397 |
+
|
398 |
+
$rtb_settings = get_option( $this->page );
|
399 |
+
$rtb_settings[ $this->id ] = $this->value;
|
400 |
+
|
401 |
+
update_option( $this->page, $rtb_settings );
|
402 |
+
|
403 |
+
return $license_data->license == 'valid' || $license_data->license == 'deactivated';
|
404 |
+
}
|
405 |
+
|
406 |
+
/**
|
407 |
+
* Process the response to an deactivation request
|
408 |
+
*
|
409 |
+
* @since 1.4.1
|
410 |
+
*/
|
411 |
+
public function process_deactivation_response( $license_data ) {
|
412 |
+
|
413 |
+
if ( $license_data->license !== 'deactivated' ) {
|
414 |
+
return false;
|
415 |
+
} else {
|
416 |
+
$this->value['status'] = false;
|
417 |
+
$this->value['expiry'] = false;
|
418 |
+
}
|
419 |
+
|
420 |
+
$rtb_settings = get_option( $this->page );
|
421 |
+
$rtb_settings[ $this->id ] = $this->value;
|
422 |
+
|
423 |
+
update_option( $this->page, $rtb_settings );
|
424 |
+
|
425 |
+
return true;
|
426 |
+
}
|
427 |
+
|
428 |
+
}
|
429 |
+
} // endif;
|
includes/Ajax.class.php
CHANGED
@@ -1,951 +1,951 @@
|
|
1 |
-
<?php
|
2 |
-
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
-
|
4 |
-
if ( !class_exists( 'rtbAJAX' ) ) {
|
5 |
-
/**
|
6 |
-
* Class to handle AJAX date interactions for Restaurant Reservations
|
7 |
-
*
|
8 |
-
* @since 2.0.0
|
9 |
-
*/
|
10 |
-
class rtbAJAX {
|
11 |
-
|
12 |
-
/**
|
13 |
-
* The location we're getting timeslots for, if specified
|
14 |
-
* @since 2.3.6
|
15 |
-
*/
|
16 |
-
public $location;
|
17 |
-
|
18 |
-
/**
|
19 |
-
* The year of the booking date we're getting timeslots for
|
20 |
-
* @since 2.0.0
|
21 |
-
*/
|
22 |
-
public $year;
|
23 |
-
|
24 |
-
/**
|
25 |
-
* The month of the booking date we're getting timeslots for
|
26 |
-
* @since 2.0.0
|
27 |
-
*/
|
28 |
-
public $month;
|
29 |
-
|
30 |
-
/**
|
31 |
-
* The day of the booking date we're getting timeslots for
|
32 |
-
* @since 2.0.0
|
33 |
-
*/
|
34 |
-
public $day;
|
35 |
-
|
36 |
-
/**
|
37 |
-
* The time of the booking we're getting timeslots for
|
38 |
-
* @since 2.1.5
|
39 |
-
*/
|
40 |
-
public $time;
|
41 |
-
|
42 |
-
/**
|
43 |
-
* The party size we're looking to find valid tables for
|
44 |
-
* @since 2.1.7
|
45 |
-
*/
|
46 |
-
public $party;
|
47 |
-
|
48 |
-
public function __construct() {
|
49 |
-
|
50 |
-
add_action( 'wp_ajax_rtb_get_available_time_slots', array( $this, 'get_time_slots' ) );
|
51 |
-
add_action( 'wp_ajax_nopriv_rtb_get_available_time_slots', array( $this, 'get_time_slots' ) );
|
52 |
-
|
53 |
-
add_action( 'wp_ajax_rtb_find_reservations', array( $this, 'get_reservations' ) );
|
54 |
-
add_action( 'wp_ajax_nopriv_rtb_find_reservations', array( $this, 'get_reservations' ) );
|
55 |
-
|
56 |
-
add_action( 'wp_ajax_rtb_cancel_reservations', array( $this, 'cancel_reservation' ), 10, 0 );
|
57 |
-
add_action( 'wp_ajax_nopriv_rtb_cancel_reservations', array( $this, 'cancel_reservation' ), 10, 0 );
|
58 |
-
|
59 |
-
add_action( 'wp_ajax_rtb_get_available_party_size', array( $this, 'get_available_party_size' ) );
|
60 |
-
add_action( 'wp_ajax_nopriv_rtb_get_available_party_size', array( $this, 'get_available_party_size' ) );
|
61 |
-
|
62 |
-
add_action( 'wp_ajax_rtb_get_available_tables', array( $this, 'get_available_tables' ) );
|
63 |
-
add_action( 'wp_ajax_nopriv_rtb_get_available_tables', array( $this, 'get_available_tables' ) );
|
64 |
-
}
|
65 |
-
|
66 |
-
/**
|
67 |
-
* Get reservations that are associated with the email address that was sent
|
68 |
-
* @since 2.1.0
|
69 |
-
*/
|
70 |
-
public function get_reservations() {
|
71 |
-
global $wpdb, $rtb_controller;
|
72 |
-
|
73 |
-
if ( !check_ajax_referer( 'rtb-booking-form', 'nonce' ) ) {
|
74 |
-
rtbHelper::bad_nonce_ajax();
|
75 |
-
}
|
76 |
-
|
77 |
-
$email = isset($_POST['booking_email']) ? sanitize_email( $_POST['booking_email'] ) : '';
|
78 |
-
|
79 |
-
if ( ! $email ) {
|
80 |
-
wp_send_json_error(
|
81 |
-
array(
|
82 |
-
'error' => 'noemail',
|
83 |
-
'msg' => __( 'The email you entered is not valid.', 'restaurant-reservations' ),
|
84 |
-
)
|
85 |
-
);
|
86 |
-
}
|
87 |
-
|
88 |
-
$booking_status_lbls = $rtb_controller->cpts->booking_statuses;
|
89 |
-
|
90 |
-
require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
|
91 |
-
|
92 |
-
$bookings = array();
|
93 |
-
$booking_ids = $wpdb->get_results(
|
94 |
-
$wpdb->prepare("
|
95 |
-
SELECT `post_id` FROM `{$wpdb->postmeta}` WHERE `meta_key` = 'rtb' AND `meta_value` LIKE %s",
|
96 |
-
'%' . $email . '%'
|
97 |
-
)
|
98 |
-
);
|
99 |
-
|
100 |
-
foreach ( $booking_ids as $booking_id ) {
|
101 |
-
$booking = new rtbBooking();
|
102 |
-
if ( $booking->load_post( $booking_id->post_id ) ) {
|
103 |
-
$booking_date = (new DateTime($booking->date, wp_timezone()))->format('U');
|
104 |
-
if ( in_array($booking->post_status, ['pending', 'payment_pending', 'payment_failed', 'confirmed'] ) and time() < $booking_date ) {
|
105 |
-
$bookings[] = array(
|
106 |
-
'ID' => $booking->ID,
|
107 |
-
'email' => $booking->email,
|
108 |
-
'datetime' => $booking->format_date( $booking->date ),
|
109 |
-
'party' => $booking->party,
|
110 |
-
'status' => $booking->post_status,
|
111 |
-
'status_lbl' => $booking_status_lbls[$booking->post_status]['label']
|
112 |
-
);
|
113 |
-
}
|
114 |
-
}
|
115 |
-
}
|
116 |
-
|
117 |
-
if ( ! empty( $bookings ) ) {
|
118 |
-
wp_send_json_success(
|
119 |
-
array(
|
120 |
-
'bookings' => $bookings
|
121 |
-
)
|
122 |
-
);
|
123 |
-
}
|
124 |
-
else {
|
125 |
-
wp_send_json_error(
|
126 |
-
array(
|
127 |
-
'error' => 'nobookings',
|
128 |
-
'msg' => esc_html( $rtb_controller->settings->get_setting( 'label-modify-no-bookings-found' ) ),
|
129 |
-
)
|
130 |
-
);
|
131 |
-
}
|
132 |
-
|
133 |
-
die();
|
134 |
-
}
|
135 |
-
|
136 |
-
/**
|
137 |
-
* Cancel a reservation based on its ID, with the email address used for confirmation
|
138 |
-
* @since 2.1.0
|
139 |
-
*/
|
140 |
-
public function cancel_reservation( $ajax = true ) {
|
141 |
-
global $rtb_controller;
|
142 |
-
|
143 |
-
if ( $ajax && !check_ajax_referer( 'rtb-booking-form', 'nonce' ) ) {
|
144 |
-
rtbHelper::bad_nonce_ajax();
|
145 |
-
}
|
146 |
-
|
147 |
-
$cancelled_redirect = $rtb_controller->settings->get_setting( 'cancelled-redirect-page' );
|
148 |
-
|
149 |
-
$booking_id = isset($_REQUEST['booking_id']) ? absint( $_REQUEST['booking_id'] ) : '';
|
150 |
-
$booking_email = isset($_REQUEST['booking_email']) ? sanitize_email( $_REQUEST['booking_email'] ) : '';
|
151 |
-
|
152 |
-
require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
|
153 |
-
|
154 |
-
$success = false;
|
155 |
-
$error = array(
|
156 |
-
'error' => 'unknown',
|
157 |
-
'msg' => __( 'Unkown error. Please try again', 'restaurant-reservations' )
|
158 |
-
);
|
159 |
-
|
160 |
-
$booking = new rtbBooking();
|
161 |
-
if ( $booking->load_post( $booking_id ) ) {
|
162 |
-
if ( $booking_email == $booking->email ) {
|
163 |
-
wp_update_post( array( 'ID' => $booking->ID, 'post_status' => 'cancelled' ) );
|
164 |
-
|
165 |
-
$success = true;
|
166 |
-
}
|
167 |
-
else {
|
168 |
-
$error = array(
|
169 |
-
'error' => 'invalidemail',
|
170 |
-
'msg' => __( 'No booking matches the information that was sent.', 'restaurant-reservations' ),
|
171 |
-
);
|
172 |
-
}
|
173 |
-
}
|
174 |
-
else {
|
175 |
-
$error = array(
|
176 |
-
'error' => 'invalidid',
|
177 |
-
'msg' => __( 'No booking matches the information that was sent.', 'restaurant-reservations' ),
|
178 |
-
);
|
179 |
-
}
|
180 |
-
|
181 |
-
if ( $ajax ) {
|
182 |
-
if ( $success ) {
|
183 |
-
|
184 |
-
$response = array( 'booking_id' => $booking_id );
|
185 |
-
|
186 |
-
if( '' != $cancelled_redirect ) {
|
187 |
-
$response['cancelled_redirect'] = $cancelled_redirect;
|
188 |
-
}
|
189 |
-
|
190 |
-
wp_send_json_success( $response );
|
191 |
-
}
|
192 |
-
else {
|
193 |
-
wp_send_json_error( $error );
|
194 |
-
}
|
195 |
-
}
|
196 |
-
else {
|
197 |
-
$redirect_url = '';
|
198 |
-
|
199 |
-
if( '' != $cancelled_redirect && $success ) {
|
200 |
-
$redirect_url = $cancelled_redirect;
|
201 |
-
}
|
202 |
-
else {
|
203 |
-
$booking_page_id = $rtb_controller->settings->get_setting( 'booking-page' );
|
204 |
-
$booking_page_url = get_permalink( $booking_page_id );
|
205 |
-
|
206 |
-
$redirect_url = add_query_arg(
|
207 |
-
array(
|
208 |
-
'bookingCancelled' => $success ? 'success' : 'fail'
|
209 |
-
),
|
210 |
-
$booking_page_url
|
211 |
-
);
|
212 |
-
}
|
213 |
-
|
214 |
-
if( wp_redirect( $redirect_url ) ) {
|
215 |
-
exit;
|
216 |
-
}
|
217 |
-
|
218 |
-
header( "Location: {$redirect_url}", true, 302 );
|
219 |
-
exit;
|
220 |
-
}
|
221 |
-
}
|
222 |
-
|
223 |
-
/**
|
224 |
-
* Get available timeslots when "Max Reservations" or "Max People" is enabled
|
225 |
-
* @since 2.0.0
|
226 |
-
*/
|
227 |
-
public function get_time_slots() {
|
228 |
-
global $rtb_controller;
|
229 |
-
|
230 |
-
if ( !check_ajax_referer( 'rtb-booking-form', 'nonce' ) ) {
|
231 |
-
rtbHelper::bad_nonce_ajax();
|
232 |
-
}
|
233 |
-
|
234 |
-
$max_reservations_enabled = $rtb_controller->settings->get_setting( 'rtb-enable-max-tables' );
|
235 |
-
|
236 |
-
// proessing request for this date
|
237 |
-
$this->location = ! empty( $_POST['location'] ) ? get_term( intval( $_POST['location'] ) ) : false;
|
238 |
-
|
239 |
-
$this->year = sanitize_text_field( $_POST['year'] );
|
240 |
-
$this->month = sanitize_text_field( $_POST['month'] );
|
241 |
-
$this->day = sanitize_text_field( $_POST['day'] );
|
242 |
-
|
243 |
-
$interval = $rtb_controller->settings->get_setting( 'time-interval' ) * 60;
|
244 |
-
|
245 |
-
// Helper functions
|
246 |
-
$finalize_response = function ( $open_close_pair_list = array() ) {
|
247 |
-
|
248 |
-
$valid_times = [];
|
249 |
-
|
250 |
-
if ( ! empty( $open_close_pair_list ) ) {
|
251 |
-
|
252 |
-
foreach ( $open_close_pair_list as $pair ) {
|
253 |
-
|
254 |
-
$valid_times[] = array(
|
255 |
-
'from' => $this->format_pickadate_time( $pair['from'] ),
|
256 |
-
'to' => $this->format_pickadate_time( $pair['to'] ),
|
257 |
-
'inverted' => true
|
258 |
-
);
|
259 |
-
}
|
260 |
-
}
|
261 |
-
|
262 |
-
echo json_encode( $valid_times );
|
263 |
-
|
264 |
-
die();
|
265 |
-
};
|
266 |
-
|
267 |
-
$all_slots_for = function( $pairs ) use( $interval ) {
|
268 |
-
$all_possible_slots = [];
|
269 |
-
|
270 |
-
if( !is_array( $pairs ) ) return $pairs;
|
271 |
-
|
272 |
-
foreach ( $pairs as $pair ) {
|
273 |
-
$all_possible_slots[] = $pair['from'];
|
274 |
-
$next = $pair['from'] + $interval;
|
275 |
-
while ( $next <= $pair['to'] ) {
|
276 |
-
$all_possible_slots[] = $next;
|
277 |
-
$next += $interval;
|
278 |
-
}
|
279 |
-
}
|
280 |
-
|
281 |
-
return $all_possible_slots;
|
282 |
-
};
|
283 |
-
|
284 |
-
$consolidating_timeslots_to_timeframes = function( $slots ) use( $interval ) {
|
285 |
-
$timeframe = [];
|
286 |
-
|
287 |
-
$slots_count = count( $slots );
|
288 |
-
if( 0 < $slots_count ) {
|
289 |
-
|
290 |
-
$current_pair = [ 'from' => $slots[ 0 ] ];
|
291 |
-
|
292 |
-
for ( $i = 1; $i < $slots_count; $i++) {
|
293 |
-
if( $slots[ $i ] - $slots[ $i - 1 ] !== $interval ) {
|
294 |
-
$current_pair[ 'to' ] = $slots[ $i - 1 ];
|
295 |
-
$timeframe[] = $current_pair;
|
296 |
-
|
297 |
-
$current_pair = [ 'from' => $slots[ $i ] ];
|
298 |
-
}
|
299 |
-
}
|
300 |
-
|
301 |
-
$current_pair[ 'to' ] = $slots[ $i - 1 ];
|
302 |
-
$timeframe[] = $current_pair;
|
303 |
-
}
|
304 |
-
|
305 |
-
return $timeframe;
|
306 |
-
};
|
307 |
-
|
308 |
-
// Get opening/closing times for this particular day
|
309 |
-
$hours = $this->get_opening_hours();
|
310 |
-
|
311 |
-
// If the restaurant is closed that day
|
312 |
-
// If Enable Max Reservation not set
|
313 |
-
if ( 1 > count( $hours ) || ! $max_reservations_enabled ) {
|
314 |
-
$slots = $all_slots_for( $hours );
|
315 |
-
|
316 |
-
sort( $slots, SORT_NUMERIC );
|
317 |
-
|
318 |
-
$timeframes = $consolidating_timeslots_to_timeframes( $slots );
|
319 |
-
|
320 |
-
$finalize_response( $timeframes );
|
321 |
-
}
|
322 |
-
|
323 |
-
$location_slug = ! empty( $this->location ) ? $this->location->slug : false;
|
324 |
-
|
325 |
-
$dining_block_seconds = (int) $rtb_controller->settings->get_setting( 'rtb-dining-block-length' ) * 60;
|
326 |
-
|
327 |
-
$min_party_size = (int) $rtb_controller->settings->get_setting( 'party-size-min' );
|
328 |
-
|
329 |
-
$max_reservations = (int) $rtb_controller->settings->get_setting( 'rtb-max-tables-count', $location_slug );
|
330 |
-
|
331 |
-
$max_people = (int) $rtb_controller->settings->get_setting( 'rtb-max-people-count', $location_slug );
|
332 |
-
|
333 |
-
$all_possible_slots = $all_slots_for( $hours );
|
334 |
-
|
335 |
-
// Get all current bookings sorted by date
|
336 |
-
$args = array(
|
337 |
-
'posts_per_page' => -1,
|
338 |
-
'date_range' => 'dates',
|
339 |
-
'start_date' => $this->year . '-' . $this->month . '-' . $this->day,
|
340 |
-
'end_date' => $this->year . '-' . $this->month . '-' . $this->day,
|
341 |
-
'post_status' => ['pending', 'payment_pending', 'confirmed', 'arrived']
|
342 |
-
);
|
343 |
-
|
344 |
-
// If there are multiple locations, a location is selected, and
|
345 |
-
// max reservations and/or seats has been enabled for this specific location
|
346 |
-
if ( ! empty( $location_slug ) and ( $rtb_controller->settings->is_location_setting_enabled( 'rtb-max-tables-count', $location_slug ) or $rtb_controller->settings->is_location_setting_enabled( 'rtb-max-people-count', $location_slug ) ) ) {
|
347 |
-
|
348 |
-
$tax_query = array(
|
349 |
-
array(
|
350 |
-
'taxonomy' => $rtb_controller->locations->location_taxonomy,
|
351 |
-
'field' => 'term_id',
|
352 |
-
'terms' => $this->location->term_id
|
353 |
-
)
|
354 |
-
);
|
355 |
-
|
356 |
-
$args['tax_query'] = $tax_query;
|
357 |
-
}
|
358 |
-
|
359 |
-
$query = new rtbQuery( $args, 'ajax-get-time-slots' );
|
360 |
-
$query->prepare_args();
|
361 |
-
$bookings = $query->get_bookings();
|
362 |
-
|
363 |
-
// This array holds bookings for all slots by expanding the booking by
|
364 |
-
// dining block length to help finding the overlapped bookings for time-slots
|
365 |
-
$all_bookings_by_slots = [];
|
366 |
-
foreach ( $bookings as $key => $booking ) {
|
367 |
-
// Convert booking date to seconds from UNIX
|
368 |
-
$booking_time = ( new DateTime( $booking->date , wp_timezone() ) )->format( 'U' );
|
369 |
-
if( ! array_key_exists( $booking_time, $all_bookings_by_slots ) ) {
|
370 |
-
$all_bookings_by_slots[$booking_time] = [
|
371 |
-
'total_bookings' => 0,
|
372 |
-
'total_guest' => 0,
|
373 |
-
'overlapped' => false
|
374 |
-
];
|
375 |
-
}
|
376 |
-
$all_bookings_by_slots[$booking_time]['total_bookings']++;
|
377 |
-
$all_bookings_by_slots[$booking_time]['total_guest'] += intval( $booking->party );
|
378 |
-
|
379 |
-
/**
|
380 |
-
* Expanding bookings
|
381 |
-
* Example: If I have someone booked at 1pm who will be in the restaurant for 120 minutes,
|
382 |
-
* that means they will be in the restaurant until 3pm. There is another booking at 2pm.
|
383 |
-
* That means, from 2pm to 3pm, there are already two separate reservations in the restaurant.
|
384 |
-
*/
|
385 |
-
$end = $booking_time + $dining_block_seconds;
|
386 |
-
$next = $booking_time + $interval;
|
387 |
-
while($next < $end) {
|
388 |
-
if( ! array_key_exists( $next, $all_bookings_by_slots ) ) {
|
389 |
-
$all_bookings_by_slots[$next] = [
|
390 |
-
'total_bookings' => 0,
|
391 |
-
'total_guest' => 0,
|
392 |
-
'overlapped' => false
|
393 |
-
];
|
394 |
-
}
|
395 |
-
$all_bookings_by_slots[$next]['overlapped'] = true;
|
396 |
-
$all_bookings_by_slots[$next]['total_bookings']++;
|
397 |
-
$all_bookings_by_slots[$next]['total_guest'] += intval( $booking->party );
|
398 |
-
$next += $interval;
|
399 |
-
}
|
400 |
-
}
|
401 |
-
|
402 |
-
$all_blocked_slots = [];
|
403 |
-
|
404 |
-
// Go through all bookings and figure out when we're at or above the
|
405 |
-
// max reservation or max people and mark that slot as blocked
|
406 |
-
if ( isset( $max_reservations ) and $max_reservations > 0 ) {
|
407 |
-
foreach ( $all_bookings_by_slots as $slot => $data ) {
|
408 |
-
if( $max_reservations <= $data['total_bookings'] ) {
|
409 |
-
$all_blocked_slots[] = $slot;
|
410 |
-
}
|
411 |
-
}
|
412 |
-
}
|
413 |
-
else if ( isset( $max_people ) and $max_people > 0 ) {
|
414 |
-
/**
|
415 |
-
* min_party_size = 10, max_people = 100, 6 bookings of total 91 guests
|
416 |
-
* Now, if anybody wants to book for at least 10 people, it is not possible
|
417 |
-
* because the total will surpass the max_people (100)
|
418 |
-
* thus reducing min_party_size from max_people
|
419 |
-
*
|
420 |
-
* $max_people can be zero when min_party_size is same as max_people
|
421 |
-
*/
|
422 |
-
$max_people = $max_people - $min_party_size;
|
423 |
-
|
424 |
-
foreach ( $all_bookings_by_slots as $slot => $data ) {
|
425 |
-
if( $max_people < $data['total_guest'] ) {
|
426 |
-
$all_blocked_slots[] = $slot;
|
427 |
-
}
|
428 |
-
}
|
429 |
-
}
|
430 |
-
|
431 |
-
// Mark slots unavailable, due to dinning block length
|
432 |
-
$additional_blocked_slots = [];
|
433 |
-
foreach ($all_blocked_slots as $slot) {
|
434 |
-
// blocking before this slot
|
435 |
-
$begin = $slot - $dining_block_seconds;
|
436 |
-
/**
|
437 |
-
* interval 30 minutes, dinning_length 120 minutes, slot 10:00am
|
438 |
-
* additional blockings before shall be 8:30am,9:00am and 9:30am
|
439 |
-
* thus skipping 8:00am which is valid
|
440 |
-
*
|
441 |
-
* @var unix timestamp
|
442 |
-
*/
|
443 |
-
$next = $begin + $interval;
|
444 |
-
while($next < $slot) {
|
445 |
-
$additional_blocked_slots[] = $next;
|
446 |
-
$next += $interval;
|
447 |
-
}
|
448 |
-
|
449 |
-
// block after this slot only when this slot is not overlapped
|
450 |
-
// Overlapped slots should block only backwards, but not afterward
|
451 |
-
if( $all_bookings_by_slots[$slot]['overlapped'] ) {
|
452 |
-
continue;
|
453 |
-
}
|
454 |
-
|
455 |
-
// blocking after this slot
|
456 |
-
$end = $slot + $dining_block_seconds;
|
457 |
-
/**
|
458 |
-
* interval 30 minutes, dinning_length 120 minutes, slot 10:00am
|
459 |
-
* additional blockings after shall be 10:30am,11:00am and 1130am
|
460 |
-
* thus skipping 12:00pm which is valid
|
461 |
-
*
|
462 |
-
* @var unix timestamp
|
463 |
-
*/
|
464 |
-
$next = $slot + $interval;
|
465 |
-
while($next < $end) {
|
466 |
-
$additional_blocked_slots[] = $next;
|
467 |
-
$next += $interval;
|
468 |
-
}
|
469 |
-
}
|
470 |
-
|
471 |
-
$all_blocked_slots = array_unique(
|
472 |
-
array_merge( $all_blocked_slots, $additional_blocked_slots ),
|
473 |
-
SORT_NUMERIC
|
474 |
-
);
|
475 |
-
|
476 |
-
sort( $all_blocked_slots, SORT_NUMERIC );
|
477 |
-
|
478 |
-
// remove blocked slots from available slots
|
479 |
-
$available_slots = array_diff( $all_possible_slots, $all_blocked_slots );
|
480 |
-
sort( $available_slots, SORT_NUMERIC );
|
481 |
-
|
482 |
-
// consolidating timeslots to timeframes
|
483 |
-
$timeframes = $consolidating_timeslots_to_timeframes( $available_slots );
|
484 |
-
|
485 |
-
$finalize_response( $timeframes );
|
486 |
-
}
|
487 |
-
|
488 |
-
public function get_opening_hours() {
|
489 |
-
global $rtb_controller;
|
490 |
-
|
491 |
-
$location_slug = ! empty( $this->location ) ? $this->location->slug : false;
|
492 |
-
|
493 |
-
$schedule_closed = $rtb_controller->settings->get_setting( 'schedule-closed', $location_slug );
|
494 |
-
$schedule_closed = is_array( $schedule_closed ) ? $schedule_closed : array();
|
495 |
-
|
496 |
-
$valid_times = array();
|
497 |
-
|
498 |
-
// Check if this date is an exception to the rules
|
499 |
-
if ( $schedule_closed !== 'undefined' ) {
|
500 |
-
|
501 |
-
$selected_date = ( new DateTime('now', wp_timezone() ) )
|
502 |
-
->setTime(0, 0, 2)
|
503 |
-
->setDate( $this->year, $this->month, $this->day );
|
504 |
-
|
505 |
-
foreach ( $schedule_closed as $ids => $closing ) {
|
506 |
-
if( array_key_exists( 'date_range', $closing ) ) {
|
507 |
-
|
508 |
-
$start = ! empty( $closing['date_range']['start'] )
|
509 |
-
? new DateTime( $closing['date_range']['start'], wp_timezone() )
|
510 |
-
: new DateTime( 'now', wp_timezone() );
|
511 |
-
$start->setTime(0, 0);
|
512 |
-
|
513 |
-
$end = !empty( $closing['date_range']['end'] )
|
514 |
-
? new DateTime( $closing['date_range']['end'], wp_timezone() )
|
515 |
-
: ( new DateTime( 'now', wp_timezone() ) )->add( new DateInterval( 'P10Y' ) );
|
516 |
-
$end->setTime(23, 59, 58);
|
517 |
-
|
518 |
-
if( $start < $selected_date && $selected_date < $end ) {
|
519 |
-
$exception = clone $selected_date;
|
520 |
-
}
|
521 |
-
else {
|
522 |
-
// Set anything to void this rule
|
523 |
-
$exception = clone $selected_date;
|
524 |
-
$exception->add( new DateInterval( 'P1Y' ) );
|
525 |
-
}
|
526 |
-
}
|
527 |
-
else {
|
528 |
-
$exception = ( new DateTime( $closing['date'], wp_timezone() ) )->setTime(0, 0, 2);
|
529 |
-
}
|
530 |
-
|
531 |
-
if ( $exception == $selected_date ) {
|
532 |
-
|
533 |
-
// Closed all day
|
534 |
-
if ( ! isset( $closing['time'] ) || $closing['time'] == 'undefined' ) {
|
535 |
-
return false;
|
536 |
-
}
|
537 |
-
|
538 |
-
if ( $closing['time']['start'] !== 'undefined' ) {
|
539 |
-
$open_time = ( new DateTime( $exception->format( 'Y-m-d' ) . ' ' . $closing['time']['start'], wp_timezone() ) )->format( 'U' );
|
540 |
-
}
|
541 |
-
else {
|
542 |
-
|
543 |
-
// Start of the day
|
544 |
-
$open_time = ( new DateTime( $exception->format( 'Y-m-d' ), wp_timezone() ) )->format('U');
|
545 |
-
}
|
546 |
-
|
547 |
-
if ( $closing['time']['end'] !== 'undefined' ) {
|
548 |
-
$close_time = ( new DateTime( $exception->format( 'Y-m-d' ) . ' ' . $closing['time']['end'], wp_timezone() ) )->format( 'U' );
|
549 |
-
}
|
550 |
-
else {
|
551 |
-
|
552 |
-
// End of the day
|
553 |
-
$close_time = ( new DateTime( $exception->format( 'Y-m-d' ) . ' 23:59:59', wp_timezone() ) )->format( 'U' );
|
554 |
-
}
|
555 |
-
|
556 |
-
$open_time = $this->get_earliest_time( $open_time );
|
557 |
-
|
558 |
-
if ( $open_time <= $close_time ) {
|
559 |
-
$valid_times[] = ['from' => $open_time, 'to' => $close_time];
|
560 |
-
}
|
561 |
-
}
|
562 |
-
}
|
563 |
-
|
564 |
-
// Exit early if this date is an exception
|
565 |
-
if ( isset( $open_time ) ) {
|
566 |
-
return $valid_times;
|
567 |
-
}
|
568 |
-
}
|
569 |
-
|
570 |
-
$schedule_open = $rtb_controller->settings->get_setting( 'schedule-open', $location_slug );
|
571 |
-
$schedule_open = is_array( $schedule_open ) ? $schedule_open : array();
|
572 |
-
|
573 |
-
// Get any rules which apply to this weekday
|
574 |
-
$day_of_week = strtolower(
|
575 |
-
( new DateTime( $this->year . '-' . $this->month . '-' . $this->day . ' 1:00:00', wp_timezone() ) )->format( 'l' )
|
576 |
-
);
|
577 |
-
|
578 |
-
$selected_date = ( new DateTime('now', wp_timezone() ) )
|
579 |
-
->setTime(0, 0, 2)
|
580 |
-
->setDate( $this->year, $this->month, $this->day );
|
581 |
-
|
582 |
-
foreach ( $schedule_open as $opening ) {
|
583 |
-
|
584 |
-
if ( $opening['weekdays'] !== 'undefined' ) {
|
585 |
-
|
586 |
-
foreach ( $opening['weekdays'] as $weekday => $value ) {
|
587 |
-
|
588 |
-
if ( $weekday == $day_of_week ) {
|
589 |
-
|
590 |
-
if ( isset( $opening['time'] ) && $opening['time']['start'] !== 'undefined' ) {
|
591 |
-
|
592 |
-
$open_time = ( new DateTime( $selected_date->format( 'Y-m-d' ) .' '. $opening['time']['start'], wp_timezone() ) )->format( 'U' );
|
593 |
-
}
|
594 |
-
else {
|
595 |
-
|
596 |
-
// Start of the day
|
597 |
-
$open_time = ( new DateTime( $selected_date->format( 'Y-m-d' ), wp_timezone() ) )->format('U');
|
598 |
-
}
|
599 |
-
|
600 |
-
if ( isset( $opening['time'] ) && $opening['time']['end'] !== 'undefined' ) {
|
601 |
-
|
602 |
-
$close_time = ( new DateTime( $selected_date->format( 'Y-m-d' ) .' '. $opening['time']['end'], wp_timezone() ) )->format( 'U' );
|
603 |
-
}
|
604 |
-
else {
|
605 |
-
|
606 |
-
// End of the day
|
607 |
-
$close_time = ( new DateTime( $selected_date->format( 'Y-m-d' ) . ' 23:59:59', wp_timezone() ) )->format( 'U' );
|
608 |
-
}
|
609 |
-
|
610 |
-
$open_time = $this->get_earliest_time( $open_time );
|
611 |
-
|
612 |
-
if ( $open_time <= $close_time ) {
|
613 |
-
|
614 |
-
$valid_times[] = ['from' => $open_time, 'to' => $close_time];
|
615 |
-
}
|
616 |
-
}
|
617 |
-
}
|
618 |
-
}
|
619 |
-
}
|
620 |
-
|
621 |
-
// Pass any valid times located
|
622 |
-
return $valid_times;
|
623 |
-
}
|
624 |
-
|
625 |
-
public function get_earliest_time( $open_time ) {
|
626 |
-
global $rtb_controller;
|
627 |
-
|
628 |
-
$interval = $rtb_controller->settings->get_setting( 'time-interval' ) * 60;
|
629 |
-
|
630 |
-
$selected_date = ( new DateTime('now', wp_timezone() ) )
|
631 |
-
->setTime(0, 0, 2)
|
632 |
-
->setDate( $this->year, $this->month, $this->day );
|
633 |
-
|
634 |
-
// adjust open time with respect to the current time of the day for upcoming timeslots
|
635 |
-
$current_time = ( new DateTime( 'now', wp_timezone() ) )->format( 'U' );
|
636 |
-
|
637 |
-
// Only make adjustments for current day selections
|
638 |
-
if ( $selected_date->format('Y-m-d') !== ( new DateTime( 'today', wp_timezone() ) )->format('Y-m-d') ) {
|
639 |
-
return $open_time;
|
640 |
-
}
|
641 |
-
|
642 |
-
$late_bookings = ( is_admin() && current_user_can( 'manage_bookings' ) ) ? '' : $rtb_controller->settings->get_setting( 'late-bookings' );
|
643 |
-
|
644 |
-
if( $current_time > $open_time ) {
|
645 |
-
while( $current_time > $open_time ) {
|
646 |
-
$open_time += $interval;
|
647 |
-
}
|
648 |
-
}
|
649 |
-
|
650 |
-
// adjust the open time for the Late Bookings option
|
651 |
-
if ( is_numeric($late_bookings) && $late_bookings % 1 === 0 ) {
|
652 |
-
$time_calc = ( new DateTime( 'now', wp_timezone() ) )->format( 'U' ) + ( $late_bookings * 60 );
|
653 |
-
while ($time_calc > $open_time) {
|
654 |
-
$open_time = $open_time + $interval;
|
655 |
-
}
|
656 |
-
}
|
657 |
-
|
658 |
-
return $open_time;
|
659 |
-
}
|
660 |
-
|
661 |
-
/**
|
662 |
-
* Get number of seats remaining avilable to be booked
|
663 |
-
* @since 2.1.5
|
664 |
-
*/
|
665 |
-
public function get_available_party_size() {
|
666 |
-
global $rtb_controller;
|
667 |
-
|
668 |
-
if ( !check_ajax_referer( 'rtb-booking-form', 'nonce' ) ) {
|
669 |
-
rtbHelper::bad_nonce_ajax();
|
670 |
-
}
|
671 |
-
|
672 |
-
$this->location = ! empty( $_POST['location'] ) ? get_term( intval( $_POST['location'] ) ) : false;
|
673 |
-
$this->year = sanitize_text_field( $_POST['year'] );
|
674 |
-
$this->month = sanitize_text_field( $_POST['month'] );
|
675 |
-
$this->day = sanitize_text_field( $_POST['day'] );
|
676 |
-
$this->time = sanitize_text_field( $_POST['time'] );
|
677 |
-
|
678 |
-
$location_slug = ! empty( $this->location ) ? $this->location->slug : false;
|
679 |
-
|
680 |
-
$max_people = (int) $rtb_controller->settings->get_setting( 'rtb-max-people-count', $location_slug );
|
681 |
-
|
682 |
-
$dining_block_seconds = (int) $rtb_controller->settings->get_setting( 'rtb-dining-block-length' )* 60 - 1; // Take 1 second off, to avoid bookings that start or end exactly at the beginning of a booking block
|
683 |
-
|
684 |
-
// Get opening/closing times for this particular day
|
685 |
-
$hours = $this->get_opening_hours();
|
686 |
-
|
687 |
-
// If the restaurant is closed that day, return false
|
688 |
-
if ( 1 > count( $hours ) ) { die(); }
|
689 |
-
|
690 |
-
// If no time is selected, return false
|
691 |
-
if ( ! $this->time ) { die(); }
|
692 |
-
|
693 |
-
$args = array(
|
694 |
-
'posts_per_page' => -1,
|
695 |
-
'date_range' => 'dates',
|
696 |
-
'start_date' => $this->year . '-' . $this->month . '-' . $this->day,
|
697 |
-
'end_date' => $this->year . '-' . $this->month . '-' . $this->day
|
698 |
-
);
|
699 |
-
|
700 |
-
// If there are multiple locations, a location is selected, and
|
701 |
-
// max seats has been enabled for this specific location
|
702 |
-
if ( ! empty( $location_slug ) and $rtb_controller->settings->is_location_setting_enabled( 'rtb-max-people-count', $location_slug ) ) {
|
703 |
-
|
704 |
-
$tax_query = array(
|
705 |
-
array(
|
706 |
-
'taxonomy' => $rtb_controller->locations->location_taxonomy,
|
707 |
-
'field' => 'term_id',
|
708 |
-
'terms' => $this->location->term_id
|
709 |
-
)
|
710 |
-
);
|
711 |
-
|
712 |
-
$args['tax_query'] = $tax_query;
|
713 |
-
}
|
714 |
-
|
715 |
-
$query = new rtbQuery( $args );
|
716 |
-
$query->prepare_args();
|
717 |
-
|
718 |
-
// Get all current bookings sorted by date
|
719 |
-
$bookings = $query->get_bookings();
|
720 |
-
|
721 |
-
$tmzn = wp_timezone();
|
722 |
-
|
723 |
-
$selected_date_time = ( new DateTime( $this->year . '-' . $this->month . '-' . $this->day . ' ' . $this->time, $tmzn ) )->format( 'U' );
|
724 |
-
$selected_date_time_start = $selected_date_time - $dining_block_seconds;
|
725 |
-
$selected_date_time_end = $selected_date_time + $dining_block_seconds;
|
726 |
-
$party_sizes = [];
|
727 |
-
|
728 |
-
if ($max_people != 'undefined' and $max_people != 0) {
|
729 |
-
|
730 |
-
$max_time_size = 0;
|
731 |
-
$current_times = array();
|
732 |
-
$party_sizes = array();
|
733 |
-
|
734 |
-
// Go through all current booking and collect the total party size
|
735 |
-
foreach ( $bookings as $key => $booking ) {
|
736 |
-
|
737 |
-
// Convert booking date to seconds from UNIX
|
738 |
-
$booking_time = ( new DateTime( $booking->date, $tmzn ) )->format( 'U' );
|
739 |
-
|
740 |
-
// Ignore bookings outside of our time range
|
741 |
-
if ($booking_time < $selected_date_time_start or $booking_time > $selected_date_time_end) { continue; }
|
742 |
-
|
743 |
-
$current_times[] = $booking_time;
|
744 |
-
$party_sizes[] = (int) $booking->party;
|
745 |
-
|
746 |
-
while ( sizeOf( $current_times ) > 0 and reset( $current_times ) < $booking_time - $dining_block_seconds ) {
|
747 |
-
//save the time to know when the blocking potentially ends
|
748 |
-
$removed_time = reset( $current_times );
|
749 |
-
|
750 |
-
// remove the expired time and party size
|
751 |
-
array_shift( $current_times );
|
752 |
-
array_shift( $party_sizes );
|
753 |
-
}
|
754 |
-
|
755 |
-
$max_time_size = max( $max_time_size, array_sum( $party_sizes ) );
|
756 |
-
}
|
757 |
-
|
758 |
-
$response = (object) array( 'available_spots' => $max_people - $max_time_size);
|
759 |
-
|
760 |
-
echo json_encode($response);
|
761 |
-
|
762 |
-
die();
|
763 |
-
} else {
|
764 |
-
return false;
|
765 |
-
}
|
766 |
-
}
|
767 |
-
|
768 |
-
/**
|
769 |
-
* Get tables available to be booked at a specific time and party size
|
770 |
-
* @since 2.1.7
|
771 |
-
*/
|
772 |
-
public function get_available_tables() {
|
773 |
-
global $rtb_controller;
|
774 |
-
|
775 |
-
if ( !check_ajax_referer( 'rtb-booking-form', 'nonce' ) ) {
|
776 |
-
rtbHelper::bad_nonce_ajax();
|
777 |
-
}
|
778 |
-
|
779 |
-
$tables = $rtb_controller->settings->get_sorted_tables();
|
780 |
-
|
781 |
-
$this->booking_id = isset( $_POST['booking_id'] ) ? intval( $_POST['booking_id'] ) : 0;
|
782 |
-
$this->year = isset( $_POST['year'] ) ? sanitize_text_field( $_POST['year'] ) : false;
|
783 |
-
$this->month = isset( $_POST['month'] ) ? sanitize_text_field( $_POST['month'] ) : false;
|
784 |
-
$this->day = isset( $_POST['day'] ) ? sanitize_text_field( $_POST['day'] ) : false;
|
785 |
-
$this->time = isset( $_POST['time'] ) ? sanitize_text_field( $_POST['time'] ) : false;
|
786 |
-
$this->party = isset( $_POST['party'] ) ? sanitize_text_field( $_POST['party'] ) : false;
|
787 |
-
|
788 |
-
/*$this->year = 2020;
|
789 |
-
$this->month = 06;
|
790 |
-
$this->day = 12;
|
791 |
-
$this->time = '02:15 PM';
|
792 |
-
$this->party = 12;*/
|
793 |
-
|
794 |
-
if ( ! isset( $this->year ) or ! isset( $this->month ) or ! isset( $this->day ) or ! isset( $this->time ) ) { return false; }
|
795 |
-
|
796 |
-
$datetime = $this->year . '-' . $this->month . '-' . $this->day . ' ' . $this->time;
|
797 |
-
|
798 |
-
$valid_tables = rtb_get_valid_tables( $datetime );
|
799 |
-
|
800 |
-
if ( $this->booking_id ) {
|
801 |
-
|
802 |
-
require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
|
803 |
-
|
804 |
-
$current_booking = new rtbBooking();
|
805 |
-
$current_booking->load_post( $this->booking_id );
|
806 |
-
|
807 |
-
if ( $current_booking->table ) { $valid_tables = array_merge( $valid_tables, $current_booking->table ); }
|
808 |
-
}
|
809 |
-
|
810 |
-
$return_tables = array();
|
811 |
-
|
812 |
-
if ( isset( $this->party ) ) {
|
813 |
-
|
814 |
-
$possible_combinations = array();
|
815 |
-
foreach ( $valid_tables as $valid_table ) {
|
816 |
-
|
817 |
-
// If the party size is between the min and max for the table, great
|
818 |
-
if ( $tables[ $valid_table ]->min_people <= $this->party and $tables[ $valid_table ]->max_people >= $this->party ) {
|
819 |
-
|
820 |
-
$possible_combinations[] = $valid_table;
|
821 |
-
}
|
822 |
-
// If the party is above the minimum for the table, look to see if combinations could work
|
823 |
-
elseif ( $tables[ $valid_table ]->min_people <= $this->party ) {
|
824 |
-
|
825 |
-
$combination = $this->get_combinations_chain( $tables, $valid_tables, $valid_table, $tables[ $valid_table ]->max_people, $this->party );
|
826 |
-
|
827 |
-
if ( $combination ) {
|
828 |
-
$possible_combinations[] = $combination;
|
829 |
-
}
|
830 |
-
}
|
831 |
-
|
832 |
-
$return_tables = $this->format_tables( $possible_combinations );
|
833 |
-
}
|
834 |
-
}
|
835 |
-
else {
|
836 |
-
$return_tables = $this->format_tables( $valid_tables );
|
837 |
-
}
|
838 |
-
|
839 |
-
$selected_table = ( isset( $current_booking ) and $current_booking->table ) ? implode(',', $current_booking->table ) : -1;
|
840 |
-
|
841 |
-
$response = (object) array( 'available_tables' => $return_tables, 'selected_table' => $selected_table );
|
842 |
-
|
843 |
-
echo json_encode($response);
|
844 |
-
|
845 |
-
die();
|
846 |
-
}
|
847 |
-
|
848 |
-
/**
|
849 |
-
* Recursively go through table combinations to find one that has enough seats
|
850 |
-
* @since 2.1.7
|
851 |
-
*/
|
852 |
-
public function get_combinations_chain(
|
853 |
-
$tables,
|
854 |
-
$valid_tables,
|
855 |
-
$current_table,
|
856 |
-
$current_size,
|
857 |
-
$needed_size
|
858 |
-
) {
|
859 |
-
$table_chain[] = $current_table;
|
860 |
-
|
861 |
-
// No combination specified
|
862 |
-
if ( ! $tables[ $current_table ]->combinations ) {
|
863 |
-
return false;
|
864 |
-
}
|
865 |
-
|
866 |
-
$possible_tables = explode( ',', $tables[ $current_table ]->combinations );
|
867 |
-
|
868 |
-
foreach ( $possible_tables as $possible_table ) {
|
869 |
-
|
870 |
-
// If the table has already been booked, continue
|
871 |
-
if ( !in_array( $possible_table, $valid_tables) ) {
|
872 |
-
continue;
|
873 |
-
}
|
874 |
-
|
875 |
-
// If the table can hold the group on its own, continue
|
876 |
-
if ( $tables[ $possible_table ]->max_people >= $needed_size ) {
|
877 |
-
continue;
|
878 |
-
}
|
879 |
-
|
880 |
-
$current_size += $tables[ $possible_table ]->max_people;
|
881 |
-
$table_chain[] = $possible_table;
|
882 |
-
|
883 |
-
if ( $current_size >= $needed_size ) {
|
884 |
-
return implode(',', $table_chain);
|
885 |
-
}
|
886 |
-
}
|
887 |
-
|
888 |
-
//no viable combination found
|
889 |
-
return false;
|
890 |
-
}
|
891 |
-
|
892 |
-
/**
|
893 |
-
* Format the tables available to be booked as number(s)_string => human_value pairs
|
894 |
-
* @since 2.1.7
|
895 |
-
*/
|
896 |
-
public function format_tables ( $table_numbers ) {
|
897 |
-
global $rtb_controller;
|
898 |
-
|
899 |
-
$formatted_tables = array();
|
900 |
-
|
901 |
-
$tables = json_decode( html_entity_decode( $rtb_controller->settings->get_setting( 'rtb-tables' ) ) );
|
902 |
-
$tables = is_array( $tables ) ? $tables : array();
|
903 |
-
|
904 |
-
foreach ( $table_numbers as $table_number ) {
|
905 |
-
|
906 |
-
$table_parts = explode( ',', $table_number );
|
907 |
-
|
908 |
-
$table_values = array(
|
909 |
-
'numbers' => '',
|
910 |
-
'min_people' => 0,
|
911 |
-
'max_people' => 0
|
912 |
-
);
|
913 |
-
|
914 |
-
foreach ( $tables as $table ) {
|
915 |
-
if ( in_array($table->number, $table_parts) ) {
|
916 |
-
$table_values['numbers'] .= ( strlen( $table_values['numbers'] ) ? ', ' : '' ) . $table->number;
|
917 |
-
$table_values['min_people'] += $table->min_people;
|
918 |
-
$table_values['max_people'] += $table->max_people;
|
919 |
-
|
920 |
-
if ( ! isset( $section_name ) ) { $section_name = $this->get_section_name( $table->section ); }
|
921 |
-
}
|
922 |
-
}
|
923 |
-
|
924 |
-
$formatted_tables[ $table_values['numbers'] ] = $table_values['numbers'] . ' - ' . $section_name . ' (min. ' . $table_values['min_people'] . '/max. ' . $table_values['max_people'] . ')';
|
925 |
-
|
926 |
-
unset( $section_name );
|
927 |
-
}
|
928 |
-
|
929 |
-
return $formatted_tables;
|
930 |
-
}
|
931 |
-
|
932 |
-
public function get_section_name( $section_id ) {
|
933 |
-
global $rtb_controller;
|
934 |
-
|
935 |
-
$sections = json_decode( html_entity_decode( $rtb_controller->settings->get_setting( 'rtb-table-sections' ) ) );
|
936 |
-
$sections = is_array( $sections ) ? $sections : array();
|
937 |
-
|
938 |
-
foreach ( $sections as $section ) {
|
939 |
-
|
940 |
-
if ( $section->section_id == $section_id ) { return $section->name; }
|
941 |
-
}
|
942 |
-
|
943 |
-
return false;
|
944 |
-
}
|
945 |
-
|
946 |
-
public function format_pickadate_time( $time ) {
|
947 |
-
$obj = ( new DateTime('now' ) )->setTimestamp( $time )->setTimezone( wp_timezone() );
|
948 |
-
return array( $obj->format( 'G' ), $obj->format( 'i' ) );
|
949 |
-
}
|
950 |
-
}
|
951 |
}
|
1 |
+
<?php
|
2 |
+
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
+
|
4 |
+
if ( !class_exists( 'rtbAJAX' ) ) {
|
5 |
+
/**
|
6 |
+
* Class to handle AJAX date interactions for Restaurant Reservations
|
7 |
+
*
|
8 |
+
* @since 2.0.0
|
9 |
+
*/
|
10 |
+
class rtbAJAX {
|
11 |
+
|
12 |
+
/**
|
13 |
+
* The location we're getting timeslots for, if specified
|
14 |
+
* @since 2.3.6
|
15 |
+
*/
|
16 |
+
public $location;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* The year of the booking date we're getting timeslots for
|
20 |
+
* @since 2.0.0
|
21 |
+
*/
|
22 |
+
public $year;
|
23 |
+
|
24 |
+
/**
|
25 |
+
* The month of the booking date we're getting timeslots for
|
26 |
+
* @since 2.0.0
|
27 |
+
*/
|
28 |
+
public $month;
|
29 |
+
|
30 |
+
/**
|
31 |
+
* The day of the booking date we're getting timeslots for
|
32 |
+
* @since 2.0.0
|
33 |
+
*/
|
34 |
+
public $day;
|
35 |
+
|
36 |
+
/**
|
37 |
+
* The time of the booking we're getting timeslots for
|
38 |
+
* @since 2.1.5
|
39 |
+
*/
|
40 |
+
public $time;
|
41 |
+
|
42 |
+
/**
|
43 |
+
* The party size we're looking to find valid tables for
|
44 |
+
* @since 2.1.7
|
45 |
+
*/
|
46 |
+
public $party;
|
47 |
+
|
48 |
+
public function __construct() {
|
49 |
+
|
50 |
+
add_action( 'wp_ajax_rtb_get_available_time_slots', array( $this, 'get_time_slots' ) );
|
51 |
+
add_action( 'wp_ajax_nopriv_rtb_get_available_time_slots', array( $this, 'get_time_slots' ) );
|
52 |
+
|
53 |
+
add_action( 'wp_ajax_rtb_find_reservations', array( $this, 'get_reservations' ) );
|
54 |
+
add_action( 'wp_ajax_nopriv_rtb_find_reservations', array( $this, 'get_reservations' ) );
|
55 |
+
|
56 |
+
add_action( 'wp_ajax_rtb_cancel_reservations', array( $this, 'cancel_reservation' ), 10, 0 );
|
57 |
+
add_action( 'wp_ajax_nopriv_rtb_cancel_reservations', array( $this, 'cancel_reservation' ), 10, 0 );
|
58 |
+
|
59 |
+
add_action( 'wp_ajax_rtb_get_available_party_size', array( $this, 'get_available_party_size' ) );
|
60 |
+
add_action( 'wp_ajax_nopriv_rtb_get_available_party_size', array( $this, 'get_available_party_size' ) );
|
61 |
+
|
62 |
+
add_action( 'wp_ajax_rtb_get_available_tables', array( $this, 'get_available_tables' ) );
|
63 |
+
add_action( 'wp_ajax_nopriv_rtb_get_available_tables', array( $this, 'get_available_tables' ) );
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Get reservations that are associated with the email address that was sent
|
68 |
+
* @since 2.1.0
|
69 |
+
*/
|
70 |
+
public function get_reservations() {
|
71 |
+
global $wpdb, $rtb_controller;
|
72 |
+
|
73 |
+
if ( !check_ajax_referer( 'rtb-booking-form', 'nonce' ) ) {
|
74 |
+
rtbHelper::bad_nonce_ajax();
|
75 |
+
}
|
76 |
+
|
77 |
+
$email = isset($_POST['booking_email']) ? sanitize_email( $_POST['booking_email'] ) : '';
|
78 |
+
|
79 |
+
if ( ! $email ) {
|
80 |
+
wp_send_json_error(
|
81 |
+
array(
|
82 |
+
'error' => 'noemail',
|
83 |
+
'msg' => __( 'The email you entered is not valid.', 'restaurant-reservations' ),
|
84 |
+
)
|
85 |
+
);
|
86 |
+
}
|
87 |
+
|
88 |
+
$booking_status_lbls = $rtb_controller->cpts->booking_statuses;
|
89 |
+
|
90 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
|
91 |
+
|
92 |
+
$bookings = array();
|
93 |
+
$booking_ids = $wpdb->get_results(
|
94 |
+
$wpdb->prepare("
|
95 |
+
SELECT `post_id` FROM `{$wpdb->postmeta}` WHERE `meta_key` = 'rtb' AND `meta_value` LIKE %s",
|
96 |
+
'%' . $email . '%'
|
97 |
+
)
|
98 |
+
);
|
99 |
+
|
100 |
+
foreach ( $booking_ids as $booking_id ) {
|
101 |
+
$booking = new rtbBooking();
|
102 |
+
if ( $booking->load_post( $booking_id->post_id ) ) {
|
103 |
+
$booking_date = (new DateTime($booking->date, wp_timezone()))->format('U');
|
104 |
+
if ( in_array($booking->post_status, ['pending', 'payment_pending', 'payment_failed', 'confirmed'] ) and time() < $booking_date ) {
|
105 |
+
$bookings[] = array(
|
106 |
+
'ID' => $booking->ID,
|
107 |
+
'email' => $booking->email,
|
108 |
+
'datetime' => $booking->format_date( $booking->date ),
|
109 |
+
'party' => $booking->party,
|
110 |
+
'status' => $booking->post_status,
|
111 |
+
'status_lbl' => $booking_status_lbls[$booking->post_status]['label']
|
112 |
+
);
|
113 |
+
}
|
114 |
+
}
|
115 |
+
}
|
116 |
+
|
117 |
+
if ( ! empty( $bookings ) ) {
|
118 |
+
wp_send_json_success(
|
119 |
+
array(
|
120 |
+
'bookings' => $bookings
|
121 |
+
)
|
122 |
+
);
|
123 |
+
}
|
124 |
+
else {
|
125 |
+
wp_send_json_error(
|
126 |
+
array(
|
127 |
+
'error' => 'nobookings',
|
128 |
+
'msg' => esc_html( $rtb_controller->settings->get_setting( 'label-modify-no-bookings-found' ) ),
|
129 |
+
)
|
130 |
+
);
|
131 |
+
}
|
132 |
+
|
133 |
+
die();
|
134 |
+
}
|
135 |
+
|
136 |
+
/**
|
137 |
+
* Cancel a reservation based on its ID, with the email address used for confirmation
|
138 |
+
* @since 2.1.0
|
139 |
+
*/
|
140 |
+
public function cancel_reservation( $ajax = true ) {
|
141 |
+
global $rtb_controller;
|
142 |
+
|
143 |
+
if ( $ajax && !check_ajax_referer( 'rtb-booking-form', 'nonce' ) ) {
|
144 |
+
rtbHelper::bad_nonce_ajax();
|
145 |
+
}
|
146 |
+
|
147 |
+
$cancelled_redirect = $rtb_controller->settings->get_setting( 'cancelled-redirect-page' );
|
148 |
+
|
149 |
+
$booking_id = isset($_REQUEST['booking_id']) ? absint( $_REQUEST['booking_id'] ) : '';
|
150 |
+
$booking_email = isset($_REQUEST['booking_email']) ? sanitize_email( $_REQUEST['booking_email'] ) : '';
|
151 |
+
|
152 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
|
153 |
+
|
154 |
+
$success = false;
|
155 |
+
$error = array(
|
156 |
+
'error' => 'unknown',
|
157 |
+
'msg' => __( 'Unkown error. Please try again', 'restaurant-reservations' )
|
158 |
+
);
|
159 |
+
|
160 |
+
$booking = new rtbBooking();
|
161 |
+
if ( $booking->load_post( $booking_id ) ) {
|
162 |
+
if ( $booking_email == $booking->email ) {
|
163 |
+
wp_update_post( array( 'ID' => $booking->ID, 'post_status' => 'cancelled' ) );
|
164 |
+
|
165 |
+
$success = true;
|
166 |
+
}
|
167 |
+
else {
|
168 |
+
$error = array(
|
169 |
+
'error' => 'invalidemail',
|
170 |
+
'msg' => __( 'No booking matches the information that was sent.', 'restaurant-reservations' ),
|
171 |
+
);
|
172 |
+
}
|
173 |
+
}
|
174 |
+
else {
|
175 |
+
$error = array(
|
176 |
+
'error' => 'invalidid',
|
177 |
+
'msg' => __( 'No booking matches the information that was sent.', 'restaurant-reservations' ),
|
178 |
+
);
|
179 |
+
}
|
180 |
+
|
181 |
+
if ( $ajax ) {
|
182 |
+
if ( $success ) {
|
183 |
+
|
184 |
+
$response = array( 'booking_id' => $booking_id );
|
185 |
+
|
186 |
+
if( '' != $cancelled_redirect ) {
|
187 |
+
$response['cancelled_redirect'] = $cancelled_redirect;
|
188 |
+
}
|
189 |
+
|
190 |
+
wp_send_json_success( $response );
|
191 |
+
}
|
192 |
+
else {
|
193 |
+
wp_send_json_error( $error );
|
194 |
+
}
|
195 |
+
}
|
196 |
+
else {
|
197 |
+
$redirect_url = '';
|
198 |
+
|
199 |
+
if( '' != $cancelled_redirect && $success ) {
|
200 |
+
$redirect_url = $cancelled_redirect;
|
201 |
+
}
|
202 |
+
else {
|
203 |
+
$booking_page_id = $rtb_controller->settings->get_setting( 'booking-page' );
|
204 |
+
$booking_page_url = get_permalink( $booking_page_id );
|
205 |
+
|
206 |
+
$redirect_url = add_query_arg(
|
207 |
+
array(
|
208 |
+
'bookingCancelled' => $success ? 'success' : 'fail'
|
209 |
+
),
|
210 |
+
$booking_page_url
|
211 |
+
);
|
212 |
+
}
|
213 |
+
|
214 |
+
if( wp_redirect( $redirect_url ) ) {
|
215 |
+
exit;
|
216 |
+
}
|
217 |
+
|
218 |
+
header( "Location: {$redirect_url}", true, 302 );
|
219 |
+
exit;
|
220 |
+
}
|
221 |
+
}
|
222 |
+
|
223 |
+
/**
|
224 |
+
* Get available timeslots when "Max Reservations" or "Max People" is enabled
|
225 |
+
* @since 2.0.0
|
226 |
+
*/
|
227 |
+
public function get_time_slots() {
|
228 |
+
global $rtb_controller;
|
229 |
+
|
230 |
+
if ( !check_ajax_referer( 'rtb-booking-form', 'nonce' ) ) {
|
231 |
+
rtbHelper::bad_nonce_ajax();
|
232 |
+
}
|
233 |
+
|
234 |
+
$max_reservations_enabled = $rtb_controller->settings->get_setting( 'rtb-enable-max-tables' );
|
235 |
+
|
236 |
+
// proessing request for this date
|
237 |
+
$this->location = ! empty( $_POST['location'] ) ? get_term( intval( $_POST['location'] ) ) : false;
|
238 |
+
|
239 |
+
$this->year = sanitize_text_field( $_POST['year'] );
|
240 |
+
$this->month = sanitize_text_field( $_POST['month'] );
|
241 |
+
$this->day = sanitize_text_field( $_POST['day'] );
|
242 |
+
|
243 |
+
$interval = $rtb_controller->settings->get_setting( 'time-interval' ) * 60;
|
244 |
+
|
245 |
+
// Helper functions
|
246 |
+
$finalize_response = function ( $open_close_pair_list = array() ) {
|
247 |
+
|
248 |
+
$valid_times = [];
|
249 |
+
|
250 |
+
if ( ! empty( $open_close_pair_list ) ) {
|
251 |
+
|
252 |
+
foreach ( $open_close_pair_list as $pair ) {
|
253 |
+
|
254 |
+
$valid_times[] = array(
|
255 |
+
'from' => $this->format_pickadate_time( $pair['from'] ),
|
256 |
+
'to' => $this->format_pickadate_time( $pair['to'] ),
|
257 |
+
'inverted' => true
|
258 |
+
);
|
259 |
+
}
|
260 |
+
}
|
261 |
+
|
262 |
+
echo json_encode( $valid_times );
|
263 |
+
|
264 |
+
die();
|
265 |
+
};
|
266 |
+
|
267 |
+
$all_slots_for = function( $pairs ) use( $interval ) {
|
268 |
+
$all_possible_slots = [];
|
269 |
+
|
270 |
+
if( !is_array( $pairs ) ) return $pairs;
|
271 |
+
|
272 |
+
foreach ( $pairs as $pair ) {
|
273 |
+
$all_possible_slots[] = $pair['from'];
|
274 |
+
$next = $pair['from'] + $interval;
|
275 |
+
while ( $next <= $pair['to'] ) {
|
276 |
+
$all_possible_slots[] = $next;
|
277 |
+
$next += $interval;
|
278 |
+
}
|
279 |
+
}
|
280 |
+
|
281 |
+
return $all_possible_slots;
|
282 |
+
};
|
283 |
+
|
284 |
+
$consolidating_timeslots_to_timeframes = function( $slots ) use( $interval ) {
|
285 |
+
$timeframe = [];
|
286 |
+
|
287 |
+
$slots_count = count( $slots );
|
288 |
+
if( 0 < $slots_count ) {
|
289 |
+
|
290 |
+
$current_pair = [ 'from' => $slots[ 0 ] ];
|
291 |
+
|
292 |
+
for ( $i = 1; $i < $slots_count; $i++) {
|
293 |
+
if( $slots[ $i ] - $slots[ $i - 1 ] !== $interval ) {
|
294 |
+
$current_pair[ 'to' ] = $slots[ $i - 1 ];
|
295 |
+
$timeframe[] = $current_pair;
|
296 |
+
|
297 |
+
$current_pair = [ 'from' => $slots[ $i ] ];
|
298 |
+
}
|
299 |
+
}
|
300 |
+
|
301 |
+
$current_pair[ 'to' ] = $slots[ $i - 1 ];
|
302 |
+
$timeframe[] = $current_pair;
|
303 |
+
}
|
304 |
+
|
305 |
+
return $timeframe;
|
306 |
+
};
|
307 |
+
|
308 |
+
// Get opening/closing times for this particular day
|
309 |
+
$hours = $this->get_opening_hours();
|
310 |
+
|
311 |
+
// If the restaurant is closed that day
|
312 |
+
// If Enable Max Reservation not set
|
313 |
+
if ( 1 > count( $hours ) || ! $max_reservations_enabled ) {
|
314 |
+
$slots = $all_slots_for( $hours );
|
315 |
+
|
316 |
+
sort( $slots, SORT_NUMERIC );
|
317 |
+
|
318 |
+
$timeframes = $consolidating_timeslots_to_timeframes( $slots );
|
319 |
+
|
320 |
+
$finalize_response( $timeframes );
|
321 |
+
}
|
322 |
+
|
323 |
+
$location_slug = ! empty( $this->location ) ? $this->location->slug : false;
|
324 |
+
|
325 |
+
$dining_block_seconds = (int) $rtb_controller->settings->get_setting( 'rtb-dining-block-length' ) * 60;
|
326 |
+
|
327 |
+
$min_party_size = (int) $rtb_controller->settings->get_setting( 'party-size-min' );
|
328 |
+
|
329 |
+
$max_reservations = (int) $rtb_controller->settings->get_setting( 'rtb-max-tables-count', $location_slug );
|
330 |
+
|
331 |
+
$max_people = (int) $rtb_controller->settings->get_setting( 'rtb-max-people-count', $location_slug );
|
332 |
+
|
333 |
+
$all_possible_slots = $all_slots_for( $hours );
|
334 |
+
|
335 |
+
// Get all current bookings sorted by date
|
336 |
+
$args = array(
|
337 |
+
'posts_per_page' => -1,
|
338 |
+
'date_range' => 'dates',
|
339 |
+
'start_date' => $this->year . '-' . $this->month . '-' . $this->day,
|
340 |
+
'end_date' => $this->year . '-' . $this->month . '-' . $this->day,
|
341 |
+
'post_status' => ['pending', 'payment_pending', 'confirmed', 'arrived']
|
342 |
+
);
|
343 |
+
|
344 |
+
// If there are multiple locations, a location is selected, and
|
345 |
+
// max reservations and/or seats has been enabled for this specific location
|
346 |
+
if ( ! empty( $location_slug ) and ( $rtb_controller->settings->is_location_setting_enabled( 'rtb-max-tables-count', $location_slug ) or $rtb_controller->settings->is_location_setting_enabled( 'rtb-max-people-count', $location_slug ) ) ) {
|
347 |
+
|
348 |
+
$tax_query = array(
|
349 |
+
array(
|
350 |
+
'taxonomy' => $rtb_controller->locations->location_taxonomy,
|
351 |
+
'field' => 'term_id',
|
352 |
+
'terms' => $this->location->term_id
|
353 |
+
)
|
354 |
+
);
|
355 |
+
|
356 |
+
$args['tax_query'] = $tax_query;
|
357 |
+
}
|
358 |
+
|
359 |
+
$query = new rtbQuery( $args, 'ajax-get-time-slots' );
|
360 |
+
$query->prepare_args();
|
361 |
+
$bookings = $query->get_bookings();
|
362 |
+
|
363 |
+
// This array holds bookings for all slots by expanding the booking by
|
364 |
+
// dining block length to help finding the overlapped bookings for time-slots
|
365 |
+
$all_bookings_by_slots = [];
|
366 |
+
foreach ( $bookings as $key => $booking ) {
|
367 |
+
// Convert booking date to seconds from UNIX
|
368 |
+
$booking_time = ( new DateTime( $booking->date , wp_timezone() ) )->format( 'U' );
|
369 |
+
if( ! array_key_exists( $booking_time, $all_bookings_by_slots ) ) {
|
370 |
+
$all_bookings_by_slots[$booking_time] = [
|
371 |
+
'total_bookings' => 0,
|
372 |
+
'total_guest' => 0,
|
373 |
+
'overlapped' => false
|
374 |
+
];
|
375 |
+
}
|
376 |
+
$all_bookings_by_slots[$booking_time]['total_bookings']++;
|
377 |
+
$all_bookings_by_slots[$booking_time]['total_guest'] += intval( $booking->party );
|
378 |
+
|
379 |
+
/**
|
380 |
+
* Expanding bookings
|
381 |
+
* Example: If I have someone booked at 1pm who will be in the restaurant for 120 minutes,
|
382 |
+
* that means they will be in the restaurant until 3pm. There is another booking at 2pm.
|
383 |
+
* That means, from 2pm to 3pm, there are already two separate reservations in the restaurant.
|
384 |
+
*/
|
385 |
+
$end = $booking_time + $dining_block_seconds;
|
386 |
+
$next = $booking_time + $interval;
|
387 |
+
while($next < $end) {
|
388 |
+
if( ! array_key_exists( $next, $all_bookings_by_slots ) ) {
|
389 |
+
$all_bookings_by_slots[$next] = [
|
390 |
+
'total_bookings' => 0,
|
391 |
+
'total_guest' => 0,
|
392 |
+
'overlapped' => false
|
393 |
+
];
|
394 |
+
}
|
395 |
+
$all_bookings_by_slots[$next]['overlapped'] = true;
|
396 |
+
$all_bookings_by_slots[$next]['total_bookings']++;
|
397 |
+
$all_bookings_by_slots[$next]['total_guest'] += intval( $booking->party );
|
398 |
+
$next += $interval;
|
399 |
+
}
|
400 |
+
}
|
401 |
+
|
402 |
+
$all_blocked_slots = [];
|
403 |
+
|
404 |
+
// Go through all bookings and figure out when we're at or above the
|
405 |
+
// max reservation or max people and mark that slot as blocked
|
406 |
+
if ( isset( $max_reservations ) and $max_reservations > 0 ) {
|
407 |
+
foreach ( $all_bookings_by_slots as $slot => $data ) {
|
408 |
+
if( $max_reservations <= $data['total_bookings'] ) {
|
409 |
+
$all_blocked_slots[] = $slot;
|
410 |
+
}
|
411 |
+
}
|
412 |
+
}
|
413 |
+
else if ( isset( $max_people ) and $max_people > 0 ) {
|
414 |
+
/**
|
415 |
+
* min_party_size = 10, max_people = 100, 6 bookings of total 91 guests
|
416 |
+
* Now, if anybody wants to book for at least 10 people, it is not possible
|
417 |
+
* because the total will surpass the max_people (100)
|
418 |
+
* thus reducing min_party_size from max_people
|
419 |
+
*
|
420 |
+
* $max_people can be zero when min_party_size is same as max_people
|
421 |
+
*/
|
422 |
+
$max_people = $max_people - $min_party_size;
|
423 |
+
|
424 |
+
foreach ( $all_bookings_by_slots as $slot => $data ) {
|
425 |
+
if( $max_people < $data['total_guest'] ) {
|
426 |
+
$all_blocked_slots[] = $slot;
|
427 |
+
}
|
428 |
+
}
|
429 |
+
}
|
430 |
+
|
431 |
+
// Mark slots unavailable, due to dinning block length
|
432 |
+
$additional_blocked_slots = [];
|
433 |
+
foreach ($all_blocked_slots as $slot) {
|
434 |
+
// blocking before this slot
|
435 |
+
$begin = $slot - $dining_block_seconds;
|
436 |
+
/**
|
437 |
+
* interval 30 minutes, dinning_length 120 minutes, slot 10:00am
|
438 |
+
* additional blockings before shall be 8:30am,9:00am and 9:30am
|
439 |
+
* thus skipping 8:00am which is valid
|
440 |
+
*
|
441 |
+
* @var unix timestamp
|
442 |
+
*/
|
443 |
+
$next = $begin + $interval;
|
444 |
+
while($next < $slot) {
|
445 |
+
$additional_blocked_slots[] = $next;
|
446 |
+
$next += $interval;
|
447 |
+
}
|
448 |
+
|
449 |
+
// block after this slot only when this slot is not overlapped
|
450 |
+
// Overlapped slots should block only backwards, but not afterward
|
451 |
+
if( $all_bookings_by_slots[$slot]['overlapped'] ) {
|
452 |
+
continue;
|
453 |
+
}
|
454 |
+
|
455 |
+
// blocking after this slot
|
456 |
+
$end = $slot + $dining_block_seconds;
|
457 |
+
/**
|
458 |
+
* interval 30 minutes, dinning_length 120 minutes, slot 10:00am
|
459 |
+
* additional blockings after shall be 10:30am,11:00am and 1130am
|
460 |
+
* thus skipping 12:00pm which is valid
|
461 |
+
*
|
462 |
+
* @var unix timestamp
|
463 |
+
*/
|
464 |
+
$next = $slot + $interval;
|
465 |
+
while($next < $end) {
|
466 |
+
$additional_blocked_slots[] = $next;
|
467 |
+
$next += $interval;
|
468 |
+
}
|
469 |
+
}
|
470 |
+
|
471 |
+
$all_blocked_slots = array_unique(
|
472 |
+
array_merge( $all_blocked_slots, $additional_blocked_slots ),
|
473 |
+
SORT_NUMERIC
|
474 |
+
);
|
475 |
+
|
476 |
+
sort( $all_blocked_slots, SORT_NUMERIC );
|
477 |
+
|
478 |
+
// remove blocked slots from available slots
|
479 |
+
$available_slots = array_diff( $all_possible_slots, $all_blocked_slots );
|
480 |
+
sort( $available_slots, SORT_NUMERIC );
|
481 |
+
|
482 |
+
// consolidating timeslots to timeframes
|
483 |
+
$timeframes = $consolidating_timeslots_to_timeframes( $available_slots );
|
484 |
+
|
485 |
+
$finalize_response( $timeframes );
|
486 |
+
}
|
487 |
+
|
488 |
+
public function get_opening_hours() {
|
489 |
+
global $rtb_controller;
|
490 |
+
|
491 |
+
$location_slug = ! empty( $this->location ) ? $this->location->slug : false;
|
492 |
+
|
493 |
+
$schedule_closed = $rtb_controller->settings->get_setting( 'schedule-closed', $location_slug );
|
494 |
+
$schedule_closed = is_array( $schedule_closed ) ? $schedule_closed : array();
|
495 |
+
|
496 |
+
$valid_times = array();
|
497 |
+
|
498 |
+
// Check if this date is an exception to the rules
|
499 |
+
if ( $schedule_closed !== 'undefined' ) {
|
500 |
+
|
501 |
+
$selected_date = ( new DateTime('now', wp_timezone() ) )
|
502 |
+
->setTime(0, 0, 2)
|
503 |
+
->setDate( $this->year, $this->month, $this->day );
|
504 |
+
|
505 |
+
foreach ( $schedule_closed as $ids => $closing ) {
|
506 |
+
if( array_key_exists( 'date_range', $closing ) ) {
|
507 |
+
|
508 |
+
$start = ! empty( $closing['date_range']['start'] )
|
509 |
+
? new DateTime( $closing['date_range']['start'], wp_timezone() )
|
510 |
+
: new DateTime( 'now', wp_timezone() );
|
511 |
+
$start->setTime(0, 0);
|
512 |
+
|
513 |
+
$end = !empty( $closing['date_range']['end'] )
|
514 |
+
? new DateTime( $closing['date_range']['end'], wp_timezone() )
|
515 |
+
: ( new DateTime( 'now', wp_timezone() ) )->add( new DateInterval( 'P10Y' ) );
|
516 |
+
$end->setTime(23, 59, 58);
|
517 |
+
|
518 |
+
if( $start < $selected_date && $selected_date < $end ) {
|
519 |
+
$exception = clone $selected_date;
|
520 |
+
}
|
521 |
+
else {
|
522 |
+
// Set anything to void this rule
|
523 |
+
$exception = clone $selected_date;
|
524 |
+
$exception->add( new DateInterval( 'P1Y' ) );
|
525 |
+
}
|
526 |
+
}
|
527 |
+
else {
|
528 |
+
$exception = ( new DateTime( $closing['date'], wp_timezone() ) )->setTime(0, 0, 2);
|
529 |
+
}
|
530 |
+
|
531 |
+
if ( $exception == $selected_date ) {
|
532 |
+
|
533 |
+
// Closed all day
|
534 |
+
if ( ! isset( $closing['time'] ) || $closing['time'] == 'undefined' ) {
|
535 |
+
return false;
|
536 |
+
}
|
537 |
+
|
538 |
+
if ( $closing['time']['start'] !== 'undefined' ) {
|
539 |
+
$open_time = ( new DateTime( $exception->format( 'Y-m-d' ) . ' ' . $closing['time']['start'], wp_timezone() ) )->format( 'U' );
|
540 |
+
}
|
541 |
+
else {
|
542 |
+
|
543 |
+
// Start of the day
|
544 |
+
$open_time = ( new DateTime( $exception->format( 'Y-m-d' ), wp_timezone() ) )->format('U');
|
545 |
+
}
|
546 |
+
|
547 |
+
if ( $closing['time']['end'] !== 'undefined' ) {
|
548 |
+
$close_time = ( new DateTime( $exception->format( 'Y-m-d' ) . ' ' . $closing['time']['end'], wp_timezone() ) )->format( 'U' );
|
549 |
+
}
|
550 |
+
else {
|
551 |
+
|
552 |
+
// End of the day
|
553 |
+
$close_time = ( new DateTime( $exception->format( 'Y-m-d' ) . ' 23:59:59', wp_timezone() ) )->format( 'U' );
|
554 |
+
}
|
555 |
+
|
556 |
+
$open_time = $this->get_earliest_time( $open_time );
|
557 |
+
|
558 |
+
if ( $open_time <= $close_time ) {
|
559 |
+
$valid_times[] = ['from' => $open_time, 'to' => $close_time];
|
560 |
+
}
|
561 |
+
}
|
562 |
+
}
|
563 |
+
|
564 |
+
// Exit early if this date is an exception
|
565 |
+
if ( isset( $open_time ) ) {
|
566 |
+
return $valid_times;
|
567 |
+
}
|
568 |
+
}
|
569 |
+
|
570 |
+
$schedule_open = $rtb_controller->settings->get_setting( 'schedule-open', $location_slug );
|
571 |
+
$schedule_open = is_array( $schedule_open ) ? $schedule_open : array();
|
572 |
+
|
573 |
+
// Get any rules which apply to this weekday
|
574 |
+
$day_of_week = strtolower(
|
575 |
+
( new DateTime( $this->year . '-' . $this->month . '-' . $this->day . ' 1:00:00', wp_timezone() ) )->format( 'l' )
|
576 |
+
);
|
577 |
+
|
578 |
+
$selected_date = ( new DateTime('now', wp_timezone() ) )
|
579 |
+
->setTime(0, 0, 2)
|
580 |
+
->setDate( $this->year, $this->month, $this->day );
|
581 |
+
|
582 |
+
foreach ( $schedule_open as $opening ) {
|
583 |
+
|
584 |
+
if ( $opening['weekdays'] !== 'undefined' ) {
|
585 |
+
|
586 |
+
foreach ( $opening['weekdays'] as $weekday => $value ) {
|
587 |
+
|
588 |
+
if ( $weekday == $day_of_week ) {
|
589 |
+
|
590 |
+
if ( isset( $opening['time'] ) && $opening['time']['start'] !== 'undefined' ) {
|
591 |
+
|
592 |
+
$open_time = ( new DateTime( $selected_date->format( 'Y-m-d' ) .' '. $opening['time']['start'], wp_timezone() ) )->format( 'U' );
|
593 |
+
}
|
594 |
+
else {
|
595 |
+
|
596 |
+
// Start of the day
|
597 |
+
$open_time = ( new DateTime( $selected_date->format( 'Y-m-d' ), wp_timezone() ) )->format('U');
|
598 |
+
}
|
599 |
+
|
600 |
+
if ( isset( $opening['time'] ) && $opening['time']['end'] !== 'undefined' ) {
|
601 |
+
|
602 |
+
$close_time = ( new DateTime( $selected_date->format( 'Y-m-d' ) .' '. $opening['time']['end'], wp_timezone() ) )->format( 'U' );
|
603 |
+
}
|
604 |
+
else {
|
605 |
+
|
606 |
+
// End of the day
|
607 |
+
$close_time = ( new DateTime( $selected_date->format( 'Y-m-d' ) . ' 23:59:59', wp_timezone() ) )->format( 'U' );
|
608 |
+
}
|
609 |
+
|
610 |
+
$open_time = $this->get_earliest_time( $open_time );
|
611 |
+
|
612 |
+
if ( $open_time <= $close_time ) {
|
613 |
+
|
614 |
+
$valid_times[] = ['from' => $open_time, 'to' => $close_time];
|
615 |
+
}
|
616 |
+
}
|
617 |
+
}
|
618 |
+
}
|
619 |
+
}
|
620 |
+
|
621 |
+
// Pass any valid times located
|
622 |
+
return $valid_times;
|
623 |
+
}
|
624 |
+
|
625 |
+
public function get_earliest_time( $open_time ) {
|
626 |
+
global $rtb_controller;
|
627 |
+
|
628 |
+
$interval = $rtb_controller->settings->get_setting( 'time-interval' ) * 60;
|
629 |
+
|
630 |
+
$selected_date = ( new DateTime('now', wp_timezone() ) )
|
631 |
+
->setTime(0, 0, 2)
|
632 |
+
->setDate( $this->year, $this->month, $this->day );
|
633 |
+
|
634 |
+
// adjust open time with respect to the current time of the day for upcoming timeslots
|
635 |
+
$current_time = ( new DateTime( 'now', wp_timezone() ) )->format( 'U' );
|
636 |
+
|
637 |
+
// Only make adjustments for current day selections
|
638 |
+
if ( $selected_date->format('Y-m-d') !== ( new DateTime( 'today', wp_timezone() ) )->format('Y-m-d') ) {
|
639 |
+
return $open_time;
|
640 |
+
}
|
641 |
+
|
642 |
+
$late_bookings = ( is_admin() && current_user_can( 'manage_bookings' ) ) ? '' : $rtb_controller->settings->get_setting( 'late-bookings' );
|
643 |
+
|
644 |
+
if( $current_time > $open_time ) {
|
645 |
+
while( $current_time > $open_time ) {
|
646 |
+
$open_time += $interval;
|
647 |
+
}
|
648 |
+
}
|
649 |
+
|
650 |
+
// adjust the open time for the Late Bookings option
|
651 |
+
if ( is_numeric($late_bookings) && $late_bookings % 1 === 0 ) {
|
652 |
+
$time_calc = ( new DateTime( 'now', wp_timezone() ) )->format( 'U' ) + ( $late_bookings * 60 );
|
653 |
+
while ($time_calc > $open_time) {
|
654 |
+
$open_time = $open_time + $interval;
|
655 |
+
}
|
656 |
+
}
|
657 |
+
|
658 |
+
return $open_time;
|
659 |
+
}
|
660 |
+
|
661 |
+
/**
|
662 |
+
* Get number of seats remaining avilable to be booked
|
663 |
+
* @since 2.1.5
|
664 |
+
*/
|
665 |
+
public function get_available_party_size() {
|
666 |
+
global $rtb_controller;
|
667 |
+
|
668 |
+
if ( !check_ajax_referer( 'rtb-booking-form', 'nonce' ) ) {
|
669 |
+
rtbHelper::bad_nonce_ajax();
|
670 |
+
}
|
671 |
+
|
672 |
+
$this->location = ! empty( $_POST['location'] ) ? get_term( intval( $_POST['location'] ) ) : false;
|
673 |
+
$this->year = sanitize_text_field( $_POST['year'] );
|
674 |
+
$this->month = sanitize_text_field( $_POST['month'] );
|
675 |
+
$this->day = sanitize_text_field( $_POST['day'] );
|
676 |
+
$this->time = sanitize_text_field( $_POST['time'] );
|
677 |
+
|
678 |
+
$location_slug = ! empty( $this->location ) ? $this->location->slug : false;
|
679 |
+
|
680 |
+
$max_people = (int) $rtb_controller->settings->get_setting( 'rtb-max-people-count', $location_slug );
|
681 |
+
|
682 |
+
$dining_block_seconds = (int) $rtb_controller->settings->get_setting( 'rtb-dining-block-length' )* 60 - 1; // Take 1 second off, to avoid bookings that start or end exactly at the beginning of a booking block
|
683 |
+
|
684 |
+
// Get opening/closing times for this particular day
|
685 |
+
$hours = $this->get_opening_hours();
|
686 |
+
|
687 |
+
// If the restaurant is closed that day, return false
|
688 |
+
if ( 1 > count( $hours ) ) { die(); }
|
689 |
+
|
690 |
+
// If no time is selected, return false
|
691 |
+
if ( ! $this->time ) { die(); }
|
692 |
+
|
693 |
+
$args = array(
|
694 |
+
'posts_per_page' => -1,
|
695 |
+
'date_range' => 'dates',
|
696 |
+
'start_date' => $this->year . '-' . $this->month . '-' . $this->day,
|
697 |
+
'end_date' => $this->year . '-' . $this->month . '-' . $this->day
|
698 |
+
);
|
699 |
+
|
700 |
+
// If there are multiple locations, a location is selected, and
|
701 |
+
// max seats has been enabled for this specific location
|
702 |
+
if ( ! empty( $location_slug ) and $rtb_controller->settings->is_location_setting_enabled( 'rtb-max-people-count', $location_slug ) ) {
|
703 |
+
|
704 |
+
$tax_query = array(
|
705 |
+
array(
|
706 |
+
'taxonomy' => $rtb_controller->locations->location_taxonomy,
|
707 |
+
'field' => 'term_id',
|
708 |
+
'terms' => $this->location->term_id
|
709 |
+
)
|
710 |
+
);
|
711 |
+
|
712 |
+
$args['tax_query'] = $tax_query;
|
713 |
+
}
|
714 |
+
|
715 |
+
$query = new rtbQuery( $args );
|
716 |
+
$query->prepare_args();
|
717 |
+
|
718 |
+
// Get all current bookings sorted by date
|
719 |
+
$bookings = $query->get_bookings();
|
720 |
+
|
721 |
+
$tmzn = wp_timezone();
|
722 |
+
|
723 |
+
$selected_date_time = ( new DateTime( $this->year . '-' . $this->month . '-' . $this->day . ' ' . $this->time, $tmzn ) )->format( 'U' );
|
724 |
+
$selected_date_time_start = $selected_date_time - $dining_block_seconds;
|
725 |
+
$selected_date_time_end = $selected_date_time + $dining_block_seconds;
|
726 |
+
$party_sizes = [];
|
727 |
+
|
728 |
+
if ($max_people != 'undefined' and $max_people != 0) {
|
729 |
+
|
730 |
+
$max_time_size = 0;
|
731 |
+
$current_times = array();
|
732 |
+
$party_sizes = array();
|
733 |
+
|
734 |
+
// Go through all current booking and collect the total party size
|
735 |
+
foreach ( $bookings as $key => $booking ) {
|
736 |
+
|
737 |
+
// Convert booking date to seconds from UNIX
|
738 |
+
$booking_time = ( new DateTime( $booking->date, $tmzn ) )->format( 'U' );
|
739 |
+
|
740 |
+
// Ignore bookings outside of our time range
|
741 |
+
if ($booking_time < $selected_date_time_start or $booking_time > $selected_date_time_end) { continue; }
|
742 |
+
|
743 |
+
$current_times[] = $booking_time;
|
744 |
+
$party_sizes[] = (int) $booking->party;
|
745 |
+
|
746 |
+
while ( sizeOf( $current_times ) > 0 and reset( $current_times ) < $booking_time - $dining_block_seconds ) {
|
747 |
+
//save the time to know when the blocking potentially ends
|
748 |
+
$removed_time = reset( $current_times );
|
749 |
+
|
750 |
+
// remove the expired time and party size
|
751 |
+
array_shift( $current_times );
|
752 |
+
array_shift( $party_sizes );
|
753 |
+
}
|
754 |
+
|
755 |
+
$max_time_size = max( $max_time_size, array_sum( $party_sizes ) );
|
756 |
+
}
|
757 |
+
|
758 |
+
$response = (object) array( 'available_spots' => $max_people - $max_time_size);
|
759 |
+
|
760 |
+
echo json_encode($response);
|
761 |
+
|
762 |
+
die();
|
763 |
+
} else {
|
764 |
+
return false;
|
765 |
+
}
|
766 |
+
}
|
767 |
+
|
768 |
+
/**
|
769 |
+
* Get tables available to be booked at a specific time and party size
|
770 |
+
* @since 2.1.7
|
771 |
+
*/
|
772 |
+
public function get_available_tables() {
|
773 |
+
global $rtb_controller;
|
774 |
+
|
775 |
+
if ( !check_ajax_referer( 'rtb-booking-form', 'nonce' ) ) {
|
776 |
+
rtbHelper::bad_nonce_ajax();
|
777 |
+
}
|
778 |
+
|
779 |
+
$tables = $rtb_controller->settings->get_sorted_tables();
|
780 |
+
|
781 |
+
$this->booking_id = isset( $_POST['booking_id'] ) ? intval( $_POST['booking_id'] ) : 0;
|
782 |
+
$this->year = isset( $_POST['year'] ) ? sanitize_text_field( $_POST['year'] ) : false;
|
783 |
+
$this->month = isset( $_POST['month'] ) ? sanitize_text_field( $_POST['month'] ) : false;
|
784 |
+
$this->day = isset( $_POST['day'] ) ? sanitize_text_field( $_POST['day'] ) : false;
|
785 |
+
$this->time = isset( $_POST['time'] ) ? sanitize_text_field( $_POST['time'] ) : false;
|
786 |
+
$this->party = isset( $_POST['party'] ) ? sanitize_text_field( $_POST['party'] ) : false;
|
787 |
+
|
788 |
+
/*$this->year = 2020;
|
789 |
+
$this->month = 06;
|
790 |
+
$this->day = 12;
|
791 |
+
$this->time = '02:15 PM';
|
792 |
+
$this->party = 12;*/
|
793 |
+
|
794 |
+
if ( ! isset( $this->year ) or ! isset( $this->month ) or ! isset( $this->day ) or ! isset( $this->time ) ) { return false; }
|
795 |
+
|
796 |
+
$datetime = $this->year . '-' . $this->month . '-' . $this->day . ' ' . $this->time;
|
797 |
+
|
798 |
+
$valid_tables = rtb_get_valid_tables( $datetime );
|
799 |
+
|
800 |
+
if ( $this->booking_id ) {
|
801 |
+
|
802 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
|
803 |
+
|
804 |
+
$current_booking = new rtbBooking();
|
805 |
+
$current_booking->load_post( $this->booking_id );
|
806 |
+
|
807 |
+
if ( $current_booking->table ) { $valid_tables = array_merge( $valid_tables, $current_booking->table ); }
|
808 |
+
}
|
809 |
+
|
810 |
+
$return_tables = array();
|
811 |
+
|
812 |
+
if ( isset( $this->party ) ) {
|
813 |
+
|
814 |
+
$possible_combinations = array();
|
815 |
+
foreach ( $valid_tables as $valid_table ) {
|
816 |
+
|
817 |
+
// If the party size is between the min and max for the table, great
|
818 |
+
if ( $tables[ $valid_table ]->min_people <= $this->party and $tables[ $valid_table ]->max_people >= $this->party ) {
|
819 |
+
|
820 |
+
$possible_combinations[] = $valid_table;
|
821 |
+
}
|
822 |
+
// If the party is above the minimum for the table, look to see if combinations could work
|
823 |
+
elseif ( $tables[ $valid_table ]->min_people <= $this->party ) {
|
824 |
+
|
825 |
+
$combination = $this->get_combinations_chain( $tables, $valid_tables, $valid_table, $tables[ $valid_table ]->max_people, $this->party );
|
826 |
+
|
827 |
+
if ( $combination ) {
|
828 |
+
$possible_combinations[] = $combination;
|
829 |
+
}
|
830 |
+
}
|
831 |
+
|
832 |
+
$return_tables = $this->format_tables( $possible_combinations );
|
833 |
+
}
|
834 |
+
}
|
835 |
+
else {
|
836 |
+
$return_tables = $this->format_tables( $valid_tables );
|
837 |
+
}
|
838 |
+
|
839 |
+
$selected_table = ( isset( $current_booking ) and $current_booking->table ) ? implode(',', $current_booking->table ) : -1;
|
840 |
+
|
841 |
+
$response = (object) array( 'available_tables' => $return_tables, 'selected_table' => $selected_table );
|
842 |
+
|
843 |
+
echo json_encode($response);
|
844 |
+
|
845 |
+
die();
|
846 |
+
}
|
847 |
+
|
848 |
+
/**
|
849 |
+
* Recursively go through table combinations to find one that has enough seats
|
850 |
+
* @since 2.1.7
|
851 |
+
*/
|
852 |
+
public function get_combinations_chain(
|
853 |
+
$tables,
|
854 |
+
$valid_tables,
|
855 |
+
$current_table,
|
856 |
+
$current_size,
|
857 |
+
$needed_size
|
858 |
+
) {
|
859 |
+
$table_chain[] = $current_table;
|
860 |
+
|
861 |
+
// No combination specified
|
862 |
+
if ( ! $tables[ $current_table ]->combinations ) {
|
863 |
+
return false;
|
864 |
+
}
|
865 |
+
|
866 |
+
$possible_tables = explode( ',', $tables[ $current_table ]->combinations );
|
867 |
+
|
868 |
+
foreach ( $possible_tables as $possible_table ) {
|
869 |
+
|
870 |
+
// If the table has already been booked, continue
|
871 |
+
if ( !in_array( $possible_table, $valid_tables) ) {
|
872 |
+
continue;
|
873 |
+
}
|
874 |
+
|
875 |
+
// If the table can hold the group on its own, continue
|
876 |
+
if ( $tables[ $possible_table ]->max_people >= $needed_size ) {
|
877 |
+
continue;
|
878 |
+
}
|
879 |
+
|
880 |
+
$current_size += $tables[ $possible_table ]->max_people;
|
881 |
+
$table_chain[] = $possible_table;
|
882 |
+
|
883 |
+
if ( $current_size >= $needed_size ) {
|
884 |
+
return implode(',', $table_chain);
|
885 |
+
}
|
886 |
+
}
|
887 |
+
|
888 |
+
//no viable combination found
|
889 |
+
return false;
|
890 |
+
}
|
891 |
+
|
892 |
+
/**
|
893 |
+
* Format the tables available to be booked as number(s)_string => human_value pairs
|
894 |
+
* @since 2.1.7
|
895 |
+
*/
|
896 |
+
public function format_tables ( $table_numbers ) {
|
897 |
+
global $rtb_controller;
|
898 |
+
|
899 |
+
$formatted_tables = array();
|
900 |
+
|
901 |
+
$tables = json_decode( html_entity_decode( $rtb_controller->settings->get_setting( 'rtb-tables' ) ) );
|
902 |
+
$tables = is_array( $tables ) ? $tables : array();
|
903 |
+
|
904 |
+
foreach ( $table_numbers as $table_number ) {
|
905 |
+
|
906 |
+
$table_parts = explode( ',', $table_number );
|
907 |
+
|
908 |
+
$table_values = array(
|
909 |
+
'numbers' => '',
|
910 |
+
'min_people' => 0,
|
911 |
+
'max_people' => 0
|
912 |
+
);
|
913 |
+
|
914 |
+
foreach ( $tables as $table ) {
|
915 |
+
if ( in_array($table->number, $table_parts) ) {
|
916 |
+
$table_values['numbers'] .= ( strlen( $table_values['numbers'] ) ? ', ' : '' ) . $table->number;
|
917 |
+
$table_values['min_people'] += $table->min_people;
|
918 |
+
$table_values['max_people'] += $table->max_people;
|
919 |
+
|
920 |
+
if ( ! isset( $section_name ) ) { $section_name = $this->get_section_name( $table->section ); }
|
921 |
+
}
|
922 |
+
}
|
923 |
+
|
924 |
+
$formatted_tables[ $table_values['numbers'] ] = $table_values['numbers'] . ' - ' . $section_name . ' (min. ' . $table_values['min_people'] . '/max. ' . $table_values['max_people'] . ')';
|
925 |
+
|
926 |
+
unset( $section_name );
|
927 |
+
}
|
928 |
+
|
929 |
+
return $formatted_tables;
|
930 |
+
}
|
931 |
+
|
932 |
+
public function get_section_name( $section_id ) {
|
933 |
+
global $rtb_controller;
|
934 |
+
|
935 |
+
$sections = json_decode( html_entity_decode( $rtb_controller->settings->get_setting( 'rtb-table-sections' ) ) );
|
936 |
+
$sections = is_array( $sections ) ? $sections : array();
|
937 |
+
|
938 |
+
foreach ( $sections as $section ) {
|
939 |
+
|
940 |
+
if ( $section->section_id == $section_id ) { return $section->name; }
|
941 |
+
}
|
942 |
+
|
943 |
+
return false;
|
944 |
+
}
|
945 |
+
|
946 |
+
public function format_pickadate_time( $time ) {
|
947 |
+
$obj = ( new DateTime('now' ) )->setTimestamp( $time )->setTimezone( wp_timezone() );
|
948 |
+
return array( $obj->format( 'G' ), $obj->format( 'i' ) );
|
949 |
+
}
|
950 |
+
}
|
951 |
}
|
includes/Blocks.class.php
CHANGED
@@ -1,99 +1,99 @@
|
|
1 |
-
<?php
|
2 |
-
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
-
|
4 |
-
if ( !class_exists( 'rtbBlocks' ) ) {
|
5 |
-
/**
|
6 |
-
* Class to create, edit and display blocks for the Gutenberg editor
|
7 |
-
*
|
8 |
-
* @since 0.0.1
|
9 |
-
*/
|
10 |
-
class rtbBlocks {
|
11 |
-
|
12 |
-
/**
|
13 |
-
* Add hooks
|
14 |
-
*/
|
15 |
-
public function __construct() {
|
16 |
-
|
17 |
-
add_action( 'init', array( $this, 'register' ) );
|
18 |
-
|
19 |
-
add_filter( 'block_categories_all', array( $this, 'add_block_category' ) );
|
20 |
-
}
|
21 |
-
|
22 |
-
/**
|
23 |
-
* Register blocks
|
24 |
-
*/
|
25 |
-
public function register() {
|
26 |
-
|
27 |
-
if ( !function_exists( 'register_block_type' ) ) {
|
28 |
-
return;
|
29 |
-
}
|
30 |
-
|
31 |
-
global $rtb_controller;
|
32 |
-
|
33 |
-
$rtb_controller->register_assets();
|
34 |
-
|
35 |
-
wp_register_script(
|
36 |
-
'restaurant-reservations-blocks',
|
37 |
-
RTB_PLUGIN_URL . '/assets/js/blocks.build.js',
|
38 |
-
array( 'wp-blocks', 'wp-i18n', 'wp-element', 'wp-editor' )
|
39 |
-
);
|
40 |
-
|
41 |
-
register_block_type( 'restaurant-reservations/booking-form', array(
|
42 |
-
'editor_script' => 'restaurant-reservations-blocks',
|
43 |
-
'editor_style' => 'rtb-booking-form',
|
44 |
-
'render_callback' => 'rtb_print_booking_form',
|
45 |
-
'attributes' => array(
|
46 |
-
'location' => array(
|
47 |
-
'type' => 'number',
|
48 |
-
'default' => 0,
|
49 |
-
),
|
50 |
-
),
|
51 |
-
) );
|
52 |
-
|
53 |
-
add_action( 'admin_init', array( $this, 'register_admin' ) );
|
54 |
-
}
|
55 |
-
|
56 |
-
/**
|
57 |
-
* Register admin-only assets for block handling
|
58 |
-
*/
|
59 |
-
public function register_admin() {
|
60 |
-
|
61 |
-
global $rtb_controller;
|
62 |
-
|
63 |
-
$locations_enabled = !!$rtb_controller->locations->post_type;
|
64 |
-
|
65 |
-
$location_options = array( array( 'value' => 0, 'label' => __('Ask the customer to select a location', 'restaurant-reservations' ) ) );
|
66 |
-
if ($locations_enabled) {
|
67 |
-
$locations = $rtb_controller->locations->get_location_options();
|
68 |
-
foreach ( $locations as $id => $name ) {
|
69 |
-
$location_options[] = array( 'value' => $id, 'label' => $name);
|
70 |
-
}
|
71 |
-
}
|
72 |
-
|
73 |
-
wp_add_inline_script(
|
74 |
-
'restaurant-reservations-blocks',
|
75 |
-
sprintf(
|
76 |
-
'var rtb_blocks = %s;',
|
77 |
-
json_encode( array(
|
78 |
-
'locationsEnabled' => $locations_enabled,
|
79 |
-
'locations' => $location_options,
|
80 |
-
) )
|
81 |
-
),
|
82 |
-
'before'
|
83 |
-
);
|
84 |
-
}
|
85 |
-
|
86 |
-
/**
|
87 |
-
* Create a new category of blocks to hold our block
|
88 |
-
*/
|
89 |
-
public function add_block_category( $categories ) {
|
90 |
-
|
91 |
-
$categories[] = array(
|
92 |
-
'slug' => 'rtb-blocks',
|
93 |
-
'title' => __( 'Five Star Restaurant Reservations', 'restaurant-reservations' ),
|
94 |
-
);
|
95 |
-
|
96 |
-
return $categories;
|
97 |
-
}
|
98 |
-
}
|
99 |
-
} // endif
|
1 |
+
<?php
|
2 |
+
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
+
|
4 |
+
if ( !class_exists( 'rtbBlocks' ) ) {
|
5 |
+
/**
|
6 |
+
* Class to create, edit and display blocks for the Gutenberg editor
|
7 |
+
*
|
8 |
+
* @since 0.0.1
|
9 |
+
*/
|
10 |
+
class rtbBlocks {
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Add hooks
|
14 |
+
*/
|
15 |
+
public function __construct() {
|
16 |
+
|
17 |
+
add_action( 'init', array( $this, 'register' ) );
|
18 |
+
|
19 |
+
add_filter( 'block_categories_all', array( $this, 'add_block_category' ) );
|
20 |
+
}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Register blocks
|
24 |
+
*/
|
25 |
+
public function register() {
|
26 |
+
|
27 |
+
if ( !function_exists( 'register_block_type' ) ) {
|
28 |
+
return;
|
29 |
+
}
|
30 |
+
|
31 |
+
global $rtb_controller;
|
32 |
+
|
33 |
+
$rtb_controller->register_assets();
|
34 |
+
|
35 |
+
wp_register_script(
|
36 |
+
'restaurant-reservations-blocks',
|
37 |
+
RTB_PLUGIN_URL . '/assets/js/blocks.build.js',
|
38 |
+
array( 'wp-blocks', 'wp-i18n', 'wp-element', 'wp-editor' )
|
39 |
+
);
|
40 |
+
|
41 |
+
register_block_type( 'restaurant-reservations/booking-form', array(
|
42 |
+
'editor_script' => 'restaurant-reservations-blocks',
|
43 |
+
'editor_style' => 'rtb-booking-form',
|
44 |
+
'render_callback' => 'rtb_print_booking_form',
|
45 |
+
'attributes' => array(
|
46 |
+
'location' => array(
|
47 |
+
'type' => 'number',
|
48 |
+
'default' => 0,
|
49 |
+
),
|
50 |
+
),
|
51 |
+
) );
|
52 |
+
|
53 |
+
add_action( 'admin_init', array( $this, 'register_admin' ) );
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Register admin-only assets for block handling
|
58 |
+
*/
|
59 |
+
public function register_admin() {
|
60 |
+
|
61 |
+
global $rtb_controller;
|
62 |
+
|
63 |
+
$locations_enabled = !!$rtb_controller->locations->post_type;
|
64 |
+
|
65 |
+
$location_options = array( array( 'value' => 0, 'label' => __('Ask the customer to select a location', 'restaurant-reservations' ) ) );
|
66 |
+
if ($locations_enabled) {
|
67 |
+
$locations = $rtb_controller->locations->get_location_options();
|
68 |
+
foreach ( $locations as $id => $name ) {
|
69 |
+
$location_options[] = array( 'value' => $id, 'label' => $name);
|
70 |
+
}
|
71 |
+
}
|
72 |
+
|
73 |
+
wp_add_inline_script(
|
74 |
+
'restaurant-reservations-blocks',
|
75 |
+
sprintf(
|
76 |
+
'var rtb_blocks = %s;',
|
77 |
+
json_encode( array(
|
78 |
+
'locationsEnabled' => $locations_enabled,
|
79 |
+
'locations' => $location_options,
|
80 |
+
) )
|
81 |
+
),
|
82 |
+
'before'
|
83 |
+
);
|
84 |
+
}
|
85 |
+
|
86 |
+
/**
|
87 |
+
* Create a new category of blocks to hold our block
|
88 |
+
*/
|
89 |
+
public function add_block_category( $categories ) {
|
90 |
+
|
91 |
+
$categories[] = array(
|
92 |
+
'slug' => 'rtb-blocks',
|
93 |
+
'title' => __( 'Five Star Restaurant Reservations', 'restaurant-reservations' ),
|
94 |
+
);
|
95 |
+
|
96 |
+
return $categories;
|
97 |
+
}
|
98 |
+
}
|
99 |
+
} // endif
|
includes/Booking.class.php
CHANGED
@@ -1,1510 +1,1510 @@
|
|
1 |
-
<?php
|
2 |
-
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
-
|
4 |
-
if ( !class_exists( 'rtbBooking' ) ) {
|
5 |
-
/**
|
6 |
-
* Class to handle a booking for Restaurant Table Bookings
|
7 |
-
*
|
8 |
-
* @since 0.0.1
|
9 |
-
*/
|
10 |
-
class rtbBooking {
|
11 |
-
|
12 |
-
/**
|
13 |
-
* Whether or not this request has been processed. Used to prevent
|
14 |
-
* duplicate forms on one page from processing a booking form more than
|
15 |
-
* once.
|
16 |
-
* @since 0.0.1
|
17 |
-
*/
|
18 |
-
public $request_processed = false;
|
19 |
-
|
20 |
-
/**
|
21 |
-
* Whether or not this request was successfully saved to the database.
|
22 |
-
* @since 0.0.1
|
23 |
-
*/
|
24 |
-
public $request_inserted = false;
|
25 |
-
|
26 |
-
/**
|
27 |
-
* Raw input, clone of $_POST
|
28 |
-
* @var array
|
29 |
-
* @since 2.3.0
|
30 |
-
*/
|
31 |
-
public $raw_input = array();
|
32 |
-
|
33 |
-
public function __construct() {}
|
34 |
-
|
35 |
-
/**
|
36 |
-
* Load the booking information from a WP_Post object or an ID
|
37 |
-
*
|
38 |
-
* @uses load_wp_post()
|
39 |
-
* @since 0.0.1
|
40 |
-
*/
|
41 |
-
public function load_post( $post ) {
|
42 |
-
|
43 |
-
if ( is_int( $post ) || is_string( $post ) ) {
|
44 |
-
$post = get_post( $post );
|
45 |
-
}
|
46 |
-
|
47 |
-
if ( is_object( $post ) && get_class( $post ) == 'WP_Post' && $post->post_type == RTB_BOOKING_POST_TYPE ) {
|
48 |
-
$this->load_wp_post( $post );
|
49 |
-
return true;
|
50 |
-
} else {
|
51 |
-
return false;
|
52 |
-
}
|
53 |
-
|
54 |
-
}
|
55 |
-
|
56 |
-
/**
|
57 |
-
* Load data from WP post object and retrieve metadata
|
58 |
-
*
|
59 |
-
* @uses load_post_metadata()
|
60 |
-
* @since 0.0.1
|
61 |
-
*/
|
62 |
-
public function load_wp_post( $post ) {
|
63 |
-
// Store post for access to other data if needed by extensions
|
64 |
-
$this->post = $post;
|
65 |
-
|
66 |
-
$this->ID = $post->ID;
|
67 |
-
$this->name = $post->post_title;
|
68 |
-
$this->date = $post->post_date;
|
69 |
-
$this->message = $post->post_content;
|
70 |
-
$this->post_status = $post->post_status;
|
71 |
-
|
72 |
-
$this->load_post_metadata();
|
73 |
-
|
74 |
-
do_action( 'rtb_booking_load_post_data', $this, $post );
|
75 |
-
}
|
76 |
-
|
77 |
-
/**
|
78 |
-
* Store metadata for post
|
79 |
-
* @since 0.0.1
|
80 |
-
*/
|
81 |
-
public function load_post_metadata() {
|
82 |
-
global $rtb_controller;
|
83 |
-
|
84 |
-
$meta_defaults = array(
|
85 |
-
'party' => '',
|
86 |
-
'email' => '',
|
87 |
-
'phone' => '',
|
88 |
-
'date_submission' => '',
|
89 |
-
'logs' => array(),
|
90 |
-
'ip' => '',
|
91 |
-
'consent_acquired' => '',
|
92 |
-
'deposit' => '0',
|
93 |
-
'table' => array(),
|
94 |
-
'payment_failure_message' => '',
|
95 |
-
'receipt_id' => '',
|
96 |
-
'reminder_sent' => false,
|
97 |
-
'late_arrival_sent' => false,
|
98 |
-
'mc_optin' => false
|
99 |
-
);
|
100 |
-
|
101 |
-
$meta_defaults = apply_filters( 'rtb_booking_metadata_defaults', $meta_defaults );
|
102 |
-
|
103 |
-
if ( is_array( $meta = get_post_meta( $this->ID, 'rtb', true ) ) ) {
|
104 |
-
$meta = array_merge( $meta_defaults, get_post_meta( $this->ID, 'rtb', true ) );
|
105 |
-
} else {
|
106 |
-
$meta = $meta_defaults;
|
107 |
-
}
|
108 |
-
|
109 |
-
$this->party = $meta['party'];
|
110 |
-
$this->email = $meta['email'];
|
111 |
-
$this->phone = $meta['phone'];
|
112 |
-
$this->date_submission = $meta['date_submission'];
|
113 |
-
$this->logs = $meta['logs'];
|
114 |
-
$this->ip = $meta['ip'];
|
115 |
-
$this->consent_acquired = $meta['consent_acquired'];
|
116 |
-
$this->deposit = $meta['deposit'];
|
117 |
-
$this->table = $meta['table'];
|
118 |
-
$this->payment_failure_message = $meta['payment_failure_message'];
|
119 |
-
$this->receipt_id = $meta['receipt_id'];
|
120 |
-
$this->late_arrival_sent = $meta['late_arrival_sent'];
|
121 |
-
$this->reminder_sent = $meta['reminder_sent'];
|
122 |
-
|
123 |
-
// Did they opt out?
|
124 |
-
$optout = $rtb_controller->settings->get_setting( 'mc-optout' );
|
125 |
-
// Because mcfrtbInit::reload_booking_meta() does not fire when needed for unknown reason
|
126 |
-
if ( $optout != 'no' ) {
|
127 |
-
$this->mc_optin = $meta['mc_optin'];
|
128 |
-
}
|
129 |
-
}
|
130 |
-
|
131 |
-
/**
|
132 |
-
* Prepare booking data loaded from the database for display in a booking
|
133 |
-
* form as request fields. This is used, eg, for splitting datetime values
|
134 |
-
* into date and time fields.
|
135 |
-
* @since 1.3
|
136 |
-
*/
|
137 |
-
public function prepare_request_data() {
|
138 |
-
|
139 |
-
// Split $date to $request_date and $request_time
|
140 |
-
if ( empty( $this->request_date ) || empty( $this->request_time ) ) {
|
141 |
-
$date = new DateTime( $this->date, wp_timezone() );
|
142 |
-
$this->request_date = $date->format( 'Y/m/d' );
|
143 |
-
$this->request_time = $date->format( 'h:i A' );
|
144 |
-
}
|
145 |
-
}
|
146 |
-
|
147 |
-
/**
|
148 |
-
* Format date
|
149 |
-
* @since 0.0.1
|
150 |
-
*/
|
151 |
-
public function format_date( $date ) {
|
152 |
-
$date = mysql2date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $date);
|
153 |
-
return apply_filters( 'get_the_date', $date );
|
154 |
-
}
|
155 |
-
|
156 |
-
/**
|
157 |
-
* Format a timestamp into a human-readable date
|
158 |
-
*
|
159 |
-
* @since 1.7.1
|
160 |
-
*/
|
161 |
-
public function format_timestamp( $timestamp ) {
|
162 |
-
$timestamp = $timestamp instanceof DateTime ? $timestamp->format('U') : $timestamp;
|
163 |
-
$time = date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $timestamp );
|
164 |
-
return $time;
|
165 |
-
}
|
166 |
-
|
167 |
-
/**
|
168 |
-
* Calculates the deposit required for a reservation, if any
|
169 |
-
*
|
170 |
-
* @since 2.1.0
|
171 |
-
*/
|
172 |
-
public function calculate_deposit() {
|
173 |
-
global $rtb_controller;
|
174 |
-
|
175 |
-
$deposit = $rtb_controller->settings->get_setting( 'rtb-deposit-amount' );
|
176 |
-
|
177 |
-
if ( $rtb_controller->settings->get_setting( 'rtb-deposit-applicable' ) == 'size_based' ) {
|
178 |
-
|
179 |
-
$deposit = $this->party < $rtb_controller->settings->get_setting( 'rtb-deposit-min-party-size' ) ? 0 : $deposit;
|
180 |
-
}
|
181 |
-
|
182 |
-
if ( $rtb_controller->settings->get_setting( 'rtb-deposit-applicable' ) == 'time_based' ) {
|
183 |
-
|
184 |
-
$deposit = empty( $this->is_time_based_deposit_applicable() ) ? 0 : $deposit;
|
185 |
-
}
|
186 |
-
|
187 |
-
if ( $rtb_controller->settings->get_setting( 'rtb-deposit-type' ) == 'guest' ) { $deposit = $deposit * $this->party; }
|
188 |
-
|
189 |
-
return $deposit;
|
190 |
-
}
|
191 |
-
|
192 |
-
|
193 |
-
/**
|
194 |
-
* Insert a new booking submission into the database
|
195 |
-
*
|
196 |
-
* Validates the data, adds it to the database and executes notifications
|
197 |
-
* @since 0.0.1
|
198 |
-
*/
|
199 |
-
public function insert_booking($by_admin = false) {
|
200 |
-
|
201 |
-
// Check if this request has already been processed. If multiple forms
|
202 |
-
// exist on the same page, this prevents a single submission from
|
203 |
-
// being added twice.
|
204 |
-
if ( $this->request_processed === true ) {
|
205 |
-
return true;
|
206 |
-
}
|
207 |
-
|
208 |
-
$this->request_processed = true;
|
209 |
-
|
210 |
-
if ( empty( $this->ID ) ) {
|
211 |
-
$action = 'insert';
|
212 |
-
} else {
|
213 |
-
$action = 'update';
|
214 |
-
}
|
215 |
-
|
216 |
-
$this->validate_submission($action, $by_admin);
|
217 |
-
if ( $this->is_valid_submission() === false ) {
|
218 |
-
return false;
|
219 |
-
}
|
220 |
-
|
221 |
-
if ( $this->insert_post_data() === false ) {
|
222 |
-
return false;
|
223 |
-
} else {
|
224 |
-
$this->request_inserted = true;
|
225 |
-
}
|
226 |
-
|
227 |
-
do_action( 'rtb_' . $action . '_booking', $this );
|
228 |
-
|
229 |
-
return true;
|
230 |
-
}
|
231 |
-
|
232 |
-
/**
|
233 |
-
* Validate submission data. Expects to find data in $_POST.
|
234 |
-
*
|
235 |
-
* ************************** NOTE **************************
|
236 |
-
* This function also create and assign all the required member variable with
|
237 |
-
* the acurate values which will be insreted in the DB. One special member,
|
238 |
-
* raw_input of type array holds the exact copy of $_POST
|
239 |
-
*
|
240 |
-
* Example:
|
241 |
-
* class a {
|
242 |
-
* public function a() {
|
243 |
-
* $this->name = 'John Doe';
|
244 |
-
* }
|
245 |
-
* public function b() {
|
246 |
-
* echo $this->name;
|
247 |
-
* }
|
248 |
-
* }
|
249 |
-
*
|
250 |
-
* $a = new a();
|
251 |
-
*
|
252 |
-
* var_dump($a);
|
253 |
-
* object(a)#1 (0) {
|
254 |
-
* }
|
255 |
-
*
|
256 |
-
* $a->a();
|
257 |
-
*
|
258 |
-
* var_dump($a);
|
259 |
-
* object(a)#1 (1) {
|
260 |
-
* ["name"]=>
|
261 |
-
* string(8) "John Doe"
|
262 |
-
* }
|
263 |
-
*
|
264 |
-
* $a->b();
|
265 |
-
* John Doe
|
266 |
-
*
|
267 |
-
* @since 0.0.1
|
268 |
-
*/
|
269 |
-
public function validate_submission($action = null, $by_admin = false) {
|
270 |
-
|
271 |
-
global $rtb_controller;
|
272 |
-
|
273 |
-
$this->validation_errors = array();
|
274 |
-
/**
|
275 |
-
* Raw, unprocessed value so that it can be used to preselect the form
|
276 |
-
* field values, eg. table and pass the value with the request. This way,
|
277 |
-
* hooked code doesn't have to check $_POST or $_GET for the data and can
|
278 |
-
* access everything posted from aw_input.
|
279 |
-
*
|
280 |
-
* Its name implies the requirement of sanitization explicitly
|
281 |
-
*/
|
282 |
-
$this->raw_input =& $_POST;
|
283 |
-
|
284 |
-
do_action( 'rtb_pre_validate_booking_submission', $this );
|
285 |
-
|
286 |
-
// Date
|
287 |
-
$date = empty( $_POST['rtb-date'] ) ? false : sanitize_text_field( $_POST['rtb-date'] );
|
288 |
-
if ( $date === false ) {
|
289 |
-
$this->validation_errors[] = array(
|
290 |
-
'field' => 'date',
|
291 |
-
'error_msg' => 'Booking request missing date',
|
292 |
-
'message' => __( 'Please enter the date you would like to book.', 'restaurant-reservations' ),
|
293 |
-
);
|
294 |
-
|
295 |
-
} else {
|
296 |
-
try {
|
297 |
-
$date = new DateTime( sanitize_text_field( $_POST['rtb-date'] ), wp_timezone() );
|
298 |
-
} catch ( Exception $e ) {
|
299 |
-
$this->validation_errors[] = array(
|
300 |
-
'field' => 'date',
|
301 |
-
'error_msg' => $e->getMessage(),
|
302 |
-
'message' => __( 'The date you entered is not valid. Please select from one of the dates in the calendar.', 'restaurant-reservations' ),
|
303 |
-
);
|
304 |
-
}
|
305 |
-
}
|
306 |
-
|
307 |
-
// Time
|
308 |
-
$time = empty( $_POST['rtb-time'] ) ? false : sanitize_text_field( $_POST['rtb-time'] );
|
309 |
-
if ( $time === false ) {
|
310 |
-
$this->validation_errors[] = array(
|
311 |
-
'field' => 'time',
|
312 |
-
'error_msg' => 'Booking request missing time',
|
313 |
-
'message' => __( 'Please enter the time you would like to book.', 'restaurant-reservations' ),
|
314 |
-
);
|
315 |
-
|
316 |
-
} else {
|
317 |
-
try {
|
318 |
-
$time = new DateTime( sanitize_text_field( $_POST['rtb-time'] ), wp_timezone() );
|
319 |
-
} catch ( Exception $e ) {
|
320 |
-
$this->validation_errors[] = array(
|
321 |
-
'field' => 'time',
|
322 |
-
'error_msg' => $e->getMessage(),
|
323 |
-
'message' => __( 'The time you entered is not valid. Please select from one of the times provided.', 'restaurant-reservations' ),
|
324 |
-
);
|
325 |
-
}
|
326 |
-
}
|
327 |
-
|
328 |
-
// Check against valid open dates/times
|
329 |
-
if ( is_object( $time ) && is_object( $date ) ) {
|
330 |
-
|
331 |
-
$request = new DateTime( $date->format( 'Y-m-d' ) . ' ' . $time->format( 'H:i:s' ), wp_timezone() );
|
332 |
-
$this->date_submission = new DateTime( 'now', wp_timezone() );
|
333 |
-
|
334 |
-
// Exempt Bookings Managers from the early and late bookings restrictions
|
335 |
-
if ( !current_user_can( 'manage_bookings' ) ) {
|
336 |
-
|
337 |
-
$early_bookings = $rtb_controller->settings->get_setting( 'early-bookings' );
|
338 |
-
if ( !empty( $early_bookings ) && is_numeric( $early_bookings ) ) {
|
339 |
-
$uppar_bound = ( new DateTime( 'now', wp_timezone() ) )->setTime( 23, 59 );
|
340 |
-
$uppar_bound->add( new DateInterval( "P{$early_bookings}D" ) );
|
341 |
-
|
342 |
-
if ( $request > $uppar_bound ) {
|
343 |
-
$this->validation_errors[] = array(
|
344 |
-
'field' => 'time',
|
345 |
-
'error_msg' => 'Booking request too far in the future',
|
346 |
-
'message' => sprintf( __( 'Sorry, bookings can not be made more than %s days in advance.', 'restaurant-reservations' ), $early_bookings ),
|
347 |
-
);
|
348 |
-
}
|
349 |
-
}
|
350 |
-
|
351 |
-
$late_bookings = $rtb_controller->settings->get_setting( 'late-bookings' );
|
352 |
-
if ( empty( $late_bookings ) ) {
|
353 |
-
if ( $request->format( 'U' ) < $this->date_submission->format( 'U' ) ) {
|
354 |
-
$this->validation_errors[] = array(
|
355 |
-
'field' => 'time',
|
356 |
-
'error_msg' => 'Booking request in the past',
|
357 |
-
'message' => __( 'Sorry, bookings can not be made in the past.', 'restaurant-reservations' ),
|
358 |
-
);
|
359 |
-
}
|
360 |
-
|
361 |
-
} elseif ( $late_bookings === 'same_day' ) {
|
362 |
-
if ( $request->format( 'Y-m-d' ) == $this->date_submission->format( 'Y-m-d' ) ) {
|
363 |
-
$this->validation_errors[] = array(
|
364 |
-
'field' => 'time',
|
365 |
-
'error_msg' => 'Booking request made on same day',
|
366 |
-
'message' => __( 'Sorry, bookings can not be made for the same day.', 'restaurant-reservations' ),
|
367 |
-
);
|
368 |
-
}
|
369 |
-
|
370 |
-
} elseif( is_numeric( $late_bookings ) ) {
|
371 |
-
$late_bookings_seconds = $late_bookings * 60; // Late bookings allowance in seconds
|
372 |
-
if ( $request->format( 'U' ) < ( $this->date_submission->format( 'U' ) + $late_bookings_seconds ) ) {
|
373 |
-
if ( $late_bookings >= 1440 ) {
|
374 |
-
$late_bookings_message = sprintf( __( 'Sorry, bookings must be made more than %s days in advance.', 'restaurant-reservations' ), $late_bookings / 1440 );
|
375 |
-
} elseif ( $late_bookings >= 60 ) {
|
376 |
-
$late_bookings_message = sprintf( __( 'Sorry, bookings must be made more than %s hours in advance.', 'restaurant-reservations' ), $late_bookings / 60 );
|
377 |
-
} else {
|
378 |
-
$late_bookings_message = sprintf( __( 'Sorry, bookings must be made more than %s minutes in advance.', 'restaurant-reservations' ), $late_bookings );
|
379 |
-
}
|
380 |
-
$this->validation_errors[] = array(
|
381 |
-
'field' => 'time',
|
382 |
-
'error_msg' => 'Booking request made too close to the reserved time',
|
383 |
-
'message' => $late_bookings_message,
|
384 |
-
);
|
385 |
-
}
|
386 |
-
}
|
387 |
-
}
|
388 |
-
|
389 |
-
// Check against scheduling exception rules
|
390 |
-
$exception_rules = $rtb_controller->settings->get_setting( 'schedule-closed' );
|
391 |
-
$exception_is_active = false;
|
392 |
-
if (
|
393 |
-
empty( $this->validation_errors )
|
394 |
-
&& !empty( $exception_rules )
|
395 |
-
&& !current_user_can( 'manage_bookings' )
|
396 |
-
) {
|
397 |
-
|
398 |
-
/**
|
399 |
-
* We are checking the booking againt exceptions which consists blacklist and whitelist rules
|
400 |
-
* - Any rule without time is a blacklist entry
|
401 |
-
* - Any rule with time is a modified white entry
|
402 |
-
* Thus consider the request as legit by default and terminate the loop when we hit first
|
403 |
-
* blacklisted rule which applies to the request
|
404 |
-
*
|
405 |
-
* $exception_is_active This prevent validation againt normal open rules
|
406 |
-
* $datetime_is_valid This is to throw error as soon as we encounter a blacklisted exception
|
407 |
-
*/
|
408 |
-
$exception_is_active = false;
|
409 |
-
$datetime_is_valid = true;
|
410 |
-
|
411 |
-
foreach( $exception_rules as $excp_rule ) {
|
412 |
-
|
413 |
-
if( array_key_exists( 'date_range', $excp_rule ) )
|
414 |
-
{
|
415 |
-
$start = ! empty( $excp_rule['date_range']['start'] )
|
416 |
-
? new DateTime( $excp_rule['date_range']['start'], wp_timezone() )
|
417 |
-
: new DateTime( 'now', wp_timezone() );
|
418 |
-
$start->setTime(0, 0);
|
419 |
-
|
420 |
-
$end = !empty( $excp_rule['date_range']['end'] )
|
421 |
-
? new DateTime( $excp_rule['date_range']['end'], wp_timezone() )
|
422 |
-
: ( new DateTime( 'now', wp_timezone() ) )->add( new DateInterval( 'P10Y' ) );
|
423 |
-
$end->setTime(23, 59, 58);
|
424 |
-
|
425 |
-
if( $start < $request && $request < $end ) {
|
426 |
-
$excp_rule_obj = clone $request;
|
427 |
-
}
|
428 |
-
else {
|
429 |
-
// Set anything to void this rule for following check
|
430 |
-
$excp_rule_obj = clone $request;
|
431 |
-
$excp_rule_obj->add( new DateInterval( 'P1Y' ) );
|
432 |
-
}
|
433 |
-
}
|
434 |
-
else {
|
435 |
-
$excp_rule_obj = ( new DateTime( $excp_rule['date'], wp_timezone() ) )->setTime(0, 0, 2);
|
436 |
-
}
|
437 |
-
|
438 |
-
if ( $excp_rule_obj->format( 'Y-m-d' ) == $request->format( 'Y-m-d' ) ) {
|
439 |
-
// This rule applies so far, thus consider this request under exception
|
440 |
-
// whielist or blacklist, yet to be determined
|
441 |
-
$exception_is_active = true;
|
442 |
-
|
443 |
-
// Closed all day
|
444 |
-
// Request denied, falls under blacklist, terminate loop
|
445 |
-
if ( empty( $excp_rule['time'] ) ) {
|
446 |
-
$datetime_is_valid = false;
|
447 |
-
break;
|
448 |
-
}
|
449 |
-
|
450 |
-
$excp_start_time = empty( $excp_rule['time']['start'] )
|
451 |
-
? $request
|
452 |
-
: new DateTime( $excp_rule_obj->format( 'Y-m-d' ) . ' ' . $excp_rule['time']['start'], wp_timezone() );
|
453 |
-
|
454 |
-
$excp_end_time = empty( $excp_rule['time']['end'] )
|
455 |
-
? $request
|
456 |
-
: new DateTime( $excp_rule_obj->format( 'Y-m-d' ) . ' ' . $excp_rule['time']['end'], wp_timezone() );
|
457 |
-
|
458 |
-
if (
|
459 |
-
$excp_start_time->format( 'U' )
|
460 |
-
<= $request->format( 'U' ) && $request->format( 'U' ) <=
|
461 |
-
$excp_end_time->format( 'U' )
|
462 |
-
) {
|
463 |
-
// If we reach here, means request is under modified whitelist rules
|
464 |
-
$datetime_is_valid = true;
|
465 |
-
break;
|
466 |
-
}
|
467 |
-
else {
|
468 |
-
// else this request falls in blacklisted area based on modified whitelist rules.
|
469 |
-
$datetime_is_valid = false;
|
470 |
-
}
|
471 |
-
}
|
472 |
-
}
|
473 |
-
|
474 |
-
if ( $exception_is_active && !$datetime_is_valid ) {
|
475 |
-
$this->validation_errors[] = array(
|
476 |
-
'field' => 'date',
|
477 |
-
'error_msg' => 'Booking request made on invalid date or time in an exception rule',
|
478 |
-
'message' => __( 'Sorry, no bookings are being accepted then.', 'restaurant-reservations' ),
|
479 |
-
);
|
480 |
-
}
|
481 |
-
}
|
482 |
-
|
483 |
-
// Check against weekly scheduling rules
|
484 |
-
$rules = $rtb_controller->settings->get_setting( 'schedule-open' );
|
485 |
-
|
486 |
-
// Order of conditions in if matters to prevent unnacessary warnings
|
487 |
-
if (
|
488 |
-
empty( $this->validation_errors )
|
489 |
-
&& !empty( $rules )
|
490 |
-
&& !current_user_can( 'manage_bookings' )
|
491 |
-
&& !$exception_is_active
|
492 |
-
) {
|
493 |
-
$request_weekday = strtolower( $request->format( 'l' ) );
|
494 |
-
$time_is_valid = null;
|
495 |
-
$day_is_valid = null;
|
496 |
-
foreach( $rules as $rule ) {
|
497 |
-
|
498 |
-
if ( !empty( $rule['weekdays'][ $request_weekday ] ) ) {
|
499 |
-
$day_is_valid = true;
|
500 |
-
|
501 |
-
if ( empty( $rule['time'] ) ) {
|
502 |
-
$time_is_valid = true; // Days with no time values are open all day
|
503 |
-
break;
|
504 |
-
}
|
505 |
-
|
506 |
-
$too_early = true;
|
507 |
-
$too_late = true;
|
508 |
-
|
509 |
-
// Too early
|
510 |
-
if ( !empty( $rule['time']['start'] ) ) {
|
511 |
-
$rule_start_time = new DateTime( $request->format( 'Y-m-d' ) . ' ' . $rule['time']['start'], wp_timezone() );
|
512 |
-
if ( $rule_start_time->format( 'U' ) <= $request->format( 'U' ) ) {
|
513 |
-
$too_early = false;
|
514 |
-
}
|
515 |
-
}
|
516 |
-
|
517 |
-
// Too late
|
518 |
-
if ( !empty( $rule['time']['end'] ) ) {
|
519 |
-
$rule_end_time = new DateTime( $request->format( 'Y-m-d' ) . ' ' . $rule['time']['end'], wp_timezone() );
|
520 |
-
if ( $rule_end_time->format( 'U' ) >= $request->format( 'U' ) ) {
|
521 |
-
$too_late = false;
|
522 |
-
}
|
523 |
-
}
|
524 |
-
|
525 |
-
// Valid time found
|
526 |
-
if ( $too_early === false && $too_late === false) {
|
527 |
-
$time_is_valid = true;
|
528 |
-
break;
|
529 |
-
}
|
530 |
-
}
|
531 |
-
}
|
532 |
-
|
533 |
-
if ( !$day_is_valid ) {
|
534 |
-
$this->validation_errors[] = array(
|
535 |
-
'field' => 'date',
|
536 |
-
'error_msg' => 'Booking request made on an invalid date',
|
537 |
-
'message' => __( 'Sorry, no bookings are being accepted on that date.', 'restaurant-reservations' ),
|
538 |
-
);
|
539 |
-
} elseif ( !$time_is_valid ) {
|
540 |
-
$this->validation_errors[] = array(
|
541 |
-
'field' => 'time',
|
542 |
-
'error_msg' => 'Booking request made at an invalid time',
|
543 |
-
'message' => __( 'Sorry, no bookings are being accepted at that time.', 'restaurant-reservations' ),
|
544 |
-
);
|
545 |
-
}
|
546 |
-
}
|
547 |
-
|
548 |
-
// Accept the date if it has passed validation
|
549 |
-
if ( empty( $this->validation_errors ) ) {
|
550 |
-
$this->date = $request->format( 'Y-m-d H:i:s' );
|
551 |
-
}
|
552 |
-
}
|
553 |
-
|
554 |
-
// Save requested date/time values in case they need to be
|
555 |
-
// printed in the form again
|
556 |
-
$this->request_date = empty( $_POST['rtb-date'] ) ? '' : sanitize_text_field( $_POST['rtb-date'] );
|
557 |
-
$this->request_time = empty( $_POST['rtb-time'] ) ? '' : sanitize_text_field( $_POST['rtb-time'] );
|
558 |
-
|
559 |
-
// Name
|
560 |
-
$this->name = empty( $_POST['rtb-name'] ) ? '' : wp_strip_all_tags( sanitize_text_field( $_POST['rtb-name'] ), true ); // @todo should I limit length?
|
561 |
-
if ( empty( $this->name ) ) {
|
562 |
-
$this->validation_errors[] = array(
|
563 |
-
'field' => 'name',
|
564 |
-
'post_variable' => $this->name,
|
565 |
-
'message' => __( 'Please enter a name for this booking.', 'restaurant-reservations' ),
|
566 |
-
);
|
567 |
-
}
|
568 |
-
|
569 |
-
// Party
|
570 |
-
$this->party = empty( $_POST['rtb-party'] ) ? '' : absint( $_POST['rtb-party'] );
|
571 |
-
if ( empty( $this->party ) ) {
|
572 |
-
$this->validation_errors[] = array(
|
573 |
-
'field' => 'party',
|
574 |
-
'post_variable' => $this->party,
|
575 |
-
'message' => __( 'Please let us know how many people will be in your party.', 'restaurant-reservations' ),
|
576 |
-
);
|
577 |
-
|
578 |
-
// Check party size
|
579 |
-
} else {
|
580 |
-
$party_size = $rtb_controller->settings->get_setting( 'party-size' );
|
581 |
-
if ( ! empty( $party_size ) && $party_size < $this->party ) {
|
582 |
-
$this->validation_errors[] = array(
|
583 |
-
'field' => 'party',
|
584 |
-
'post_variable' => $this->party,
|
585 |
-
'message' => sprintf( __( 'We only accept bookings for parties of up to %d people.', 'restaurant-reservations' ), $party_size ),
|
586 |
-
);
|
587 |
-
}
|
588 |
-
$party_size_min = $rtb_controller->settings->get_setting( 'party-size-min' );
|
589 |
-
if ( ! empty( $party_size_min ) && $party_size_min > $this->party ) {
|
590 |
-
$this->validation_errors[] = array(
|
591 |
-
'field' => 'party',
|
592 |
-
'post_variable' => $this->party,
|
593 |
-
'message' => sprintf( __( 'We only accept bookings for parties of more than %d people.', 'restaurant-reservations' ), $party_size_min ),
|
594 |
-
);
|
595 |
-
}
|
596 |
-
}
|
597 |
-
|
598 |
-
// Email
|
599 |
-
$this->email = empty( $_POST['rtb-email'] ) ? '' : sanitize_email( $_POST['rtb-email'] ); // @todo email validation? send notification back to form on bad email address.
|
600 |
-
if ( empty( $this->email ) ) {
|
601 |
-
$this->validation_errors[] = array(
|
602 |
-
'field' => 'email',
|
603 |
-
'post_variable' => $this->email,
|
604 |
-
'message' => __( 'Please enter an email address so we can confirm your booking.', 'restaurant-reservations' ),
|
605 |
-
);
|
606 |
-
} elseif ( !is_email( $this->email ) && apply_filters( 'rtb_require_valid_email', true ) ) {
|
607 |
-
$this->validation_errors[] = array(
|
608 |
-
'field' => 'email',
|
609 |
-
'post_variable' => $this->email,
|
610 |
-
'message' => __( 'Please enter a valid email address so we can confirm your booking.', 'restaurant-reservations' ),
|
611 |
-
);
|
612 |
-
}
|
613 |
-
|
614 |
-
// Phone
|
615 |
-
$this->phone = empty( $_POST['rtb-phone'] ) ? '' : sanitize_text_field( $_POST['rtb-phone'] );
|
616 |
-
$phone_required = $rtb_controller->settings->get_setting( 'require-phone' );
|
617 |
-
if ( $phone_required && empty( $this->phone ) ) {
|
618 |
-
$this->validation_errors[] = array(
|
619 |
-
'field' => 'phone',
|
620 |
-
'post_variable' => $this->phone,
|
621 |
-
'message' => __( 'Please provide a phone number so we can confirm your booking.', 'restaurant-reservations' ),
|
622 |
-
);
|
623 |
-
}
|
624 |
-
|
625 |
-
// Table
|
626 |
-
$table = empty( $_POST['rtb-table'] ) ? array() : explode( ',', sanitize_text_field( $_POST['rtb-table'] ) );
|
627 |
-
$this->table = is_array( $table ) ? array_map( 'sanitize_text_field', $table ) : array();
|
628 |
-
|
629 |
-
$table_required = $rtb_controller->settings->get_setting( 'require-table' );
|
630 |
-
if ( $table_required && empty( $this->table ) ) {
|
631 |
-
$this->validation_errors[] = array(
|
632 |
-
'field' => 'table',
|
633 |
-
'post_variable' => $this->table,
|
634 |
-
'message' => __( 'Please select a table for your booking.', 'restaurant-reservations' ),
|
635 |
-
);
|
636 |
-
}
|
637 |
-
|
638 |
-
// check whether there is a time conflict for a particular table
|
639 |
-
$valid_table = $this->table ? $this->is_valid_table() : true;
|
640 |
-
if ( ! $valid_table ) {
|
641 |
-
$this->validation_errors[] = array(
|
642 |
-
'field' => 'table',
|
643 |
-
'post_variable' => $this->table,
|
644 |
-
'message' => __( 'Please select a valid table for your booking.', 'restaurant-reservations' ),
|
645 |
-
);
|
646 |
-
}
|
647 |
-
|
648 |
-
// reCAPTCHA
|
649 |
-
if ( $rtb_controller->settings->get_setting( 'enable-captcha' ) && !is_admin() ) {
|
650 |
-
if ( ! isset($_POST['g-recaptcha-response']) ) {
|
651 |
-
$this->validation_errors[] = array(
|
652 |
-
'field' => 'recaptcha',
|
653 |
-
'error_msg' => 'No reCAPTCHA code',
|
654 |
-
'message' => __( 'Please fill out the reCAPTCHA box before submitting.', 'restaurant-reservations' ),
|
655 |
-
);
|
656 |
-
}
|
657 |
-
else {
|
658 |
-
$secret_key = $rtb_controller->settings->get_setting( 'captcha-secret-key' );
|
659 |
-
$captcha = $_POST['g-recaptcha-response'];
|
660 |
-
|
661 |
-
$url = 'https://www.google.com/recaptcha/api/siteverify?secret=' . urlencode($secret_key) . '&response=' . urlencode($captcha);
|
662 |
-
$json_response = file_get_contents( $url );
|
663 |
-
$response = json_decode( $json_response );
|
664 |
-
|
665 |
-
$reCaptcha_error = false;
|
666 |
-
if(json_last_error() != JSON_ERROR_NONE) {
|
667 |
-
$response = new stdClass();
|
668 |
-
$response->success = false;
|
669 |
-
$reCaptcha_error = true;
|
670 |
-
if(defined('WP_DEBUG') && WP_DEBUG) {
|
671 |
-
error_log('RTB reCAPTCHA error. Raw respose: '.print_r([$json_response], true));
|
672 |
-
}
|
673 |
-
}
|
674 |
-
|
675 |
-
if ( ! $response->success ) {
|
676 |
-
$message = __( 'Please fill out the reCAPTCHA box again and re-submit.', 'restaurant-reservations' );
|
677 |
-
if($reCaptcha_error) {
|
678 |
-
$message .= __( ' If you encounter reCAPTCHA error multiple times, please contact us.', 'restaurant-reservations' );
|
679 |
-
}
|
680 |
-
$this->validation_errors[] = array(
|
681 |
-
'field' => 'recaptcha',
|
682 |
-
'error_msg' => 'Invalid reCAPTCHA code',
|
683 |
-
'message' => $message,
|
684 |
-
);
|
685 |
-
}
|
686 |
-
}
|
687 |
-
}
|
688 |
-
|
689 |
-
// Message
|
690 |
-
$this->message = empty( $_POST['rtb-message'] ) ? '' : sanitize_text_field( nl2br( $_POST['rtb-message'] ) );
|
691 |
-
|
692 |
-
// Post Status (define a default post status if none passed)
|
693 |
-
$this->determine_status();
|
694 |
-
|
695 |
-
// Consent
|
696 |
-
$require_consent = $rtb_controller->settings->get_setting( 'require-consent' );
|
697 |
-
$consent_statement = $rtb_controller->settings->get_setting( 'consent-statement' );
|
698 |
-
if ( $require_consent && $consent_statement ) {
|
699 |
-
// Don't change consent status once initial consent has been collected
|
700 |
-
if ( empty( $this->consent_acquired ) ) {
|
701 |
-
$this->consent_acquired = !empty( $_POST['rtb-consent-statement'] );
|
702 |
-
}
|
703 |
-
}
|
704 |
-
|
705 |
-
// Check if any required fields are empty
|
706 |
-
$required_fields = $rtb_controller->settings->get_required_fields();
|
707 |
-
foreach( $required_fields as $slug => $field ) {
|
708 |
-
if ( !$this->field_has_error( $slug ) && $this->is_field_empty( $slug ) ) {
|
709 |
-
$this->validation_errors[] = array(
|
710 |
-
'field' => $slug,
|
711 |
-
'post_variable' => '',
|
712 |
-
'message' => __( 'Please complete this field to request a booking.', 'restaurant-reservations' ),
|
713 |
-
);
|
714 |
-
}
|
715 |
-
}
|
716 |
-
|
717 |
-
// Check if the email or IP is banned
|
718 |
-
if ( !current_user_can( 'manage_bookings' ) ) {
|
719 |
-
$ip = $_SERVER['REMOTE_ADDR'];
|
720 |
-
if ( !$this->is_valid_ip( $ip ) || !$this->is_valid_email( $this->email ) ) {
|
721 |
-
$this->validation_errors[] = array(
|
722 |
-
'field' => 'date',
|
723 |
-
'post_variable' => $ip,
|
724 |
-
'message' => __( 'Your booking has been rejected. Please call us if you would like to make a booking.', 'restaurant-reservations' ),
|
725 |
-
);
|
726 |
-
} elseif ( empty( $this->ip ) and ! $rtb_controller->settings->get_setting( 'disable-ip-capture' ) ) {
|
727 |
-
$this->ip = sanitize_text_field( $ip );
|
728 |
-
}
|
729 |
-
} elseif ( empty( $this->ip ) and ! $rtb_controller->settings->get_setting( 'disable-ip-capture' ) ) {
|
730 |
-
$this->ip = sanitize_text_field( $_SERVER['REMOTE_ADDR'] );
|
731 |
-
}
|
732 |
-
|
733 |
-
// Check to make sure that the maximum number of reservations has not already been made
|
734 |
-
if ( ! $this->is_under_max_reservations() ){
|
735 |
-
$this->validation_errors[] = array(
|
736 |
-
'field' => 'time',
|
737 |
-
'error_msg' => 'maximum reservations exceeded',
|
738 |
-
'message' => __( 'The maximum number of reservations for that timeslot has been reached. Please select a different timeslot.', 'restaurant-reservations' ),
|
739 |
-
);
|
740 |
-
}
|
741 |
-
|
742 |
-
// Check to make sure that the maximum number of seats has not already been made
|
743 |
-
if ( ! $this->is_under_max_seats() ){
|
744 |
-
$this->validation_errors[] = array(
|
745 |
-
'field' => 'time',
|
746 |
-
'error_msg' => 'maximum seats exceeded',
|
747 |
-
'message' => __( 'With your party, the maximum number of seats for that timeslot would be exceeded. Please select a different timeslot or reduce your party size.', 'restaurant-reservations' ),
|
748 |
-
);
|
749 |
-
}
|
750 |
-
|
751 |
-
// Check if there is a booking already made with the exact same information, to prevent double bookings on refresh
|
752 |
-
if ( (!$by_admin || $by_admin && $action !== 'update') && $this->is_duplicate_booking() ) {
|
753 |
-
$this->validation_errors[] = array(
|
754 |
-
'field' => 'date',
|
755 |
-
'error_msg' => 'duplicate booking',
|
756 |
-
'message' => __( 'Your booking and personal information exactly matches another booking. If this was not caused by refreshing the page, please call us to make a booking.', 'restaurant-reservations' ),
|
757 |
-
);
|
758 |
-
}
|
759 |
-
|
760 |
-
do_action( 'rtb_validate_booking_submission', $this );
|
761 |
-
|
762 |
-
}
|
763 |
-
|
764 |
-
/**
|
765 |
-
* Check if submission is valid
|
766 |
-
*
|
767 |
-
* @since 0.0.1
|
768 |
-
*/
|
769 |
-
public function is_valid_submission() {
|
770 |
-
|
771 |
-
if ( !count( $this->validation_errors ) ) {
|
772 |
-
return true;
|
773 |
-
}
|
774 |
-
|
775 |
-
return false;
|
776 |
-
}
|
777 |
-
|
778 |
-
/**
|
779 |
-
* Check if a field already has an error attached to it
|
780 |
-
*
|
781 |
-
* @field string Field slug
|
782 |
-
* @since 1.3
|
783 |
-
*/
|
784 |
-
public function field_has_error( $field_slug ) {
|
785 |
-
|
786 |
-
foreach( $this->validation_errors as $error ) {
|
787 |
-
if ( $error['field'] == $field_slug ) {
|
788 |
-
return true;
|
789 |
-
}
|
790 |
-
}
|
791 |
-
|
792 |
-
return false;
|
793 |
-
}
|
794 |
-
|
795 |
-
/**
|
796 |
-
* Check if a field is missing
|
797 |
-
*
|
798 |
-
* Checks for empty strings and arrays, but accepts '0'
|
799 |
-
* @since 0.1
|
800 |
-
*/
|
801 |
-
public function is_field_empty( $slug ) {
|
802 |
-
|
803 |
-
$field_key = 'rtb-' . $slug;
|
804 |
-
|
805 |
-
if (
|
806 |
-
! isset( $_POST[ $field_key ] )
|
807 |
-
|| ( is_string( $_POST[ $field_key ] ) && trim( $_POST[ $field_key ] ) == '' )
|
808 |
-
|| ( is_array( $_POST[ $field_key ] ) && empty( $_POST[ $field_key ] ) )
|
809 |
-
)
|
810 |
-
{
|
811 |
-
return true;
|
812 |
-
}
|
813 |
-
|
814 |
-
return false;
|
815 |
-
}
|
816 |
-
|
817 |
-
/**
|
818 |
-
* Check if an IP address has been banned
|
819 |
-
*
|
820 |
-
* @param string $ip
|
821 |
-
* @return bool
|
822 |
-
* @since 1.7
|
823 |
-
*/
|
824 |
-
public function is_valid_ip( $ip = null ) {
|
825 |
-
|
826 |
-
if ( is_null( $ip ) ) {
|
827 |
-
$ip = isset( $this->ip ) ? $this->ip : null;
|
828 |
-
if ( is_null( $ip ) ) {
|
829 |
-
return false;
|
830 |
-
}
|
831 |
-
}
|
832 |
-
|
833 |
-
global $rtb_controller;
|
834 |
-
|
835 |
-
$banned_ips = array_filter( explode( "\n", $rtb_controller->settings->get_setting( 'ban-ips' ) ) );
|
836 |
-
|
837 |
-
foreach( $banned_ips as $banned_ip ) {
|
838 |
-
if ( $ip == trim( $banned_ip ) ) {
|
839 |
-
return false;
|
840 |
-
}
|
841 |
-
}
|
842 |
-
|
843 |
-
return true;
|
844 |
-
}
|
845 |
-
|
846 |
-
/**
|
847 |
-
* Check if an email address has been banned
|
848 |
-
*
|
849 |
-
* @param string $email
|
850 |
-
* @return bool
|
851 |
-
* @since 1.7
|
852 |
-
*/
|
853 |
-
public function is_valid_email( $email = null ) {
|
854 |
-
|
855 |
-
if ( is_null( $email ) ) {
|
856 |
-
$email = isset( $this->email ) ? $this->email : null;
|
857 |
-
if ( is_null( $email ) ) {
|
858 |
-
return false;
|
859 |
-
}
|
860 |
-
}
|
861 |
-
|
862 |
-
global $rtb_controller;
|
863 |
-
|
864 |
-
$banned_emails = array_filter( explode( "\n", $rtb_controller->settings->get_setting( 'ban-emails' ) ) );
|
865 |
-
|
866 |
-
foreach( $banned_emails as $banned_email ) {
|
867 |
-
if ( $email == trim( $banned_email ) ) {
|
868 |
-
return false;
|
869 |
-
}
|
870 |
-
}
|
871 |
-
|
872 |
-
return true;
|
873 |
-
}
|
874 |
-
|
875 |
-
/**
|
876 |
-
* Check if a table(s) is valid (not already taken during a specific timeslot)
|
877 |
-
*
|
878 |
-
* @return bool
|
879 |
-
* @since 2.1.7
|
880 |
-
*/
|
881 |
-
public function is_valid_table() {
|
882 |
-
global $rtb_controller;
|
883 |
-
|
884 |
-
if ( ! $this->table or ! is_array( $this->table ) ) { return false; }
|
885 |
-
|
886 |
-
$valid_tables = rtb_get_valid_tables( $this->date );
|
887 |
-
|
888 |
-
if ( isset( $this->ID ) ) {
|
889 |
-
|
890 |
-
$post_meta = get_post_meta( $this->ID, 'rtb', true );
|
891 |
-
|
892 |
-
if ( isset( $post_meta['table'] ) and is_array( $post_meta['table'] ) ) { $valid_tables = array_merge( $valid_tables, $post_meta['table'] ); }
|
893 |
-
}
|
894 |
-
|
895 |
-
return $this->table == array_intersect( $this->table, $valid_tables );
|
896 |
-
}
|
897 |
-
|
898 |
-
/**
|
899 |
-
* Check if this booking would put the restaurant over the maximum, if set
|
900 |
-
*
|
901 |
-
* @return bool
|
902 |
-
* @since 2.1.20
|
903 |
-
*/
|
904 |
-
public function is_under_max_reservations() {
|
905 |
-
global $rtb_controller;
|
906 |
-
|
907 |
-
$location = ( ! empty( $this->location ) and term_exists( $this->location ) ) ? get_term( $this->location ) : false;
|
908 |
-
$location_slug = ! empty( $location ) ? $location->slug : false;
|
909 |
-
|
910 |
-
$max_reservations_enabled = $rtb_controller->settings->get_setting( 'rtb-enable-max-tables', $location_slug );
|
911 |
-
|
912 |
-
if ( ! $max_reservations_enabled ) { return true; }
|
913 |
-
|
914 |
-
$max_reservations = (int) $rtb_controller->settings->get_setting( 'rtb-max-tables-count', $location_slug );
|
915 |
-
|
916 |
-
if ( $max_reservations == 'undefined' or ! $max_reservations ) { return true; }
|
917 |
-
|
918 |
-
$dining_block_seconds = (int) $rtb_controller->settings->get_setting( 'rtb-dining-block-length' ) * 60 - 1; // Take 1 second off, to avoid bookings that start or end exactly at the beginning of a booking block
|
919 |
-
|
920 |
-
$tmp = ( new DateTime( $this->date, wp_timezone() ) )->format( 'U' );
|
921 |
-
$after_time = $tmp - $dining_block_seconds;
|
922 |
-
$before_time = $tmp + $dining_block_seconds;
|
923 |
-
|
924 |
-
$args = array(
|
925 |
-
'posts_per_page' => -1,
|
926 |
-
'post_status' => array( 'pending', 'payment_pending', 'confirmed', 'arrived' ),
|
927 |
-
'date_query' => array(
|
928 |
-
'before' => date( 'c', $before_time ),
|
929 |
-
'after' => date( 'c', $after_time )
|
930 |
-
)
|
931 |
-
);
|
932 |
-
|
933 |
-
// If there are multiple locations, a location is selected, and
|
934 |
-
// max seats has been enabled for this specific location
|
935 |
-
if ( ! empty( $location_slug ) and $rtb_controller->settings->is_location_setting_enabled( 'rtb-max-tables-count', $location_slug ) ) {
|
936 |
-
|
937 |
-
$tax_query = array(
|
938 |
-
array(
|
939 |
-
'taxonomy' => $rtb_controller->locations->location_taxonomy,
|
940 |
-
'field' => 'term_id',
|
941 |
-
'terms' => $location->term_id
|
942 |
-
)
|
943 |
-
);
|
944 |
-
|
945 |
-
$args['tax_query'] = $tax_query;
|
946 |
-
}
|
947 |
-
|
948 |
-
require_once( RTB_PLUGIN_DIR . '/includes/Query.class.php' );
|
949 |
-
$query = new rtbQuery( $args );
|
950 |
-
$query->prepare_args();
|
951 |
-
|
952 |
-
$tmzn = wp_timezone();
|
953 |
-
|
954 |
-
$times = array();
|
955 |
-
foreach ( $query->get_bookings() as $booking ) {
|
956 |
-
|
957 |
-
if ( isset( $this->ID ) and $booking->ID == $this->ID ) { continue; }
|
958 |
-
|
959 |
-
$times[] = ( new DateTime( $booking->date, $tmzn ) )->format( 'U' );
|
960 |
-
}
|
961 |
-
|
962 |
-
sort( $times );
|
963 |
-
|
964 |
-
$current_times = array();
|
965 |
-
foreach ( $times as $time ) {
|
966 |
-
|
967 |
-
$current_times[] = $time;
|
968 |
-
|
969 |
-
if ( reset( $current_times ) < ( $time - $dining_block_seconds ) ) { array_shift( $current_times ); }
|
970 |
-
|
971 |
-
// Check if we go above the max confirmation number
|
972 |
-
if ( sizeOf( $current_times ) + 1 > $max_reservations ) { return false; }
|
973 |
-
}
|
974 |
-
|
975 |
-
return true;
|
976 |
-
}
|
977 |
-
|
978 |
-
/**
|
979 |
-
* Check if this booking would put the restaurant over the maximum number of people, if set
|
980 |
-
*
|
981 |
-
* @return bool
|
982 |
-
* @since 2.1.20
|
983 |
-
*/
|
984 |
-
public function is_under_max_seats() {
|
985 |
-
global $rtb_controller;
|
986 |
-
|
987 |
-
$location = ( ! empty( $this->location ) and term_exists( $this->location ) ) ? get_term( $this->location ) : false;
|
988 |
-
$location_slug = ! empty( $location ) ? $location->slug : false;
|
989 |
-
|
990 |
-
$max_reservations_enabled = $rtb_controller->settings->get_setting( 'rtb-enable-max-tables', $location_slug );
|
991 |
-
|
992 |
-
if ( ! $max_reservations_enabled ) { return true; }
|
993 |
-
|
994 |
-
$max_seats = (int) $rtb_controller->settings->get_setting( 'rtb-max-people-count', $location_slug );
|
995 |
-
|
996 |
-
if ( $max_seats == 'undefined' or ! $max_seats ) { return true; }
|
997 |
-
if ( $this->party > $max_seats ) { return false; }
|
998 |
-
|
999 |
-
$dining_block_seconds = (int) $rtb_controller->settings->get_setting( 'rtb-dining-block-length' ) * 60 - 1; // Take 1 second off, to avoid bookings that start or end exactly at the beginning of a booking block
|
1000 |
-
|
1001 |
-
$tmp = ( new DateTime( $this->date, wp_timezone() ) )->format( 'U' );
|
1002 |
-
$after_time = $tmp - $dining_block_seconds;
|
1003 |
-
$before_time = $tmp + $dining_block_seconds;
|
1004 |
-
|
1005 |
-
$args = array(
|
1006 |
-
'posts_per_page' => -1,
|
1007 |
-
'post_status' => array( 'pending', 'payment_pending', 'confirmed', 'arrived' ),
|
1008 |
-
'date_query' => array(
|
1009 |
-
'before' => date( 'c', $before_time ),
|
1010 |
-
'after' => date( 'c', $after_time )
|
1011 |
-
)
|
1012 |
-
);
|
1013 |
-
|
1014 |
-
// If there are multiple locations, a location is selected, and
|
1015 |
-
// max seats has been enabled for this specific location
|
1016 |
-
if ( ! empty( $location_slug ) and $rtb_controller->settings->is_location_setting_enabled( 'rtb-max-people-count', $location_slug ) ) {
|
1017 |
-
|
1018 |
-
$tax_query = array(
|
1019 |
-
array(
|
1020 |
-
'taxonomy' => $rtb_controller->locations->location_taxonomy,
|
1021 |
-
'field' => 'term_id',
|
1022 |
-
'terms' => $location->term_id
|
1023 |
-
)
|
1024 |
-
);
|
1025 |
-
|
1026 |
-
$args['tax_query'] = $tax_query;
|
1027 |
-
}
|
1028 |
-
|
1029 |
-
require_once( RTB_PLUGIN_DIR . '/includes/Query.class.php' );
|
1030 |
-
$query = new rtbQuery( $args );
|
1031 |
-
|
1032 |
-
$tmzn = wp_timezone();
|
1033 |
-
$times = array();
|
1034 |
-
foreach ( $query->get_bookings() as $booking ) {
|
1035 |
-
|
1036 |
-
if ( isset( $this->ID ) and $booking->ID == $this->ID ) { continue; }
|
1037 |
-
|
1038 |
-
$booking_time = (new DateTime( $booking->date, $tmzn ) )->format( 'U' );
|
1039 |
-
if ( isset( $times[$booking_time] ) ) { $times[$booking_time] += intval( $booking->party ); }
|
1040 |
-
else { $times[$booking_time] = (int) $booking->party; }
|
1041 |
-
}
|
1042 |
-
|
1043 |
-
ksort( $times );
|
1044 |
-
|
1045 |
-
$current_seats = array();
|
1046 |
-
foreach ( $times as $time => $seats ) {
|
1047 |
-
|
1048 |
-
$current_seats[$time] = $seats;
|
1049 |
-
|
1050 |
-
reset( $current_seats );
|
1051 |
-
|
1052 |
-
if ( key ( $current_seats ) < $time - $dining_block_seconds ) { array_shift( $current_seats ); }
|
1053 |
-
|
1054 |
-
// Check if adding the current party puts us above the max confirmation number
|
1055 |
-
if ( array_sum( $current_seats ) + $this->party > $max_seats ) { return false; }
|
1056 |
-
}
|
1057 |
-
|
1058 |
-
return true;
|
1059 |
-
|
1060 |
-
}
|
1061 |
-
|
1062 |
-
/**
|
1063 |
-
* Check if the information in a booking exactly matches another booking
|
1064 |
-
*
|
1065 |
-
* @return bool
|
1066 |
-
* @since 2.1.20
|
1067 |
-
*/
|
1068 |
-
public function is_duplicate_booking() {
|
1069 |
-
global $wpdb, $rtb_controller;
|
1070 |
-
|
1071 |
-
if( 0 < count($this->validation_errors) ) {
|
1072 |
-
/**
|
1073 |
-
* Do not run this check if there is an error already
|
1074 |
-
* There could abe a moment when someminfo could be missing, which is required
|
1075 |
-
* for this qurey to function.
|
1076 |
-
*/
|
1077 |
-
return null;
|
1078 |
-
}
|
1079 |
-
|
1080 |
-
$valid_status = ['confirmed', 'pending'];
|
1081 |
-
|
1082 |
-
// This is an intermediate status when payment is pending
|
1083 |
-
if ( $rtb_controller->settings->get_setting( 'require-deposit' ) ) {
|
1084 |
-
$valid_status = array_merge($valid_status, ['payment_pending']);
|
1085 |
-
}
|
1086 |
-
|
1087 |
-
$args = array_merge(
|
1088 |
-
array(
|
1089 |
-
RTB_BOOKING_POST_TYPE,
|
1090 |
-
$this->date,
|
1091 |
-
$this->name
|
1092 |
-
),
|
1093 |
-
$valid_status
|
1094 |
-
);
|
1095 |
-
|
1096 |
-
$status_placeholder = implode( ',', array_fill( 0, count( $valid_status ), '%s' ) );
|
1097 |
-
|
1098 |
-
$sql = "SELECT ID FROM {$wpdb->posts} WHERE post_type=%s AND post_date=%s AND post_title=%s AND post_status IN ({$status_placeholder})";
|
1099 |
-
|
1100 |
-
if ( isset( $this->ID ) ) {
|
1101 |
-
$sql .= ' AND ID!=%d';
|
1102 |
-
$args[] = $this->ID;
|
1103 |
-
}
|
1104 |
-
|
1105 |
-
$booking_result = $wpdb->get_row( $wpdb->prepare( $sql, $args ) );
|
1106 |
-
|
1107 |
-
if ( $booking_result ) {
|
1108 |
-
$meta = get_post_meta( $booking_result->ID, 'rtb', true );
|
1109 |
-
$meta = is_array( $meta ) ? $meta : array();
|
1110 |
-
|
1111 |
-
if ( $this->party == $meta['party'] and $this->email == $meta['email'] and $this->phone == $meta['phone'] ) {
|
1112 |
-
|
1113 |
-
return true;
|
1114 |
-
}
|
1115 |
-
}
|
1116 |
-
|
1117 |
-
return false;
|
1118 |
-
}
|
1119 |
-
|
1120 |
-
/**
|
1121 |
-
* Should we ask for deposit based on required minimum party size?
|
1122 |
-
* @param string $value [description]
|
1123 |
-
*/
|
1124 |
-
public function is_size_based_deposit_applicable() {
|
1125 |
-
global $rtb_controller;
|
1126 |
-
|
1127 |
-
if ($this->party < $rtb_controller->settings->get_setting( 'rtb-deposit-min-party-size' ) ) {
|
1128 |
-
return false;
|
1129 |
-
}
|
1130 |
-
|
1131 |
-
return true;
|
1132 |
-
}
|
1133 |
-
|
1134 |
-
/**
|
1135 |
-
* Shoudl we ask for deposit or not based on the given schedule?
|
1136 |
-
*
|
1137 |
-
* @since 2.0.0
|
1138 |
-
*/
|
1139 |
-
public function is_time_based_deposit_applicable() {
|
1140 |
-
global $rtb_controller;
|
1141 |
-
|
1142 |
-
$deposit_applicable = is_array( $rtb_controller->settings->get_setting( 'rtb-deposit-schedule' ) )
|
1143 |
-
? $rtb_controller->settings->get_setting( 'rtb-deposit-schedule' )
|
1144 |
-
: array();
|
1145 |
-
|
1146 |
-
// Get any rules which apply to this weekday
|
1147 |
-
if ( $deposit_applicable != 'undefined' ) {
|
1148 |
-
|
1149 |
-
$tmzn = wp_timezone();
|
1150 |
-
$date_object = new DateTime( $this->date, $tmzn );
|
1151 |
-
|
1152 |
-
$time = $date_object->format( 'U' );
|
1153 |
-
|
1154 |
-
$day_of_week = strtolower( $date_object->format( 'l' ) );
|
1155 |
-
|
1156 |
-
foreach ( $deposit_applicable as $applicable ) {
|
1157 |
-
|
1158 |
-
if ( $applicable['weekdays'] !== 'undefined' ) {
|
1159 |
-
|
1160 |
-
foreach ( $applicable['weekdays'] as $weekday => $value ) {
|
1161 |
-
|
1162 |
-
if ( $weekday == $day_of_week ) {
|
1163 |
-
|
1164 |
-
// applicable all day
|
1165 |
-
if ( !isset( $applicable['time'] ) || $applicable['time'] == 'undefined' ) {
|
1166 |
-
|
1167 |
-
return true;
|
1168 |
-
}
|
1169 |
-
|
1170 |
-
if ( $applicable['time']['start'] !== 'undefined' ) {
|
1171 |
-
|
1172 |
-
$applicable_start_time = ( new DateTime( $date_object->format( 'Y-m-d' ) . ' ' . $applicable['time']['start'], $tmzn ) )->format( 'U' );
|
1173 |
-
}
|
1174 |
-
else {
|
1175 |
-
|
1176 |
-
$applicable_start_time = ( new DateTime( $date_object->format( 'Y-m-d' ), $tmzn ) )->format( 'U' );
|
1177 |
-
}
|
1178 |
-
|
1179 |
-
if ( $applicable['time']['end'] !== 'undefined' ) {
|
1180 |
-
|
1181 |
-
$applicable_end_time = ( new DateTime( $date_object->format( 'Y-m-d' ) . ' ' . $applicable['time']['end'], $tmzn ) )->format( 'U' );
|
1182 |
-
}
|
1183 |
-
else {
|
1184 |
-
// End of the day
|
1185 |
-
$applicable_end_time = ( new DateTime( $date_object->format( 'Y-m-d' ) . ' 23:59:59', $tmzn ) )->format( 'U' );
|
1186 |
-
}
|
1187 |
-
|
1188 |
-
if ( $time > $applicable_start_time and $time < $applicable_end_time ) {
|
1189 |
-
|
1190 |
-
return true;
|
1191 |
-
}
|
1192 |
-
}
|
1193 |
-
}
|
1194 |
-
}
|
1195 |
-
}
|
1196 |
-
}
|
1197 |
-
|
1198 |
-
return false;
|
1199 |
-
}
|
1200 |
-
|
1201 |
-
/**
|
1202 |
-
* Check whether the number of reservations occurring at the same time is below the threshold
|
1203 |
-
* where reservations get automatically confirmed
|
1204 |
-
*
|
1205 |
-
* @since 2.0.0
|
1206 |
-
*/
|
1207 |
-
public function under_max_confirm_reservations() {
|
1208 |
-
global $rtb_controller;
|
1209 |
-
|
1210 |
-
$max_reservations = (int) $rtb_controller->settings->get_setting( 'auto-confirm-max-reservations' );
|
1211 |
-
|
1212 |
-
if ( $max_reservations == 'undefined' or $max_reservations <= 1 ) { return false; }
|
1213 |
-
|
1214 |
-
$dining_block_seconds = (int) $rtb_controller->settings->get_setting( 'rtb-dining-block-length' ) * 60 - 1; // Take 1 second off, to avoid bookings that start or end exactly at the beginning of a booking block
|
1215 |
-
|
1216 |
-
$tmp = (new DateTime( $this->date, wp_timezone() ) )->format( 'U' );
|
1217 |
-
$after_time = $tmp - $dining_block_seconds;
|
1218 |
-
$before_time = $tmp + $dining_block_seconds;
|
1219 |
-
|
1220 |
-
$args = array(
|
1221 |
-
'posts_per_page' => -1,
|
1222 |
-
'post_status' => ['confirmed', 'arrived'],
|
1223 |
-
'date_query' => array(
|
1224 |
-
'before' => date( 'c', $before_time ),
|
1225 |
-
'after' => date( 'c', $after_time )
|
1226 |
-
)
|
1227 |
-
);
|
1228 |
-
|
1229 |
-
require_once( RTB_PLUGIN_DIR . '/includes/Query.class.php' );
|
1230 |
-
$query = new rtbQuery( $args );
|
1231 |
-
$query->prepare_args();
|
1232 |
-
|
1233 |
-
$tmzn = wp_timezone();
|
1234 |
-
$times = array();
|
1235 |
-
foreach ( $query->get_bookings() as $booking ) {
|
1236 |
-
$times[] = (new DateTime( $booking->date, $tmzn ) )->format( "U" );
|
1237 |
-
}
|
1238 |
-
|
1239 |
-
sort( $times );
|
1240 |
-
|
1241 |
-
$auto_confirm = true;
|
1242 |
-
$current_times = array();
|
1243 |
-
foreach ( $times as $time ) {
|
1244 |
-
$current_times[] = $time;
|
1245 |
-
|
1246 |
-
if ( reset( $current_times ) < ($time - $dining_block_seconds) ) { array_shift( $current_times ); }
|
1247 |
-
|
1248 |
-
// Check if we've reached 1 below the max confirmation number, since adding the current booking will put us at the threshold
|
1249 |
-
if ( sizeOf( $current_times ) + 1 >= $max_reservations ) { $auto_confirm = false; break; }
|
1250 |
-
}
|
1251 |
-
|
1252 |
-
return $auto_confirm;
|
1253 |
-
}
|
1254 |
-
|
1255 |
-
/**
|
1256 |
-
* Check whether the number of seats occurring at the same time is below the threshold
|
1257 |
-
* where reservations get automatically confirmed
|
1258 |
-
*
|
1259 |
-
* @since 2.0.0
|
1260 |
-
*/
|
1261 |
-
public function under_max_confirm_seats() {
|
1262 |
-
global $rtb_controller;
|
1263 |
-
|
1264 |
-
$max_seats = (int) $rtb_controller->settings->get_setting( 'auto-confirm-max-seats' );
|
1265 |
-
|
1266 |
-
if ( $max_seats == 'undefined' or $max_seats < 2 or $this->party >= $max_seats ) { return false; }
|
1267 |
-
|
1268 |
-
$dining_block_seconds = (int) $rtb_controller->settings->get_setting( 'rtb-dining-block-length' ) * 60 - 1; // Take 1 second off, to avoid bookings that start or end exactly at the beginning of a booking block
|
1269 |
-
|
1270 |
-
$tmp = (new DateTime( $this->date, wp_timezone() ) )->format( 'U' );
|
1271 |
-
$after_time = $tmp - $dining_block_seconds;
|
1272 |
-
$before_time = $tmp + $dining_block_seconds;
|
1273 |
-
|
1274 |
-
$args = array(
|
1275 |
-
'posts_per_page' => -1,
|
1276 |
-
'post_status' => ['confirmed', 'arrived'],
|
1277 |
-
'date_query' => array(
|
1278 |
-
'before' => date( 'c', $before_time ),
|
1279 |
-
'after' => date( 'c', $after_time )
|
1280 |
-
)
|
1281 |
-
);
|
1282 |
-
|
1283 |
-
require_once( RTB_PLUGIN_DIR . '/includes/Query.class.php' );
|
1284 |
-
$query = new rtbQuery( $args );
|
1285 |
-
|
1286 |
-
$tmzn = wp_timezone();
|
1287 |
-
$times = array();
|
1288 |
-
foreach ( $query->get_bookings() as $booking ) {
|
1289 |
-
$booking_time = ( new DateTime( $booking->date, $tmzn ) )->format( 'U' );
|
1290 |
-
if ( isset( $times[$booking_time] ) ) { $times[$booking_time] += $booking->party; }
|
1291 |
-
else { $times[$booking_time] = $booking->party; }
|
1292 |
-
}
|
1293 |
-
|
1294 |
-
ksort( $times );
|
1295 |
-
|
1296 |
-
$auto_confirm = true;
|
1297 |
-
$current_seats = array();
|
1298 |
-
foreach ( $times as $time => $seats ) {
|
1299 |
-
$current_seats[$time] = $seats;
|
1300 |
-
|
1301 |
-
reset( $current_seats );
|
1302 |
-
|
1303 |
-
if ( key ( $current_seats ) < $time - $dining_block_seconds ) { array_shift( $current_seats ); }
|
1304 |
-
|
1305 |
-
// Check if adding the current party puts us at or above the max confirmation number
|
1306 |
-
if ( array_sum( $current_seats ) + $this->party >= $max_seats ) { $auto_confirm = false; break; }
|
1307 |
-
}
|
1308 |
-
|
1309 |
-
return $auto_confirm;
|
1310 |
-
}
|
1311 |
-
|
1312 |
-
/**
|
1313 |
-
* Determine what status a booking should have
|
1314 |
-
*
|
1315 |
-
* @since 2.1.0
|
1316 |
-
*/
|
1317 |
-
public function determine_status( $payment_made = false ) {
|
1318 |
-
global $rtb_controller;
|
1319 |
-
|
1320 |
-
if ( !empty( $_POST['rtb-post-status'] ) && array_key_exists( $_POST['rtb-post-status'], $rtb_controller->cpts->booking_statuses ) ) {
|
1321 |
-
$post_status = sanitize_text_field( $_POST['rtb-post-status'] );
|
1322 |
-
} elseif (
|
1323 |
-
(
|
1324 |
-
$rtb_controller->settings->get_setting( 'require-deposit' )
|
1325 |
-
&& ! $payment_made
|
1326 |
-
&& ! $this->is_conditional_deposit_enabled()
|
1327 |
-
)
|
1328 |
-
||
|
1329 |
-
(
|
1330 |
-
$rtb_controller->settings->get_setting( 'require-deposit' )
|
1331 |
-
&& ! $payment_made
|
1332 |
-
&& $this->is_conditional_deposit_enabled()
|
1333 |
-
&&
|
1334 |
-
(
|
1335 |
-
( $this->is_time_based_deposit_enabled() && $this->is_time_based_deposit_applicable() )
|
1336 |
-
||
|
1337 |
-
( $this->is_size_based_deposit_enabled() && $this->is_size_based_deposit_applicable() )
|
1338 |
-
)
|
1339 |
-
)
|
1340 |
-
) {
|
1341 |
-
$post_status = 'payment_pending';
|
1342 |
-
} elseif ( $this->party < $rtb_controller->settings->get_setting( 'auto-confirm-max-party-size' ) ) {
|
1343 |
-
$post_status = 'confirmed';
|
1344 |
-
} elseif ($rtb_controller->settings->get_setting( 'auto-confirm-max-reservations' ) and $this->under_max_confirm_reservations() ) {
|
1345 |
-
$post_status = 'confirmed';
|
1346 |
-
} elseif ( $rtb_controller->settings->get_setting( 'auto-confirm-max-seats' ) and $this->under_max_confirm_seats() ) {
|
1347 |
-
$post_status = 'confirmed';
|
1348 |
-
} else {
|
1349 |
-
$post_status = 'pending';
|
1350 |
-
}
|
1351 |
-
|
1352 |
-
$this->post_status = apply_filters( 'rtb_determine_booking_status', $post_status, $this );
|
1353 |
-
}
|
1354 |
-
|
1355 |
-
/**
|
1356 |
-
* Add a log entry to the booking
|
1357 |
-
*
|
1358 |
-
* @since 1.3.1
|
1359 |
-
*/
|
1360 |
-
public function add_log( $type, $title, $message = '', $datetime = null ) {
|
1361 |
-
|
1362 |
-
if ( empty( $datetime ) ) {
|
1363 |
-
$datetime = date( 'Y-m-d H:i:s');
|
1364 |
-
}
|
1365 |
-
|
1366 |
-
if ( empty( $this->logs ) ) {
|
1367 |
-
$this->logs = array();
|
1368 |
-
}
|
1369 |
-
|
1370 |
-
array_push( $this->logs, array( $type, $title, $message, $datetime ) );
|
1371 |
-
}
|
1372 |
-
|
1373 |
-
/**
|
1374 |
-
* Insert post data for a new booking or update a booking
|
1375 |
-
* @since 0.0.1
|
1376 |
-
*/
|
1377 |
-
public function insert_post_data() {
|
1378 |
-
|
1379 |
-
$args = array(
|
1380 |
-
'post_type' => RTB_BOOKING_POST_TYPE,
|
1381 |
-
'post_title' => $this->name,
|
1382 |
-
'post_content' => $this->message,
|
1383 |
-
'post_date' => $this->date,
|
1384 |
-
'post_date_gmt' => get_gmt_from_date( $this->date ), // fix for post_date_gmt not being set for some bookings
|
1385 |
-
'post_status' => $this->post_status,
|
1386 |
-
);
|
1387 |
-
|
1388 |
-
if ( !empty( $this->ID ) ) {
|
1389 |
-
$args['ID'] = $this->ID;
|
1390 |
-
}
|
1391 |
-
|
1392 |
-
$args = apply_filters( 'rtb_insert_booking_data', $args, $this );
|
1393 |
-
|
1394 |
-
// When updating a booking, we need to update the metadata first, so that
|
1395 |
-
// notifications hooked to the status changes go out with the new metadata.
|
1396 |
-
// If we're inserting a new booking, we have to insert it before we can
|
1397 |
-
// add metadata, and the default notifications don't fire until it's all done.
|
1398 |
-
if ( !empty( $this->ID ) ) {
|
1399 |
-
$this->insert_post_meta();
|
1400 |
-
$id = wp_insert_post( $args );
|
1401 |
-
} else {
|
1402 |
-
$id = wp_insert_post( $args );
|
1403 |
-
if ( $id && !is_wp_error( $id ) ) {
|
1404 |
-
$this->ID = $id;
|
1405 |
-
$this->insert_post_meta();
|
1406 |
-
}
|
1407 |
-
}
|
1408 |
-
|
1409 |
-
return !is_wp_error( $id ) && $id !== false;
|
1410 |
-
}
|
1411 |
-
|
1412 |
-
/**
|
1413 |
-
* Insert the post metadata for a new booking or when updating a booking
|
1414 |
-
* @since 1.7.7
|
1415 |
-
*/
|
1416 |
-
public function insert_post_meta() {
|
1417 |
-
|
1418 |
-
$meta = array(
|
1419 |
-
'party' => $this->party,
|
1420 |
-
'email' => $this->email,
|
1421 |
-
'phone' => $this->phone,
|
1422 |
-
);
|
1423 |
-
|
1424 |
-
if ( !empty( $this->ip ) ) {
|
1425 |
-
$meta['ip'] = $this->ip;
|
1426 |
-
}
|
1427 |
-
|
1428 |
-
if ( empty( $this->date_submission ) ) {
|
1429 |
-
$meta['date_submission'] = (new DateTime( 'now', wp_timezone() ))->format( 'U' );
|
1430 |
-
} else {
|
1431 |
-
$meta['date_submission'] = $this->date_submission instanceof DateTime
|
1432 |
-
? $this->date_submission->format('U')
|
1433 |
-
: $this->date_submission;
|
1434 |
-
}
|
1435 |
-
|
1436 |
-
if ( !empty( $this->consent_acquired ) ) {
|
1437 |
-
$meta['consent_acquired'] = $this->consent_acquired;
|
1438 |
-
}
|
1439 |
-
|
1440 |
-
if ( !empty( $this->logs ) ) {
|
1441 |
-
$meta['logs'] = $this->logs;
|
1442 |
-
}
|
1443 |
-
|
1444 |
-
if ( !empty( $this->table ) ) {
|
1445 |
-
$meta['table'] = $this->table;
|
1446 |
-
}
|
1447 |
-
|
1448 |
-
if ( !empty( $this->deposit ) ) {
|
1449 |
-
$meta['deposit'] = $this->deposit;
|
1450 |
-
}
|
1451 |
-
|
1452 |
-
if ( !empty( $this->payment_failure_message ) ) {
|
1453 |
-
$meta['payment_failure_message'] = $this->payment_failure_message;
|
1454 |
-
}
|
1455 |
-
|
1456 |
-
if ( !empty( $this->receipt_id ) ) {
|
1457 |
-
$meta['receipt_id'] = $this->receipt_id;
|
1458 |
-
}
|
1459 |
-
|
1460 |
-
if ( !empty( $this->reminder_sent ) ) {
|
1461 |
-
$meta['reminder_sent'] = $this->reminder_sent;
|
1462 |
-
}
|
1463 |
-
|
1464 |
-
if ( !empty( $this->late_arrival_sent ) ) {
|
1465 |
-
$meta['late_arrival_sent'] = $this->late_arrival_sent;
|
1466 |
-
}
|
1467 |
-
|
1468 |
-
$meta = apply_filters( 'rtb_insert_booking_metadata', $meta, $this );
|
1469 |
-
|
1470 |
-
return update_post_meta( $this->ID, 'rtb', $meta );
|
1471 |
-
}
|
1472 |
-
|
1473 |
-
public function payment_paid()
|
1474 |
-
{
|
1475 |
-
if( isset( $this->ID ) ) {
|
1476 |
-
$this->determine_status( true );
|
1477 |
-
|
1478 |
-
$this->insert_post_data();
|
1479 |
-
|
1480 |
-
do_action( 'rtb_booking_paid', $this );
|
1481 |
-
}
|
1482 |
-
}
|
1483 |
-
|
1484 |
-
public function payment_failed( $message = '' )
|
1485 |
-
{
|
1486 |
-
$this->post_status = 'payment_failed';
|
1487 |
-
$this->payment_failure_message = $message;
|
1488 |
-
|
1489 |
-
$this->insert_post_data();
|
1490 |
-
|
1491 |
-
do_action( 'rtb_booking_paid', $this );
|
1492 |
-
}
|
1493 |
-
|
1494 |
-
function is_conditional_deposit_enabled() {
|
1495 |
-
global $rtb_controller;
|
1496 |
-
return in_array( $rtb_controller->settings->get_setting( 'rtb-deposit-applicable' ), ['time_based', 'size_based'] );
|
1497 |
-
}
|
1498 |
-
|
1499 |
-
function is_time_based_deposit_enabled() {
|
1500 |
-
global $rtb_controller;
|
1501 |
-
return 'time_based' === $rtb_controller->settings->get_setting( 'rtb-deposit-applicable' );
|
1502 |
-
}
|
1503 |
-
|
1504 |
-
function is_size_based_deposit_enabled() {
|
1505 |
-
global $rtb_controller;
|
1506 |
-
return 'size_based' === $rtb_controller->settings->get_setting( 'rtb-deposit-applicable' );
|
1507 |
-
}
|
1508 |
-
|
1509 |
-
}
|
1510 |
-
} // endif;
|
1 |
+
<?php
|
2 |
+
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
+
|
4 |
+
if ( !class_exists( 'rtbBooking' ) ) {
|
5 |
+
/**
|
6 |
+
* Class to handle a booking for Restaurant Table Bookings
|
7 |
+
*
|
8 |
+
* @since 0.0.1
|
9 |
+
*/
|
10 |
+
class rtbBooking {
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Whether or not this request has been processed. Used to prevent
|
14 |
+
* duplicate forms on one page from processing a booking form more than
|
15 |
+
* once.
|
16 |
+
* @since 0.0.1
|
17 |
+
*/
|
18 |
+
public $request_processed = false;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Whether or not this request was successfully saved to the database.
|
22 |
+
* @since 0.0.1
|
23 |
+
*/
|
24 |
+
public $request_inserted = false;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Raw input, clone of $_POST
|
28 |
+
* @var array
|
29 |
+
* @since 2.3.0
|
30 |
+
*/
|
31 |
+
public $raw_input = array();
|
32 |
+
|
33 |
+
public function __construct() {}
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Load the booking information from a WP_Post object or an ID
|
37 |
+
*
|
38 |
+
* @uses load_wp_post()
|
39 |
+
* @since 0.0.1
|
40 |
+
*/
|
41 |
+
public function load_post( $post ) {
|
42 |
+
|
43 |
+
if ( is_int( $post ) || is_string( $post ) ) {
|
44 |
+
$post = get_post( $post );
|
45 |
+
}
|
46 |
+
|
47 |
+
if ( is_object( $post ) && get_class( $post ) == 'WP_Post' && $post->post_type == RTB_BOOKING_POST_TYPE ) {
|
48 |
+
$this->load_wp_post( $post );
|
49 |
+
return true;
|
50 |
+
} else {
|
51 |
+
return false;
|
52 |
+
}
|
53 |
+
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Load data from WP post object and retrieve metadata
|
58 |
+
*
|
59 |
+
* @uses load_post_metadata()
|
60 |
+
* @since 0.0.1
|
61 |
+
*/
|
62 |
+
public function load_wp_post( $post ) {
|
63 |
+
// Store post for access to other data if needed by extensions
|
64 |
+
$this->post = $post;
|
65 |
+
|
66 |
+
$this->ID = $post->ID;
|
67 |
+
$this->name = $post->post_title;
|
68 |
+
$this->date = $post->post_date;
|
69 |
+
$this->message = $post->post_content;
|
70 |
+
$this->post_status = $post->post_status;
|
71 |
+
|
72 |
+
$this->load_post_metadata();
|
73 |
+
|
74 |
+
do_action( 'rtb_booking_load_post_data', $this, $post );
|
75 |
+
}
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Store metadata for post
|
79 |
+
* @since 0.0.1
|
80 |
+
*/
|
81 |
+
public function load_post_metadata() {
|
82 |
+
global $rtb_controller;
|
83 |
+
|
84 |
+
$meta_defaults = array(
|
85 |
+
'party' => '',
|
86 |
+
'email' => '',
|
87 |
+
'phone' => '',
|
88 |
+
'date_submission' => '',
|
89 |
+
'logs' => array(),
|
90 |
+
'ip' => '',
|
91 |
+
'consent_acquired' => '',
|
92 |
+
'deposit' => '0',
|
93 |
+
'table' => array(),
|
94 |
+
'payment_failure_message' => '',
|
95 |
+
'receipt_id' => '',
|
96 |
+
'reminder_sent' => false,
|
97 |
+
'late_arrival_sent' => false,
|
98 |
+
'mc_optin' => false
|
99 |
+
);
|
100 |
+
|
101 |
+
$meta_defaults = apply_filters( 'rtb_booking_metadata_defaults', $meta_defaults );
|
102 |
+
|
103 |
+
if ( is_array( $meta = get_post_meta( $this->ID, 'rtb', true ) ) ) {
|
104 |
+
$meta = array_merge( $meta_defaults, get_post_meta( $this->ID, 'rtb', true ) );
|
105 |
+
} else {
|
106 |
+
$meta = $meta_defaults;
|
107 |
+
}
|
108 |
+
|
109 |
+
$this->party = $meta['party'];
|
110 |
+
$this->email = $meta['email'];
|
111 |
+
$this->phone = $meta['phone'];
|
112 |
+
$this->date_submission = $meta['date_submission'];
|
113 |
+
$this->logs = $meta['logs'];
|
114 |
+
$this->ip = $meta['ip'];
|
115 |
+
$this->consent_acquired = $meta['consent_acquired'];
|
116 |
+
$this->deposit = $meta['deposit'];
|
117 |
+
$this->table = $meta['table'];
|
118 |
+
$this->payment_failure_message = $meta['payment_failure_message'];
|
119 |
+
$this->receipt_id = $meta['receipt_id'];
|
120 |
+
$this->late_arrival_sent = $meta['late_arrival_sent'];
|
121 |
+
$this->reminder_sent = $meta['reminder_sent'];
|
122 |
+
|
123 |
+
// Did they opt out?
|
124 |
+
$optout = $rtb_controller->settings->get_setting( 'mc-optout' );
|
125 |
+
// Because mcfrtbInit::reload_booking_meta() does not fire when needed for unknown reason
|
126 |
+
if ( $optout != 'no' ) {
|
127 |
+
$this->mc_optin = $meta['mc_optin'];
|
128 |
+
}
|
129 |
+
}
|
130 |
+
|
131 |
+
/**
|
132 |
+
* Prepare booking data loaded from the database for display in a booking
|
133 |
+
* form as request fields. This is used, eg, for splitting datetime values
|
134 |
+
* into date and time fields.
|
135 |
+
* @since 1.3
|
136 |
+
*/
|
137 |
+
public function prepare_request_data() {
|
138 |
+
|
139 |
+
// Split $date to $request_date and $request_time
|
140 |
+
if ( empty( $this->request_date ) || empty( $this->request_time ) ) {
|
141 |
+
$date = new DateTime( $this->date, wp_timezone() );
|
142 |
+
$this->request_date = $date->format( 'Y/m/d' );
|
143 |
+
$this->request_time = $date->format( 'h:i A' );
|
144 |
+
}
|
145 |
+
}
|
146 |
+
|
147 |
+
/**
|
148 |
+
* Format date
|
149 |
+
* @since 0.0.1
|
150 |
+
*/
|
151 |
+
public function format_date( $date ) {
|
152 |
+
$date = mysql2date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $date);
|
153 |
+
return apply_filters( 'get_the_date', $date );
|
154 |
+
}
|
155 |
+
|
156 |
+
/**
|
157 |
+
* Format a timestamp into a human-readable date
|
158 |
+
*
|
159 |
+
* @since 1.7.1
|
160 |
+
*/
|
161 |
+
public function format_timestamp( $timestamp ) {
|
162 |
+
$timestamp = $timestamp instanceof DateTime ? $timestamp->format('U') : $timestamp;
|
163 |
+
$time = date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $timestamp );
|
164 |
+
return $time;
|
165 |
+
}
|
166 |
+
|
167 |
+
/**
|
168 |
+
* Calculates the deposit required for a reservation, if any
|
169 |
+
*
|
170 |
+
* @since 2.1.0
|
171 |
+
*/
|
172 |
+
public function calculate_deposit() {
|
173 |
+
global $rtb_controller;
|
174 |
+
|
175 |
+
$deposit = $rtb_controller->settings->get_setting( 'rtb-deposit-amount' );
|
176 |
+
|
177 |
+
if ( $rtb_controller->settings->get_setting( 'rtb-deposit-applicable' ) == 'size_based' ) {
|
178 |
+
|
179 |
+
$deposit = $this->party < $rtb_controller->settings->get_setting( 'rtb-deposit-min-party-size' ) ? 0 : $deposit;
|
180 |
+
}
|
181 |
+
|
182 |
+
if ( $rtb_controller->settings->get_setting( 'rtb-deposit-applicable' ) == 'time_based' ) {
|
183 |
+
|
184 |
+
$deposit = empty( $this->is_time_based_deposit_applicable() ) ? 0 : $deposit;
|
185 |
+
}
|
186 |
+
|
187 |
+
if ( $rtb_controller->settings->get_setting( 'rtb-deposit-type' ) == 'guest' ) { $deposit = $deposit * $this->party; }
|
188 |
+
|
189 |
+
return $deposit;
|
190 |
+
}
|
191 |
+
|
192 |
+
|
193 |
+
/**
|
194 |
+
* Insert a new booking submission into the database
|
195 |
+
*
|
196 |
+
* Validates the data, adds it to the database and executes notifications
|
197 |
+
* @since 0.0.1
|
198 |
+
*/
|
199 |
+
public function insert_booking($by_admin = false) {
|
200 |
+
|
201 |
+
// Check if this request has already been processed. If multiple forms
|
202 |
+
// exist on the same page, this prevents a single submission from
|
203 |
+
// being added twice.
|
204 |
+
if ( $this->request_processed === true ) {
|
205 |
+
return true;
|
206 |
+
}
|
207 |
+
|
208 |
+
$this->request_processed = true;
|
209 |
+
|
210 |
+
if ( empty( $this->ID ) ) {
|
211 |
+
$action = 'insert';
|
212 |
+
} else {
|
213 |
+
$action = 'update';
|
214 |
+
}
|
215 |
+
|
216 |
+
$this->validate_submission($action, $by_admin);
|
217 |
+
if ( $this->is_valid_submission() === false ) {
|
218 |
+
return false;
|
219 |
+
}
|
220 |
+
|
221 |
+
if ( $this->insert_post_data() === false ) {
|
222 |
+
return false;
|
223 |
+
} else {
|
224 |
+
$this->request_inserted = true;
|
225 |
+
}
|
226 |
+
|
227 |
+
do_action( 'rtb_' . $action . '_booking', $this );
|
228 |
+
|
229 |
+
return true;
|
230 |
+
}
|
231 |
+
|
232 |
+
/**
|
233 |
+
* Validate submission data. Expects to find data in $_POST.
|
234 |
+
*
|
235 |
+
* ************************** NOTE **************************
|
236 |
+
* This function also create and assign all the required member variable with
|
237 |
+
* the acurate values which will be insreted in the DB. One special member,
|
238 |
+
* raw_input of type array holds the exact copy of $_POST
|
239 |
+
*
|
240 |
+
* Example:
|
241 |
+
* class a {
|
242 |
+
* public function a() {
|
243 |
+
* $this->name = 'John Doe';
|
244 |
+
* }
|
245 |
+
* public function b() {
|
246 |
+
* echo $this->name;
|
247 |
+
* }
|
248 |
+
* }
|
249 |
+
*
|
250 |
+
* $a = new a();
|
251 |
+
*
|
252 |
+
* var_dump($a);
|
253 |
+
* object(a)#1 (0) {
|
254 |
+
* }
|
255 |
+
*
|
256 |
+
* $a->a();
|
257 |
+
*
|
258 |
+
* var_dump($a);
|
259 |
+
* object(a)#1 (1) {
|
260 |
+
* ["name"]=>
|
261 |
+
* string(8) "John Doe"
|
262 |
+
* }
|
263 |
+
*
|
264 |
+
* $a->b();
|
265 |
+
* John Doe
|
266 |
+
*
|
267 |
+
* @since 0.0.1
|
268 |
+
*/
|
269 |
+
public function validate_submission($action = null, $by_admin = false) {
|
270 |
+
|
271 |
+
global $rtb_controller;
|
272 |
+
|
273 |
+
$this->validation_errors = array();
|
274 |
+
/**
|
275 |
+
* Raw, unprocessed value so that it can be used to preselect the form
|
276 |
+
* field values, eg. table and pass the value with the request. This way,
|
277 |
+
* hooked code doesn't have to check $_POST or $_GET for the data and can
|
278 |
+
* access everything posted from aw_input.
|
279 |
+
*
|
280 |
+
* Its name implies the requirement of sanitization explicitly
|
281 |
+
*/
|
282 |
+
$this->raw_input =& $_POST;
|
283 |
+
|
284 |
+
do_action( 'rtb_pre_validate_booking_submission', $this );
|
285 |
+
|
286 |
+
// Date
|
287 |
+
$date = empty( $_POST['rtb-date'] ) ? false : sanitize_text_field( $_POST['rtb-date'] );
|
288 |
+
if ( $date === false ) {
|
289 |
+
$this->validation_errors[] = array(
|
290 |
+
'field' => 'date',
|
291 |
+
'error_msg' => 'Booking request missing date',
|
292 |
+
'message' => __( 'Please enter the date you would like to book.', 'restaurant-reservations' ),
|
293 |
+
);
|
294 |
+
|
295 |
+
} else {
|
296 |
+
try {
|
297 |
+
$date = new DateTime( sanitize_text_field( $_POST['rtb-date'] ), wp_timezone() );
|
298 |
+
} catch ( Exception $e ) {
|
299 |
+
$this->validation_errors[] = array(
|
300 |
+
'field' => 'date',
|
301 |
+
'error_msg' => $e->getMessage(),
|
302 |
+
'message' => __( 'The date you entered is not valid. Please select from one of the dates in the calendar.', 'restaurant-reservations' ),
|
303 |
+
);
|
304 |
+
}
|
305 |
+
}
|
306 |
+
|
307 |
+
// Time
|
308 |
+
$time = empty( $_POST['rtb-time'] ) ? false : sanitize_text_field( $_POST['rtb-time'] );
|
309 |
+
if ( $time === false ) {
|
310 |
+
$this->validation_errors[] = array(
|
311 |
+
'field' => 'time',
|
312 |
+
'error_msg' => 'Booking request missing time',
|
313 |
+
'message' => __( 'Please enter the time you would like to book.', 'restaurant-reservations' ),
|
314 |
+
);
|
315 |
+
|
316 |
+
} else {
|
317 |
+
try {
|
318 |
+
$time = new DateTime( sanitize_text_field( $_POST['rtb-time'] ), wp_timezone() );
|
319 |
+
} catch ( Exception $e ) {
|
320 |
+
$this->validation_errors[] = array(
|
321 |
+
'field' => 'time',
|
322 |
+
'error_msg' => $e->getMessage(),
|
323 |
+
'message' => __( 'The time you entered is not valid. Please select from one of the times provided.', 'restaurant-reservations' ),
|
324 |
+
);
|
325 |
+
}
|
326 |
+
}
|
327 |
+
|
328 |
+
// Check against valid open dates/times
|
329 |
+
if ( is_object( $time ) && is_object( $date ) ) {
|
330 |
+
|
331 |
+
$request = new DateTime( $date->format( 'Y-m-d' ) . ' ' . $time->format( 'H:i:s' ), wp_timezone() );
|
332 |
+
$this->date_submission = new DateTime( 'now', wp_timezone() );
|
333 |
+
|
334 |
+
// Exempt Bookings Managers from the early and late bookings restrictions
|
335 |
+
if ( !current_user_can( 'manage_bookings' ) ) {
|
336 |
+
|
337 |
+
$early_bookings = $rtb_controller->settings->get_setting( 'early-bookings' );
|
338 |
+
if ( !empty( $early_bookings ) && is_numeric( $early_bookings ) ) {
|
339 |
+
$uppar_bound = ( new DateTime( 'now', wp_timezone() ) )->setTime( 23, 59 );
|
340 |
+
$uppar_bound->add( new DateInterval( "P{$early_bookings}D" ) );
|
341 |
+
|
342 |
+
if ( $request > $uppar_bound ) {
|
343 |
+
$this->validation_errors[] = array(
|
344 |
+
'field' => 'time',
|
345 |
+
'error_msg' => 'Booking request too far in the future',
|
346 |
+
'message' => sprintf( __( 'Sorry, bookings can not be made more than %s days in advance.', 'restaurant-reservations' ), $early_bookings ),
|
347 |
+
);
|
348 |
+
}
|
349 |
+
}
|
350 |
+
|
351 |
+
$late_bookings = $rtb_controller->settings->get_setting( 'late-bookings' );
|
352 |
+
if ( empty( $late_bookings ) ) {
|
353 |
+
if ( $request->format( 'U' ) < $this->date_submission->format( 'U' ) ) {
|
354 |
+
$this->validation_errors[] = array(
|
355 |
+
'field' => 'time',
|
356 |
+
'error_msg' => 'Booking request in the past',
|
357 |
+
'message' => __( 'Sorry, bookings can not be made in the past.', 'restaurant-reservations' ),
|
358 |
+
);
|
359 |
+
}
|
360 |
+
|
361 |
+
} elseif ( $late_bookings === 'same_day' ) {
|
362 |
+
if ( $request->format( 'Y-m-d' ) == $this->date_submission->format( 'Y-m-d' ) ) {
|
363 |
+
$this->validation_errors[] = array(
|
364 |
+
'field' => 'time',
|
365 |
+
'error_msg' => 'Booking request made on same day',
|
366 |
+
'message' => __( 'Sorry, bookings can not be made for the same day.', 'restaurant-reservations' ),
|
367 |
+
);
|
368 |
+
}
|
369 |
+
|
370 |
+
} elseif( is_numeric( $late_bookings ) ) {
|
371 |
+
$late_bookings_seconds = $late_bookings * 60; // Late bookings allowance in seconds
|
372 |
+
if ( $request->format( 'U' ) < ( $this->date_submission->format( 'U' ) + $late_bookings_seconds ) ) {
|
373 |
+
if ( $late_bookings >= 1440 ) {
|
374 |
+
$late_bookings_message = sprintf( __( 'Sorry, bookings must be made more than %s days in advance.', 'restaurant-reservations' ), $late_bookings / 1440 );
|
375 |
+
} elseif ( $late_bookings >= 60 ) {
|
376 |
+
$late_bookings_message = sprintf( __( 'Sorry, bookings must be made more than %s hours in advance.', 'restaurant-reservations' ), $late_bookings / 60 );
|
377 |
+
} else {
|
378 |
+
$late_bookings_message = sprintf( __( 'Sorry, bookings must be made more than %s minutes in advance.', 'restaurant-reservations' ), $late_bookings );
|
379 |
+
}
|
380 |
+
$this->validation_errors[] = array(
|
381 |
+
'field' => 'time',
|
382 |
+
'error_msg' => 'Booking request made too close to the reserved time',
|
383 |
+
'message' => $late_bookings_message,
|
384 |
+
);
|
385 |
+
}
|
386 |
+
}
|
387 |
+
}
|
388 |
+
|
389 |
+
// Check against scheduling exception rules
|
390 |
+
$exception_rules = $rtb_controller->settings->get_setting( 'schedule-closed' );
|
391 |
+
$exception_is_active = false;
|
392 |
+
if (
|
393 |
+
empty( $this->validation_errors )
|
394 |
+
&& !empty( $exception_rules )
|
395 |
+
&& !current_user_can( 'manage_bookings' )
|
396 |
+
) {
|
397 |
+
|
398 |
+
/**
|
399 |
+
* We are checking the booking againt exceptions which consists blacklist and whitelist rules
|
400 |
+
* - Any rule without time is a blacklist entry
|
401 |
+
* - Any rule with time is a modified white entry
|
402 |
+
* Thus consider the request as legit by default and terminate the loop when we hit first
|
403 |
+
* blacklisted rule which applies to the request
|
404 |
+
*
|
405 |
+
* $exception_is_active This prevent validation againt normal open rules
|
406 |
+
* $datetime_is_valid This is to throw error as soon as we encounter a blacklisted exception
|
407 |
+
*/
|
408 |
+
$exception_is_active = false;
|
409 |
+
$datetime_is_valid = true;
|
410 |
+
|
411 |
+
foreach( $exception_rules as $excp_rule ) {
|
412 |
+
|
413 |
+
if( array_key_exists( 'date_range', $excp_rule ) )
|
414 |
+
{
|
415 |
+
$start = ! empty( $excp_rule['date_range']['start'] )
|
416 |
+
? new DateTime( $excp_rule['date_range']['start'], wp_timezone() )
|
417 |
+
: new DateTime( 'now', wp_timezone() );
|
418 |
+
$start->setTime(0, 0);
|
419 |
+
|
420 |
+
$end = !empty( $excp_rule['date_range']['end'] )
|
421 |
+
? new DateTime( $excp_rule['date_range']['end'], wp_timezone() )
|
422 |
+
: ( new DateTime( 'now', wp_timezone() ) )->add( new DateInterval( 'P10Y' ) );
|
423 |
+
$end->setTime(23, 59, 58);
|
424 |
+
|
425 |
+
if( $start < $request && $request < $end ) {
|
426 |
+
$excp_rule_obj = clone $request;
|
427 |
+
}
|
428 |
+
else {
|
429 |
+
// Set anything to void this rule for following check
|
430 |
+
$excp_rule_obj = clone $request;
|
431 |
+
$excp_rule_obj->add( new DateInterval( 'P1Y' ) );
|
432 |
+
}
|
433 |
+
}
|
434 |
+
else {
|
435 |
+
$excp_rule_obj = ( new DateTime( $excp_rule['date'], wp_timezone() ) )->setTime(0, 0, 2);
|
436 |
+
}
|
437 |
+
|
438 |
+
if ( $excp_rule_obj->format( 'Y-m-d' ) == $request->format( 'Y-m-d' ) ) {
|
439 |
+
// This rule applies so far, thus consider this request under exception
|
440 |
+
// whielist or blacklist, yet to be determined
|
441 |
+
$exception_is_active = true;
|
442 |
+
|
443 |
+
// Closed all day
|
444 |
+
// Request denied, falls under blacklist, terminate loop
|
445 |
+
if ( empty( $excp_rule['time'] ) ) {
|
446 |
+
$datetime_is_valid = false;
|
447 |
+
break;
|
448 |
+
}
|
449 |
+
|
450 |
+
$excp_start_time = empty( $excp_rule['time']['start'] )
|
451 |
+
? $request
|
452 |
+
: new DateTime( $excp_rule_obj->format( 'Y-m-d' ) . ' ' . $excp_rule['time']['start'], wp_timezone() );
|
453 |
+
|
454 |
+
$excp_end_time = empty( $excp_rule['time']['end'] )
|
455 |
+
? $request
|
456 |
+
: new DateTime( $excp_rule_obj->format( 'Y-m-d' ) . ' ' . $excp_rule['time']['end'], wp_timezone() );
|
457 |
+
|
458 |
+
if (
|
459 |
+
$excp_start_time->format( 'U' )
|
460 |
+
<= $request->format( 'U' ) && $request->format( 'U' ) <=
|
461 |
+
$excp_end_time->format( 'U' )
|
462 |
+
) {
|
463 |
+
// If we reach here, means request is under modified whitelist rules
|
464 |
+
$datetime_is_valid = true;
|
465 |
+
break;
|
466 |
+
}
|
467 |
+
else {
|
468 |
+
// else this request falls in blacklisted area based on modified whitelist rules.
|
469 |
+
$datetime_is_valid = false;
|
470 |
+
}
|
471 |
+
}
|
472 |
+
}
|
473 |
+
|
474 |
+
if ( $exception_is_active && !$datetime_is_valid ) {
|
475 |
+
$this->validation_errors[] = array(
|
476 |
+
'field' => 'date',
|
477 |
+
'error_msg' => 'Booking request made on invalid date or time in an exception rule',
|
478 |
+
'message' => __( 'Sorry, no bookings are being accepted then.', 'restaurant-reservations' ),
|
479 |
+
);
|
480 |
+
}
|
481 |
+
}
|
482 |
+
|
483 |
+
// Check against weekly scheduling rules
|
484 |
+
$rules = $rtb_controller->settings->get_setting( 'schedule-open' );
|
485 |
+
|
486 |
+
// Order of conditions in if matters to prevent unnacessary warnings
|
487 |
+
if (
|
488 |
+
empty( $this->validation_errors )
|
489 |
+
&& !empty( $rules )
|
490 |
+
&& !current_user_can( 'manage_bookings' )
|
491 |
+
&& !$exception_is_active
|
492 |
+
) {
|
493 |
+
$request_weekday = strtolower( $request->format( 'l' ) );
|
494 |
+
$time_is_valid = null;
|
495 |
+
$day_is_valid = null;
|
496 |
+
foreach( $rules as $rule ) {
|
497 |
+
|
498 |
+
if ( !empty( $rule['weekdays'][ $request_weekday ] ) ) {
|
499 |
+
$day_is_valid = true;
|
500 |
+
|
501 |
+
if ( empty( $rule['time'] ) ) {
|
502 |
+
$time_is_valid = true; // Days with no time values are open all day
|
503 |
+
break;
|
504 |
+
}
|
505 |
+
|
506 |
+
$too_early = true;
|
507 |
+
$too_late = true;
|
508 |
+
|
509 |
+
// Too early
|
510 |
+
if ( !empty( $rule['time']['start'] ) ) {
|
511 |
+
$rule_start_time = new DateTime( $request->format( 'Y-m-d' ) . ' ' . $rule['time']['start'], wp_timezone() );
|
512 |
+
if ( $rule_start_time->format( 'U' ) <= $request->format( 'U' ) ) {
|
513 |
+
$too_early = false;
|
514 |
+
}
|
515 |
+
}
|
516 |
+
|
517 |
+
// Too late
|
518 |
+
if ( !empty( $rule['time']['end'] ) ) {
|
519 |
+
$rule_end_time = new DateTime( $request->format( 'Y-m-d' ) . ' ' . $rule['time']['end'], wp_timezone() );
|
520 |
+
if ( $rule_end_time->format( 'U' ) >= $request->format( 'U' ) ) {
|
521 |
+
$too_late = false;
|
522 |
+
}
|
523 |
+
}
|
524 |
+
|
525 |
+
// Valid time found
|
526 |
+
if ( $too_early === false && $too_late === false) {
|
527 |
+
$time_is_valid = true;
|
528 |
+
break;
|
529 |
+
}
|
530 |
+
}
|
531 |
+
}
|
532 |
+
|
533 |
+
if ( !$day_is_valid ) {
|
534 |
+
$this->validation_errors[] = array(
|
535 |
+
'field' => 'date',
|
536 |
+
'error_msg' => 'Booking request made on an invalid date',
|
537 |
+
'message' => __( 'Sorry, no bookings are being accepted on that date.', 'restaurant-reservations' ),
|
538 |
+
);
|
539 |
+
} elseif ( !$time_is_valid ) {
|
540 |
+
$this->validation_errors[] = array(
|
541 |
+
'field' => 'time',
|
542 |
+
'error_msg' => 'Booking request made at an invalid time',
|
543 |
+
'message' => __( 'Sorry, no bookings are being accepted at that time.', 'restaurant-reservations' ),
|
544 |
+
);
|
545 |
+
}
|
546 |
+
}
|
547 |
+
|
548 |
+
// Accept the date if it has passed validation
|
549 |
+
if ( empty( $this->validation_errors ) ) {
|
550 |
+
$this->date = $request->format( 'Y-m-d H:i:s' );
|
551 |
+
}
|
552 |
+
}
|
553 |
+
|
554 |
+
// Save requested date/time values in case they need to be
|
555 |
+
// printed in the form again
|
556 |
+
$this->request_date = empty( $_POST['rtb-date'] ) ? '' : sanitize_text_field( $_POST['rtb-date'] );
|
557 |
+
$this->request_time = empty( $_POST['rtb-time'] ) ? '' : sanitize_text_field( $_POST['rtb-time'] );
|
558 |
+
|
559 |
+
// Name
|
560 |
+
$this->name = empty( $_POST['rtb-name'] ) ? '' : wp_strip_all_tags( sanitize_text_field( $_POST['rtb-name'] ), true ); // @todo should I limit length?
|
561 |
+
if ( empty( $this->name ) ) {
|
562 |
+
$this->validation_errors[] = array(
|
563 |
+
'field' => 'name',
|
564 |
+
'post_variable' => $this->name,
|
565 |
+
'message' => __( 'Please enter a name for this booking.', 'restaurant-reservations' ),
|
566 |
+
);
|
567 |
+
}
|
568 |
+
|
569 |
+
// Party
|
570 |
+
$this->party = empty( $_POST['rtb-party'] ) ? '' : absint( $_POST['rtb-party'] );
|
571 |
+
if ( empty( $this->party ) ) {
|
572 |
+
$this->validation_errors[] = array(
|
573 |
+
'field' => 'party',
|
574 |
+
'post_variable' => $this->party,
|
575 |
+
'message' => __( 'Please let us know how many people will be in your party.', 'restaurant-reservations' ),
|
576 |
+
);
|
577 |
+
|
578 |
+
// Check party size
|
579 |
+
} else {
|
580 |
+
$party_size = $rtb_controller->settings->get_setting( 'party-size' );
|
581 |
+
if ( ! empty( $party_size ) && $party_size < $this->party ) {
|
582 |
+
$this->validation_errors[] = array(
|
583 |
+
'field' => 'party',
|
584 |
+
'post_variable' => $this->party,
|
585 |
+
'message' => sprintf( __( 'We only accept bookings for parties of up to %d people.', 'restaurant-reservations' ), $party_size ),
|
586 |
+
);
|
587 |
+
}
|
588 |
+
$party_size_min = $rtb_controller->settings->get_setting( 'party-size-min' );
|
589 |
+
if ( ! empty( $party_size_min ) && $party_size_min > $this->party ) {
|
590 |
+
$this->validation_errors[] = array(
|
591 |
+
'field' => 'party',
|
592 |
+
'post_variable' => $this->party,
|
593 |
+
'message' => sprintf( __( 'We only accept bookings for parties of more than %d people.', 'restaurant-reservations' ), $party_size_min ),
|
594 |
+
);
|
595 |
+
}
|
596 |
+
}
|
597 |
+
|
598 |
+
// Email
|
599 |
+
$this->email = empty( $_POST['rtb-email'] ) ? '' : sanitize_email( $_POST['rtb-email'] ); // @todo email validation? send notification back to form on bad email address.
|
600 |
+
if ( empty( $this->email ) ) {
|
601 |
+
$this->validation_errors[] = array(
|
602 |
+
'field' => 'email',
|
603 |
+
'post_variable' => $this->email,
|
604 |
+
'message' => __( 'Please enter an email address so we can confirm your booking.', 'restaurant-reservations' ),
|
605 |
+
);
|
606 |
+
} elseif ( !is_email( $this->email ) && apply_filters( 'rtb_require_valid_email', true ) ) {
|
607 |
+
$this->validation_errors[] = array(
|
608 |
+
'field' => 'email',
|
609 |
+
'post_variable' => $this->email,
|
610 |
+
'message' => __( 'Please enter a valid email address so we can confirm your booking.', 'restaurant-reservations' ),
|
611 |
+
);
|
612 |
+
}
|
613 |
+
|
614 |
+
// Phone
|
615 |
+
$this->phone = empty( $_POST['rtb-phone'] ) ? '' : sanitize_text_field( $_POST['rtb-phone'] );
|
616 |
+
$phone_required = $rtb_controller->settings->get_setting( 'require-phone' );
|
617 |
+
if ( $phone_required && empty( $this->phone ) ) {
|
618 |
+
$this->validation_errors[] = array(
|
619 |
+
'field' => 'phone',
|
620 |
+
'post_variable' => $this->phone,
|
621 |
+
'message' => __( 'Please provide a phone number so we can confirm your booking.', 'restaurant-reservations' ),
|
622 |
+
);
|
623 |
+
}
|
624 |
+
|
625 |
+
// Table
|
626 |
+
$table = empty( $_POST['rtb-table'] ) ? array() : explode( ',', sanitize_text_field( $_POST['rtb-table'] ) );
|
627 |
+
$this->table = is_array( $table ) ? array_map( 'sanitize_text_field', $table ) : array();
|
628 |
+
|
629 |
+
$table_required = $rtb_controller->settings->get_setting( 'require-table' );
|
630 |
+
if ( $table_required && empty( $this->table ) ) {
|
631 |
+
$this->validation_errors[] = array(
|
632 |
+
'field' => 'table',
|
633 |
+
'post_variable' => $this->table,
|
634 |
+
'message' => __( 'Please select a table for your booking.', 'restaurant-reservations' ),
|
635 |
+
);
|
636 |
+
}
|
637 |
+
|
638 |
+
// check whether there is a time conflict for a particular table
|
639 |
+
$valid_table = $this->table ? $this->is_valid_table() : true;
|
640 |
+
if ( ! $valid_table ) {
|
641 |
+
$this->validation_errors[] = array(
|
642 |
+
'field' => 'table',
|
643 |
+
'post_variable' => $this->table,
|
644 |
+
'message' => __( 'Please select a valid table for your booking.', 'restaurant-reservations' ),
|
645 |
+
);
|
646 |
+
}
|
647 |
+
|
648 |
+
// reCAPTCHA
|
649 |
+
if ( $rtb_controller->settings->get_setting( 'enable-captcha' ) && !is_admin() ) {
|
650 |
+
if ( ! isset($_POST['g-recaptcha-response']) ) {
|
651 |
+
$this->validation_errors[] = array(
|
652 |
+
'field' => 'recaptcha',
|
653 |
+
'error_msg' => 'No reCAPTCHA code',
|
654 |
+
'message' => __( 'Please fill out the reCAPTCHA box before submitting.', 'restaurant-reservations' ),
|
655 |
+
);
|
656 |
+
}
|
657 |
+
else {
|
658 |
+
$secret_key = $rtb_controller->settings->get_setting( 'captcha-secret-key' );
|
659 |
+
$captcha = $_POST['g-recaptcha-response'];
|
660 |
+
|
661 |
+
$url = 'https://www.google.com/recaptcha/api/siteverify?secret=' . urlencode($secret_key) . '&response=' . urlencode($captcha);
|
662 |
+
$json_response = file_get_contents( $url );
|
663 |
+
$response = json_decode( $json_response );
|
664 |
+
|
665 |
+
$reCaptcha_error = false;
|
666 |
+
if(json_last_error() != JSON_ERROR_NONE) {
|
667 |
+
$response = new stdClass();
|
668 |
+
$response->success = false;
|
669 |
+
$reCaptcha_error = true;
|
670 |
+
if(defined('WP_DEBUG') && WP_DEBUG) {
|
671 |
+
error_log('RTB reCAPTCHA error. Raw respose: '.print_r([$json_response], true));
|
672 |
+
}
|
673 |
+
}
|
674 |
+
|
675 |
+
if ( ! $response->success ) {
|
676 |
+
$message = __( 'Please fill out the reCAPTCHA box again and re-submit.', 'restaurant-reservations' );
|
677 |
+
if($reCaptcha_error) {
|
678 |
+
$message .= __( ' If you encounter reCAPTCHA error multiple times, please contact us.', 'restaurant-reservations' );
|
679 |
+
}
|
680 |
+
$this->validation_errors[] = array(
|
681 |
+
'field' => 'recaptcha',
|
682 |
+
'error_msg' => 'Invalid reCAPTCHA code',
|
683 |
+
'message' => $message,
|
684 |
+
);
|
685 |
+
}
|
686 |
+
}
|
687 |
+
}
|
688 |
+
|
689 |
+
// Message
|
690 |
+
$this->message = empty( $_POST['rtb-message'] ) ? '' : sanitize_text_field( nl2br( $_POST['rtb-message'] ) );
|
691 |
+
|
692 |
+
// Post Status (define a default post status if none passed)
|
693 |
+
$this->determine_status();
|
694 |
+
|
695 |
+
// Consent
|
696 |
+
$require_consent = $rtb_controller->settings->get_setting( 'require-consent' );
|
697 |
+
$consent_statement = $rtb_controller->settings->get_setting( 'consent-statement' );
|
698 |
+
if ( $require_consent && $consent_statement ) {
|
699 |
+
// Don't change consent status once initial consent has been collected
|
700 |
+
if ( empty( $this->consent_acquired ) ) {
|
701 |
+
$this->consent_acquired = !empty( $_POST['rtb-consent-statement'] );
|
702 |
+
}
|
703 |
+
}
|
704 |
+
|
705 |
+
// Check if any required fields are empty
|
706 |
+
$required_fields = $rtb_controller->settings->get_required_fields();
|
707 |
+
foreach( $required_fields as $slug => $field ) {
|
708 |
+
if ( !$this->field_has_error( $slug ) && $this->is_field_empty( $slug ) ) {
|
709 |
+
$this->validation_errors[] = array(
|
710 |
+
'field' => $slug,
|
711 |
+
'post_variable' => '',
|
712 |
+
'message' => __( 'Please complete this field to request a booking.', 'restaurant-reservations' ),
|
713 |
+
);
|
714 |
+
}
|
715 |
+
}
|
716 |
+
|
717 |
+
// Check if the email or IP is banned
|
718 |
+
if ( !current_user_can( 'manage_bookings' ) ) {
|
719 |
+
$ip = $_SERVER['REMOTE_ADDR'];
|
720 |
+
if ( !$this->is_valid_ip( $ip ) || !$this->is_valid_email( $this->email ) ) {
|
721 |
+
$this->validation_errors[] = array(
|
722 |
+
'field' => 'date',
|
723 |
+
'post_variable' => $ip,
|
724 |
+
'message' => __( 'Your booking has been rejected. Please call us if you would like to make a booking.', 'restaurant-reservations' ),
|
725 |
+
);
|
726 |
+
} elseif ( empty( $this->ip ) and ! $rtb_controller->settings->get_setting( 'disable-ip-capture' ) ) {
|
727 |
+
$this->ip = sanitize_text_field( $ip );
|
728 |
+
}
|
729 |
+
} elseif ( empty( $this->ip ) and ! $rtb_controller->settings->get_setting( 'disable-ip-capture' ) ) {
|
730 |
+
$this->ip = sanitize_text_field( $_SERVER['REMOTE_ADDR'] );
|
731 |
+
}
|
732 |
+
|
733 |
+
// Check to make sure that the maximum number of reservations has not already been made
|
734 |
+
if ( ! $this->is_under_max_reservations() ){
|
735 |
+
$this->validation_errors[] = array(
|
736 |
+
'field' => 'time',
|
737 |
+
'error_msg' => 'maximum reservations exceeded',
|
738 |
+
'message' => __( 'The maximum number of reservations for that timeslot has been reached. Please select a different timeslot.', 'restaurant-reservations' ),
|
739 |
+
);
|
740 |
+
}
|
741 |
+
|
742 |
+
// Check to make sure that the maximum number of seats has not already been made
|
743 |
+
if ( ! $this->is_under_max_seats() ){
|
744 |
+
$this->validation_errors[] = array(
|
745 |
+
'field' => 'time',
|
746 |
+
'error_msg' => 'maximum seats exceeded',
|
747 |
+
'message' => __( 'With your party, the maximum number of seats for that timeslot would be exceeded. Please select a different timeslot or reduce your party size.', 'restaurant-reservations' ),
|
748 |
+
);
|
749 |
+
}
|
750 |
+
|
751 |
+
// Check if there is a booking already made with the exact same information, to prevent double bookings on refresh
|
752 |
+
if ( (!$by_admin || $by_admin && $action !== 'update') && $this->is_duplicate_booking() ) {
|
753 |
+
$this->validation_errors[] = array(
|
754 |
+
'field' => 'date',
|
755 |
+
'error_msg' => 'duplicate booking',
|
756 |
+
'message' => __( 'Your booking and personal information exactly matches another booking. If this was not caused by refreshing the page, please call us to make a booking.', 'restaurant-reservations' ),
|
757 |
+
);
|
758 |
+
}
|
759 |
+
|
760 |
+
do_action( 'rtb_validate_booking_submission', $this );
|
761 |
+
|
762 |
+
}
|
763 |
+
|
764 |
+
/**
|
765 |
+
* Check if submission is valid
|
766 |
+
*
|
767 |
+
* @since 0.0.1
|
768 |
+
*/
|
769 |
+
public function is_valid_submission() {
|
770 |
+
|
771 |
+
if ( !count( $this->validation_errors ) ) {
|
772 |
+
return true;
|
773 |
+
}
|
774 |
+
|
775 |
+
return false;
|
776 |
+
}
|
777 |
+
|
778 |
+
/**
|
779 |
+
* Check if a field already has an error attached to it
|
780 |
+
*
|
781 |
+
* @field string Field slug
|
782 |
+
* @since 1.3
|
783 |
+
*/
|
784 |
+
public function field_has_error( $field_slug ) {
|
785 |
+
|
786 |
+
foreach( $this->validation_errors as $error ) {
|
787 |
+
if ( $error['field'] == $field_slug ) {
|
788 |
+
return true;
|
789 |
+
}
|
790 |
+
}
|
791 |
+
|
792 |
+
return false;
|
793 |
+
}
|
794 |
+
|
795 |
+
/**
|
796 |
+
* Check if a field is missing
|
797 |
+
*
|
798 |
+
* Checks for empty strings and arrays, but accepts '0'
|
799 |
+
* @since 0.1
|
800 |
+
*/
|
801 |
+
public function is_field_empty( $slug ) {
|
802 |
+
|
803 |
+
$field_key = 'rtb-' . $slug;
|
804 |
+
|
805 |
+
if (
|
806 |
+
! isset( $_POST[ $field_key ] )
|
807 |
+
|| ( is_string( $_POST[ $field_key ] ) && trim( $_POST[ $field_key ] ) == '' )
|
808 |
+
|| ( is_array( $_POST[ $field_key ] ) && empty( $_POST[ $field_key ] ) )
|
809 |
+
)
|
810 |
+
{
|
811 |
+
return true;
|
812 |
+
}
|
813 |
+
|
814 |
+
return false;
|
815 |
+
}
|
816 |
+
|
817 |
+
/**
|
818 |
+
* Check if an IP address has been banned
|
819 |
+
*
|
820 |
+
* @param string $ip
|
821 |
+
* @return bool
|
822 |
+
* @since 1.7
|
823 |
+
*/
|
824 |
+
public function is_valid_ip( $ip = null ) {
|
825 |
+
|
826 |
+
if ( is_null( $ip ) ) {
|
827 |
+
$ip = isset( $this->ip ) ? $this->ip : null;
|
828 |
+
if ( is_null( $ip ) ) {
|
829 |
+
return false;
|
830 |
+
}
|
831 |
+
}
|
832 |
+
|
833 |
+
global $rtb_controller;
|
834 |
+
|
835 |
+
$banned_ips = array_filter( explode( "\n", $rtb_controller->settings->get_setting( 'ban-ips' ) ) );
|
836 |
+
|
837 |
+
foreach( $banned_ips as $banned_ip ) {
|
838 |
+
if ( $ip == trim( $banned_ip ) ) {
|
839 |
+
return false;
|
840 |
+
}
|
841 |
+
}
|
842 |
+
|
843 |
+
return true;
|
844 |
+
}
|
845 |
+
|
846 |
+
/**
|
847 |
+
* Check if an email address has been banned
|
848 |
+
*
|
849 |
+
* @param string $email
|
850 |
+
* @return bool
|
851 |
+
* @since 1.7
|
852 |
+
*/
|
853 |
+
public function is_valid_email( $email = null ) {
|
854 |
+
|
855 |
+
if ( is_null( $email ) ) {
|
856 |
+
$email = isset( $this->email ) ? $this->email : null;
|
857 |
+
if ( is_null( $email ) ) {
|
858 |
+
return false;
|
859 |
+
}
|
860 |
+
}
|
861 |
+
|
862 |
+
global $rtb_controller;
|
863 |
+
|
864 |
+
$banned_emails = array_filter( explode( "\n", $rtb_controller->settings->get_setting( 'ban-emails' ) ) );
|
865 |
+
|
866 |
+
foreach( $banned_emails as $banned_email ) {
|
867 |
+
if ( $email == trim( $banned_email ) ) {
|
868 |
+
return false;
|
869 |
+
}
|
870 |
+
}
|
871 |
+
|
872 |
+
return true;
|
873 |
+
}
|
874 |
+
|
875 |
+
/**
|
876 |
+
* Check if a table(s) is valid (not already taken during a specific timeslot)
|
877 |
+
*
|
878 |
+
* @return bool
|
879 |
+
* @since 2.1.7
|
880 |
+
*/
|
881 |
+
public function is_valid_table() {
|
882 |
+
global $rtb_controller;
|
883 |
+
|
884 |
+
if ( ! $this->table or ! is_array( $this->table ) ) { return false; }
|
885 |
+
|
886 |
+
$valid_tables = rtb_get_valid_tables( $this->date );
|
887 |
+
|
888 |
+
if ( isset( $this->ID ) ) {
|
889 |
+
|
890 |
+
$post_meta = get_post_meta( $this->ID, 'rtb', true );
|
891 |
+
|
892 |
+
if ( isset( $post_meta['table'] ) and is_array( $post_meta['table'] ) ) { $valid_tables = array_merge( $valid_tables, $post_meta['table'] ); }
|
893 |
+
}
|
894 |
+
|
895 |
+
return $this->table == array_intersect( $this->table, $valid_tables );
|
896 |
+
}
|
897 |
+
|
898 |
+
/**
|
899 |
+
* Check if this booking would put the restaurant over the maximum, if set
|
900 |
+
*
|
901 |
+
* @return bool
|
902 |
+
* @since 2.1.20
|
903 |
+
*/
|
904 |
+
public function is_under_max_reservations() {
|
905 |
+
global $rtb_controller;
|
906 |
+
|
907 |
+
$location = ( ! empty( $this->location ) and term_exists( $this->location ) ) ? get_term( $this->location ) : false;
|
908 |
+
$location_slug = ! empty( $location ) ? $location->slug : false;
|
909 |
+
|
910 |
+
$max_reservations_enabled = $rtb_controller->settings->get_setting( 'rtb-enable-max-tables', $location_slug );
|
911 |
+
|
912 |
+
if ( ! $max_reservations_enabled ) { return true; }
|
913 |
+
|
914 |
+
$max_reservations = (int) $rtb_controller->settings->get_setting( 'rtb-max-tables-count', $location_slug );
|
915 |
+
|
916 |
+
if ( $max_reservations == 'undefined' or ! $max_reservations ) { return true; }
|
917 |
+
|
918 |
+
$dining_block_seconds = (int) $rtb_controller->settings->get_setting( 'rtb-dining-block-length' ) * 60 - 1; // Take 1 second off, to avoid bookings that start or end exactly at the beginning of a booking block
|
919 |
+
|
920 |
+
$tmp = ( new DateTime( $this->date, wp_timezone() ) )->format( 'U' );
|
921 |
+
$after_time = $tmp - $dining_block_seconds;
|
922 |
+
$before_time = $tmp + $dining_block_seconds;
|
923 |
+
|
924 |
+
$args = array(
|
925 |
+
'posts_per_page' => -1,
|
926 |
+
'post_status' => array( 'pending', 'payment_pending', 'confirmed', 'arrived' ),
|
927 |
+
'date_query' => array(
|
928 |
+
'before' => date( 'c', $before_time ),
|
929 |
+
'after' => date( 'c', $after_time )
|
930 |
+
)
|
931 |
+
);
|
932 |
+
|
933 |
+
// If there are multiple locations, a location is selected, and
|
934 |
+
// max seats has been enabled for this specific location
|
935 |
+
if ( ! empty( $location_slug ) and $rtb_controller->settings->is_location_setting_enabled( 'rtb-max-tables-count', $location_slug ) ) {
|
936 |
+
|
937 |
+
$tax_query = array(
|
938 |
+
array(
|
939 |
+
'taxonomy' => $rtb_controller->locations->location_taxonomy,
|
940 |
+
'field' => 'term_id',
|
941 |
+
'terms' => $location->term_id
|
942 |
+
)
|
943 |
+
);
|
944 |
+
|
945 |
+
$args['tax_query'] = $tax_query;
|
946 |
+
}
|
947 |
+
|
948 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Query.class.php' );
|
949 |
+
$query = new rtbQuery( $args );
|
950 |
+
$query->prepare_args();
|
951 |
+
|
952 |
+
$tmzn = wp_timezone();
|
953 |
+
|
954 |
+
$times = array();
|
955 |
+
foreach ( $query->get_bookings() as $booking ) {
|
956 |
+
|
957 |
+
if ( isset( $this->ID ) and $booking->ID == $this->ID ) { continue; }
|
958 |
+
|
959 |
+
$times[] = ( new DateTime( $booking->date, $tmzn ) )->format( 'U' );
|
960 |
+
}
|
961 |
+
|
962 |
+
sort( $times );
|
963 |
+
|
964 |
+
$current_times = array();
|
965 |
+
foreach ( $times as $time ) {
|
966 |
+
|
967 |
+
$current_times[] = $time;
|
968 |
+
|
969 |
+
if ( reset( $current_times ) < ( $time - $dining_block_seconds ) ) { array_shift( $current_times ); }
|
970 |
+
|
971 |
+
// Check if we go above the max confirmation number
|
972 |
+
if ( sizeOf( $current_times ) + 1 > $max_reservations ) { return false; }
|
973 |
+
}
|
974 |
+
|
975 |
+
return true;
|
976 |
+
}
|
977 |
+
|
978 |
+
/**
|
979 |
+
* Check if this booking would put the restaurant over the maximum number of people, if set
|
980 |
+
*
|
981 |
+
* @return bool
|
982 |
+
* @since 2.1.20
|
983 |
+
*/
|
984 |
+
public function is_under_max_seats() {
|
985 |
+
global $rtb_controller;
|
986 |
+
|
987 |
+
$location = ( ! empty( $this->location ) and term_exists( $this->location ) ) ? get_term( $this->location ) : false;
|
988 |
+
$location_slug = ! empty( $location ) ? $location->slug : false;
|
989 |
+
|
990 |
+
$max_reservations_enabled = $rtb_controller->settings->get_setting( 'rtb-enable-max-tables', $location_slug );
|
991 |
+
|
992 |
+
if ( ! $max_reservations_enabled ) { return true; }
|
993 |
+
|
994 |
+
$max_seats = (int) $rtb_controller->settings->get_setting( 'rtb-max-people-count', $location_slug );
|
995 |
+
|
996 |
+
if ( $max_seats == 'undefined' or ! $max_seats ) { return true; }
|
997 |
+
if ( $this->party > $max_seats ) { return false; }
|
998 |
+
|
999 |
+
$dining_block_seconds = (int) $rtb_controller->settings->get_setting( 'rtb-dining-block-length' ) * 60 - 1; // Take 1 second off, to avoid bookings that start or end exactly at the beginning of a booking block
|
1000 |
+
|
1001 |
+
$tmp = ( new DateTime( $this->date, wp_timezone() ) )->format( 'U' );
|
1002 |
+
$after_time = $tmp - $dining_block_seconds;
|
1003 |
+
$before_time = $tmp + $dining_block_seconds;
|
1004 |
+
|
1005 |
+
$args = array(
|
1006 |
+
'posts_per_page' => -1,
|
1007 |
+
'post_status' => array( 'pending', 'payment_pending', 'confirmed', 'arrived' ),
|
1008 |
+
'date_query' => array(
|
1009 |
+
'before' => date( 'c', $before_time ),
|
1010 |
+
'after' => date( 'c', $after_time )
|
1011 |
+
)
|
1012 |
+
);
|
1013 |
+
|
1014 |
+
// If there are multiple locations, a location is selected, and
|
1015 |
+
// max seats has been enabled for this specific location
|
1016 |
+
if ( ! empty( $location_slug ) and $rtb_controller->settings->is_location_setting_enabled( 'rtb-max-people-count', $location_slug ) ) {
|
1017 |
+
|
1018 |
+
$tax_query = array(
|
1019 |
+
array(
|
1020 |
+
'taxonomy' => $rtb_controller->locations->location_taxonomy,
|
1021 |
+
'field' => 'term_id',
|
1022 |
+
'terms' => $location->term_id
|
1023 |
+
)
|
1024 |
+
);
|
1025 |
+
|
1026 |
+
$args['tax_query'] = $tax_query;
|
1027 |
+
}
|
1028 |
+
|
1029 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Query.class.php' );
|
1030 |
+
$query = new rtbQuery( $args );
|
1031 |
+
|
1032 |
+
$tmzn = wp_timezone();
|
1033 |
+
$times = array();
|
1034 |
+
foreach ( $query->get_bookings() as $booking ) {
|
1035 |
+
|
1036 |
+
if ( isset( $this->ID ) and $booking->ID == $this->ID ) { continue; }
|
1037 |
+
|
1038 |
+
$booking_time = (new DateTime( $booking->date, $tmzn ) )->format( 'U' );
|
1039 |
+
if ( isset( $times[$booking_time] ) ) { $times[$booking_time] += intval( $booking->party ); }
|
1040 |
+
else { $times[$booking_time] = (int) $booking->party; }
|
1041 |
+
}
|
1042 |
+
|
1043 |
+
ksort( $times );
|
1044 |
+
|
1045 |
+
$current_seats = array();
|
1046 |
+
foreach ( $times as $time => $seats ) {
|
1047 |
+
|
1048 |
+
$current_seats[$time] = $seats;
|
1049 |
+
|
1050 |
+
reset( $current_seats );
|
1051 |
+
|
1052 |
+
if ( key ( $current_seats ) < $time - $dining_block_seconds ) { array_shift( $current_seats ); }
|
1053 |
+
|
1054 |
+
// Check if adding the current party puts us above the max confirmation number
|
1055 |
+
if ( array_sum( $current_seats ) + $this->party > $max_seats ) { return false; }
|
1056 |
+
}
|
1057 |
+
|
1058 |
+
return true;
|
1059 |
+
|
1060 |
+
}
|
1061 |
+
|
1062 |
+
/**
|
1063 |
+
* Check if the information in a booking exactly matches another booking
|
1064 |
+
*
|
1065 |
+
* @return bool
|
1066 |
+
* @since 2.1.20
|
1067 |
+
*/
|
1068 |
+
public function is_duplicate_booking() {
|
1069 |
+
global $wpdb, $rtb_controller;
|
1070 |
+
|
1071 |
+
if( 0 < count($this->validation_errors) ) {
|
1072 |
+
/**
|
1073 |
+
* Do not run this check if there is an error already
|
1074 |
+
* There could abe a moment when someminfo could be missing, which is required
|
1075 |
+
* for this qurey to function.
|
1076 |
+
*/
|
1077 |
+
return null;
|
1078 |
+
}
|
1079 |
+
|
1080 |
+
$valid_status = ['confirmed', 'pending'];
|
1081 |
+
|
1082 |
+
// This is an intermediate status when payment is pending
|
1083 |
+
if ( $rtb_controller->settings->get_setting( 'require-deposit' ) ) {
|
1084 |
+
$valid_status = array_merge($valid_status, ['payment_pending']);
|
1085 |
+
}
|
1086 |
+
|
1087 |
+
$args = array_merge(
|
1088 |
+
array(
|
1089 |
+
RTB_BOOKING_POST_TYPE,
|
1090 |
+
$this->date,
|
1091 |
+
$this->name
|
1092 |
+
),
|
1093 |
+
$valid_status
|
1094 |
+
);
|
1095 |
+
|
1096 |
+
$status_placeholder = implode( ',', array_fill( 0, count( $valid_status ), '%s' ) );
|
1097 |
+
|
1098 |
+
$sql = "SELECT ID FROM {$wpdb->posts} WHERE post_type=%s AND post_date=%s AND post_title=%s AND post_status IN ({$status_placeholder})";
|
1099 |
+
|
1100 |
+
if ( isset( $this->ID ) ) {
|
1101 |
+
$sql .= ' AND ID!=%d';
|
1102 |
+
$args[] = $this->ID;
|
1103 |
+
}
|
1104 |
+
|
1105 |
+
$booking_result = $wpdb->get_row( $wpdb->prepare( $sql, $args ) );
|
1106 |
+
|
1107 |
+
if ( $booking_result ) {
|
1108 |
+
$meta = get_post_meta( $booking_result->ID, 'rtb', true );
|
1109 |
+
$meta = is_array( $meta ) ? $meta : array();
|
1110 |
+
|
1111 |
+
if ( $this->party == $meta['party'] and $this->email == $meta['email'] and $this->phone == $meta['phone'] ) {
|
1112 |
+
|
1113 |
+
return true;
|
1114 |
+
}
|
1115 |
+
}
|
1116 |
+
|
1117 |
+
return false;
|
1118 |
+
}
|
1119 |
+
|
1120 |
+
/**
|
1121 |
+
* Should we ask for deposit based on required minimum party size?
|
1122 |
+
* @param string $value [description]
|
1123 |
+
*/
|
1124 |
+
public function is_size_based_deposit_applicable() {
|
1125 |
+
global $rtb_controller;
|
1126 |
+
|
1127 |
+
if ($this->party < $rtb_controller->settings->get_setting( 'rtb-deposit-min-party-size' ) ) {
|
1128 |
+
return false;
|
1129 |
+
}
|
1130 |
+
|
1131 |
+
return true;
|
1132 |
+
}
|
1133 |
+
|
1134 |
+
/**
|
1135 |
+
* Shoudl we ask for deposit or not based on the given schedule?
|
1136 |
+
*
|
1137 |
+
* @since 2.0.0
|
1138 |
+
*/
|
1139 |
+
public function is_time_based_deposit_applicable() {
|
1140 |
+
global $rtb_controller;
|
1141 |
+
|
1142 |
+
$deposit_applicable = is_array( $rtb_controller->settings->get_setting( 'rtb-deposit-schedule' ) )
|
1143 |
+
? $rtb_controller->settings->get_setting( 'rtb-deposit-schedule' )
|
1144 |
+
: array();
|
1145 |
+
|
1146 |
+
// Get any rules which apply to this weekday
|
1147 |
+
if ( $deposit_applicable != 'undefined' ) {
|
1148 |
+
|
1149 |
+
$tmzn = wp_timezone();
|
1150 |
+
$date_object = new DateTime( $this->date, $tmzn );
|
1151 |
+
|
1152 |
+
$time = $date_object->format( 'U' );
|
1153 |
+
|
1154 |
+
$day_of_week = strtolower( $date_object->format( 'l' ) );
|
1155 |
+
|
1156 |
+
foreach ( $deposit_applicable as $applicable ) {
|
1157 |
+
|
1158 |
+
if ( $applicable['weekdays'] !== 'undefined' ) {
|
1159 |
+
|
1160 |
+
foreach ( $applicable['weekdays'] as $weekday => $value ) {
|
1161 |
+
|
1162 |
+
if ( $weekday == $day_of_week ) {
|
1163 |
+
|
1164 |
+
// applicable all day
|
1165 |
+
if ( !isset( $applicable['time'] ) || $applicable['time'] == 'undefined' ) {
|
1166 |
+
|
1167 |
+
return true;
|
1168 |
+
}
|
1169 |
+
|
1170 |
+
if ( $applicable['time']['start'] !== 'undefined' ) {
|
1171 |
+
|
1172 |
+
$applicable_start_time = ( new DateTime( $date_object->format( 'Y-m-d' ) . ' ' . $applicable['time']['start'], $tmzn ) )->format( 'U' );
|
1173 |
+
}
|
1174 |
+
else {
|
1175 |
+
|
1176 |
+
$applicable_start_time = ( new DateTime( $date_object->format( 'Y-m-d' ), $tmzn ) )->format( 'U' );
|
1177 |
+
}
|
1178 |
+
|
1179 |
+
if ( $applicable['time']['end'] !== 'undefined' ) {
|
1180 |
+
|
1181 |
+
$applicable_end_time = ( new DateTime( $date_object->format( 'Y-m-d' ) . ' ' . $applicable['time']['end'], $tmzn ) )->format( 'U' );
|
1182 |
+
}
|
1183 |
+
else {
|
1184 |
+
// End of the day
|
1185 |
+
$applicable_end_time = ( new DateTime( $date_object->format( 'Y-m-d' ) . ' 23:59:59', $tmzn ) )->format( 'U' );
|
1186 |
+
}
|
1187 |
+
|
1188 |
+
if ( $time > $applicable_start_time and $time < $applicable_end_time ) {
|
1189 |
+
|
1190 |
+
return true;
|
1191 |
+
}
|
1192 |
+
}
|
1193 |
+
}
|
1194 |
+
}
|
1195 |
+
}
|
1196 |
+
}
|
1197 |
+
|
1198 |
+
return false;
|
1199 |
+
}
|
1200 |
+
|
1201 |
+
/**
|
1202 |
+
* Check whether the number of reservations occurring at the same time is below the threshold
|
1203 |
+
* where reservations get automatically confirmed
|
1204 |
+
*
|
1205 |
+
* @since 2.0.0
|
1206 |
+
*/
|
1207 |
+
public function under_max_confirm_reservations() {
|
1208 |
+
global $rtb_controller;
|
1209 |
+
|
1210 |
+
$max_reservations = (int) $rtb_controller->settings->get_setting( 'auto-confirm-max-reservations' );
|
1211 |
+
|
1212 |
+
if ( $max_reservations == 'undefined' or $max_reservations <= 1 ) { return false; }
|
1213 |
+
|
1214 |
+
$dining_block_seconds = (int) $rtb_controller->settings->get_setting( 'rtb-dining-block-length' ) * 60 - 1; // Take 1 second off, to avoid bookings that start or end exactly at the beginning of a booking block
|
1215 |
+
|
1216 |
+
$tmp = (new DateTime( $this->date, wp_timezone() ) )->format( 'U' );
|
1217 |
+
$after_time = $tmp - $dining_block_seconds;
|
1218 |
+
$before_time = $tmp + $dining_block_seconds;
|
1219 |
+
|
1220 |
+
$args = array(
|
1221 |
+
'posts_per_page' => -1,
|
1222 |
+
'post_status' => ['confirmed', 'arrived'],
|
1223 |
+
'date_query' => array(
|
1224 |
+
'before' => date( 'c', $before_time ),
|
1225 |
+
'after' => date( 'c', $after_time )
|
1226 |
+
)
|
1227 |
+
);
|
1228 |
+
|
1229 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Query.class.php' );
|
1230 |
+
$query = new rtbQuery( $args );
|
1231 |
+
$query->prepare_args();
|
1232 |
+
|
1233 |
+
$tmzn = wp_timezone();
|
1234 |
+
$times = array();
|
1235 |
+
foreach ( $query->get_bookings() as $booking ) {
|
1236 |
+
$times[] = (new DateTime( $booking->date, $tmzn ) )->format( "U" );
|
1237 |
+
}
|
1238 |
+
|
1239 |
+
sort( $times );
|
1240 |
+
|
1241 |
+
$auto_confirm = true;
|
1242 |
+
$current_times = array();
|
1243 |
+
foreach ( $times as $time ) {
|
1244 |
+
$current_times[] = $time;
|
1245 |
+
|
1246 |
+
if ( reset( $current_times ) < ($time - $dining_block_seconds) ) { array_shift( $current_times ); }
|
1247 |
+
|
1248 |
+
// Check if we've reached 1 below the max confirmation number, since adding the current booking will put us at the threshold
|
1249 |
+
if ( sizeOf( $current_times ) + 1 >= $max_reservations ) { $auto_confirm = false; break; }
|
1250 |
+
}
|
1251 |
+
|
1252 |
+
return $auto_confirm;
|
1253 |
+
}
|
1254 |
+
|
1255 |
+
/**
|
1256 |
+
* Check whether the number of seats occurring at the same time is below the threshold
|
1257 |
+
* where reservations get automatically confirmed
|
1258 |
+
*
|
1259 |
+
* @since 2.0.0
|
1260 |
+
*/
|
1261 |
+
public function under_max_confirm_seats() {
|
1262 |
+
global $rtb_controller;
|
1263 |
+
|
1264 |
+
$max_seats = (int) $rtb_controller->settings->get_setting( 'auto-confirm-max-seats' );
|
1265 |
+
|
1266 |
+
if ( $max_seats == 'undefined' or $max_seats < 2 or $this->party >= $max_seats ) { return false; }
|
1267 |
+
|
1268 |
+
$dining_block_seconds = (int) $rtb_controller->settings->get_setting( 'rtb-dining-block-length' ) * 60 - 1; // Take 1 second off, to avoid bookings that start or end exactly at the beginning of a booking block
|
1269 |
+
|
1270 |
+
$tmp = (new DateTime( $this->date, wp_timezone() ) )->format( 'U' );
|
1271 |
+
$after_time = $tmp - $dining_block_seconds;
|
1272 |
+
$before_time = $tmp + $dining_block_seconds;
|
1273 |
+
|
1274 |
+
$args = array(
|
1275 |
+
'posts_per_page' => -1,
|
1276 |
+
'post_status' => ['confirmed', 'arrived'],
|
1277 |
+
'date_query' => array(
|
1278 |
+
'before' => date( 'c', $before_time ),
|
1279 |
+
'after' => date( 'c', $after_time )
|
1280 |
+
)
|
1281 |
+
);
|
1282 |
+
|
1283 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Query.class.php' );
|
1284 |
+
$query = new rtbQuery( $args );
|
1285 |
+
|
1286 |
+
$tmzn = wp_timezone();
|
1287 |
+
$times = array();
|
1288 |
+
foreach ( $query->get_bookings() as $booking ) {
|
1289 |
+
$booking_time = ( new DateTime( $booking->date, $tmzn ) )->format( 'U' );
|
1290 |
+
if ( isset( $times[$booking_time] ) ) { $times[$booking_time] += $booking->party; }
|
1291 |
+
else { $times[$booking_time] = $booking->party; }
|
1292 |
+
}
|
1293 |
+
|
1294 |
+
ksort( $times );
|
1295 |
+
|
1296 |
+
$auto_confirm = true;
|
1297 |
+
$current_seats = array();
|
1298 |
+
foreach ( $times as $time => $seats ) {
|
1299 |
+
$current_seats[$time] = $seats;
|
1300 |
+
|
1301 |
+
reset( $current_seats );
|
1302 |
+
|
1303 |
+
if ( key ( $current_seats ) < $time - $dining_block_seconds ) { array_shift( $current_seats ); }
|
1304 |
+
|
1305 |
+
// Check if adding the current party puts us at or above the max confirmation number
|
1306 |
+
if ( array_sum( $current_seats ) + $this->party >= $max_seats ) { $auto_confirm = false; break; }
|
1307 |
+
}
|
1308 |
+
|
1309 |
+
return $auto_confirm;
|
1310 |
+
}
|
1311 |
+
|
1312 |
+
/**
|
1313 |
+
* Determine what status a booking should have
|
1314 |
+
*
|
1315 |
+
* @since 2.1.0
|
1316 |
+
*/
|
1317 |
+
public function determine_status( $payment_made = false ) {
|
1318 |
+
global $rtb_controller;
|
1319 |
+
|
1320 |
+
if ( !empty( $_POST['rtb-post-status'] ) && array_key_exists( $_POST['rtb-post-status'], $rtb_controller->cpts->booking_statuses ) ) {
|
1321 |
+
$post_status = sanitize_text_field( $_POST['rtb-post-status'] );
|
1322 |
+
} elseif (
|
1323 |
+
(
|
1324 |
+
$rtb_controller->settings->get_setting( 'require-deposit' )
|
1325 |
+
&& ! $payment_made
|
1326 |
+
&& ! $this->is_conditional_deposit_enabled()
|
1327 |
+
)
|
1328 |
+
||
|
1329 |
+
(
|
1330 |
+
$rtb_controller->settings->get_setting( 'require-deposit' )
|
1331 |
+
&& ! $payment_made
|
1332 |
+
&& $this->is_conditional_deposit_enabled()
|
1333 |
+
&&
|
1334 |
+
(
|
1335 |
+
( $this->is_time_based_deposit_enabled() && $this->is_time_based_deposit_applicable() )
|
1336 |
+
||
|
1337 |
+
( $this->is_size_based_deposit_enabled() && $this->is_size_based_deposit_applicable() )
|
1338 |
+
)
|
1339 |
+
)
|
1340 |
+
) {
|
1341 |
+
$post_status = 'payment_pending';
|
1342 |
+
} elseif ( $this->party < $rtb_controller->settings->get_setting( 'auto-confirm-max-party-size' ) ) {
|
1343 |
+
$post_status = 'confirmed';
|
1344 |
+
} elseif ($rtb_controller->settings->get_setting( 'auto-confirm-max-reservations' ) and $this->under_max_confirm_reservations() ) {
|
1345 |
+
$post_status = 'confirmed';
|
1346 |
+
} elseif ( $rtb_controller->settings->get_setting( 'auto-confirm-max-seats' ) and $this->under_max_confirm_seats() ) {
|
1347 |
+
$post_status = 'confirmed';
|
1348 |
+
} else {
|
1349 |
+
$post_status = 'pending';
|
1350 |
+
}
|
1351 |
+
|
1352 |
+
$this->post_status = apply_filters( 'rtb_determine_booking_status', $post_status, $this );
|
1353 |
+
}
|
1354 |
+
|
1355 |
+
/**
|
1356 |
+
* Add a log entry to the booking
|
1357 |
+
*
|
1358 |
+
* @since 1.3.1
|
1359 |
+
*/
|
1360 |
+
public function add_log( $type, $title, $message = '', $datetime = null ) {
|
1361 |
+
|
1362 |
+
if ( empty( $datetime ) ) {
|
1363 |
+
$datetime = date( 'Y-m-d H:i:s');
|
1364 |
+
}
|
1365 |
+
|
1366 |
+
if ( empty( $this->logs ) ) {
|
1367 |
+
$this->logs = array();
|
1368 |
+
}
|
1369 |
+
|
1370 |
+
array_push( $this->logs, array( $type, $title, $message, $datetime ) );
|
1371 |
+
}
|
1372 |
+
|
1373 |
+
/**
|
1374 |
+
* Insert post data for a new booking or update a booking
|
1375 |
+
* @since 0.0.1
|
1376 |
+
*/
|
1377 |
+
public function insert_post_data() {
|
1378 |
+
|
1379 |
+
$args = array(
|
1380 |
+
'post_type' => RTB_BOOKING_POST_TYPE,
|
1381 |
+
'post_title' => $this->name,
|
1382 |
+
'post_content' => $this->message,
|
1383 |
+
'post_date' => $this->date,
|
1384 |
+
'post_date_gmt' => get_gmt_from_date( $this->date ), // fix for post_date_gmt not being set for some bookings
|
1385 |
+
'post_status' => $this->post_status,
|
1386 |
+
);
|
1387 |
+
|
1388 |
+
if ( !empty( $this->ID ) ) {
|
1389 |
+
$args['ID'] = $this->ID;
|
1390 |
+
}
|
1391 |
+
|
1392 |
+
$args = apply_filters( 'rtb_insert_booking_data', $args, $this );
|
1393 |
+
|
1394 |
+
// When updating a booking, we need to update the metadata first, so that
|
1395 |
+
// notifications hooked to the status changes go out with the new metadata.
|
1396 |
+
// If we're inserting a new booking, we have to insert it before we can
|
1397 |
+
// add metadata, and the default notifications don't fire until it's all done.
|
1398 |
+
if ( !empty( $this->ID ) ) {
|
1399 |
+
$this->insert_post_meta();
|
1400 |
+
$id = wp_insert_post( $args );
|
1401 |
+
} else {
|
1402 |
+
$id = wp_insert_post( $args );
|
1403 |
+
if ( $id && !is_wp_error( $id ) ) {
|
1404 |
+
$this->ID = $id;
|
1405 |
+
$this->insert_post_meta();
|
1406 |
+
}
|
1407 |
+
}
|
1408 |
+
|
1409 |
+
return !is_wp_error( $id ) && $id !== false;
|
1410 |
+
}
|
1411 |
+
|
1412 |
+
/**
|
1413 |
+
* Insert the post metadata for a new booking or when updating a booking
|
1414 |
+
* @since 1.7.7
|
1415 |
+
*/
|
1416 |
+
public function insert_post_meta() {
|
1417 |
+
|
1418 |
+
$meta = array(
|
1419 |
+
'party' => $this->party,
|
1420 |
+
'email' => $this->email,
|
1421 |
+
'phone' => $this->phone,
|
1422 |
+
);
|
1423 |
+
|
1424 |
+
if ( !empty( $this->ip ) ) {
|
1425 |
+
$meta['ip'] = $this->ip;
|
1426 |
+
}
|
1427 |
+
|
1428 |
+
if ( empty( $this->date_submission ) ) {
|
1429 |
+
$meta['date_submission'] = (new DateTime( 'now', wp_timezone() ))->format( 'U' );
|
1430 |
+
} else {
|
1431 |
+
$meta['date_submission'] = $this->date_submission instanceof DateTime
|
1432 |
+
? $this->date_submission->format('U')
|
1433 |
+
: $this->date_submission;
|
1434 |
+
}
|
1435 |
+
|
1436 |
+
if ( !empty( $this->consent_acquired ) ) {
|
1437 |
+
$meta['consent_acquired'] = $this->consent_acquired;
|
1438 |
+
}
|
1439 |
+
|
1440 |
+
if ( !empty( $this->logs ) ) {
|
1441 |
+
$meta['logs'] = $this->logs;
|
1442 |
+
}
|
1443 |
+
|
1444 |
+
if ( !empty( $this->table ) ) {
|
1445 |
+
$meta['table'] = $this->table;
|
1446 |
+
}
|
1447 |
+
|
1448 |
+
if ( !empty( $this->deposit ) ) {
|
1449 |
+
$meta['deposit'] = $this->deposit;
|
1450 |
+
}
|
1451 |
+
|
1452 |
+
if ( !empty( $this->payment_failure_message ) ) {
|
1453 |
+
$meta['payment_failure_message'] = $this->payment_failure_message;
|
1454 |
+
}
|
1455 |
+
|
1456 |
+
if ( !empty( $this->receipt_id ) ) {
|
1457 |
+
$meta['receipt_id'] = $this->receipt_id;
|
1458 |
+
}
|
1459 |
+
|
1460 |
+
if ( !empty( $this->reminder_sent ) ) {
|
1461 |
+
$meta['reminder_sent'] = $this->reminder_sent;
|
1462 |
+
}
|
1463 |
+
|
1464 |
+
if ( !empty( $this->late_arrival_sent ) ) {
|
1465 |
+
$meta['late_arrival_sent'] = $this->late_arrival_sent;
|
1466 |
+
}
|
1467 |
+
|
1468 |
+
$meta = apply_filters( 'rtb_insert_booking_metadata', $meta, $this );
|
1469 |
+
|
1470 |
+
return update_post_meta( $this->ID, 'rtb', $meta );
|
1471 |
+
}
|
1472 |
+
|
1473 |
+
public function payment_paid()
|
1474 |
+
{
|
1475 |
+
if( isset( $this->ID ) ) {
|
1476 |
+
$this->determine_status( true );
|
1477 |
+
|
1478 |
+
$this->insert_post_data();
|
1479 |
+
|
1480 |
+
do_action( 'rtb_booking_paid', $this );
|
1481 |
+
}
|
1482 |
+
}
|
1483 |
+
|
1484 |
+
public function payment_failed( $message = '' )
|
1485 |
+
{
|
1486 |
+
$this->post_status = 'payment_failed';
|
1487 |
+
$this->payment_failure_message = $message;
|
1488 |
+
|
1489 |
+
$this->insert_post_data();
|
1490 |
+
|
1491 |
+
do_action( 'rtb_booking_paid', $this );
|
1492 |
+
}
|
1493 |
+
|
1494 |
+
function is_conditional_deposit_enabled() {
|
1495 |
+
global $rtb_controller;
|
1496 |
+
return in_array( $rtb_controller->settings->get_setting( 'rtb-deposit-applicable' ), ['time_based', 'size_based'] );
|
1497 |
+
}
|
1498 |
+
|
1499 |
+
function is_time_based_deposit_enabled() {
|
1500 |
+
global $rtb_controller;
|
1501 |
+
return 'time_based' === $rtb_controller->settings->get_setting( 'rtb-deposit-applicable' );
|
1502 |
+
}
|
1503 |
+
|
1504 |
+
function is_size_based_deposit_enabled() {
|
1505 |
+
global $rtb_controller;
|
1506 |
+
return 'size_based' === $rtb_controller->settings->get_setting( 'rtb-deposit-applicable' );
|
1507 |
+
}
|
1508 |
+
|
1509 |
+
}
|
1510 |
+
} // endif;
|
includes/Compatibility.class.php
CHANGED
@@ -1,191 +1,191 @@
|
|
1 |
-
<?php
|
2 |
-
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
-
|
4 |
-
if ( !class_exists( 'rtbCompatibility' ) ) {
|
5 |
-
/**
|
6 |
-
* Class to handle backwards compatibilty and cross-plugin compatibility issues
|
7 |
-
* for Restaurant Reservations
|
8 |
-
*
|
9 |
-
* @since 1.3
|
10 |
-
*/
|
11 |
-
class rtbCompatibility {
|
12 |
-
|
13 |
-
/**
|
14 |
-
* Set up hooks
|
15 |
-
*/
|
16 |
-
public function __construct() {
|
17 |
-
|
18 |
-
// Preserve defined constants in case anyone relied on them
|
19 |
-
// to check if the plugin was active
|
20 |
-
define( 'RTB_TEXTDOMAIN', 'rtbdomain' );
|
21 |
-
define( 'RTB_LOAD_FRONTEND_ASSETS', apply_filters( 'rtb-load-frontend-assets', true ) );
|
22 |
-
|
23 |
-
// Load a .mo file for an old textdomain if one exists
|
24 |
-
add_filter( 'load_textdomain_mofile', array( $this, 'load_old_textdomain' ), 10, 2 );
|
25 |
-
|
26 |
-
// Run a filter deprecrated in 1.4.3
|
27 |
-
add_filter( 'rtb_bookings_table_views_date_range', array( $this, 'rtn_bookings_table_views_schedule' ) );
|
28 |
-
|
29 |
-
add_action( 'admin_init', array($this, 'check_rtb_version' ) );
|
30 |
-
|
31 |
-
// Make sure custom fields don't completely disappear in 1.5, should no longer be needed
|
32 |
-
//add_action( 'admin_init', array( $this, 'maybe_bridge_cffrtb_to_1_5' ) );
|
33 |
-
|
34 |
-
}
|
35 |
-
|
36 |
-
/**
|
37 |
-
* Load a .mo file for an old textdomain if one exists
|
38 |
-
*
|
39 |
-
* In versions prior to 1.3, the textdomain did not match the plugin
|
40 |
-
* slug. This had to be changed to comply with upcoming changes to
|
41 |
-
* how translations are managed in the .org repo. This function
|
42 |
-
* checks to see if an old translation file exists and loads it if
|
43 |
-
* it does, so that people don't lose their translations.
|
44 |
-
*
|
45 |
-
* Old textdomain: rtbdomain
|
46 |
-
*/
|
47 |
-
public function load_old_textdomain( $mofile, $textdomain ) {
|
48 |
-
|
49 |
-
if ( $textdomain === 'restaurant-reservations' && 0 === strpos( $mofile, WP_LANG_DIR . '/plugins/' ) && !file_exists( $mofile ) ) {
|
50 |
-
$mofile = dirname( $mofile ) . DIRECTORY_SEPARATOR . str_replace( $textdomain, 'rtbdomain', basename( $mofile ) );
|
51 |
-
}
|
52 |
-
|
53 |
-
return $mofile;
|
54 |
-
}
|
55 |
-
|
56 |
-
/**
|
57 |
-
* Run a filter on the admin bookings page display views that was
|
58 |
-
* deprecrated in v1.4.3
|
59 |
-
*
|
60 |
-
* @since 1.4.3
|
61 |
-
*/
|
62 |
-
public function rtn_bookings_table_views_schedule( $views ) {
|
63 |
-
return apply_filters( 'rtn_bookings_table_views_schedule', $views );
|
64 |
-
}
|
65 |
-
|
66 |
-
public function check_rtb_version() {
|
67 |
-
global $rtb_controller;
|
68 |
-
|
69 |
-
if ( get_option( 'rtb-version' ) != RTB_VERSION ) {
|
70 |
-
$rtb_controller->permissions->set_permissions();
|
71 |
-
|
72 |
-
update_option( 'rtb-version', RTB_VERSION );
|
73 |
-
}
|
74 |
-
}
|
75 |
-
|
76 |
-
/**
|
77 |
-
* Check whether or not we need to run some compatibiilty code for older
|
78 |
-
* versions of the Custom Fields addon.
|
79 |
-
*
|
80 |
-
* @since 0.1 // THIS FUNCTION SHOULD NO LONGER BE NECESSARY AFTER MERGING CODE BASES
|
81 |
-
*/
|
82 |
-
/*public function maybe_bridge_cffrtb_to_1_5() {
|
83 |
-
if ( !function_exists( 'cffrtbInit' ) || !function_exists( 'get_plugin_data' ) ) {
|
84 |
-
return;
|
85 |
-
}
|
86 |
-
|
87 |
-
$cffrtb = get_plugin_data( cffrtbInit::$plugin_dir . '/custom-fields-for-rtb.php', false );
|
88 |
-
if ( !isset( $cffrtb['Version'] ) || $cffrtb['Version'] >= 1.2 ) {
|
89 |
-
return;
|
90 |
-
}
|
91 |
-
|
92 |
-
add_filter( 'rtb_bookings_table_column_details', array( $this, 'add_cffrtb_fields_to_details' ), 11, 2 );
|
93 |
-
}*/
|
94 |
-
|
95 |
-
/**
|
96 |
-
* Add custom fields output to details column
|
97 |
-
*
|
98 |
-
* This function eases the transition to v1.5 for users of the Custom Fields
|
99 |
-
* addon. Some of the details around how the custom field data is added to
|
100 |
-
* the bookings table was changed. If the user is using an outdated version
|
101 |
-
* of Custom Fields, this function will ensure the data gets dropped into
|
102 |
-
* the new details column. There is still some wonky behavior: the custom
|
103 |
-
* fields appear as potential columns but adding them won't do anything.
|
104 |
-
* But this will at least ensure that the data doesn't disappear and users
|
105 |
-
* can then update to get the full functionality.
|
106 |
-
*
|
107 |
-
* @since 1.5 // THIS FUNCTION SHOULD NO LONGER BE NECESSARY AFTER MERGING CODE BASES
|
108 |
-
*/
|
109 |
-
/*public function add_cffrtb_fields_to_details( $details, $booking ) {
|
110 |
-
|
111 |
-
if ( !isset( $booking->custom_fields ) ) {
|
112 |
-
return $details;
|
113 |
-
}
|
114 |
-
|
115 |
-
$fields = cffrtbInit()->fields->get_booking_fields_display_array( $booking );
|
116 |
-
foreach( $fields as $field ) {
|
117 |
-
$details[] = array(
|
118 |
-
'label' => $field['title'],
|
119 |
-
'value' => $field['display_val'],
|
120 |
-
);
|
121 |
-
}
|
122 |
-
|
123 |
-
return $details;
|
124 |
-
}*/
|
125 |
-
}
|
126 |
-
} // endif
|
127 |
-
|
128 |
-
/**
|
129 |
-
* This adds a function missing in PHP versions less than 5.3 which is used to
|
130 |
-
* properly format non-standard Latin characters in the name portion of an
|
131 |
-
* email's Reply-To headers. The name variable is passed through this function
|
132 |
-
* before being added to the headers.
|
133 |
-
*
|
134 |
-
* If it detects that the function already exists, it will do nothing.
|
135 |
-
*
|
136 |
-
* From: http://php.net/manual/en/function.quoted-printable-encode.php#115840#
|
137 |
-
*/
|
138 |
-
if ( !function_exists( 'quoted_printable_encode' ) ) {
|
139 |
-
function quoted_printable_encode($str) {
|
140 |
-
$php_qprint_maxl = 75;
|
141 |
-
$lp = 0;
|
142 |
-
$ret = '';
|
143 |
-
$hex = "0123456789ABCDEF";
|
144 |
-
$length = strlen($str);
|
145 |
-
$str_index = 0;
|
146 |
-
|
147 |
-
while ($length--) {
|
148 |
-
if ((($c = $str[$str_index++]) == "\015") && ($str[$str_index] == "\012") && $length > 0) {
|
149 |
-
$ret .= "\015";
|
150 |
-
$ret .= $str[$str_index++];
|
151 |
-
$length--;
|
152 |
-
$lp = 0;
|
153 |
-
} else {
|
154 |
-
if (ctype_cntrl($c)
|
155 |
-
|| (ord($c) == 0x7f)
|
156 |
-
|| (ord($c) & 0x80)
|
157 |
-
|| ($c == '=')
|
158 |
-
|| (($c == ' ') && ($str[$str_index] == "\015")))
|
159 |
-
{
|
160 |
-
if (($lp += 3) > $php_qprint_maxl)
|
161 |
-
{
|
162 |
-
$ret .= '=';
|
163 |
-
$ret .= "\015";
|
164 |
-
$ret .= "\012";
|
165 |
-
$lp = 3;
|
166 |
-
}
|
167 |
-
$ret .= '=';
|
168 |
-
$ret .= $hex[ord($c) >> 4];
|
169 |
-
$ret .= $hex[ord($c) & 0xf];
|
170 |
-
}
|
171 |
-
else
|
172 |
-
{
|
173 |
-
if ((++$lp) > $php_qprint_maxl)
|
174 |
-
{
|
175 |
-
$ret .= '=';
|
176 |
-
$ret .= "\015";
|
177 |
-
$ret .= "\012";
|
178 |
-
$lp = 1;
|
179 |
-
}
|
180 |
-
$ret .= $c;
|
181 |
-
if($lp == 1 && $c == '.') {
|
182 |
-
$ret .= '.';
|
183 |
-
$lp++;
|
184 |
-
}
|
185 |
-
}
|
186 |
-
}
|
187 |
-
}
|
188 |
-
|
189 |
-
return $ret;
|
190 |
-
}
|
191 |
-
} // endif
|
1 |
+
<?php
|
2 |
+
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
+
|
4 |
+
if ( !class_exists( 'rtbCompatibility' ) ) {
|
5 |
+
/**
|
6 |
+
* Class to handle backwards compatibilty and cross-plugin compatibility issues
|
7 |
+
* for Restaurant Reservations
|
8 |
+
*
|
9 |
+
* @since 1.3
|
10 |
+
*/
|
11 |
+
class rtbCompatibility {
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Set up hooks
|
15 |
+
*/
|
16 |
+
public function __construct() {
|
17 |
+
|
18 |
+
// Preserve defined constants in case anyone relied on them
|
19 |
+
// to check if the plugin was active
|
20 |
+
define( 'RTB_TEXTDOMAIN', 'rtbdomain' );
|
21 |
+
define( 'RTB_LOAD_FRONTEND_ASSETS', apply_filters( 'rtb-load-frontend-assets', true ) );
|
22 |
+
|
23 |
+
// Load a .mo file for an old textdomain if one exists
|
24 |
+
add_filter( 'load_textdomain_mofile', array( $this, 'load_old_textdomain' ), 10, 2 );
|
25 |
+
|
26 |
+
// Run a filter deprecrated in 1.4.3
|
27 |
+
add_filter( 'rtb_bookings_table_views_date_range', array( $this, 'rtn_bookings_table_views_schedule' ) );
|
28 |
+
|
29 |
+
add_action( 'admin_init', array($this, 'check_rtb_version' ) );
|
30 |
+
|
31 |
+
// Make sure custom fields don't completely disappear in 1.5, should no longer be needed
|
32 |
+
//add_action( 'admin_init', array( $this, 'maybe_bridge_cffrtb_to_1_5' ) );
|
33 |
+
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Load a .mo file for an old textdomain if one exists
|
38 |
+
*
|
39 |
+
* In versions prior to 1.3, the textdomain did not match the plugin
|
40 |
+
* slug. This had to be changed to comply with upcoming changes to
|
41 |
+
* how translations are managed in the .org repo. This function
|
42 |
+
* checks to see if an old translation file exists and loads it if
|
43 |
+
* it does, so that people don't lose their translations.
|
44 |
+
*
|
45 |
+
* Old textdomain: rtbdomain
|
46 |
+
*/
|
47 |
+
public function load_old_textdomain( $mofile, $textdomain ) {
|
48 |
+
|
49 |
+
if ( $textdomain === 'restaurant-reservations' && 0 === strpos( $mofile, WP_LANG_DIR . '/plugins/' ) && !file_exists( $mofile ) ) {
|
50 |
+
$mofile = dirname( $mofile ) . DIRECTORY_SEPARATOR . str_replace( $textdomain, 'rtbdomain', basename( $mofile ) );
|
51 |
+
}
|
52 |
+
|
53 |
+
return $mofile;
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Run a filter on the admin bookings page display views that was
|
58 |
+
* deprecrated in v1.4.3
|
59 |
+
*
|
60 |
+
* @since 1.4.3
|
61 |
+
*/
|
62 |
+
public function rtn_bookings_table_views_schedule( $views ) {
|
63 |
+
return apply_filters( 'rtn_bookings_table_views_schedule', $views );
|
64 |
+
}
|
65 |
+
|
66 |
+
public function check_rtb_version() {
|
67 |
+
global $rtb_controller;
|
68 |
+
|
69 |
+
if ( get_option( 'rtb-version' ) != RTB_VERSION ) {
|
70 |
+
$rtb_controller->permissions->set_permissions();
|
71 |
+
|
72 |
+
update_option( 'rtb-version', RTB_VERSION );
|
73 |
+
}
|
74 |
+
}
|
75 |
+
|
76 |
+
/**
|
77 |
+
* Check whether or not we need to run some compatibiilty code for older
|
78 |
+
* versions of the Custom Fields addon.
|
79 |
+
*
|
80 |
+
* @since 0.1 // THIS FUNCTION SHOULD NO LONGER BE NECESSARY AFTER MERGING CODE BASES
|
81 |
+
*/
|
82 |
+
/*public function maybe_bridge_cffrtb_to_1_5() {
|
83 |
+
if ( !function_exists( 'cffrtbInit' ) || !function_exists( 'get_plugin_data' ) ) {
|
84 |
+
return;
|
85 |
+
}
|
86 |
+
|
87 |
+
$cffrtb = get_plugin_data( cffrtbInit::$plugin_dir . '/custom-fields-for-rtb.php', false );
|
88 |
+
if ( !isset( $cffrtb['Version'] ) || $cffrtb['Version'] >= 1.2 ) {
|
89 |
+
return;
|
90 |
+
}
|
91 |
+
|
92 |
+
add_filter( 'rtb_bookings_table_column_details', array( $this, 'add_cffrtb_fields_to_details' ), 11, 2 );
|
93 |
+
}*/
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Add custom fields output to details column
|
97 |
+
*
|
98 |
+
* This function eases the transition to v1.5 for users of the Custom Fields
|
99 |
+
* addon. Some of the details around how the custom field data is added to
|
100 |
+
* the bookings table was changed. If the user is using an outdated version
|
101 |
+
* of Custom Fields, this function will ensure the data gets dropped into
|
102 |
+
* the new details column. There is still some wonky behavior: the custom
|
103 |
+
* fields appear as potential columns but adding them won't do anything.
|
104 |
+
* But this will at least ensure that the data doesn't disappear and users
|
105 |
+
* can then update to get the full functionality.
|
106 |
+
*
|
107 |
+
* @since 1.5 // THIS FUNCTION SHOULD NO LONGER BE NECESSARY AFTER MERGING CODE BASES
|
108 |
+
*/
|
109 |
+
/*public function add_cffrtb_fields_to_details( $details, $booking ) {
|
110 |
+
|
111 |
+
if ( !isset( $booking->custom_fields ) ) {
|
112 |
+
return $details;
|
113 |
+
}
|
114 |
+
|
115 |
+
$fields = cffrtbInit()->fields->get_booking_fields_display_array( $booking );
|
116 |
+
foreach( $fields as $field ) {
|
117 |
+
$details[] = array(
|
118 |
+
'label' => $field['title'],
|
119 |
+
'value' => $field['display_val'],
|
120 |
+
);
|
121 |
+
}
|
122 |
+
|
123 |
+
return $details;
|
124 |
+
}*/
|
125 |
+
}
|
126 |
+
} // endif
|
127 |
+
|
128 |
+
/**
|
129 |
+
* This adds a function missing in PHP versions less than 5.3 which is used to
|
130 |
+
* properly format non-standard Latin characters in the name portion of an
|
131 |
+
* email's Reply-To headers. The name variable is passed through this function
|
132 |
+
* before being added to the headers.
|
133 |
+
*
|
134 |
+
* If it detects that the function already exists, it will do nothing.
|
135 |
+
*
|
136 |
+
* From: http://php.net/manual/en/function.quoted-printable-encode.php#115840#
|
137 |
+
*/
|
138 |
+
if ( !function_exists( 'quoted_printable_encode' ) ) {
|
139 |
+
function quoted_printable_encode($str) {
|
140 |
+
$php_qprint_maxl = 75;
|
141 |
+
$lp = 0;
|
142 |
+
$ret = '';
|
143 |
+
$hex = "0123456789ABCDEF";
|
144 |
+
$length = strlen($str);
|
145 |
+
$str_index = 0;
|
146 |
+
|
147 |
+
while ($length--) {
|
148 |
+
if ((($c = $str[$str_index++]) == "\015") && ($str[$str_index] == "\012") && $length > 0) {
|
149 |
+
$ret .= "\015";
|
150 |
+
$ret .= $str[$str_index++];
|
151 |
+
$length--;
|
152 |
+
$lp = 0;
|
153 |
+
} else {
|
154 |
+
if (ctype_cntrl($c)
|
155 |
+
|| (ord($c) == 0x7f)
|
156 |
+
|| (ord($c) & 0x80)
|
157 |
+
|| ($c == '=')
|
158 |
+
|| (($c == ' ') && ($str[$str_index] == "\015")))
|
159 |
+
{
|
160 |
+
if (($lp += 3) > $php_qprint_maxl)
|
161 |
+
{
|
162 |
+
$ret .= '=';
|
163 |
+
$ret .= "\015";
|
164 |
+
$ret .= "\012";
|
165 |
+
$lp = 3;
|
166 |
+
}
|
167 |
+
$ret .= '=';
|
168 |
+
$ret .= $hex[ord($c) >> 4];
|
169 |
+
$ret .= $hex[ord($c) & 0xf];
|
170 |
+
}
|
171 |
+
else
|
172 |
+
{
|
173 |
+
if ((++$lp) > $php_qprint_maxl)
|
174 |
+
{
|
175 |
+
$ret .= '=';
|
176 |
+
$ret .= "\015";
|
177 |
+
$ret .= "\012";
|
178 |
+
$lp = 1;
|
179 |
+
}
|
180 |
+
$ret .= $c;
|
181 |
+
if($lp == 1 && $c == '.') {
|
182 |
+
$ret .= '.';
|
183 |
+
$lp++;
|
184 |
+
}
|
185 |
+
}
|
186 |
+
}
|
187 |
+
}
|
188 |
+
|
189 |
+
return $ret;
|
190 |
+
}
|
191 |
+
} // endif
|
includes/Cron.class.php
CHANGED
@@ -1,252 +1,252 @@
|
|
1 |
-
<?php
|
2 |
-
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
-
|
4 |
-
if ( !class_exists( 'rtbCron' ) ) {
|
5 |
-
/**
|
6 |
-
* This class handles scheduling of cron jobs for different notifications
|
7 |
-
* such as reservation reminders or when customers are late for their reservations
|
8 |
-
*
|
9 |
-
* @since 2.0.0
|
10 |
-
*/
|
11 |
-
class rtbCron {
|
12 |
-
|
13 |
-
/**
|
14 |
-
* Adds the necessary filter and action calls
|
15 |
-
* @since 2.0.0
|
16 |
-
*/
|
17 |
-
public function __construct() {
|
18 |
-
add_filter( 'cron_schedules', array($this, 'add_cron_interval') );
|
19 |
-
|
20 |
-
add_action( 'rtb_cron_jobs', array($this, 'handle_late_arrivals_task') );
|
21 |
-
add_action( 'rtb_cron_jobs', array($this, 'handle_reminder_task') );
|
22 |
-
|
23 |
-
// if ( isset($_GET['debug']) ) { add_action('admin_init', array($this, 'handle_reminder_task') ); } // Used for testing
|
24 |
-
}
|
25 |
-
|
26 |
-
/**
|
27 |
-
* Adds in 10 minute cron interval
|
28 |
-
*
|
29 |
-
* @var array $schedules
|
30 |
-
* @since 2.0.0
|
31 |
-
*/
|
32 |
-
public function add_cron_interval( $schedules ) {
|
33 |
-
$schedules['ten_minutes'] = array(
|
34 |
-
'interval' => 600,
|
35 |
-
'display' => esc_html__( 'Every Ten Minutes' )
|
36 |
-
);
|
37 |
-
|
38 |
-
return $schedules;
|
39 |
-
}
|
40 |
-
|
41 |
-
/**
|
42 |
-
* Creates a scheduled action called by wp_cron every 10 minutes
|
43 |
-
* The class hooks into those calls for reminders and late arrivals
|
44 |
-
*
|
45 |
-
* @since 2.0.0
|
46 |
-
*/
|
47 |
-
public function schedule_events() {
|
48 |
-
if (! wp_next_scheduled ( 'rtb_cron_jobs' )) {
|
49 |
-
wp_schedule_event( time(), 'ten_minutes', 'rtb_cron_jobs' );
|
50 |
-
}
|
51 |
-
}
|
52 |
-
|
53 |
-
/**
|
54 |
-
* Clears the rtb_cron_job hook so that it's no longer called after the plugin is deactivated
|
55 |
-
*
|
56 |
-
* @since 2.0.0
|
57 |
-
*/
|
58 |
-
public function unschedule_events() {
|
59 |
-
wp_clear_scheduled_hook( 'rtb_cron_jobs' );
|
60 |
-
}
|
61 |
-
|
62 |
-
/**
|
63 |
-
* Handles the late arrival event when called by wp_scheduler
|
64 |
-
*
|
65 |
-
* @since 2.0.0
|
66 |
-
*/
|
67 |
-
public function handle_late_arrivals_task() {
|
68 |
-
global $rtb_controller;
|
69 |
-
|
70 |
-
if ( empty( $rtb_controller->settings->get_setting( 'time-late-user' ) ) ) { return; }
|
71 |
-
|
72 |
-
require_once( RTB_PLUGIN_DIR . '/includes/Notification.class.php' );
|
73 |
-
require_once( RTB_PLUGIN_DIR . '/includes/Notification.Email.class.php' );
|
74 |
-
require_once( RTB_PLUGIN_DIR . '/includes/Notification.SMS.class.php' );
|
75 |
-
|
76 |
-
$bookings = $this->get_late_arrival_posts();
|
77 |
-
|
78 |
-
foreach ($bookings as $booking) {
|
79 |
-
|
80 |
-
if ( ! $booking->late_arrival_sent ) {
|
81 |
-
if ( $rtb_controller->settings->get_setting( 'late-notification-format' ) == 'text' ) {
|
82 |
-
$notification = new rtbNotificationSMS( 'late_user', 'user' );
|
83 |
-
}
|
84 |
-
elseif ( $rtb_controller->settings->get_setting( 'late-notification-format' ) == 'email' ) {
|
85 |
-
$notification = new rtbNotificationEmail( 'late_user', 'user' );
|
86 |
-
}
|
87 |
-
|
88 |
-
$notification->set_booking($booking);
|
89 |
-
|
90 |
-
$notification->prepare_notification();
|
91 |
-
|
92 |
-
do_action( 'rtb_send_notification_before', $notification );
|
93 |
-
$sent = $notification->send_notification();
|
94 |
-
do_action( 'rtb_send_notification_after', $notification );
|
95 |
-
|
96 |
-
if ( $sent ) {
|
97 |
-
$booking->late_arrival_sent = true;
|
98 |
-
$booking->insert_post_meta();
|
99 |
-
}
|
100 |
-
}
|
101 |
-
}
|
102 |
-
|
103 |
-
wp_reset_postdata();
|
104 |
-
}
|
105 |
-
|
106 |
-
/**
|
107 |
-
* Handles the notification reminders event when called by wp_scheduler
|
108 |
-
*
|
109 |
-
* @since 2.0.0
|
110 |
-
*/
|
111 |
-
public function handle_reminder_task() {
|
112 |
-
global $rtb_controller;
|
113 |
-
|
114 |
-
if ( empty( $rtb_controller->settings->get_setting( 'time-reminder-user' ) ) ) { return; }
|
115 |
-
|
116 |
-
require_once( RTB_PLUGIN_DIR . '/includes/Notification.class.php' );
|
117 |
-
require_once( RTB_PLUGIN_DIR . '/includes/Notification.Email.class.php' );
|
118 |
-
require_once( RTB_PLUGIN_DIR . '/includes/Notification.SMS.class.php' );
|
119 |
-
|
120 |
-
$bookings = $this->get_reminder_posts();
|
121 |
-
|
122 |
-
foreach ($bookings as $booking) {
|
123 |
-
|
124 |
-
if ( ! $booking->reminder_sent ) {
|
125 |
-
if ( $rtb_controller->settings->get_setting( 'reminder-notification-format' ) == 'text' ) {
|
126 |
-
$notification = new rtbNotificationSMS( 'reminder', 'user' );
|
127 |
-
}
|
128 |
-
elseif ( $rtb_controller->settings->get_setting( 'reminder-notification-format' ) == 'email' ) {
|
129 |
-
$notification = new rtbNotificationEmail( 'reminder', 'user' );
|
130 |
-
}
|
131 |
-
|
132 |
-
$notification->set_booking($booking);
|
133 |
-
|
134 |
-
$notification->prepare_notification();
|
135 |
-
|
136 |
-
do_action( 'rtb_send_notification_before', $notification );
|
137 |
-
$sent = $notification->send_notification();
|
138 |
-
do_action( 'rtb_send_notification_after', $notification );
|
139 |
-
|
140 |
-
if ( $sent ) {
|
141 |
-
$booking->reminder_sent = true;
|
142 |
-
$booking->insert_post_meta();
|
143 |
-
}
|
144 |
-
}
|
145 |
-
}
|
146 |
-
|
147 |
-
wp_reset_postdata();
|
148 |
-
}
|
149 |
-
|
150 |
-
/**
|
151 |
-
* Gets the bookings that might need reminders sent to them
|
152 |
-
*
|
153 |
-
* @since 2.0.0
|
154 |
-
*/
|
155 |
-
public function get_late_arrival_posts() {
|
156 |
-
global $rtb_controller;
|
157 |
-
|
158 |
-
$time_interval = $this->get_time_interval( 'time-late-user' );
|
159 |
-
|
160 |
-
$after_datetime = new DateTime( 'now', wp_timezone() );
|
161 |
-
$before_datetime = new DateTime( 'now', wp_timezone() );
|
162 |
-
|
163 |
-
$after_datetime->setTimestamp( time() - ( $time_interval + 3600 ) );
|
164 |
-
$before_datetime->setTimestamp( time() - $time_interval );
|
165 |
-
|
166 |
-
$args = array(
|
167 |
-
'post_status' => 'confirmed,',
|
168 |
-
'posts_per_page' => -1,
|
169 |
-
'date_query' => array(
|
170 |
-
'before' => $before_datetime->format( 'Y-m-d H:i:s' ),
|
171 |
-
'after' => $after_datetime->format( 'Y-m-d H:i:s' ),
|
172 |
-
'column' => 'post_date'
|
173 |
-
)
|
174 |
-
);
|
175 |
-
require_once( RTB_PLUGIN_DIR . '/includes/Query.class.php' );
|
176 |
-
$query = new rtbQuery( $args );
|
177 |
-
|
178 |
-
$query->prepare_args();
|
179 |
-
|
180 |
-
return $query->get_bookings();
|
181 |
-
}
|
182 |
-
|
183 |
-
/**
|
184 |
-
* Gets the bookings that might need reminders sent to them
|
185 |
-
*
|
186 |
-
* @since 2.0.0
|
187 |
-
*/
|
188 |
-
public function get_reminder_posts() {
|
189 |
-
global $rtb_controller;
|
190 |
-
|
191 |
-
$time_interval = $this->get_time_interval( 'time-reminder-user' );
|
192 |
-
$time_interval = new DateInterval( "PT{$time_interval}S" );
|
193 |
-
|
194 |
-
$reminder_time_window_start = new DateTime( 'now', wp_timezone() );
|
195 |
-
$one_hour = new DateInterval( "PT1H" );
|
196 |
-
$reminder_time_window_start->sub( $one_hour );
|
197 |
-
|
198 |
-
$reminder_time_window_end = new DateTime( 'now', wp_timezone() );
|
199 |
-
$reminder_time_window_end->add( $time_interval );
|
200 |
-
|
201 |
-
$args = array(
|
202 |
-
'post_status' => 'confirmed,',
|
203 |
-
'post_count' => -1,
|
204 |
-
'date_query' => array(
|
205 |
-
'after' => $reminder_time_window_start->format( 'Y-m-d H:i:s' ),
|
206 |
-
'before' => $reminder_time_window_end->format( 'Y-m-d H:i:s' ),
|
207 |
-
'column' => 'post_date'
|
208 |
-
)
|
209 |
-
);
|
210 |
-
|
211 |
-
require_once( RTB_PLUGIN_DIR . '/includes/Query.class.php' );
|
212 |
-
$query = new rtbQuery( $args );
|
213 |
-
|
214 |
-
$query->prepare_args();
|
215 |
-
|
216 |
-
return $query->get_bookings();
|
217 |
-
}
|
218 |
-
|
219 |
-
/**
|
220 |
-
* Converts a time unit and interval into its value in seconds
|
221 |
-
*
|
222 |
-
* @since 2.0.0
|
223 |
-
*/
|
224 |
-
public function get_time_interval( $setting ) {
|
225 |
-
global $rtb_controller;
|
226 |
-
|
227 |
-
$late_arrival_time = $rtb_controller->settings->get_setting( $setting );
|
228 |
-
|
229 |
-
$count = intval( substr( $late_arrival_time, 0, strpos( $late_arrival_time, "_" ) ) );
|
230 |
-
$unit = substr( $late_arrival_time, strpos( $late_arrival_time, "_" ) + 1 );
|
231 |
-
|
232 |
-
switch ($unit) {
|
233 |
-
case 'days':
|
234 |
-
$multiplier = 24*3600;
|
235 |
-
break;
|
236 |
-
case 'hours':
|
237 |
-
$multiplier = 3600;
|
238 |
-
break;
|
239 |
-
case 'minutes':
|
240 |
-
$multiplier = 60;
|
241 |
-
break;
|
242 |
-
|
243 |
-
default:
|
244 |
-
$multiplier = 1;
|
245 |
-
break;
|
246 |
-
}
|
247 |
-
|
248 |
-
return $count * $multiplier;
|
249 |
-
}
|
250 |
-
|
251 |
-
}
|
252 |
-
} // endif;
|
1 |
+
<?php
|
2 |
+
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
+
|
4 |
+
if ( !class_exists( 'rtbCron' ) ) {
|
5 |
+
/**
|
6 |
+
* This class handles scheduling of cron jobs for different notifications
|
7 |
+
* such as reservation reminders or when customers are late for their reservations
|
8 |
+
*
|
9 |
+
* @since 2.0.0
|
10 |
+
*/
|
11 |
+
class rtbCron {
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Adds the necessary filter and action calls
|
15 |
+
* @since 2.0.0
|
16 |
+
*/
|
17 |
+
public function __construct() {
|
18 |
+
add_filter( 'cron_schedules', array($this, 'add_cron_interval') );
|
19 |
+
|
20 |
+
add_action( 'rtb_cron_jobs', array($this, 'handle_late_arrivals_task') );
|
21 |
+
add_action( 'rtb_cron_jobs', array($this, 'handle_reminder_task') );
|
22 |
+
|
23 |
+
// if ( isset($_GET['debug']) ) { add_action('admin_init', array($this, 'handle_reminder_task') ); } // Used for testing
|
24 |
+
}
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Adds in 10 minute cron interval
|
28 |
+
*
|
29 |
+
* @var array $schedules
|
30 |
+
* @since 2.0.0
|
31 |
+
*/
|
32 |
+
public function add_cron_interval( $schedules ) {
|
33 |
+
$schedules['ten_minutes'] = array(
|
34 |
+
'interval' => 600,
|
35 |
+
'display' => esc_html__( 'Every Ten Minutes' )
|
36 |
+
);
|
37 |
+
|
38 |
+
return $schedules;
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Creates a scheduled action called by wp_cron every 10 minutes
|
43 |
+
* The class hooks into those calls for reminders and late arrivals
|
44 |
+
*
|
45 |
+
* @since 2.0.0
|
46 |
+
*/
|
47 |
+
public function schedule_events() {
|
48 |
+
if (! wp_next_scheduled ( 'rtb_cron_jobs' )) {
|
49 |
+
wp_schedule_event( time(), 'ten_minutes', 'rtb_cron_jobs' );
|
50 |
+
}
|
51 |
+
}
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Clears the rtb_cron_job hook so that it's no longer called after the plugin is deactivated
|
55 |
+
*
|
56 |
+
* @since 2.0.0
|
57 |
+
*/
|
58 |
+
public function unschedule_events() {
|
59 |
+
wp_clear_scheduled_hook( 'rtb_cron_jobs' );
|
60 |
+
}
|
61 |
+
|
62 |
+
/**
|
63 |
+
* Handles the late arrival event when called by wp_scheduler
|
64 |
+
*
|
65 |
+
* @since 2.0.0
|
66 |
+
*/
|
67 |
+
public function handle_late_arrivals_task() {
|
68 |
+
global $rtb_controller;
|
69 |
+
|
70 |
+
if ( empty( $rtb_controller->settings->get_setting( 'time-late-user' ) ) ) { return; }
|
71 |
+
|
72 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Notification.class.php' );
|
73 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Notification.Email.class.php' );
|
74 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Notification.SMS.class.php' );
|
75 |
+
|
76 |
+
$bookings = $this->get_late_arrival_posts();
|
77 |
+
|
78 |
+
foreach ($bookings as $booking) {
|
79 |
+
|
80 |
+
if ( ! $booking->late_arrival_sent ) {
|
81 |
+
if ( $rtb_controller->settings->get_setting( 'late-notification-format' ) == 'text' ) {
|
82 |
+
$notification = new rtbNotificationSMS( 'late_user', 'user' );
|
83 |
+
}
|
84 |
+
elseif ( $rtb_controller->settings->get_setting( 'late-notification-format' ) == 'email' ) {
|
85 |
+
$notification = new rtbNotificationEmail( 'late_user', 'user' );
|
86 |
+
}
|
87 |
+
|
88 |
+
$notification->set_booking($booking);
|
89 |
+
|
90 |
+
$notification->prepare_notification();
|
91 |
+
|
92 |
+
do_action( 'rtb_send_notification_before', $notification );
|
93 |
+
$sent = $notification->send_notification();
|
94 |
+
do_action( 'rtb_send_notification_after', $notification );
|
95 |
+
|
96 |
+
if ( $sent ) {
|
97 |
+
$booking->late_arrival_sent = true;
|
98 |
+
$booking->insert_post_meta();
|
99 |
+
}
|
100 |
+
}
|
101 |
+
}
|
102 |
+
|
103 |
+
wp_reset_postdata();
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* Handles the notification reminders event when called by wp_scheduler
|
108 |
+
*
|
109 |
+
* @since 2.0.0
|
110 |
+
*/
|
111 |
+
public function handle_reminder_task() {
|
112 |
+
global $rtb_controller;
|
113 |
+
|
114 |
+
if ( empty( $rtb_controller->settings->get_setting( 'time-reminder-user' ) ) ) { return; }
|
115 |
+
|
116 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Notification.class.php' );
|
117 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Notification.Email.class.php' );
|
118 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Notification.SMS.class.php' );
|
119 |
+
|
120 |
+
$bookings = $this->get_reminder_posts();
|
121 |
+
|
122 |
+
foreach ($bookings as $booking) {
|
123 |
+
|
124 |
+
if ( ! $booking->reminder_sent ) {
|
125 |
+
if ( $rtb_controller->settings->get_setting( 'reminder-notification-format' ) == 'text' ) {
|
126 |
+
$notification = new rtbNotificationSMS( 'reminder', 'user' );
|
127 |
+
}
|
128 |
+
elseif ( $rtb_controller->settings->get_setting( 'reminder-notification-format' ) == 'email' ) {
|
129 |
+
$notification = new rtbNotificationEmail( 'reminder', 'user' );
|
130 |
+
}
|
131 |
+
|
132 |
+
$notification->set_booking($booking);
|
133 |
+
|
134 |
+
$notification->prepare_notification();
|
135 |
+
|
136 |
+
do_action( 'rtb_send_notification_before', $notification );
|
137 |
+
$sent = $notification->send_notification();
|
138 |
+
do_action( 'rtb_send_notification_after', $notification );
|
139 |
+
|
140 |
+
if ( $sent ) {
|
141 |
+
$booking->reminder_sent = true;
|
142 |
+
$booking->insert_post_meta();
|
143 |
+
}
|
144 |
+
}
|
145 |
+
}
|
146 |
+
|
147 |
+
wp_reset_postdata();
|
148 |
+
}
|
149 |
+
|
150 |
+
/**
|
151 |
+
* Gets the bookings that might need reminders sent to them
|
152 |
+
*
|
153 |
+
* @since 2.0.0
|
154 |
+
*/
|
155 |
+
public function get_late_arrival_posts() {
|
156 |
+
global $rtb_controller;
|
157 |
+
|
158 |
+
$time_interval = $this->get_time_interval( 'time-late-user' );
|
159 |
+
|
160 |
+
$after_datetime = new DateTime( 'now', wp_timezone() );
|
161 |
+
$before_datetime = new DateTime( 'now', wp_timezone() );
|
162 |
+
|
163 |
+
$after_datetime->setTimestamp( time() - ( $time_interval + 3600 ) );
|
164 |
+
$before_datetime->setTimestamp( time() - $time_interval );
|
165 |
+
|
166 |
+
$args = array(
|
167 |
+
'post_status' => 'confirmed,',
|
168 |
+
'posts_per_page' => -1,
|
169 |
+
'date_query' => array(
|
170 |
+
'before' => $before_datetime->format( 'Y-m-d H:i:s' ),
|
171 |
+
'after' => $after_datetime->format( 'Y-m-d H:i:s' ),
|
172 |
+
'column' => 'post_date'
|
173 |
+
)
|
174 |
+
);
|
175 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Query.class.php' );
|
176 |
+
$query = new rtbQuery( $args );
|
177 |
+
|
178 |
+
$query->prepare_args();
|
179 |
+
|
180 |
+
return $query->get_bookings();
|
181 |
+
}
|
182 |
+
|
183 |
+
/**
|
184 |
+
* Gets the bookings that might need reminders sent to them
|
185 |
+
*
|
186 |
+
* @since 2.0.0
|
187 |
+
*/
|
188 |
+
public function get_reminder_posts() {
|
189 |
+
global $rtb_controller;
|
190 |
+
|
191 |
+
$time_interval = $this->get_time_interval( 'time-reminder-user' );
|
192 |
+
$time_interval = new DateInterval( "PT{$time_interval}S" );
|
193 |
+
|
194 |
+
$reminder_time_window_start = new DateTime( 'now', wp_timezone() );
|
195 |
+
$one_hour = new DateInterval( "PT1H" );
|
196 |
+
$reminder_time_window_start->sub( $one_hour );
|
197 |
+
|
198 |
+
$reminder_time_window_end = new DateTime( 'now', wp_timezone() );
|
199 |
+
$reminder_time_window_end->add( $time_interval );
|
200 |
+
|
201 |
+
$args = array(
|
202 |
+
'post_status' => 'confirmed,',
|
203 |
+
'post_count' => -1,
|
204 |
+
'date_query' => array(
|
205 |
+
'after' => $reminder_time_window_start->format( 'Y-m-d H:i:s' ),
|
206 |
+
'before' => $reminder_time_window_end->format( 'Y-m-d H:i:s' ),
|
207 |
+
'column' => 'post_date'
|
208 |
+
)
|
209 |
+
);
|
210 |
+
|
211 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Query.class.php' );
|
212 |
+
$query = new rtbQuery( $args );
|
213 |
+
|
214 |
+
$query->prepare_args();
|
215 |
+
|
216 |
+
return $query->get_bookings();
|
217 |
+
}
|
218 |
+
|
219 |
+
/**
|
220 |
+
* Converts a time unit and interval into its value in seconds
|
221 |
+
*
|
222 |
+
* @since 2.0.0
|
223 |
+
*/
|
224 |
+
public function get_time_interval( $setting ) {
|
225 |
+
global $rtb_controller;
|
226 |
+
|
227 |
+
$late_arrival_time = $rtb_controller->settings->get_setting( $setting );
|
228 |
+
|
229 |
+
$count = intval( substr( $late_arrival_time, 0, strpos( $late_arrival_time, "_" ) ) );
|
230 |
+
$unit = substr( $late_arrival_time, strpos( $late_arrival_time, "_" ) + 1 );
|
231 |
+
|
232 |
+
switch ($unit) {
|
233 |
+
case 'days':
|
234 |
+
$multiplier = 24*3600;
|
235 |
+
break;
|
236 |
+
case 'hours':
|
237 |
+
$multiplier = 3600;
|
238 |
+
break;
|
239 |
+
case 'minutes':
|
240 |
+
$multiplier = 60;
|
241 |
+
break;
|
242 |
+
|
243 |
+
default:
|
244 |
+
$multiplier = 1;
|
245 |
+
break;
|
246 |
+
}
|
247 |
+
|
248 |
+
return $count * $multiplier;
|
249 |
+
}
|
250 |
+
|
251 |
+
}
|
252 |
+
} // endif;
|
includes/CustomFields.class.php
CHANGED
@@ -1,193 +1,193 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* A class that handles the main custom field functions
|
4 |
-
*/
|
5 |
-
if ( ! defined( 'ABSPATH' ) )
|
6 |
-
exit;
|
7 |
-
|
8 |
-
if ( !class_exists( 'rtbCustomFields' ) ) {
|
9 |
-
class rtbCustomFields {
|
10 |
-
|
11 |
-
/**
|
12 |
-
* Option name for storing modified default fields
|
13 |
-
*
|
14 |
-
* @since 0.1
|
15 |
-
*/
|
16 |
-
public $modified_option_key;
|
17 |
-
|
18 |
-
/**
|
19 |
-
* Common string tacked onto the end of error messages
|
20 |
-
*
|
21 |
-
* @since 0.1
|
22 |
-
*/
|
23 |
-
public $common_error_msg;
|
24 |
-
|
25 |
-
/**
|
26 |
-
* Initialize the plugin and register hooks
|
27 |
-
*
|
28 |
-
* @since 0.1
|
29 |
-
*/
|
30 |
-
public function __construct() {
|
31 |
-
|
32 |
-
// Option key where information about default fields that have
|
33 |
-
// been modified and disabled is stored in the database
|
34 |
-
$this->modified_option_key = apply_filters( 'cffrtb_modified_fields_option_key', 'cffrtb_modified_fields' );
|
35 |
-
|
36 |
-
// Common string tacked onto the end of error messages
|
37 |
-
$this->common_error_msg = sprintf( _x( 'Please try again. If the problem persists, you may need to refresh the page. If that does not solve the problem, please %scontact support%s for help.', 'A common phrase added to the end of error messages', 'custom-fields-for-rtb' ), '<a href="http://fivestarplugins.com/contact-us/">', '</a>' );
|
38 |
-
|
39 |
-
// Validate user input for custom fields
|
40 |
-
add_action( 'rtb_validate_booking_submission', array( $this, 'validate_custom_fields_input' ) );
|
41 |
-
|
42 |
-
// Filter required phone setting when phone field is disabled
|
43 |
-
add_filter( 'rtb-setting-require-phone', array( $this, 'never_require_phone' ) );
|
44 |
-
|
45 |
-
// Insert/load custom field input with booking metadata
|
46 |
-
add_filter( 'rtb_insert_booking_metadata', array( $this, 'insert_booking_metadata' ), 10, 2 );
|
47 |
-
add_action( 'rtb_booking_load_post_data', array( $this, 'load_booking_meta_data' ), 10, 2 );
|
48 |
-
|
49 |
-
// Print custom fields in notification template tags
|
50 |
-
add_filter( 'rtb_notification_template_tags', array( $this, 'add_notification_template_tags' ), 10, 2 );
|
51 |
-
add_filter( 'rtb_notification_template_tag_descriptions', array( $this, 'add_notification_template_tag_descriptions' ) );
|
52 |
-
|
53 |
-
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
|
54 |
-
}
|
55 |
-
|
56 |
-
public function enqueue_scripts() {
|
57 |
-
$currentScreen = get_current_screen();
|
58 |
-
if ( $currentScreen->id == 'bookings_page_cffrtb-editor' ) {
|
59 |
-
wp_enqueue_style( 'rtb-admin-css', RTB_PLUGIN_URL . '/assets/css/admin.css', array(), RTB_VERSION );
|
60 |
-
wp_enqueue_script( 'rtb-admin-js', RTB_PLUGIN_URL . '/assets/js/admin.js', array( 'jquery' ), RTB_VERSION, true );
|
61 |
-
}
|
62 |
-
}
|
63 |
-
|
64 |
-
/**
|
65 |
-
* Validate user input for custom fields
|
66 |
-
*
|
67 |
-
* @since 0.1
|
68 |
-
*/
|
69 |
-
public function validate_custom_fields_input( $booking ) {
|
70 |
-
|
71 |
-
$fields = rtb_get_custom_fields();
|
72 |
-
|
73 |
-
if ( !count( $fields ) ) {
|
74 |
-
return;
|
75 |
-
}
|
76 |
-
|
77 |
-
foreach( $fields as $field ) {
|
78 |
-
$validation = $field->validate_input( $booking );
|
79 |
-
}
|
80 |
-
}
|
81 |
-
|
82 |
-
/**
|
83 |
-
* Add custom fields to metadata when a booking is saved
|
84 |
-
*
|
85 |
-
* @since 0.1
|
86 |
-
*/
|
87 |
-
public function insert_booking_metadata( $meta, $booking ) {
|
88 |
-
|
89 |
-
if ( empty( $booking->custom_fields ) ) {
|
90 |
-
return $meta;
|
91 |
-
}
|
92 |
-
|
93 |
-
if ( !is_array( $meta ) ) {
|
94 |
-
$meta = array();
|
95 |
-
}
|
96 |
-
|
97 |
-
if ( !isset( $meta['custom_fields'] ) ) {
|
98 |
-
$meta['custom_fields'] = array();
|
99 |
-
}
|
100 |
-
|
101 |
-
$meta['custom_fields'] = $booking->custom_fields;
|
102 |
-
|
103 |
-
return $meta;
|
104 |
-
}
|
105 |
-
|
106 |
-
/**
|
107 |
-
* Add custom fields to metadata when booking is loaded
|
108 |
-
*
|
109 |
-
* @since 0.1
|
110 |
-
*/
|
111 |
-
public function load_booking_meta_data( $booking, $post ) {
|
112 |
-
|
113 |
-
$meta = get_post_meta( $booking->ID, 'rtb', true );
|
114 |
-
|
115 |
-
if ( empty( $meta['custom_fields'] ) ) {
|
116 |
-
return;
|
117 |
-
}
|
118 |
-
|
119 |
-
$booking->custom_fields = $meta['custom_fields'];
|
120 |
-
}
|
121 |
-
|
122 |
-
/**
|
123 |
-
* Add custom fields as notification template tags
|
124 |
-
*
|
125 |
-
* @since 0.1
|
126 |
-
*/
|
127 |
-
public function add_notification_template_tags( $tags, $notification ) {
|
128 |
-
global $rtb_controller;
|
129 |
-
|
130 |
-
$fields = rtb_get_custom_fields();
|
131 |
-
|
132 |
-
$cf = isset( $notification->booking->custom_fields ) ? $notification->booking->custom_fields : array();
|
133 |
-
$checkbox_icon = apply_filters( 'cffrtb_checkbox_icon_notification', '', $notification );
|
134 |
-
|
135 |
-
foreach( $fields as $field ) {
|
136 |
-
|
137 |
-
if ( $field->type == 'fieldset' ) {
|
138 |
-
continue;
|
139 |
-
}
|
140 |
-
|
141 |
-
if ( isset( $cf[ $field->slug ] ) ) {
|
142 |
-
$display_val = apply_filters( 'cffrtb_display_value_notification', $rtb_controller->fields->get_display_value( $cf[ $field->slug ], $field, $checkbox_icon ), $cf[ $field->slug ], $field, $notification );
|
143 |
-
} else {
|
144 |
-
$display_val = '';
|
145 |
-
}
|
146 |
-
$tags[ '{cf-' . esc_attr( $field->slug ) . '}' ] = $display_val;
|
147 |
-
}
|
148 |
-
|
149 |
-
return $tags;
|
150 |
-
}
|
151 |
-
|
152 |
-
/**
|
153 |
-
* Add custom field notification template tag descriptions
|
154 |
-
*
|
155 |
-
* @since 0.1
|
156 |
-
*/
|
157 |
-
public function add_notification_template_tag_descriptions( $tags ) {
|
158 |
-
|
159 |
-
$fields = rtb_get_custom_fields();
|
160 |
-
|
161 |
-
foreach( $fields as $field ) {
|
162 |
-
|
163 |
-
if ( $field->type == 'fieldset' ) {
|
164 |
-
continue;
|
165 |
-
}
|
166 |
-
|
167 |
-
$tags[ '{cf-' . esc_attr( $field->slug ) . '}' ] = esc_html( $field->title );
|
168 |
-
}
|
169 |
-
|
170 |
-
return $tags;
|
171 |
-
}
|
172 |
-
|
173 |
-
/**
|
174 |
-
* Override the required phone setting when the phone field has been
|
175 |
-
* disabled.
|
176 |
-
*
|
177 |
-
* @param string $value The value of the setting
|
178 |
-
* @since 1.2.3
|
179 |
-
*/
|
180 |
-
public function never_require_phone( $value ) {
|
181 |
-
global $rtb_controller;
|
182 |
-
|
183 |
-
$modified = get_option( $rtb_controller->custom_fields->modified_option_key );
|
184 |
-
|
185 |
-
if ( $modified && isset( $modified['phone'] ) && !empty( $modified['phone']['disabled'] ) ) {
|
186 |
-
return '';
|
187 |
-
}
|
188 |
-
|
189 |
-
return $value;
|
190 |
-
}
|
191 |
-
|
192 |
-
}
|
193 |
-
} // endif;
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* A class that handles the main custom field functions
|
4 |
+
*/
|
5 |
+
if ( ! defined( 'ABSPATH' ) )
|
6 |
+
exit;
|
7 |
+
|
8 |
+
if ( !class_exists( 'rtbCustomFields' ) ) {
|
9 |
+
class rtbCustomFields {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Option name for storing modified default fields
|
13 |
+
*
|
14 |
+
* @since 0.1
|
15 |
+
*/
|
16 |
+
public $modified_option_key;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Common string tacked onto the end of error messages
|
20 |
+
*
|
21 |
+
* @since 0.1
|
22 |
+
*/
|
23 |
+
public $common_error_msg;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Initialize the plugin and register hooks
|
27 |
+
*
|
28 |
+
* @since 0.1
|
29 |
+
*/
|
30 |
+
public function __construct() {
|
31 |
+
|
32 |
+
// Option key where information about default fields that have
|
33 |
+
// been modified and disabled is stored in the database
|
34 |
+
$this->modified_option_key = apply_filters( 'cffrtb_modified_fields_option_key', 'cffrtb_modified_fields' );
|
35 |
+
|
36 |
+
// Common string tacked onto the end of error messages
|
37 |
+
$this->common_error_msg = sprintf( _x( 'Please try again. If the problem persists, you may need to refresh the page. If that does not solve the problem, please %scontact support%s for help.', 'A common phrase added to the end of error messages', 'custom-fields-for-rtb' ), '<a href="http://fivestarplugins.com/contact-us/">', '</a>' );
|
38 |
+
|
39 |
+
// Validate user input for custom fields
|
40 |
+
add_action( 'rtb_validate_booking_submission', array( $this, 'validate_custom_fields_input' ) );
|
41 |
+
|
42 |
+
// Filter required phone setting when phone field is disabled
|
43 |
+
add_filter( 'rtb-setting-require-phone', array( $this, 'never_require_phone' ) );
|
44 |
+
|
45 |
+
// Insert/load custom field input with booking metadata
|
46 |
+
add_filter( 'rtb_insert_booking_metadata', array( $this, 'insert_booking_metadata' ), 10, 2 );
|
47 |
+
add_action( 'rtb_booking_load_post_data', array( $this, 'load_booking_meta_data' ), 10, 2 );
|
48 |
+
|
49 |
+
// Print custom fields in notification template tags
|
50 |
+
add_filter( 'rtb_notification_template_tags', array( $this, 'add_notification_template_tags' ), 10, 2 );
|
51 |
+
add_filter( 'rtb_notification_template_tag_descriptions', array( $this, 'add_notification_template_tag_descriptions' ) );
|
52 |
+
|
53 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
|
54 |
+
}
|
55 |
+
|
56 |
+
public function enqueue_scripts() {
|
57 |
+
$currentScreen = get_current_screen();
|
58 |
+
if ( $currentScreen->id == 'bookings_page_cffrtb-editor' ) {
|
59 |
+
wp_enqueue_style( 'rtb-admin-css', RTB_PLUGIN_URL . '/assets/css/admin.css', array(), RTB_VERSION );
|
60 |
+
wp_enqueue_script( 'rtb-admin-js', RTB_PLUGIN_URL . '/assets/js/admin.js', array( 'jquery' ), RTB_VERSION, true );
|
61 |
+
}
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Validate user input for custom fields
|
66 |
+
*
|
67 |
+
* @since 0.1
|
68 |
+
*/
|
69 |
+
public function validate_custom_fields_input( $booking ) {
|
70 |
+
|
71 |
+
$fields = rtb_get_custom_fields();
|
72 |
+
|
73 |
+
if ( !count( $fields ) ) {
|
74 |
+
return;
|
75 |
+
}
|
76 |
+
|
77 |
+
foreach( $fields as $field ) {
|
78 |
+
$validation = $field->validate_input( $booking );
|
79 |
+
}
|
80 |
+
}
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Add custom fields to metadata when a booking is saved
|
84 |
+
*
|
85 |
+
* @since 0.1
|
86 |
+
*/
|
87 |
+
public function insert_booking_metadata( $meta, $booking ) {
|
88 |
+
|
89 |
+
if ( empty( $booking->custom_fields ) ) {
|
90 |
+
return $meta;
|
91 |
+
}
|
92 |
+
|
93 |
+
if ( !is_array( $meta ) ) {
|
94 |
+
$meta = array();
|
95 |
+
}
|
96 |
+
|
97 |
+
if ( !isset( $meta['custom_fields'] ) ) {
|
98 |
+
$meta['custom_fields'] = array();
|
99 |
+
}
|
100 |
+
|
101 |
+
$meta['custom_fields'] = $booking->custom_fields;
|
102 |
+
|
103 |
+
return $meta;
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* Add custom fields to metadata when booking is loaded
|
108 |
+
*
|
109 |
+
* @since 0.1
|
110 |
+
*/
|
111 |
+
public function load_booking_meta_data( $booking, $post ) {
|
112 |
+
|
113 |
+
$meta = get_post_meta( $booking->ID, 'rtb', true );
|
114 |
+
|
115 |
+
if ( empty( $meta['custom_fields'] ) ) {
|
116 |
+
return;
|
117 |
+
}
|
118 |
+
|
119 |
+
$booking->custom_fields = $meta['custom_fields'];
|
120 |
+
}
|
121 |
+
|
122 |
+
/**
|
123 |
+
* Add custom fields as notification template tags
|
124 |
+
*
|
125 |
+
* @since 0.1
|
126 |
+
*/
|
127 |
+
public function add_notification_template_tags( $tags, $notification ) {
|
128 |
+
global $rtb_controller;
|
129 |
+
|
130 |
+
$fields = rtb_get_custom_fields();
|
131 |
+
|
132 |
+
$cf = isset( $notification->booking->custom_fields ) ? $notification->booking->custom_fields : array();
|
133 |
+
$checkbox_icon = apply_filters( 'cffrtb_checkbox_icon_notification', '', $notification );
|
134 |
+
|
135 |
+
foreach( $fields as $field ) {
|
136 |
+
|
137 |
+
if ( $field->type == 'fieldset' ) {
|
138 |
+
continue;
|
139 |
+
}
|
140 |
+
|
141 |
+
if ( isset( $cf[ $field->slug ] ) ) {
|
142 |
+
$display_val = apply_filters( 'cffrtb_display_value_notification', $rtb_controller->fields->get_display_value( $cf[ $field->slug ], $field, $checkbox_icon ), $cf[ $field->slug ], $field, $notification );
|
143 |
+
} else {
|
144 |
+
$display_val = '';
|
145 |
+
}
|
146 |
+
$tags[ '{cf-' . esc_attr( $field->slug ) . '}' ] = $display_val;
|
147 |
+
}
|
148 |
+
|
149 |
+
return $tags;
|
150 |
+
}
|
151 |
+
|
152 |
+
/**
|
153 |
+
* Add custom field notification template tag descriptions
|
154 |
+
*
|
155 |
+
* @since 0.1
|
156 |
+
*/
|
157 |
+
public function add_notification_template_tag_descriptions( $tags ) {
|
158 |
+
|
159 |
+
$fields = rtb_get_custom_fields();
|
160 |
+
|
161 |
+
foreach( $fields as $field ) {
|
162 |
+
|
163 |
+
if ( $field->type == 'fieldset' ) {
|
164 |
+
continue;
|
165 |
+
}
|
166 |
+
|
167 |
+
$tags[ '{cf-' . esc_attr( $field->slug ) . '}' ] = esc_html( $field->title );
|
168 |
+
}
|
169 |
+
|
170 |
+
return $tags;
|
171 |
+
}
|
172 |
+
|
173 |
+
/**
|
174 |
+
* Override the required phone setting when the phone field has been
|
175 |
+
* disabled.
|
176 |
+
*
|
177 |
+
* @param string $value The value of the setting
|
178 |
+
* @since 1.2.3
|
179 |
+
*/
|
180 |
+
public function never_require_phone( $value ) {
|
181 |
+
global $rtb_controller;
|
182 |
+
|
183 |
+
$modified = get_option( $rtb_controller->custom_fields->modified_option_key );
|
184 |
+
|
185 |
+
if ( $modified && isset( $modified['phone'] ) && !empty( $modified['phone']['disabled'] ) ) {
|
186 |
+
return '';
|
187 |
+
}
|
188 |
+
|
189 |
+
return $value;
|
190 |
+
}
|
191 |
+
|
192 |
+
}
|
193 |
+
} // endif;
|
includes/CustomPostTypes.class.php
CHANGED
@@ -1,445 +1,445 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* Class to handle all custom post type definitions for Restaurant Reservations
|
4 |
-
*/
|
5 |
-
|
6 |
-
if ( !defined( 'ABSPATH' ) )
|
7 |
-
exit;
|
8 |
-
|
9 |
-
if ( !class_exists( 'rtbCustomPostTypes' ) ) {
|
10 |
-
class rtbCustomPostTypes {
|
11 |
-
|
12 |
-
// Array of valid post statuses
|
13 |
-
// @sa set_booking_statuses()
|
14 |
-
public $booking_statuses = array();
|
15 |
-
|
16 |
-
// Cached select fields for booking statuses
|
17 |
-
public $status_select_html = array();
|
18 |
-
|
19 |
-
public function __construct() {
|
20 |
-
|
21 |
-
// Call when plugin is initialized on every page load
|
22 |
-
add_action( 'init', array( $this, 'load_cpts' ) );
|
23 |
-
|
24 |
-
// Set up $booking_statuses array and register new post statuses
|
25 |
-
add_action( 'init', array( $this, 'set_booking_statuses' ) );
|
26 |
-
add_filter( 'rtb_post_statuses_args' , array( $this, 'add_arrived_status' ) );
|
27 |
-
add_filter( 'rtb_post_statuses_args' , array( $this, 'add_cancelled_status' ) );
|
28 |
-
add_filter( 'rtb_post_statuses_args' , array( $this, 'add_payment_failed_status' ) );
|
29 |
-
|
30 |
-
// Display the count of pending bookings
|
31 |
-
add_action( 'admin_footer', array( $this, 'show_pending_count' ) );
|
32 |
-
|
33 |
-
// Maintain the count of pending bookings
|
34 |
-
add_action( 'rtb_insert_booking', array( $this, 'update_pending_count' ) );
|
35 |
-
add_action( 'rtb_update_booking', array( $this, 'update_pending_count' ) );
|
36 |
-
add_action( 'transition_post_status', array( $this, 'maybe_update_pending_count' ), 999, 3 );
|
37 |
-
|
38 |
-
}
|
39 |
-
|
40 |
-
/**
|
41 |
-
* Initialize custom post types
|
42 |
-
* @since 0.1
|
43 |
-
*/
|
44 |
-
public function load_cpts() {
|
45 |
-
global $rtb_controller;
|
46 |
-
|
47 |
-
// Define the booking custom post type
|
48 |
-
$args = array(
|
49 |
-
'labels' => array(
|
50 |
-
'name' => __( 'Bookings', 'restaurant-reservations' ),
|
51 |
-
'singular_name' => __( 'Booking', 'restaurant-reservations' ),
|
52 |
-
'menu_name' => __( 'Bookings', 'restaurant-reservations' ),
|
53 |
-
'name_admin_bar' => __( 'Bookings', 'restaurant-reservations' ),
|
54 |
-
'add_new' => __( 'Add New', 'restaurant-reservations' ),
|
55 |
-
'add_new_item' => __( 'Add New Booking', 'restaurant-reservations' ),
|
56 |
-
'edit_item' => __( 'Edit Booking', 'restaurant-reservations' ),
|
57 |
-
'new_item' => __( 'New Booking', 'restaurant-reservations' ),
|
58 |
-
'view_item' => __( 'View Booking', 'restaurant-reservations' ),
|
59 |
-
'search_items' => __( 'Search Bookings', 'restaurant-reservations' ),
|
60 |
-
'not_found' => __( 'No bookings found', 'restaurant-reservations' ),
|
61 |
-
'not_found_in_trash' => __( 'No bookings found in trash', 'restaurant-reservations' ),
|
62 |
-
'all_items' => __( 'All Bookings', 'restaurant-reservations' ),
|
63 |
-
),
|
64 |
-
'menu_icon' => 'dashicons-calendar',
|
65 |
-
'public' => false,
|
66 |
-
'supports' => array(
|
67 |
-
'title',
|
68 |
-
'revisions'
|
69 |
-
)
|
70 |
-
);
|
71 |
-
|
72 |
-
// Create filter so addons can modify the arguments
|
73 |
-
$args = apply_filters( 'rtb_booking_args', $args );
|
74 |
-
|
75 |
-
// Add an action so addons can hook in before the post type is registered
|
76 |
-
do_action( 'rtb_booking_pre_register' );
|
77 |
-
|
78 |
-
// Register the post type
|
79 |
-
register_post_type( RTB_BOOKING_POST_TYPE, $args );
|
80 |
-
|
81 |
-
// Add an action so addons can hook in after the post type is registered
|
82 |
-
do_action( 'rtb_booking_post_register' );
|
83 |
-
|
84 |
-
if ( $rtb_controller->permissions->check_permission( 'custom_fields' ) ) {
|
85 |
-
// Define the field custom post type
|
86 |
-
$args = array(
|
87 |
-
'labels' => array(
|
88 |
-
'name' => __( 'Field', 'custom-fields-for-rtb' ),
|
89 |
-
'singular_name' => __( 'Field', 'custom-fields-for-rtb' ),
|
90 |
-
'menu_name' => __( 'Fields', 'custom-fields-for-rtb' ),
|
91 |
-
'name_admin_bar' => __( 'Fields', 'custom-fields-for-rtb' ),
|
92 |
-
'add_new' => __( 'Add Field', 'custom-fields-for-rtb' ),
|
93 |
-
'add_new_item' => __( 'Add New Field', 'custom-fields-for-rtb' ),
|
94 |
-
'edit_item' => __( 'Edit Field', 'custom-fields-for-rtb' ),
|
95 |
-
'new_item' => __( 'New Field', 'custom-fields-for-rtb' ),
|
96 |
-
'view_item' => __( 'View Field', 'custom-fields-for-rtb' ),
|
97 |
-
'search_items' => __( 'Search Fields', 'custom-fields-for-rtb' ),
|
98 |
-
'not_found' => __( 'No fields found', 'custom-fields-for-rtb' ),
|
99 |
-
'not_found_in_trash' => __( 'No fields found in trash', 'custom-fields-for-rtb' ),
|
100 |
-
'all_items' => __( 'All Fields', 'custom-fields-for-rtb' ),
|
101 |
-
),
|
102 |
-
'public' => false
|
103 |
-
);
|
104 |
-
|
105 |
-
$args = apply_filters( 'cffrtb_field_post_type_args', $args );
|
106 |
-
|
107 |
-
register_post_type( 'cffrtb_field', $args );
|
108 |
-
}
|
109 |
-
}
|
110 |
-
|
111 |
-
/**
|
112 |
-
* Set an array of valid booking statuses and register any custom statuses
|
113 |
-
* @since 0.0.1
|
114 |
-
*/
|
115 |
-
public function set_booking_statuses() {
|
116 |
-
|
117 |
-
$this->booking_statuses['pending'] = array(
|
118 |
-
'label' => _x( 'Pending', 'Booking status when it is pending review', 'restaurant-reservations' ),
|
119 |
-
'default' => true, // Whether or not this status is part of WP Core
|
120 |
-
'user_selectable' => true, // Whether or not a user can set a booking to this status
|
121 |
-
'label_count' => _n_noop( 'Pending <span class="count">(%s)</span>', 'Pending <span class="count">(%s)</span>'),
|
122 |
-
);
|
123 |
-
|
124 |
-
$this->booking_statuses['confirmed'] = array (
|
125 |
-
'label' => _x( 'Confirmed', 'Booking status for a confirmed booking', 'restaurant-reservations' ),
|
126 |
-
'default' => false, // Whether or not this status is part of WP Core
|
127 |
-
'user_selectable' => true, // Whether or not a user can set a booking to this status
|
128 |
-
'public' => false,
|
129 |
-
'exclude_from_search' => true,
|
130 |
-
'show_in_admin_all_list' => true,
|
131 |
-
'show_in_admin_status_list' => true,
|
132 |
-
'label_count' => _n_noop( 'Confirmed <span class="count">(%s)</span>', 'Confirmed <span class="count">(%s)</span>', 'restaurant-reservations' ),
|
133 |
-
);
|
134 |
-
|
135 |
-
$this->booking_statuses['closed'] = array(
|
136 |
-
'label' => _x( 'Closed', 'Booking status for a closed booking', 'restaurant-reservations' ),
|
137 |
-
'default' => false, // Whether or not this status is part of WP Core
|
138 |
-
'user_selectable' => true, // Whether or not a user can set a booking to this status
|
139 |
-
'public' => false,
|
140 |
-
'exclude_from_search' => true,
|
141 |
-
'show_in_admin_all_list' => true,
|
142 |
-
'show_in_admin_status_list' => true,
|
143 |
-
'label_count' => _n_noop( 'Closed <span class="count">(%s)</span>', 'Closed <span class="count">(%s)</span>', 'restaurant-reservations' )
|
144 |
-
);
|
145 |
-
|
146 |
-
// Let addons hook in to add/edit/remove post statuses
|
147 |
-
$this->booking_statuses = apply_filters( 'rtb_post_statuses_args', $this->booking_statuses );
|
148 |
-
|
149 |
-
// Register the custom post statuses
|
150 |
-
foreach ( $this->booking_statuses as $status => $args ) {
|
151 |
-
if ( $args['default'] === false ) {
|
152 |
-
register_post_status( $status, $args );
|
153 |
-
}
|
154 |
-
}
|
155 |
-
|
156 |
-
}
|
157 |
-
|
158 |
-
|
159 |
-
/**
|
160 |
-
* @since 2.1.0
|
161 |
-
* Adds in a "Cancelled" status if the option to allow guest to cancel
|
162 |
-
* their reservation has been toggled on.
|
163 |
-
*/
|
164 |
-
public function add_cancelled_status( $booking_statuses = array() ) {
|
165 |
-
global $rtb_controller;
|
166 |
-
|
167 |
-
if ( $rtb_controller->settings->get_setting( 'allow-cancellations' ) ) {
|
168 |
-
$booking_statuses['cancelled'] = array(
|
169 |
-
'label' => _x( 'Cancelled', 'The guest has cancelled their reservation themselves.', 'restaurant-reservations' ),
|
170 |
-
'default' => false, // Whether or not this status is part of WP Core
|
171 |
-
'user_selectable' => false, // Whether or not a user can set a booking to this status
|
172 |
-
'public' => false,
|
173 |
-
'exclude_from_search' => true,
|
174 |
-
'show_in_admin_all_list' => true,
|
175 |
-
'show_in_admin_status_list' => true,
|
176 |
-
'label_count' => _n_noop( 'Cancelled <span class="count">(%s)</span>', 'Cancelled <span class="count">(%s)</span>', 'restaurant-reservations' )
|
177 |
-
);
|
178 |
-
}
|
179 |
-
|
180 |
-
return $booking_statuses;
|
181 |
-
}
|
182 |
-
|
183 |
-
|
184 |
-
/**
|
185 |
-
* @since 2.0.0
|
186 |
-
* Adds in an "Arrived" status if the option to check guests in on arrival
|
187 |
-
* has been toggled on.
|
188 |
-
*/
|
189 |
-
public function add_arrived_status( $booking_statuses = array() ) {
|
190 |
-
global $rtb_controller;
|
191 |
-
|
192 |
-
if ( $rtb_controller->settings->get_setting( 'view-bookings-arrivals' ) ) {
|
193 |
-
$booking_statuses['arrived'] = array(
|
194 |
-
'label' => _x( 'Arrived', 'The guests have arrived for their reservation', 'restaurant-reservations' ),
|
195 |
-
'default' => false, // Whether or not this status is part of WP Core
|
196 |
-
'user_selectable' => true, // Whether or not a user can set a booking to this status
|
197 |
-
'public' => false,
|
198 |
-
'exclude_from_search' => true,
|
199 |
-
'show_in_admin_all_list' => true,
|
200 |
-
'show_in_admin_status_list' => true,
|
201 |
-
'label_count' => _n_noop( 'Arrived <span class="count">(%s)</span>', 'Arrived <span class="count">(%s)</span>', 'restaurant-reservations' )
|
202 |
-
);
|
203 |
-
}
|
204 |
-
|
205 |
-
return $booking_statuses;
|
206 |
-
}
|
207 |
-
|
208 |
-
/**
|
209 |
-
* @since 2.1.9
|
210 |
-
* Adds in a "Payment Failed" status if the option to require deposits when
|
211 |
-
* booking a reservation has been toggled on.
|
212 |
-
*/
|
213 |
-
public function add_payment_failed_status( $booking_statuses = array() ) {
|
214 |
-
global $rtb_controller;
|
215 |
-
|
216 |
-
if ( $rtb_controller->settings->get_setting( 'require-deposit' ) ) {
|
217 |
-
$booking_statuses['payment_failed'] = array(
|
218 |
-
'label' => _x( 'Payment Failed', 'The guest has tried to make a payment but it was declined.', 'restaurant-reservations' ),
|
219 |
-
'default' => false, // Whether or not this status is part of WP Core
|
220 |
-
'user_selectable' => false, // Whether or not a user can set a booking to this status
|
221 |
-
'public' => false,
|
222 |
-
'exclude_from_search' => true,
|
223 |
-
'show_in_admin_all_list' => true,
|
224 |
-
'show_in_admin_status_list' => true,
|
225 |
-
'label_count' => _n_noop( 'Payment Failed <span class="count">(%s)</span>', 'Payment Failed <span class="count">(%s)</span>', 'restaurant-reservations' )
|
226 |
-
);
|
227 |
-
|
228 |
-
// This is an intermediate status when payment is pending
|
229 |
-
$booking_statuses['payment_pending'] = array(
|
230 |
-
'label' => _x( 'Payment Pending', 'The guest has booked but payment is pending', 'restaurant-reservations' ),
|
231 |
-
'default' => false, // Whether or not this status is part of WP Core
|
232 |
-
'user_selectable' => false, // Whether or not a user can set a booking to this status
|
233 |
-
'public' => false,
|
234 |
-
'exclude_from_search' => true,
|
235 |
-
'show_in_admin_all_list' => true,
|
236 |
-
'show_in_admin_status_list' => true,
|
237 |
-
'label_count' => _n_noop( 'Payment Pending <span class="count">(%s)</span>', 'Payment Pending <span class="count">(%s)</span>', 'restaurant-reservations' )
|
238 |
-
);
|
239 |
-
}
|
240 |
-
|
241 |
-
return $booking_statuses;
|
242 |
-
}
|
243 |
-
|
244 |
-
/**
|
245 |
-
* Print an HTML element to select a booking status
|
246 |
-
* @since 0.0.1
|
247 |
-
* @note This is no longer used in the bookings table, but it could be
|
248 |
-
* useful in the future, so leave it in for now (0.0.1) until the plugin is
|
249 |
-
* more fleshed out.
|
250 |
-
*/
|
251 |
-
public function print_booking_status_select( $current = false ) {
|
252 |
-
|
253 |
-
if ( $current === false ) {
|
254 |
-
$current = 'none';
|
255 |
-
}
|
256 |
-
|
257 |
-
// Output stored select field if available
|
258 |
-
if ( !empty( $this->status_select_html[$current] ) ) {
|
259 |
-
return $this->status_select_html[$current];
|
260 |
-
}
|
261 |
-
|
262 |
-
ob_start();
|
263 |
-
?>
|
264 |
-
|
265 |
-
<select name="rtb-select-status">
|
266 |
-
<?php foreach ( $this->booking_statuses as $status => $args ) : ?>
|
267 |
-
<?php if ( $args['user_selectable'] === true ) : ?>
|
268 |
-
<option value="<?php echo esc_attr( $status ); ?>"<?php echo $status == $current ? ' selected="selected"' : ''; ?>><?php echo esc_attr( $args['label'] ); ?></option>
|
269 |
-
<?php endif; ?>
|
270 |
-
<?php endforeach; ?>
|
271 |
-
</select>
|
272 |
-
|
273 |
-
<?php
|
274 |
-
$output = ob_get_clean();
|
275 |
-
|
276 |
-
// Store output so we don't need to loop for every row
|
277 |
-
$this->status_select_html[$current] = $output;
|
278 |
-
|
279 |
-
return $output;
|
280 |
-
|
281 |
-
}
|
282 |
-
|
283 |
-
/**
|
284 |
-
* Delete a booking request (or send to trash)
|
285 |
-
*
|
286 |
-
* @since 0.0.1
|
287 |
-
*/
|
288 |
-
public function delete_booking( $id ) {
|
289 |
-
|
290 |
-
$id = absint( $id );
|
291 |
-
if ( !current_user_can( 'manage_bookings' ) ) {
|
292 |
-
return false;
|
293 |
-
}
|
294 |
-
|
295 |
-
$booking = get_post( $id );
|
296 |
-
|
297 |
-
if ( !$this->is_valid_booking_post_object( $booking ) ) {
|
298 |
-
return false;
|
299 |
-
}
|
300 |
-
|
301 |
-
// If we're already looking at trashed posts, delete it for good.
|
302 |
-
// Otherwise, just send it to trash.
|
303 |
-
if ( !empty( $_GET['status'] ) && $_GET['status'] == 'trash' ) {
|
304 |
-
$screen = get_current_screen();
|
305 |
-
if ( $screen->base == 'toplevel_page_rtb-bookings' ) {
|
306 |
-
$result = wp_delete_post( $id, true );
|
307 |
-
}
|
308 |
-
} else {
|
309 |
-
$result = wp_trash_post( $id );
|
310 |
-
}
|
311 |
-
|
312 |
-
if ( $result === false ) {
|
313 |
-
return false;
|
314 |
-
} else {
|
315 |
-
return true;
|
316 |
-
}
|
317 |
-
}
|
318 |
-
|
319 |
-
/**
|
320 |
-
* Update a booking status.
|
321 |
-
* @since 0.0.1
|
322 |
-
*/
|
323 |
-
function update_booking_status( $id, $status ) {
|
324 |
-
|
325 |
-
$id = absint( $id );
|
326 |
-
if ( !current_user_can( 'manage_bookings' ) ) {
|
327 |
-
return false;
|
328 |
-
}
|
329 |
-
|
330 |
-
if ( !$this->is_valid_booking_status( $status ) ) {
|
331 |
-
return false;
|
332 |
-
}
|
333 |
-
|
334 |
-
$booking = get_post( $id );
|
335 |
-
|
336 |
-
if ( !$this->is_valid_booking_post_object( $booking ) ) {
|
337 |
-
return false;
|
338 |
-
}
|
339 |
-
|
340 |
-
if ( $booking->post_status === $status ) {
|
341 |
-
return null;
|
342 |
-
}
|
343 |
-
|
344 |
-
$result = wp_update_post(
|
345 |
-
array(
|
346 |
-
'ID' => $id,
|
347 |
-
'post_status' => $status,
|
348 |
-
'edit_date' => current_time( 'mysql' ),
|
349 |
-
)
|
350 |
-
);
|
351 |
-
|
352 |
-
return $result ? true : false;
|
353 |
-
}
|
354 |
-
|
355 |
-
/**
|
356 |
-
* Check if status is valid for bookings
|
357 |
-
* @since 0.0.1
|
358 |
-
*/
|
359 |
-
public function is_valid_booking_status( $status ) {
|
360 |
-
return isset( $this->booking_statuses[$status] ) ? true : false;
|
361 |
-
}
|
362 |
-
|
363 |
-
/**
|
364 |
-
* Check if booking is a valid Post object with the correct post type
|
365 |
-
* @since 0.0.1
|
366 |
-
*/
|
367 |
-
public function is_valid_booking_post_object( $booking ) {
|
368 |
-
return !is_wp_error( $booking ) && is_object( $booking ) && $booking->post_type == RTB_BOOKING_POST_TYPE;
|
369 |
-
}
|
370 |
-
|
371 |
-
/**
|
372 |
-
* Show the count of upcoming pending bookings in admin nav menu
|
373 |
-
*
|
374 |
-
* This is hooked to admin_footer to ensure that any actions on the page
|
375 |
-
* which might effect booking statuses have already fired, such as the
|
376 |
-
* bulk actions on the bookings page, which are processed after the nav
|
377 |
-
* menu has been loaded.
|
378 |
-
*
|
379 |
-
* @since 1.7.5
|
380 |
-
*/
|
381 |
-
public function show_pending_count() {
|
382 |
-
|
383 |
-
global $rtb_controller;
|
384 |
-
$rtb_controller->cpts->update_pending_count();
|
385 |
-
$pending_count = get_option( 'rtb_pending_count', 0 );
|
386 |
-
|
387 |
-
if ( !$pending_count ) {
|
388 |
-
return;
|
389 |
-
}
|
390 |
-
|
391 |
-
$pending_bubble = ' <span class="update-plugins count-' . (int) $pending_count . '">' .
|
392 |
-
'<span class="plugin-count" aria-hidden="true">' . (int) $pending_count . '</span></span>';
|
393 |
-
|
394 |
-
?>
|
395 |
-
|
396 |
-
<script type="text/javascript">
|
397 |
-
jQuery(document).ready(function ($) {
|
398 |
-
$( '#toplevel_page_rtb-bookings > a .wp-menu-name' ).append( '<?php echo $pending_bubble; ?>' );
|
399 |
-
});
|
400 |
-
</script>
|
401 |
-
|
402 |
-
<?php
|
403 |
-
}
|
404 |
-
|
405 |
-
/**
|
406 |
-
* Update the count of upcoming pending bookings
|
407 |
-
*
|
408 |
-
* This is hooked to fire whenever a booking is added or updated. But it can
|
409 |
-
* also be called directly once to set the initial option.
|
410 |
-
*
|
411 |
-
* @param rtbBooking $booking Optional. The booking being added or updated.
|
412 |
-
* @since 1.7.5
|
413 |
-
*/
|
414 |
-
public function update_pending_count( $booking = null ) {
|
415 |
-
|
416 |
-
global $wpdb;
|
417 |
-
$current_date_time = date( 'Y-m-d H:i:s', current_time( 'timestamp' ) - HOUR_IN_SECONDS );
|
418 |
-
$count = $wpdb->get_var(
|
419 |
-
$wpdb->prepare(
|
420 |
-
"SELECT COUNT(ID) FROM {$wpdb->prefix}posts WHERE post_type=%s AND post_status='pending' AND post_date>=%s;",
|
421 |
-
RTB_BOOKING_POST_TYPE,
|
422 |
-
$current_date_time
|
423 |
-
)
|
424 |
-
);
|
425 |
-
|
426 |
-
update_option( 'rtb_pending_count', (int) $count );
|
427 |
-
}
|
428 |
-
|
429 |
-
/**
|
430 |
-
* Update the count of upcoming pending bookings whenever a booking status
|
431 |
-
* is modified
|
432 |
-
*
|
433 |
-
* @param string $new_status The status being transitioned to
|
434 |
-
* @param string $old_status The status the post used to have
|
435 |
-
* @param WP_Post $post The post being transitioned
|
436 |
-
* @since 1.7.5
|
437 |
-
*/
|
438 |
-
public function maybe_update_pending_count( $new_status, $old_status, $post ) {
|
439 |
-
if ( $post->post_type === RTB_BOOKING_POST_TYPE ) {
|
440 |
-
$this->update_pending_count();
|
441 |
-
}
|
442 |
-
}
|
443 |
-
|
444 |
-
}
|
445 |
-
} // endif;
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class to handle all custom post type definitions for Restaurant Reservations
|
4 |
+
*/
|
5 |
+
|
6 |
+
if ( !defined( 'ABSPATH' ) )
|
7 |
+
exit;
|
8 |
+
|
9 |
+
if ( !class_exists( 'rtbCustomPostTypes' ) ) {
|
10 |
+
class rtbCustomPostTypes {
|
11 |
+
|
12 |
+
// Array of valid post statuses
|
13 |
+
// @sa set_booking_statuses()
|
14 |
+
public $booking_statuses = array();
|
15 |
+
|
16 |
+
// Cached select fields for booking statuses
|
17 |
+
public $status_select_html = array();
|
18 |
+
|
19 |
+
public function __construct() {
|
20 |
+
|
21 |
+
// Call when plugin is initialized on every page load
|
22 |
+
add_action( 'init', array( $this, 'load_cpts' ) );
|
23 |
+
|
24 |
+
// Set up $booking_statuses array and register new post statuses
|
25 |
+
add_action( 'init', array( $this, 'set_booking_statuses' ) );
|
26 |
+
add_filter( 'rtb_post_statuses_args' , array( $this, 'add_arrived_status' ) );
|
27 |
+
add_filter( 'rtb_post_statuses_args' , array( $this, 'add_cancelled_status' ) );
|
28 |
+
add_filter( 'rtb_post_statuses_args' , array( $this, 'add_payment_failed_status' ) );
|
29 |
+
|
30 |
+
// Display the count of pending bookings
|
31 |
+
add_action( 'admin_footer', array( $this, 'show_pending_count' ) );
|
32 |
+
|
33 |
+
// Maintain the count of pending bookings
|
34 |
+
add_action( 'rtb_insert_booking', array( $this, 'update_pending_count' ) );
|
35 |
+
add_action( 'rtb_update_booking', array( $this, 'update_pending_count' ) );
|
36 |
+
add_action( 'transition_post_status', array( $this, 'maybe_update_pending_count' ), 999, 3 );
|
37 |
+
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Initialize custom post types
|
42 |
+
* @since 0.1
|
43 |
+
*/
|
44 |
+
public function load_cpts() {
|
45 |
+
global $rtb_controller;
|
46 |
+
|
47 |
+
// Define the booking custom post type
|
48 |
+
$args = array(
|
49 |
+
'labels' => array(
|
50 |
+
'name' => __( 'Bookings', 'restaurant-reservations' ),
|
51 |
+
'singular_name' => __( 'Booking', 'restaurant-reservations' ),
|
52 |
+
'menu_name' => __( 'Bookings', 'restaurant-reservations' ),
|
53 |
+
'name_admin_bar' => __( 'Bookings', 'restaurant-reservations' ),
|
54 |
+
'add_new' => __( 'Add New', 'restaurant-reservations' ),
|
55 |
+
'add_new_item' => __( 'Add New Booking', 'restaurant-reservations' ),
|
56 |
+
'edit_item' => __( 'Edit Booking', 'restaurant-reservations' ),
|
57 |
+
'new_item' => __( 'New Booking', 'restaurant-reservations' ),
|
58 |
+
'view_item' => __( 'View Booking', 'restaurant-reservations' ),
|
59 |
+
'search_items' => __( 'Search Bookings', 'restaurant-reservations' ),
|
60 |
+
'not_found' => __( 'No bookings found', 'restaurant-reservations' ),
|
61 |
+
'not_found_in_trash' => __( 'No bookings found in trash', 'restaurant-reservations' ),
|
62 |
+
'all_items' => __( 'All Bookings', 'restaurant-reservations' ),
|
63 |
+
),
|
64 |
+
'menu_icon' => 'dashicons-calendar',
|
65 |
+
'public' => false,
|
66 |
+
'supports' => array(
|
67 |
+
'title',
|
68 |
+
'revisions'
|
69 |
+
)
|
70 |
+
);
|
71 |
+
|
72 |
+
// Create filter so addons can modify the arguments
|
73 |
+
$args = apply_filters( 'rtb_booking_args', $args );
|
74 |
+
|
75 |
+
// Add an action so addons can hook in before the post type is registered
|
76 |
+
do_action( 'rtb_booking_pre_register' );
|
77 |
+
|
78 |
+
// Register the post type
|
79 |
+
register_post_type( RTB_BOOKING_POST_TYPE, $args );
|
80 |
+
|
81 |
+
// Add an action so addons can hook in after the post type is registered
|
82 |
+
do_action( 'rtb_booking_post_register' );
|
83 |
+
|
84 |
+
if ( $rtb_controller->permissions->check_permission( 'custom_fields' ) ) {
|
85 |
+
// Define the field custom post type
|
86 |
+
$args = array(
|
87 |
+
'labels' => array(
|
88 |
+
'name' => __( 'Field', 'custom-fields-for-rtb' ),
|
89 |
+
'singular_name' => __( 'Field', 'custom-fields-for-rtb' ),
|
90 |
+
'menu_name' => __( 'Fields', 'custom-fields-for-rtb' ),
|
91 |
+
'name_admin_bar' => __( 'Fields', 'custom-fields-for-rtb' ),
|
92 |
+
'add_new' => __( 'Add Field', 'custom-fields-for-rtb' ),
|
93 |
+
'add_new_item' => __( 'Add New Field', 'custom-fields-for-rtb' ),
|
94 |
+
'edit_item' => __( 'Edit Field', 'custom-fields-for-rtb' ),
|
95 |
+
'new_item' => __( 'New Field', 'custom-fields-for-rtb' ),
|
96 |
+
'view_item' => __( 'View Field', 'custom-fields-for-rtb' ),
|
97 |
+
'search_items' => __( 'Search Fields', 'custom-fields-for-rtb' ),
|
98 |
+
'not_found' => __( 'No fields found', 'custom-fields-for-rtb' ),
|
99 |
+
'not_found_in_trash' => __( 'No fields found in trash', 'custom-fields-for-rtb' ),
|
100 |
+
'all_items' => __( 'All Fields', 'custom-fields-for-rtb' ),
|
101 |
+
),
|
102 |
+
'public' => false
|
103 |
+
);
|
104 |
+
|
105 |
+
$args = apply_filters( 'cffrtb_field_post_type_args', $args );
|
106 |
+
|
107 |
+
register_post_type( 'cffrtb_field', $args );
|
108 |
+
}
|
109 |
+
}
|
110 |
+
|
111 |
+
/**
|
112 |
+
* Set an array of valid booking statuses and register any custom statuses
|
113 |
+
* @since 0.0.1
|
114 |
+
*/
|
115 |
+
public function set_booking_statuses() {
|
116 |
+
|
117 |
+
$this->booking_statuses['pending'] = array(
|
118 |
+
'label' => _x( 'Pending', 'Booking status when it is pending review', 'restaurant-reservations' ),
|
119 |
+
'default' => true, // Whether or not this status is part of WP Core
|
120 |
+
'user_selectable' => true, // Whether or not a user can set a booking to this status
|
121 |
+
'label_count' => _n_noop( 'Pending <span class="count">(%s)</span>', 'Pending <span class="count">(%s)</span>'),
|
122 |
+
);
|
123 |
+
|
124 |
+
$this->booking_statuses['confirmed'] = array (
|
125 |
+
'label' => _x( 'Confirmed', 'Booking status for a confirmed booking', 'restaurant-reservations' ),
|
126 |
+
'default' => false, // Whether or not this status is part of WP Core
|
127 |
+
'user_selectable' => true, // Whether or not a user can set a booking to this status
|
128 |
+
'public' => false,
|
129 |
+
'exclude_from_search' => true,
|
130 |
+
'show_in_admin_all_list' => true,
|
131 |
+
'show_in_admin_status_list' => true,
|
132 |
+
'label_count' => _n_noop( 'Confirmed <span class="count">(%s)</span>', 'Confirmed <span class="count">(%s)</span>', 'restaurant-reservations' ),
|
133 |
+
);
|
134 |
+
|
135 |
+
$this->booking_statuses['closed'] = array(
|
136 |
+
'label' => _x( 'Closed', 'Booking status for a closed booking', 'restaurant-reservations' ),
|
137 |
+
'default' => false, // Whether or not this status is part of WP Core
|
138 |
+
'user_selectable' => true, // Whether or not a user can set a booking to this status
|
139 |
+
'public' => false,
|
140 |
+
'exclude_from_search' => true,
|
141 |
+
'show_in_admin_all_list' => true,
|
142 |
+
'show_in_admin_status_list' => true,
|
143 |
+
'label_count' => _n_noop( 'Closed <span class="count">(%s)</span>', 'Closed <span class="count">(%s)</span>', 'restaurant-reservations' )
|
144 |
+
);
|
145 |
+
|
146 |
+
// Let addons hook in to add/edit/remove post statuses
|
147 |
+
$this->booking_statuses = apply_filters( 'rtb_post_statuses_args', $this->booking_statuses );
|
148 |
+
|
149 |
+
// Register the custom post statuses
|
150 |
+
foreach ( $this->booking_statuses as $status => $args ) {
|
151 |
+
if ( $args['default'] === false ) {
|
152 |
+
register_post_status( $status, $args );
|
153 |
+
}
|
154 |
+
}
|
155 |
+
|
156 |
+
}
|
157 |
+
|
158 |
+
|
159 |
+
/**
|
160 |
+
* @since 2.1.0
|
161 |
+
* Adds in a "Cancelled" status if the option to allow guest to cancel
|
162 |
+
* their reservation has been toggled on.
|
163 |
+
*/
|
164 |
+
public function add_cancelled_status( $booking_statuses = array() ) {
|
165 |
+
global $rtb_controller;
|
166 |
+
|
167 |
+
if ( $rtb_controller->settings->get_setting( 'allow-cancellations' ) ) {
|
168 |
+
$booking_statuses['cancelled'] = array(
|
169 |
+
'label' => _x( 'Cancelled', 'The guest has cancelled their reservation themselves.', 'restaurant-reservations' ),
|
170 |
+
'default' => false, // Whether or not this status is part of WP Core
|
171 |
+
'user_selectable' => false, // Whether or not a user can set a booking to this status
|
172 |
+
'public' => false,
|
173 |
+
'exclude_from_search' => true,
|
174 |
+
'show_in_admin_all_list' => true,
|
175 |
+
'show_in_admin_status_list' => true,
|
176 |
+
'label_count' => _n_noop( 'Cancelled <span class="count">(%s)</span>', 'Cancelled <span class="count">(%s)</span>', 'restaurant-reservations' )
|
177 |
+
);
|
178 |
+
}
|
179 |
+
|
180 |
+
return $booking_statuses;
|
181 |
+
}
|
182 |
+
|
183 |
+
|
184 |
+
/**
|
185 |
+
* @since 2.0.0
|
186 |
+
* Adds in an "Arrived" status if the option to check guests in on arrival
|
187 |
+
* has been toggled on.
|
188 |
+
*/
|
189 |
+
public function add_arrived_status( $booking_statuses = array() ) {
|
190 |
+
global $rtb_controller;
|
191 |
+
|
192 |
+
if ( $rtb_controller->settings->get_setting( 'view-bookings-arrivals' ) ) {
|
193 |
+
$booking_statuses['arrived'] = array(
|
194 |
+
'label' => _x( 'Arrived', 'The guests have arrived for their reservation', 'restaurant-reservations' ),
|
195 |
+
'default' => false, // Whether or not this status is part of WP Core
|
196 |
+
'user_selectable' => true, // Whether or not a user can set a booking to this status
|
197 |
+
'public' => false,
|
198 |
+
'exclude_from_search' => true,
|
199 |
+
'show_in_admin_all_list' => true,
|
200 |
+
'show_in_admin_status_list' => true,
|
201 |
+
'label_count' => _n_noop( 'Arrived <span class="count">(%s)</span>', 'Arrived <span class="count">(%s)</span>', 'restaurant-reservations' )
|
202 |
+
);
|
203 |
+
}
|
204 |
+
|
205 |
+
return $booking_statuses;
|
206 |
+
}
|
207 |
+
|
208 |
+
/**
|
209 |
+
* @since 2.1.9
|
210 |
+
* Adds in a "Payment Failed" status if the option to require deposits when
|
211 |
+
* booking a reservation has been toggled on.
|
212 |
+
*/
|
213 |
+
public function add_payment_failed_status( $booking_statuses = array() ) {
|
214 |
+
global $rtb_controller;
|
215 |
+
|
216 |
+
if ( $rtb_controller->settings->get_setting( 'require-deposit' ) ) {
|
217 |
+
$booking_statuses['payment_failed'] = array(
|
218 |
+
'label' => _x( 'Payment Failed', 'The guest has tried to make a payment but it was declined.', 'restaurant-reservations' ),
|
219 |
+
'default' => false, // Whether or not this status is part of WP Core
|
220 |
+
'user_selectable' => false, // Whether or not a user can set a booking to this status
|
221 |
+
'public' => false,
|
222 |
+
'exclude_from_search' => true,
|
223 |
+
'show_in_admin_all_list' => true,
|
224 |
+
'show_in_admin_status_list' => true,
|
225 |
+
'label_count' => _n_noop( 'Payment Failed <span class="count">(%s)</span>', 'Payment Failed <span class="count">(%s)</span>', 'restaurant-reservations' )
|
226 |
+
);
|
227 |
+
|
228 |
+
// This is an intermediate status when payment is pending
|
229 |
+
$booking_statuses['payment_pending'] = array(
|
230 |
+
'label' => _x( 'Payment Pending', 'The guest has booked but payment is pending', 'restaurant-reservations' ),
|
231 |
+
'default' => false, // Whether or not this status is part of WP Core
|
232 |
+
'user_selectable' => false, // Whether or not a user can set a booking to this status
|
233 |
+
'public' => false,
|
234 |
+
'exclude_from_search' => true,
|
235 |
+
'show_in_admin_all_list' => true,
|
236 |
+
'show_in_admin_status_list' => true,
|
237 |
+
'label_count' => _n_noop( 'Payment Pending <span class="count">(%s)</span>', 'Payment Pending <span class="count">(%s)</span>', 'restaurant-reservations' )
|
238 |
+
);
|
239 |
+
}
|
240 |
+
|
241 |
+
return $booking_statuses;
|
242 |
+
}
|
243 |
+
|
244 |
+
/**
|
245 |
+
* Print an HTML element to select a booking status
|
246 |
+
* @since 0.0.1
|
247 |
+
* @note This is no longer used in the bookings table, but it could be
|
248 |
+
* useful in the future, so leave it in for now (0.0.1) until the plugin is
|
249 |
+
* more fleshed out.
|
250 |
+
*/
|
251 |
+
public function print_booking_status_select( $current = false ) {
|
252 |
+
|
253 |
+
if ( $current === false ) {
|
254 |
+
$current = 'none';
|
255 |
+
}
|
256 |
+
|
257 |
+
// Output stored select field if available
|
258 |
+
if ( !empty( $this->status_select_html[$current] ) ) {
|
259 |
+
return $this->status_select_html[$current];
|
260 |
+
}
|
261 |
+
|
262 |
+
ob_start();
|
263 |
+
?>
|
264 |
+
|
265 |
+
<select name="rtb-select-status">
|
266 |
+
<?php foreach ( $this->booking_statuses as $status => $args ) : ?>
|
267 |
+
<?php if ( $args['user_selectable'] === true ) : ?>
|
268 |
+
<option value="<?php echo esc_attr( $status ); ?>"<?php echo $status == $current ? ' selected="selected"' : ''; ?>><?php echo esc_attr( $args['label'] ); ?></option>
|
269 |
+
<?php endif; ?>
|
270 |
+
<?php endforeach; ?>
|
271 |
+
</select>
|
272 |
+
|
273 |
+
<?php
|
274 |
+
$output = ob_get_clean();
|
275 |
+
|
276 |
+
// Store output so we don't need to loop for every row
|
277 |
+
$this->status_select_html[$current] = $output;
|
278 |
+
|
279 |
+
return $output;
|
280 |
+
|
281 |
+
}
|
282 |
+
|
283 |
+
/**
|
284 |
+
* Delete a booking request (or send to trash)
|
285 |
+
*
|
286 |
+
* @since 0.0.1
|
287 |
+
*/
|
288 |
+
public function delete_booking( $id ) {
|
289 |
+
|
290 |
+
$id = absint( $id );
|
291 |
+
if ( !current_user_can( 'manage_bookings' ) ) {
|
292 |
+
return false;
|
293 |
+
}
|
294 |
+
|
295 |
+
$booking = get_post( $id );
|
296 |
+
|
297 |
+
if ( !$this->is_valid_booking_post_object( $booking ) ) {
|
298 |
+
return false;
|
299 |
+
}
|
300 |
+
|
301 |
+
// If we're already looking at trashed posts, delete it for good.
|
302 |
+
// Otherwise, just send it to trash.
|
303 |
+
if ( !empty( $_GET['status'] ) && $_GET['status'] == 'trash' ) {
|
304 |
+
$screen = get_current_screen();
|
305 |
+
if ( $screen->base == 'toplevel_page_rtb-bookings' ) {
|
306 |
+
$result = wp_delete_post( $id, true );
|
307 |
+
}
|
308 |
+
} else {
|
309 |
+
$result = wp_trash_post( $id );
|
310 |
+
}
|
311 |
+
|
312 |
+
if ( $result === false ) {
|
313 |
+
return false;
|
314 |
+
} else {
|
315 |
+
return true;
|
316 |
+
}
|
317 |
+
}
|
318 |
+
|
319 |
+
/**
|
320 |
+
* Update a booking status.
|
321 |
+
* @since 0.0.1
|
322 |
+
*/
|
323 |
+
function update_booking_status( $id, $status ) {
|
324 |
+
|
325 |
+
$id = absint( $id );
|
326 |
+
if ( !current_user_can( 'manage_bookings' ) ) {
|
327 |
+
return false;
|
328 |
+
}
|
329 |
+
|
330 |
+
if ( !$this->is_valid_booking_status( $status ) ) {
|
331 |
+
return false;
|
332 |
+
}
|
333 |
+
|
334 |
+
$booking = get_post( $id );
|
335 |
+
|
336 |
+
if ( !$this->is_valid_booking_post_object( $booking ) ) {
|
337 |
+
return false;
|
338 |
+
}
|
339 |
+
|
340 |
+
if ( $booking->post_status === $status ) {
|
341 |
+
return null;
|
342 |
+
}
|
343 |
+
|
344 |
+
$result = wp_update_post(
|
345 |
+
array(
|
346 |
+
'ID' => $id,
|
347 |
+
'post_status' => $status,
|
348 |
+
'edit_date' => current_time( 'mysql' ),
|
349 |
+
)
|
350 |
+
);
|
351 |
+
|
352 |
+
return $result ? true : false;
|
353 |
+
}
|
354 |
+
|
355 |
+
/**
|
356 |
+
* Check if status is valid for bookings
|
357 |
+
* @since 0.0.1
|
358 |
+
*/
|
359 |
+
public function is_valid_booking_status( $status ) {
|
360 |
+
return isset( $this->booking_statuses[$status] ) ? true : false;
|
361 |
+
}
|
362 |
+
|
363 |
+
/**
|
364 |
+
* Check if booking is a valid Post object with the correct post type
|
365 |
+
* @since 0.0.1
|
366 |
+
*/
|
367 |
+
public function is_valid_booking_post_object( $booking ) {
|
368 |
+
return !is_wp_error( $booking ) && is_object( $booking ) && $booking->post_type == RTB_BOOKING_POST_TYPE;
|
369 |
+
}
|
370 |
+
|
371 |
+
/**
|
372 |
+
* Show the count of upcoming pending bookings in admin nav menu
|
373 |
+
*
|
374 |
+
* This is hooked to admin_footer to ensure that any actions on the page
|
375 |
+
* which might effect booking statuses have already fired, such as the
|
376 |
+
* bulk actions on the bookings page, which are processed after the nav
|
377 |
+
* menu has been loaded.
|
378 |
+
*
|
379 |
+
* @since 1.7.5
|
380 |
+
*/
|
381 |
+
public function show_pending_count() {
|
382 |
+
|
383 |
+
global $rtb_controller;
|
384 |
+
$rtb_controller->cpts->update_pending_count();
|
385 |
+
$pending_count = get_option( 'rtb_pending_count', 0 );
|
386 |
+
|
387 |
+
if ( !$pending_count ) {
|
388 |
+
return;
|
389 |
+
}
|
390 |
+
|
391 |
+
$pending_bubble = ' <span class="update-plugins count-' . (int) $pending_count . '">' .
|
392 |
+
'<span class="plugin-count" aria-hidden="true">' . (int) $pending_count . '</span></span>';
|
393 |
+
|
394 |
+
?>
|
395 |
+
|
396 |
+
<script type="text/javascript">
|
397 |
+
jQuery(document).ready(function ($) {
|
398 |
+
$( '#toplevel_page_rtb-bookings > a .wp-menu-name' ).append( '<?php echo $pending_bubble; ?>' );
|
399 |
+
});
|
400 |
+
</script>
|
401 |
+
|
402 |
+
<?php
|
403 |
+
}
|
404 |
+
|
405 |
+
/**
|
406 |
+
* Update the count of upcoming pending bookings
|
407 |
+
*
|
408 |
+
* This is hooked to fire whenever a booking is added or updated. But it can
|
409 |
+
* also be called directly once to set the initial option.
|
410 |
+
*
|
411 |
+
* @param rtbBooking $booking Optional. The booking being added or updated.
|
412 |
+
* @since 1.7.5
|
413 |
+
*/
|
414 |
+
public function update_pending_count( $booking = null ) {
|
415 |
+
|
416 |
+
global $wpdb;
|
417 |
+
$current_date_time = date( 'Y-m-d H:i:s', current_time( 'timestamp' ) - HOUR_IN_SECONDS );
|
418 |
+
$count = $wpdb->get_var(
|
419 |
+
$wpdb->prepare(
|
420 |
+
"SELECT COUNT(ID) FROM {$wpdb->prefix}posts WHERE post_type=%s AND post_status='pending' AND post_date>=%s;",
|
421 |
+
RTB_BOOKING_POST_TYPE,
|
422 |
+
$current_date_time
|
423 |
+
)
|
424 |
+
);
|
425 |
+
|
426 |
+
update_option( 'rtb_pending_count', (int) $count );
|
427 |
+
}
|
428 |
+
|
429 |
+
/**
|
430 |
+
* Update the count of upcoming pending bookings whenever a booking status
|
431 |
+
* is modified
|
432 |
+
*
|
433 |
+
* @param string $new_status The status being transitioned to
|
434 |
+
* @param string $old_status The status the post used to have
|
435 |
+
* @param WP_Post $post The post being transitioned
|
436 |
+
* @since 1.7.5
|
437 |
+
*/
|
438 |
+
public function maybe_update_pending_count( $new_status, $old_status, $post ) {
|
439 |
+
if ( $post->post_type === RTB_BOOKING_POST_TYPE ) {
|
440 |
+
$this->update_pending_count();
|
441 |
+
}
|
442 |
+
}
|
443 |
+
|
444 |
+
}
|
445 |
+
} // endif;
|
includes/DeactivationSurvey.class.php
CHANGED
@@ -1,84 +1,84 @@
|
|
1 |
-
<?php
|
2 |
-
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
-
|
4 |
-
if ( !class_exists( 'rtbDeactivationSurvey' ) ) {
|
5 |
-
/**
|
6 |
-
* Class to handle plugin deactivation survey
|
7 |
-
*
|
8 |
-
* @since 2.0.15
|
9 |
-
*/
|
10 |
-
class rtbDeactivationSurvey {
|
11 |
-
|
12 |
-
public function __construct() {
|
13 |
-
add_action( 'current_screen', array( $this, 'maybe_add_survey' ) );
|
14 |
-
}
|
15 |
-
|
16 |
-
public function maybe_add_survey() {
|
17 |
-
if ( in_array( get_current_screen()->id, array( 'plugins', 'plugins-network' ), true) ) {
|
18 |
-
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_deactivation_scripts') );
|
19 |
-
add_action( 'admin_footer', array( $this, 'add_deactivation_html') );
|
20 |
-
}
|
21 |
-
}
|
22 |
-
|
23 |
-
public function enqueue_deactivation_scripts() {
|
24 |
-
wp_enqueue_style( 'rtb-deactivation-css', RTB_PLUGIN_URL . '/assets/css/plugin-deactivation.css' );
|
25 |
-
wp_enqueue_script( 'rtb-deactivation-js', RTB_PLUGIN_URL . '/assets/js/plugin-deactivation.js', array( 'jquery' ) );
|
26 |
-
|
27 |
-
wp_localize_script( 'rtb-deactivation-js', 'rtb_deactivation_data', array( 'site_url' => site_url() ) );
|
28 |
-
}
|
29 |
-
|
30 |
-
public function add_deactivation_html() {
|
31 |
-
|
32 |
-
$install_time = get_option( 'rtb-installation-time' );
|
33 |
-
|
34 |
-
$options = array(
|
35 |
-
1 => array(
|
36 |
-
'title' => esc_html__( 'I no longer need the plugin', 'restaurant-reservations' ),
|
37 |
-
),
|
38 |
-
2 => array(
|
39 |
-
'title' => esc_html__( 'I\'m switching to a different plugin', 'restaurant-reservations' ),
|
40 |
-
'details' => esc_html__( 'Please share which plugin', 'restaurant-reservations' ),
|
41 |
-
),
|
42 |
-
3 => array(
|
43 |
-
'title' => esc_html__( 'I couldn\'t get the plugin to work', 'restaurant-reservations' ),
|
44 |
-
'details' => esc_html__( 'Please share what wasn\'t working', 'restaurant-reservations' ),
|
45 |
-
),
|
46 |
-
4 => array(
|
47 |
-
'title' => esc_html__( 'It\'s a temporary deactivation', 'restaurant-reservations' ),
|
48 |
-
),
|
49 |
-
5 => array(
|
50 |
-
'title' => esc_html__( 'Other', 'restaurant-reservations' ),
|
51 |
-
'details' => esc_html__( 'Please share the reason', 'restaurant-reservations' ),
|
52 |
-
),
|
53 |
-
);
|
54 |
-
?>
|
55 |
-
<div class="rtb-deactivate-survey-modal" id="rtb-deactivate-survey-restaurant-reservations">
|
56 |
-
<div class="rtb-deactivate-survey-wrap">
|
57 |
-
<form class="rtb-deactivate-survey" method="post" data-installtime="<?php echo $install_time; ?>">
|
58 |
-
<span class="rtb-deactivate-survey-title"><span class="dashicons dashicons-testimonial"></span><?php echo ' ' . __( 'Quick Feedback', 'restaurant-reservations' ); ?></span>
|
59 |
-
<span class="rtb-deactivate-survey-desc"><?php echo __('If you have a moment, please share why you are deactivating Five-Star Restaurant Reservations:', 'restaurant-reservations' ); ?></span>
|
60 |
-
<div class="rtb-deactivate-survey-options">
|
61 |
-
<?php foreach ( $options as $id => $option ) : ?>
|
62 |
-
<div class="rtb-deactivate-survey-option">
|
63 |
-
<label for="rtb-deactivate-survey-option-restaurant-reservations-<?php echo $id; ?>" class="rtb-deactivate-survey-option-label">
|
64 |
-
<input id="rtb-deactivate-survey-option-restaurant-reservations-<?php echo $id; ?>" class="rtb-deactivate-survey-option-input" type="radio" name="code" value="<?php echo $id; ?>" />
|
65 |
-
<span class="rtb-deactivate-survey-option-reason"><?php echo $option['title']; ?></span>
|
66 |
-
</label>
|
67 |
-
<?php if ( ! empty( $option['details'] ) ) : ?>
|
68 |
-
<input class="rtb-deactivate-survey-option-details" type="text" placeholder="<?php echo $option['details']; ?>" />
|
69 |
-
<?php endif; ?>
|
70 |
-
</div>
|
71 |
-
<?php endforeach; ?>
|
72 |
-
</div>
|
73 |
-
<div class="rtb-deactivate-survey-footer">
|
74 |
-
<button type="submit" class="rtb-deactivate-survey-submit button button-primary button-large"><?php _e('Submit and Deactivate', 'restaurant-reservations' ); ?></button>
|
75 |
-
<a href="#" class="rtb-deactivate-survey-deactivate"><?php _e('Skip and Deactivate', 'restaurant-reservations' ); ?></a>
|
76 |
-
</div>
|
77 |
-
</form>
|
78 |
-
</div>
|
79 |
-
</div>
|
80 |
-
<?php
|
81 |
-
}
|
82 |
-
}
|
83 |
-
|
84 |
}
|
1 |
+
<?php
|
2 |
+
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
+
|
4 |
+
if ( !class_exists( 'rtbDeactivationSurvey' ) ) {
|
5 |
+
/**
|
6 |
+
* Class to handle plugin deactivation survey
|
7 |
+
*
|
8 |
+
* @since 2.0.15
|
9 |
+
*/
|
10 |
+
class rtbDeactivationSurvey {
|
11 |
+
|
12 |
+
public function __construct() {
|
13 |
+
add_action( 'current_screen', array( $this, 'maybe_add_survey' ) );
|
14 |
+
}
|
15 |
+
|
16 |
+
public function maybe_add_survey() {
|
17 |
+
if ( in_array( get_current_screen()->id, array( 'plugins', 'plugins-network' ), true) ) {
|
18 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_deactivation_scripts') );
|
19 |
+
add_action( 'admin_footer', array( $this, 'add_deactivation_html') );
|
20 |
+
}
|
21 |
+
}
|
22 |
+
|
23 |
+
public function enqueue_deactivation_scripts() {
|
24 |
+
wp_enqueue_style( 'rtb-deactivation-css', RTB_PLUGIN_URL . '/assets/css/plugin-deactivation.css' );
|
25 |
+
wp_enqueue_script( 'rtb-deactivation-js', RTB_PLUGIN_URL . '/assets/js/plugin-deactivation.js', array( 'jquery' ) );
|
26 |
+
|
27 |
+
wp_localize_script( 'rtb-deactivation-js', 'rtb_deactivation_data', array( 'site_url' => site_url() ) );
|
28 |
+
}
|
29 |
+
|
30 |
+
public function add_deactivation_html() {
|
31 |
+
|
32 |
+
$install_time = get_option( 'rtb-installation-time' );
|
33 |
+
|
34 |
+
$options = array(
|
35 |
+
1 => array(
|
36 |
+
'title' => esc_html__( 'I no longer need the plugin', 'restaurant-reservations' ),
|
37 |
+
),
|
38 |
+
2 => array(
|
39 |
+
'title' => esc_html__( 'I\'m switching to a different plugin', 'restaurant-reservations' ),
|
40 |
+
'details' => esc_html__( 'Please share which plugin', 'restaurant-reservations' ),
|
41 |
+
),
|
42 |
+
3 => array(
|
43 |
+
'title' => esc_html__( 'I couldn\'t get the plugin to work', 'restaurant-reservations' ),
|
44 |
+
'details' => esc_html__( 'Please share what wasn\'t working', 'restaurant-reservations' ),
|
45 |
+
),
|
46 |
+
4 => array(
|
47 |
+
'title' => esc_html__( 'It\'s a temporary deactivation', 'restaurant-reservations' ),
|
48 |
+
),
|
49 |
+
5 => array(
|
50 |
+
'title' => esc_html__( 'Other', 'restaurant-reservations' ),
|
51 |
+
'details' => esc_html__( 'Please share the reason', 'restaurant-reservations' ),
|
52 |
+
),
|
53 |
+
);
|
54 |
+
?>
|
55 |
+
<div class="rtb-deactivate-survey-modal" id="rtb-deactivate-survey-restaurant-reservations">
|
56 |
+
<div class="rtb-deactivate-survey-wrap">
|
57 |
+
<form class="rtb-deactivate-survey" method="post" data-installtime="<?php echo $install_time; ?>">
|
58 |
+
<span class="rtb-deactivate-survey-title"><span class="dashicons dashicons-testimonial"></span><?php echo ' ' . __( 'Quick Feedback', 'restaurant-reservations' ); ?></span>
|
59 |
+
<span class="rtb-deactivate-survey-desc"><?php echo __('If you have a moment, please share why you are deactivating Five-Star Restaurant Reservations:', 'restaurant-reservations' ); ?></span>
|
60 |
+
<div class="rtb-deactivate-survey-options">
|
61 |
+
<?php foreach ( $options as $id => $option ) : ?>
|
62 |
+
<div class="rtb-deactivate-survey-option">
|
63 |
+
<label for="rtb-deactivate-survey-option-restaurant-reservations-<?php echo $id; ?>" class="rtb-deactivate-survey-option-label">
|
64 |
+
<input id="rtb-deactivate-survey-option-restaurant-reservations-<?php echo $id; ?>" class="rtb-deactivate-survey-option-input" type="radio" name="code" value="<?php echo $id; ?>" />
|
65 |
+
<span class="rtb-deactivate-survey-option-reason"><?php echo $option['title']; ?></span>
|
66 |
+
</label>
|
67 |
+
<?php if ( ! empty( $option['details'] ) ) : ?>
|
68 |
+
<input class="rtb-deactivate-survey-option-details" type="text" placeholder="<?php echo $option['details']; ?>" />
|
69 |
+
<?php endif; ?>
|
70 |
+
</div>
|
71 |
+
<?php endforeach; ?>
|
72 |
+
</div>
|
73 |
+
<div class="rtb-deactivate-survey-footer">
|
74 |
+
<button type="submit" class="rtb-deactivate-survey-submit button button-primary button-large"><?php _e('Submit and Deactivate', 'restaurant-reservations' ); ?></button>
|
75 |
+
<a href="#" class="rtb-deactivate-survey-deactivate"><?php _e('Skip and Deactivate', 'restaurant-reservations' ); ?></a>
|
76 |
+
</div>
|
77 |
+
</form>
|
78 |
+
</div>
|
79 |
+
</div>
|
80 |
+
<?php
|
81 |
+
}
|
82 |
+
}
|
83 |
+
|
84 |
}
|
includes/Editor.class.php
CHANGED
@@ -1,845 +1,845 @@
|
|
1 |
-
<?php
|
2 |
-
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
-
|
4 |
-
if ( !class_exists( 'cffrtbEditor' ) ) {
|
5 |
-
/**
|
6 |
-
* Class which builds the form editor for Custom Fields for Restaurant
|
7 |
-
* Reservations
|
8 |
-
*
|
9 |
-
* @since 0.1
|
10 |
-
*/
|
11 |
-
class cffrtbEditor {
|
12 |
-
|
13 |
-
/**
|
14 |
-
* Hook suffix for the page
|
15 |
-
*
|
16 |
-
* @since 0.1
|
17 |
-
*/
|
18 |
-
public $hook_suffix;
|
19 |
-
|
20 |
-
public function __construct() {
|
21 |
-
|
22 |
-
// Add the admin menu
|
23 |
-
add_action( 'admin_menu', array( $this, 'add_menu_page' ), 20 );
|
24 |
-
|
25 |
-
// Handle ajax requests
|
26 |
-
add_action( 'wp_ajax_nopriv_cffrtb-save-field' , array( $this , 'ajax_nopriv' ) );
|
27 |
-
add_action( 'wp_ajax_cffrtb-save-field', array( $this, 'ajax_save_field' ) );
|
28 |
-
add_action( 'wp_ajax_nopriv_cffrtb-save-order' , array( $this , 'ajax_nopriv' ) );
|
29 |
-
add_action( 'wp_ajax_cffrtb-save-order', array( $this, 'ajax_save_order' ) );
|
30 |
-
add_action( 'wp_ajax_nopriv_cffrtb-load-field' , array( $this , 'ajax_nopriv' ) );
|
31 |
-
add_action( 'wp_ajax_cffrtb-load-field', array( $this, 'ajax_load_field' ) );
|
32 |
-
add_action( 'wp_ajax_nopriv_cffrtb-delete-field' , array( $this , 'ajax_nopriv' ) );
|
33 |
-
add_action( 'wp_ajax_cffrtb-delete-field', array( $this, 'ajax_delete_field' ) );
|
34 |
-
add_action( 'wp_ajax_nopriv_cffrtb-enable-field' , array( $this , 'ajax_nopriv' ) );
|
35 |
-
add_action( 'wp_ajax_cffrtb-enable-field', array( $this, 'ajax_enable_field' ) );
|
36 |
-
add_action( 'wp_ajax_nopriv_cffrtb-reset-all' , array( $this , 'ajax_nopriv' ) );
|
37 |
-
add_action( 'wp_ajax_cffrtb-reset-all', array( $this, 'ajax_reset_all' ) );
|
38 |
-
|
39 |
-
// Load "pointers" (help tooltips on initial load)
|
40 |
-
require_once( RTB_PLUGIN_DIR . '/includes/custom_fields_pointers.php' );
|
41 |
-
}
|
42 |
-
|
43 |
-
/**
|
44 |
-
* Add the booking form editor page to the admin menu
|
45 |
-
*
|
46 |
-
* @since 0.1
|
47 |
-
*/
|
48 |
-
public function add_menu_page() {
|
49 |
-
$this->hook_suffix = add_submenu_page(
|
50 |
-
'rtb-bookings',
|
51 |
-
_x( 'Custom Fields', 'Title of the Custom Fields editor page', 'restaurant-reservations' ),
|
52 |
-
_x( 'Custom Fields', 'Title of Custom Fields editor link in the admin menu', 'restaurant-reservations' ),
|
53 |
-
'manage_options',
|
54 |
-
'cffrtb-editor',
|
55 |
-
array( $this, 'display_editor_page' )
|
56 |
-
);
|
57 |
-
|
58 |
-
// Print the error modal and enqueue assets
|
59 |
-
add_action( 'load-' . $this->hook_suffix, array( $this, 'enqueue_admin_assets' ) );
|
60 |
-
add_action( 'admin_footer-' . $this->hook_suffix, array( $this, 'print_modals' ) );
|
61 |
-
}
|
62 |
-
|
63 |
-
/**
|
64 |
-
* Enqueue assets on the editor page
|
65 |
-
*
|
66 |
-
* @since 0.1
|
67 |
-
*/
|
68 |
-
public function enqueue_admin_assets() {
|
69 |
-
global $rtb_controller;
|
70 |
-
|
71 |
-
if ( ! $rtb_controller->permissions->check_permission( 'custom_fields' ) ) { return; }
|
72 |
-
|
73 |
-
// Retrieve pointers (admin tooltips)
|
74 |
-
$pointers = apply_filters( 'cffrtb_pointers', array() );
|
75 |
-
|
76 |
-
// Determine editor dependencies
|
77 |
-
$editor_css_deps = array( 'rtb-booking-form' );
|
78 |
-
$editor_js_deps = array( 'jquery-ui-sortable', 'rtb-booking-form' );
|
79 |
-
if ( !empty( $pointers ) ) {
|
80 |
-
$editor_css_deps[] = 'wp-pointer';
|
81 |
-
$editor_js_deps[] = 'wp-pointer';
|
82 |
-
}
|
83 |
-
|
84 |
-
// Booking form assets
|
85 |
-
$rtb_controller->register_assets();
|
86 |
-
rtb_enqueue_assets();
|
87 |
-
|
88 |
-
// Editor assets
|
89 |
-
wp_enqueue_style( 'cffrtb-editor', RTB_PLUGIN_URL . '/assets/css/editor.css', $editor_css_deps, false );
|
90 |
-
wp_enqueue_script( 'cffrtb-editor', RTB_PLUGIN_URL . '/assets/js/editor.js', $editor_js_deps, false, true );
|
91 |
-
|
92 |
-
// Pass the fields array to the script
|
93 |
-
$field_controller = $rtb_controller->fields;
|
94 |
-
$field_controller->get_valid_field_types();
|
95 |
-
wp_localize_script(
|
96 |
-
'cffrtb-editor',
|
97 |
-
'cffrtb_editor',
|
98 |
-
array(
|
99 |
-
'ajax_nonce' => wp_create_nonce( 'cffrtb-editor' ),
|
100 |
-
'default_type' => key( $field_controller->valid_field_types ),
|
101 |
-
'default_subtype' => key( $field_controller->valid_field_types[ key( $field_controller->valid_field_types ) ]['subtypes'] ),
|
102 |
-
'pointers' => $pointers,
|
103 |
-
'strings' => array(
|
104 |
-
'save' => __( 'Save', 'restaurant-reservations' ),
|
105 |
-
'editor_add_field' => __( 'Add Field', 'restaurant-reservations' ),
|
106 |
-
'editor_edit_field' => __( 'Edit Field', 'restaurant-reservations' ),
|
107 |
-
'editor_save_field' => __( 'Save Field', 'restaurant-reservations' ),
|
108 |
-
'editor_add_fieldset' => __( 'Add Fieldset', 'restaurant-reservations' ),
|
109 |
-
'editor_save_fieldset' => __( 'Save Fieldset', 'restaurant-reservations' ),
|
110 |
-
'field_missing_title' => __( 'Please enter a label for this field.', 'restaurant-reservations' ),
|
111 |
-
'field_missing_options' => __( 'To add an Option field you must add at least one option below.', 'restaurant-reservations' ),
|
112 |
-
'fieldset_not_empty' => __( 'This fieldset can not be deleted until all of its attached fields are removed or assigned to another fieldset.', 'restaurant-reservations' ),
|
113 |
-
'confirm_reset_all' => __( 'Are you sure you want to reset the booking form? All of your changes and custom fields will be removed. This action can not be undone.', 'restaurant-reservations' ),
|
114 |
-
'unknown_error' => __( 'An unspecified error occurred. Please try again. If the problem persists, try logging out and logging back in.', 'restaurant-reservations' ),
|
115 |
-
),
|
116 |
-
)
|
117 |
-
);
|
118 |
-
}
|
119 |
-
|
120 |
-
/**
|
121 |
-
* Display the booking form editor page
|
122 |
-
*
|
123 |
-
* @since 0.1
|
124 |
-
*/
|
125 |
-
public function display_editor_page() {
|
126 |
-
global $rtb_controller;
|
127 |
-
|
128 |
-
$custom_fields_permission = $rtb_controller->permissions->check_permission( 'custom_fields' )
|
129 |
-
|
130 |
-
?>
|
131 |
-
|
132 |
-
<div class="wrap">
|
133 |
-
<h2>
|
134 |
-
<?php _e( 'Custom Fields Editor', 'restaurant-reservations' ); ?>
|
135 |
-
<a href="#" class="add-new-h2 add-field">Add New</a>
|
136 |
-
</h2>
|
137 |
-
<?php if ( $custom_fields_permission ) { ?>
|
138 |
-
<div id="cffrtb-editor">
|
139 |
-
<?php $this->print_booking_form_fields(); ?>
|
140 |
-
</div>
|
141 |
-
<?php } else { ?>
|
142 |
-
<div class='rtb-premium-locked'>
|
143 |
-
<a href="https://www.fivestarplugins.com/license-payment/?Selected=RTB&Quantity=1" target="_blank">Upgrade</a> to the premium version to use this feature
|
144 |
-
</div>
|
145 |
-
<?php } ?>
|
146 |
-
</div>
|
147 |
-
|
148 |
-
<?php
|
149 |
-
}
|
150 |
-
|
151 |
-
/**
|
152 |
-
* Print the booking form fields for editing
|
153 |
-
*
|
154 |
-
* @since 0.0.1
|
155 |
-
*/
|
156 |
-
public function print_booking_form_fields() {
|
157 |
-
global $rtb_controller;
|
158 |
-
|
159 |
-
// Retrieve the form fields
|
160 |
-
$fields = $rtb_controller->settings->get_booking_form_fields();
|
161 |
-
|
162 |
-
// Retrieve system fields with limited editing abilities
|
163 |
-
$field_controller = $rtb_controller->fields;
|
164 |
-
$field_controller->get_system_fields();
|
165 |
-
?>
|
166 |
-
|
167 |
-
<div class="cffrtb-lft">
|
168 |
-
|
169 |
-
<ul id="cffrtb-list" class="cffrtb-list">
|
170 |
-
|
171 |
-
<?php
|
172 |
-
foreach( $fields as $fieldset => $contents ) {
|
173 |
-
echo $this->print_field( $fieldset, $contents, 'fieldset' );
|
174 |
-
}
|
175 |
-
?>
|
176 |
-
|
177 |
-
</ul>
|
178 |
-
|
179 |
-
<a href="#" class="add-field">
|
180 |
-
<span class="dashicons dashicons-plus-alt"></span>
|
181 |
-
<?php _e( 'Add New', 'restaurant-reservations' ); ?>
|
182 |
-
</a>
|
183 |
-
|
184 |
-
</div>
|
185 |
-
<div class="cffrtb-rgt">
|
186 |
-
|
187 |
-
<div id="cffrtb-disabled" class="cffrtb-list">
|
188 |
-
<h3><?php _e( 'Disabled Fields', 'restaurant-reservations' ); ?></h3>
|
189 |
-
|
190 |
-
<?php
|
191 |
-
$modified = get_option( $rtb_controller->custom_fields->modified_option_key );
|
192 |
-
if ( $modified ) :
|
193 |
-
|
194 |
-
$list = '';
|
195 |
-
foreach( $modified as $slug => $field ) {
|
196 |
-
|
197 |
-
if ( !empty( $field['disabled'] ) ) {
|
198 |
-
|
199 |
-
$default_field = $slug == $field['fieldset'] ? $field_controller->default_fields[ $slug ] : $field_controller->get_nested_field( $slug, $field_controller->default_fields );
|
200 |
-
if ( empty( $default_field ) || !is_array( $default_field ) ) {
|
201 |
-
continue;
|
202 |
-
}
|
203 |
-
|
204 |
-
$type = $slug == $field['fieldset'] ? 'fieldset' : 'field';
|
205 |
-
|
206 |
-
$list .= $this->print_field( $slug, array_merge( $default_field, $field ), $type );
|
207 |
-
}
|
208 |
-
}
|
209 |
-
|
210 |
-
if ( !empty( $list ) ) :
|
211 |
-
?>
|
212 |
-
|
213 |
-
<ul class="fields">
|
214 |
-
<?php echo $list; ?>
|
215 |
-
</ul>
|
216 |
-
|
217 |
-
<?php else : ?>
|
218 |
-
|
219 |
-
<p class="description no-disabled-fields"><?php _e( 'You have not disabled any default fields yet.', 'restaurant-reservations' ); ?></p>
|
220 |
-
|
221 |
-
<?php
|
222 |
-
endif;
|
223 |
-
endif;
|
224 |
-
?>
|
225 |
-
|
226 |
-
<div class="reset<?php echo !empty( $modified ) ? ' is-visible' : ''; ?>">
|
227 |
-
<a href="#" class="button reset-all">
|
228 |
-
<?php _e( 'Revert to default', 'restaurant-reservations' ); ?>
|
229 |
-
</a>
|
230 |
-
<a href="#" class="learn-more">
|
231 |
-
<?php _e( 'Learn more', 'restaurant-reservations' ); ?>
|
232 |
-
</a>
|
233 |
-
<p class="description learn-more-details">
|
234 |
-
<?php _e( 'All of your changes and custom fields will be discarded if you revert to default. This is not advised unless you want to remove all of your changes and restore the default booking form.', 'restaurant-reservations' ); ?>
|
235 |
-
</p>
|
236 |
-
</div>
|
237 |
-
</div>
|
238 |
-
|
239 |
-
</div>
|
240 |
-
|
241 |
-
<?php
|
242 |
-
}
|
243 |
-
|
244 |
-
/**
|
245 |
-
* Print a single field in the fields list
|
246 |
-
*
|
247 |
-
* @since 0.1
|
248 |
-
*/
|
249 |
-
public function print_field( $slug, $field, $type = 'field' ) {
|
250 |
-
global $rtb_controller;
|
251 |
-
|
252 |
-
$field_controller = $rtb_controller->fields;
|
253 |
-
if ( empty( $field_controller->system_fields ) ) {
|
254 |
-
$field_controller->get_system_fields();
|
255 |
-
}
|
256 |
-
|
257 |
-
$title = '';
|
258 |
-
if ( $type == 'fieldset' && !empty( $field['legend'] ) ) {
|
259 |
-
$title = $field['legend'];
|
260 |
-
} elseif ( !empty( $field['title'] ) ) {
|
261 |
-
$title = $field['title'];
|
262 |
-
}
|
263 |
-
|
264 |
-
ob_start();
|
265 |
-
?>
|
266 |
-
|
267 |
-
<li class="<?php echo $type; echo !empty( $field['disabled'] ) ? ' disabled' : ''; ?>" data-slug="<?php echo esc_attr( $slug ); ?>"<?php echo empty( $field['ID'] ) ? '' : ' data-id="' . (int) $field['ID'] . '"'; ?>>
|
268 |
-
|
269 |
-
<div class="title">
|
270 |
-
<div class="view">
|
271 |
-
<span class="value">
|
272 |
-
<?php echo esc_html( $title ); ?>
|
273 |
-
</span>
|
274 |
-
|
275 |
-
<div class="controls">
|
276 |
-
<?php if ( empty( $field['disabled'] ) ) : ?>
|
277 |
-
<a href="#" class="label" title="<?php _e( 'Edit title', 'restaurant-reservations' ); ?>">
|
278 |
-
<span class="dashicons dashicons-edit"></span>
|
279 |
-
</a>
|
280 |
-
<?php endif; ?>
|
281 |
-
<?php if ( !empty( $field['disabled'] ) ) : ?>
|
282 |
-
<a href="#" class="enable" title="<?php _e( 'Enable field', 'restaurant-reservations' ); ?>">
|
283 |
-
<span class="dashicons dashicons-visibility"></span>
|
284 |
-
</a>
|
285 |
-
<?php elseif ( ( $type == 'field' && !in_array( $slug, $field_controller->system_fields ) ) || ( $type == 'fieldset' && !in_array( $slug, $field_controller->system_fieldsets ) ) ) : ?>
|
286 |
-
<?php if ( $type == 'field' && !empty( $field['ID'] ) ) : ?>
|
287 |
-
<a href="#" class="options" title="<?php _e( 'Edit field', 'restaurant-reservations' ); ?>">
|
288 |
-
<span class="dashicons dashicons-admin-tools"></span>
|
289 |
-
</a>
|
290 |
-
<?php endif; ?>
|
291 |
-
<a href="#" class="delete" title="<?php _e( 'Delete field', 'restaurant-reservations' ); ?>">
|
292 |
-
<span class="dashicons dashicons-no"></span>
|
293 |
-
</a>
|
294 |
-
<?php endif; ?>
|
295 |
-
</div>
|
296 |
-
</div>
|
297 |
-
|
298 |
-
<?php if ( empty( $field['disabled'] ) ) : ?>
|
299 |
-
<div class="edit">
|
300 |
-
<input type="text" name="title" value="<?php echo esc_html( $title ); ?>" tabindex="-1">
|
301 |
-
|
302 |
-
<div class="controls">
|
303 |
-
<a href="#" class="save" tabindex="-1">
|
304 |
-
<?php _e( 'Save', 'restaurant-reservations' ); ?>
|
305 |
-
</a>
|
306 |
-
</div>
|
307 |
-
|
308 |
-
<div class="status">
|
309 |
-
<span class="load-spinner"></span>
|
310 |
-
</div>
|
311 |
-
</div>
|
312 |
-
<?php endif; ?>
|
313 |
-
|
314 |
-
</div>
|
315 |
-
|
316 |
-
<?php if ( $type == 'fieldset' && empty( $field['disabled'] ) && empty( $field['exclude_fields'] ) ) : ?>
|
317 |
-
<ul class="fields">
|
318 |
-
<?php
|
319 |
-
if( !empty( $field['fields'] ) ) :
|
320 |
-
foreach( $field['fields'] as $field_slug => $sub_field ) :
|
321 |
-
echo $this->print_field( $field_slug, $sub_field );
|
322 |
-
endforeach;
|
323 |
-
endif;
|
324 |
-
?>
|
325 |
-
</ul>
|
326 |
-
<?php endif; ?>
|
327 |
-
</li>
|
328 |
-
<?php
|
329 |
-
|
330 |
-
return ob_get_clean();
|
331 |
-
}
|
332 |
-
|
333 |
-
/**
|
334 |
-
* Print a label field for the editing form
|
335 |
-
*
|
336 |
-
* @since 0.1
|
337 |
-
*/
|
338 |
-
public function print_label_input( $slug, $label ) {
|
339 |
-
?>
|
340 |
-
|
341 |
-
<div class="label">
|
342 |
-
<label for="<?php echo esc_attr( $slug ); ?>_label">
|
343 |
-
<?php _e( 'Label', 'restaurant-reservations' ); ?>
|
344 |
-
</label>
|
345 |
-
<input type="text" name="label" id="<?php echo esc_attr( $slug ); ?>_label" value="<?php echo esc_attr( $label ); ?>">
|
346 |
-
</div>
|
347 |
-
|
348 |
-
<?php
|
349 |
-
}
|
350 |
-
|
351 |
-
/**
|
352 |
-
* Print the error modal in the footer. This re-uses the error modal
|
353 |
-
* markup and styling from Restaurant Reservations
|
354 |
-
*
|
355 |
-
* @since 0.1
|
356 |
-
*/
|
357 |
-
public function print_modals() {
|
358 |
-
global $rtb_controller;
|
359 |
-
|
360 |
-
$field_controller = $rtb_controller->fields;
|
361 |
-
$field_controller->get_valid_field_types();
|
362 |
-
|
363 |
-
$default_type = key( $field_controller->valid_field_types );
|
364 |
-
$default_subtype = key( $field_controller->valid_field_types[ $default_type ]['subtypes'] );
|
365 |
-
?>
|
366 |
-
<div id="cffrtb-field-editor" class="rtb-admin-modal">
|
367 |
-
|
368 |
-
<form id="cffrtb-field-editor-form" class="rtb-container">
|
369 |
-
<input type="hidden" name="id" value="">
|
370 |
-
<input type="hidden" name="type" value="<?php echo esc_attr( $default_type ); ?>">
|
371 |
-
<input type="hidden" name="subtype" value="<?php echo esc_attr( $default_type ); ?>">
|
372 |
-
|
373 |
-
<div class="title">
|
374 |
-
<h2><?php _e( 'Add Field', 'restaurant-reservations' ); ?></h2>
|
375 |
-
</div>
|
376 |
-
|
377 |
-
<div class="type">
|
378 |
-
<label>
|
379 |
-
<?php _e( 'Field Type', 'restaurant-reservations' ); ?>
|
380 |
-
</label>
|
381 |
-
|
382 |
-
<div class="selector">
|
383 |
-
<ul class="types">
|
384 |
-
<?php foreach( $field_controller->valid_field_types as $slug => $type ) : ?>
|
385 |
-
<li>
|
386 |
-
<a href="#" class="<?php echo esc_attr( $slug ); if ( $default_type == $slug ) : ?> current<?php endif; ?>" data-type="<?php echo esc_attr( $slug ); ?>">
|
387 |
-
<?php echo $type['title']; ?>
|
388 |
-
</a>
|
389 |
-
</li>
|
390 |
-
<?php endforeach; ?>
|
391 |
-
</ul>
|
392 |
-
|
393 |
-
<?php foreach( $field_controller->valid_field_types as $slug => $type ) : ?>
|
394 |
-
<ul class="subtypes <?php echo $slug; if ( $default_type == $slug ) : ?> current<?php endif; ?>">
|
395 |
-
<?php foreach( $type['subtypes'] as $sub_slug => $subtype ) : ?>
|
396 |
-
<li>
|
397 |
-
<a href="#" class="<?php echo esc_attr( $sub_slug ); if ( $default_type == $sub_slug ) : ?> current<?php endif; ?>" data-subtype="<?php echo esc_attr( $sub_slug ); ?>">
|
398 |
-
<?php echo $subtype['title']; ?>
|
399 |
-
</a>
|
400 |
-
</li>
|
401 |
-
<?php endforeach; ?>
|
402 |
-
</ul>
|
403 |
-
<?php endforeach; ?>
|
404 |
-
</div>
|
405 |
-
</div>
|
406 |
-
<div class="settings">
|
407 |
-
|
408 |
-
<div class="item">
|
409 |
-
<label for="title">
|
410 |
-
<?php _e( 'Label', 'restaurant-reservations' ); ?>
|
411 |
-
</label>
|
412 |
-
<input type="text" name="title" id="title">
|
413 |
-
</div>
|
414 |
-
|
415 |
-
<div class="settings-panel options">
|
416 |
-
<div class="item">
|
417 |
-
<label for="options-options">
|
418 |
-
<?php _e( 'Options', 'restaurant-reservations' ); ?>
|
419 |
-
</label>
|
420 |
-
<div class="add">
|
421 |
-
<input type="text" name="options" id="options-options">
|
422 |
-
<a href="#">
|
423 |
-
<span class="dashicons dashicons-plus-alt"></span>
|
424 |
-
<?php _e( 'Add', 'restaurant-reservations' ); ?>
|
425 |
-
</a>
|
426 |
-
</div>
|
427 |
-
<ul class="options">
|
428 |
-
</ul>
|
429 |
-
</div>
|
430 |
-
</div>
|
431 |
-
|
432 |
-
<?php do_action( 'cffrtb_field_editor_settings_panel' ); ?>
|
433 |
-
|
434 |
-
</div>
|
435 |
-
|
436 |
-
<div class="required">
|
437 |
-
<label>
|
438 |
-
<input type="checkbox" name="required" value="1">
|
439 |
-
<?php _e( 'Required', 'restaurant-reservations' ); ?>
|
440 |
-
</label>
|
441 |
-
</div>
|
442 |
-
|
443 |
-
<div class="actions">
|
444 |
-
<a href="#" class="button-primary save">
|
445 |
-
<?php _e( 'Add Field', 'restaurant-reservations' ); ?>
|
446 |
-
</a>
|
447 |
-
<a href="#" class="button cancel">
|
448 |
-
<?php _e( 'Cancel', 'restaurant-reservations' ); ?>
|
449 |
-
</a>
|
450 |
-
<div class="status">
|
451 |
-
<span class="load-spinner"></span>
|
452 |
-
</div>
|
453 |
-
</div>
|
454 |
-
</form>
|
455 |
-
</div>
|
456 |
-
|
457 |
-
<div id="cffrtb-field-editor-option" class="rtb-admin-modal">
|
458 |
-
<div class="rtb-container">
|
459 |
-
<div class="option">
|
460 |
-
<a href="#" class="field button-primary">
|
461 |
-
<?php _e( 'Add Field', 'restaurant-reservations' ); ?>
|
462 |
-
</a>
|
463 |
-
<p class="description">
|
464 |
-
<?php _e( 'Fields prompt the user to enter information or select from options.' ); ?>
|
465 |
-
</p>
|
466 |
-
</div>
|
467 |
-
<div class="option">
|
468 |
-
<a href="#" class="fieldset button">
|
469 |
-
<?php _e( 'Add Fieldset', 'restaurant-reservations' ); ?>
|
470 |
-
</a>
|
471 |
-
<p class="description">
|
472 |
-
<?php _e( 'Fieldsets group other fields under a common label.' ); ?>
|
473 |
-
</p>
|
474 |
-
</div>
|
475 |
-
</div>
|
476 |
-
</div>
|
477 |
-
|
478 |
-
<div id="rtb-error-modal" class="rtb-admin-modal">
|
479 |
-
<div class="rtb-error rtb-container"">
|
480 |
-
<div class="rtb-error-msg"></div>
|
481 |
-
<a href="#" class="button"><?php _e( 'Close', 'restaurant-reservations' ); ?></a>
|
482 |
-
</div>
|
483 |
-
</div>
|
484 |
-
<?php
|
485 |
-
}
|
486 |
-
|
487 |
-
/**
|
488 |
-
* Handle ajax requests from logged out users
|
489 |
-
*
|
490 |
-
* @since 0.1
|
491 |
-
*/
|
492 |
-
public function ajax_nopriv() {
|
493 |
-
|
494 |
-
wp_send_json_error(
|
495 |
-
array(
|
496 |
-
'error' => 'loggedout',
|
497 |
-
'msg' => sprintf( __( 'You have been logged out. Please %slogin again%s.', 'restaurant-reservations' ), '<a href="' . wp_login_url( admin_url( 'admin.php?page=cffrtb-editor' ) ) . '">', '</a>' ),
|
498 |
-
)
|
499 |
-
);
|
500 |
-
}
|
501 |
-
|
502 |
-
/**
|
503 |
-
* Handle ajax request to save a field
|
504 |
-
*
|
505 |
-
* @since 0.1
|
506 |
-
*/
|
507 |
-
public function ajax_save_field() {
|
508 |
-
global $rtb_controller;
|
509 |
-
|
510 |
-
// Authenticate request
|
511 |
-
if ( !check_ajax_referer( 'cffrtb-editor', 'nonce' ) || !current_user_can( 'manage_options' ) ) {
|
512 |
-
$this->nopriv_ajax();
|
513 |
-
}
|
514 |
-
|
515 |
-
// Missing data
|
516 |
-
if ( empty( $_POST['field'] ) ) {
|
517 |
-
wp_send_json_error(
|
518 |
-
array(
|
519 |
-
'error' => 'no_field_data',
|
520 |
-
'msg' => __( 'No field data was received with your request.', 'restaurant-reservations' ) . ' ' . $rtb_controller->custom_fields->common_error_msg
|
521 |
-
)
|
522 |
-
);
|
523 |
-
}
|
524 |
-
|
525 |
-
// Missing request specification
|
526 |
-
if ( empty( $_POST['request'] ) ) {
|
527 |
-
wp_send_json_error(
|
528 |
-
array(
|
529 |
-
'error' => 'no_request',
|
530 |
-
'msg' => __( 'Internal data that was supposed to be passed with your request was not received.', 'restaurant-reservations' ) . ' ' . $rtb_controller->custom_fields->common_error_msg
|
531 |
-
)
|
532 |
-
);
|
533 |
-
}
|
534 |
-
|
535 |
-
$field = new cffrtbField( $_POST['field'] );
|
536 |
-
|
537 |
-
// Just the label
|
538 |
-
if ( $_POST['request'] == 'save_label' ) {
|
539 |
-
$this->send_ajax_response( $field->save_label() );
|
540 |
-
|
541 |
-
// The whole field
|
542 |
-
} elseif ( $_POST['request'] == 'save_field' ) {
|
543 |
-
$this->send_ajax_response( $field->save_field() );
|
544 |
-
}
|
545 |
-
|
546 |
-
wp_send_json_error(
|
547 |
-
array(
|
548 |
-
'error' => 'unknown',
|
549 |
-
'msg' => __( 'An unknown error has occurred.', 'restaurant-reservations' ) . ' ' . $rtb_controller->custom_fields->common_error_msg
|
550 |
-
)
|
551 |
-
);
|
552 |
-
}
|
553 |
-
|
554 |
-
/**
|
555 |
-
* Handle ajax request to save the order of fields and fieldsets
|
556 |
-
*
|
557 |
-
* @since 0.1
|
558 |
-
*/
|
559 |
-
public function ajax_save_order() {
|
560 |
-
global $rtb_controller;
|
561 |
-
|
562 |
-
// Authenticate request
|
563 |
-
if ( !check_ajax_referer( 'cffrtb-editor', 'nonce' ) || !current_user_can( 'manage_options' ) ) {
|
564 |
-
$this->nopriv_ajax();
|
565 |
-
}
|
566 |
-
|
567 |
-
// Missing data
|
568 |
-
if ( empty( $_POST['order'] ) ) {
|
569 |
-
wp_send_json_error(
|
570 |
-
array(
|
571 |
-
'error' => 'no_fields_data',
|
572 |
-
'msg' => __( 'No fields data was received with your request.', 'restaurant-reservations' ) . ' ' . $rtb_controller->custom_fields->common_error_msg
|
573 |
-
)
|
574 |
-
);
|
575 |
-
}
|
576 |
-
|
577 |
-
$modified = get_option( $rtb_controller->custom_fields->modified_option_key );
|
578 |
-
$orig_modified = $modified;
|
579 |
-
|
580 |
-
$custom_fields_error = array();
|
581 |
-
$has_custom_fields = false;
|
582 |
-
foreach( $_POST['order'] as $field ) {
|
583 |
-
|
584 |
-
if ( !isset( $field['order'] ) || empty( $field['fieldset'] ) ) {
|
585 |
-
continue; // @todo this indicates some kind of data error, though
|
586 |
-
}
|
587 |
-
|
588 |
-
// Default fields
|
589 |
-
if ( empty( $field['ID'] ) ) {
|
590 |
-
|
591 |
-
if ( empty( $modified[ $field['slug'] ] ) ) {
|
592 |
-
$modified[ $field['slug'] ] = array();
|
593 |
-
}
|
594 |
-
|
595 |
-
// Skip if the order isn't changing
|
596 |
-
if ( isset( $modified[ $field['slug'] ]['order'] ) && $modified[ $field['slug'] ]['order'] == $field['order'] && isset( $modified[ $field['slug'] ]['fieldset'] ) && $modified[ $field['slug'] ]['fieldset'] == $field['fieldset'] ) {
|
597 |
-
continue;
|
598 |
-
}
|
599 |
-
|
600 |
-
$modified[ $field['slug'] ]['order'] = (int) $field['order'];
|
601 |
-
$modified[ $field['slug'] ]['fieldset'] = sanitize_key( $field['fieldset'] );
|
602 |
-
|
603 |
-
// Custom fields
|
604 |
-
} else {
|
605 |
-
|
606 |
-
$custom_field = new cffrtbField( $field );
|
607 |
-
$result = $custom_field->save_field();
|
608 |
-
|
609 |
-
if ( !$result[0] ) {
|
610 |
-
array_push( $custom_fields_error, $result[1] );
|
611 |
-
}
|
612 |
-
|
613 |
-
$has_custom_fields = true;
|
614 |
-
}
|
615 |
-
}
|
616 |
-
|
617 |
-
update_option( $rtb_controller->custom_fields->modified_option_key, $modified );
|
618 |
-
|
619 |
-
if ( !$has_custom_fields || ( $has_custom_fields && empty( $custom_fields_error ) ) ) {
|
620 |
-
wp_send_json_success();
|
621 |
-
|
622 |
-
} else {
|
623 |
-
wp_send_json_error(
|
624 |
-
array(
|
625 |
-
'error' => 'save_order_failed',
|
626 |
-
'msg' => __( 'An error occurred while saving the new field order. Please try again.', 'restaurant-reservations' ),
|
627 |
-
'fields' => sanitize_text_field( $_POST['order'] ),
|
628 |
-
'custom_fields_error' => $custom_fields_error,
|
629 |
-
)
|
630 |
-
);
|
631 |
-
}
|
632 |
-
|
633 |
-
wp_send_json_error(
|
634 |
-
array(
|
635 |
-
'error' => 'unknown',
|
636 |
-
'msg' => __( 'An unknown error has occurred.', 'restaurant-reservations' ) . ' ' . $rtb_controller->custom_fields->common_error_msg
|
637 |
-
)
|
638 |
-
);
|
639 |
-
}
|
640 |
-
|
641 |
-
/**
|
642 |
-
* Handle ajax request to load a field
|
643 |
-
*
|
644 |
-
* @since 0.1
|
645 |
-
*/
|
646 |
-
public function ajax_load_field() {
|
647 |
-
global $rtb_controller;
|
648 |
-
|
649 |
-
// Authenticate request
|
650 |
-
if ( !check_ajax_referer( 'cffrtb-editor', 'nonce' ) || !current_user_can( 'manage_options' ) ) {
|
651 |
-
$this->nopriv_ajax();
|
652 |
-
}
|
653 |
-
|
654 |
-
// Missing data
|
655 |
-
if ( empty( $_POST['ID'] ) ) {
|
656 |
-
wp_send_json_error(
|
657 |
-
array(
|
658 |
-
'error' => 'no_id',
|
659 |
-
'msg' => __( 'The requested field could not be loaded because no ID was received with your request.', 'restaurant-reservations' ) . ' ' . $rtb_controller->custom_fields->common_error_msg
|
660 |
-
)
|
661 |
-
);
|
662 |
-
}
|
663 |
-
|
664 |
-
$field = new cffrtbField( array( 'ID' => (int) $_POST['ID'] ) );
|
665 |
-
|
666 |
-
wp_send_json_success(
|
667 |
-
array(
|
668 |
-
'field' => $field,
|
669 |
-
)
|
670 |
-
);
|
671 |
-
}
|
672 |
-
|
673 |
-
/**
|
674 |
-
* Handle ajax request to delete or disable a field
|
675 |
-
*
|
676 |
-
* @since 0.1
|
677 |
-
*/
|
678 |
-
public function ajax_delete_field() {
|
679 |
-
global $rtb_controller;
|
680 |
-
|
681 |
-
// Authenticate request
|
682 |
-
if ( !check_ajax_referer( 'cffrtb-editor', 'nonce' ) || !current_user_can( 'manage_options' ) ) {
|
683 |
-
$this->nopriv_ajax();
|
684 |
-
}
|
685 |
-
|
686 |
-
// Missing data
|
687 |
-
if ( empty( $_POST['ID'] ) && ( empty( $_POST['slug'] ) || empty( $_POST['fieldset'] ) ) ) {
|
688 |
-
wp_send_json_error(
|
689 |
-
array(
|
690 |
-
'error' => 'no_id',
|
691 |
-
'msg' => __( 'The requested field could not be deleted because no ID or slug was received with your request.', 'restaurant-reservations' ) . ' ' . $rtb_controller->custom_fields->common_error_msg
|
692 |
-
)
|
693 |
-
);
|
694 |
-
}
|
695 |
-
|
696 |
-
if ( !empty( $_POST['ID'] ) ) {
|
697 |
-
$post = wp_delete_post( (int) $_POST['ID'] );
|
698 |
-
|
699 |
-
if ( !$post ) {
|
700 |
-
wp_send_json_error(
|
701 |
-
array(
|
702 |
-
'error' => 'delete_field_failed',
|
703 |
-
'msg' => __( 'An error occurred while deleting this field.', 'restaurant-reservations' ) . ' ' . $rtb_controller->custom_fields->common_error_msg
|
704 |
-
)
|
705 |
-
);
|
706 |
-
}
|
707 |
-
|
708 |
-
wp_send_json_success();
|
709 |
-
|
710 |
-
} else {
|
711 |
-
$modified = get_option( $rtb_controller->custom_fields->modified_option_key );
|
712 |
-
$slug = sanitize_key( $_POST['slug'] );
|
713 |
-
$fieldset = sanitize_key( $_POST['fieldset'] );
|
714 |
-
$modified[ $slug ]['disabled'] = 1;
|
715 |
-
$modified[ $slug ]['fieldset'] = $fieldset;
|
716 |
-
update_option( $rtb_controller->custom_fields->modified_option_key, $modified );
|
717 |
-
|
718 |
-
global $rtb_controller;
|
719 |
-
$fields = $rtb_controller->settings->get_booking_form_fields();
|
720 |
-
$field = $slug == $fieldset ? $rtb_controller->fields->default_fields[ $slug ] : $rtb_controller->fields->get_nested_field( $slug, $rtb_controller->fields->default_fields );
|
721 |
-
$field['disabled'] = 1;
|
722 |
-
|
723 |
-
$type = $slug == $fieldset ? 'fieldset' : 'field';
|
724 |
-
|
725 |
-
wp_send_json_success(
|
726 |
-
array(
|
727 |
-
'field' => $this->print_field( $slug, $field, $type ),
|
728 |
-
)
|
729 |
-
);
|
730 |
-
}
|
731 |
-
}
|
732 |
-
|
733 |
-
/**
|
734 |
-
* Handle an ajax request to enable a field that has been disabled
|
735 |
-
*
|
736 |
-
* @since 0.1
|
737 |
-
*/
|
738 |
-
public function ajax_enable_field() {
|
739 |
-
global $rtb_controller;
|
740 |
-
|
741 |
-
// Authenticate request
|
742 |
-
if ( !check_ajax_referer( 'cffrtb-editor', 'nonce' ) || !current_user_can( 'manage_options' ) ) {
|
743 |
-
$this->nopriv_ajax();
|
744 |
-
}
|
745 |
-
|
746 |
-
// Missing data
|
747 |
-
if ( empty( $_POST['slug'] ) ) {
|
748 |
-
wp_send_json_error(
|
749 |
-
array(
|
750 |
-
'error' => 'no_id',
|
751 |
-
'msg' => __( 'The requested field could not be enabled because no identifying slug was received with your request.', 'restaurant-reservations' ) . ' ' . $rtb_controller->custom_fields->common_error_msg
|
752 |
-
)
|
753 |
-
);
|
754 |
-
}
|
755 |
-
|
756 |
-
$slug = sanitize_key( $_POST['slug'] );
|
757 |
-
$modified = get_option( $rtb_controller->custom_fields->modified_option_key );
|
758 |
-
|
759 |
-
if ( empty( $modified ) || !is_array( $modified ) || !array_key_exists( $slug, $modified ) ) {
|
760 |
-
wp_send_json_error(
|
761 |
-
array(
|
762 |
-
'error' => 'field_not_found',
|
763 |
-
'msg' => __( 'The requested field could not be enabled because it did not appear to be disabled.', 'restaurant-reservations' ) . ' ' . $rtb_controller->custom_fields->common_error_msg
|
764 |
-
)
|
765 |
-
);
|
766 |
-
}
|
767 |
-
|
768 |
-
if ( isset( $modified[ $slug ]['disabled'] ) ) {
|
769 |
-
unset( $modified[ $slug ]['disabled'] );
|
770 |
-
update_option( $rtb_controller->custom_fields->modified_option_key, $modified );
|
771 |
-
}
|
772 |
-
|
773 |
-
global $rtb_controller;
|
774 |
-
$fields = $rtb_controller->settings->get_booking_form_fields();
|
775 |
-
$field = $_POST['type'] == 'fieldset' ? $rtb_controller->fields->default_fields[ $slug ] : $rtb_controller->fields->get_nested_field( $slug, $fields );
|
776 |
-
|
777 |
-
$type = $_POST['type'] == 'fieldset' ? 'fieldset' : 'field';
|
778 |
-
|
779 |
-
if ( $type == 'fieldset' ) {
|
780 |
-
$field['exclude_fields'] = true;
|
781 |
-
}
|
782 |
-
|
783 |
-
wp_send_json_success(
|
784 |
-
array(
|
785 |
-
'field' => $this->print_field( $slug, $field, $type )
|
786 |
-
)
|
787 |
-
);
|
788 |
-
}
|
789 |
-
|
790 |
-
/**
|
791 |
-
* Handle an ajax request to reset all changes and delete custom
|
792 |
-
* fields
|
793 |
-
*
|
794 |
-
* @since 0.1
|
795 |
-
*/
|
796 |
-
public function ajax_reset_all( $authorized = false) {
|
797 |
-
global $rtb_controller;
|
798 |
-
|
799 |
-
// Authenticate request
|
800 |
-
if ( ( !check_ajax_referer( 'cffrtb-editor', 'nonce' ) || !current_user_can( 'manage_options' ) ) and ! $authorized ) {
|
801 |
-
$this->nopriv_ajax();
|
802 |
-
}
|
803 |
-
|
804 |
-
// Delete modifications to default fields
|
805 |
-
delete_option( $rtb_controller->custom_fields->modified_option_key );
|
806 |
-
|
807 |
-
// Delete all custom posts
|
808 |
-
$posts = new WP_Query(
|
809 |
-
array(
|
810 |
-
'post_type' => 'cffrtb_field',
|
811 |
-
'posts_per_page' => 1000, // Very large upper limit
|
812 |
-
)
|
813 |
-
);
|
814 |
-
|
815 |
-
while( $posts->have_posts() ) {
|
816 |
-
$posts->the_post();
|
817 |
-
wp_delete_post( get_the_ID() );
|
818 |
-
}
|
819 |
-
|
820 |
-
wp_send_json_success();
|
821 |
-
}
|
822 |
-
|
823 |
-
/**
|
824 |
-
* Send an ajax response
|
825 |
-
*
|
826 |
-
* This is a generic response sender which will search for an error
|
827 |
-
* in the response and make the appropriate wp_send_json_*() call.
|
828 |
-
*
|
829 |
-
* @since 0.1
|
830 |
-
*/
|
831 |
-
public function send_ajax_response( $response = array() ) {
|
832 |
-
|
833 |
-
$response[1] = empty( $response[1] ) ? '' : $response[1];
|
834 |
-
|
835 |
-
if ( !empty( $response[0] ) ) {
|
836 |
-
wp_send_json_success( $response[1] );
|
837 |
-
|
838 |
-
} else {
|
839 |
-
wp_send_json_error( $response[1] );
|
840 |
-
}
|
841 |
-
|
842 |
-
}
|
843 |
-
|
844 |
-
}
|
845 |
-
} // endif;
|
1 |
+
<?php
|
2 |
+
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
+
|
4 |
+
if ( !class_exists( 'cffrtbEditor' ) ) {
|
5 |
+
/**
|
6 |
+
* Class which builds the form editor for Custom Fields for Restaurant
|
7 |
+
* Reservations
|
8 |
+
*
|
9 |
+
* @since 0.1
|
10 |
+
*/
|
11 |
+
class cffrtbEditor {
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Hook suffix for the page
|
15 |
+
*
|
16 |
+
* @since 0.1
|
17 |
+
*/
|
18 |
+
public $hook_suffix;
|
19 |
+
|
20 |
+
public function __construct() {
|
21 |
+
|
22 |
+
// Add the admin menu
|
23 |
+
add_action( 'admin_menu', array( $this, 'add_menu_page' ), 20 );
|
24 |
+
|
25 |
+
// Handle ajax requests
|
26 |
+
add_action( 'wp_ajax_nopriv_cffrtb-save-field' , array( $this , 'ajax_nopriv' ) );
|
27 |
+
add_action( 'wp_ajax_cffrtb-save-field', array( $this, 'ajax_save_field' ) );
|
28 |
+
add_action( 'wp_ajax_nopriv_cffrtb-save-order' , array( $this , 'ajax_nopriv' ) );
|
29 |
+
add_action( 'wp_ajax_cffrtb-save-order', array( $this, 'ajax_save_order' ) );
|
30 |
+
add_action( 'wp_ajax_nopriv_cffrtb-load-field' , array( $this , 'ajax_nopriv' ) );
|
31 |
+
add_action( 'wp_ajax_cffrtb-load-field', array( $this, 'ajax_load_field' ) );
|
32 |
+
add_action( 'wp_ajax_nopriv_cffrtb-delete-field' , array( $this , 'ajax_nopriv' ) );
|
33 |
+
add_action( 'wp_ajax_cffrtb-delete-field', array( $this, 'ajax_delete_field' ) );
|
34 |
+
add_action( 'wp_ajax_nopriv_cffrtb-enable-field' , array( $this , 'ajax_nopriv' ) );
|
35 |
+
add_action( 'wp_ajax_cffrtb-enable-field', array( $this, 'ajax_enable_field' ) );
|
36 |
+
add_action( 'wp_ajax_nopriv_cffrtb-reset-all' , array( $this , 'ajax_nopriv' ) );
|
37 |
+
add_action( 'wp_ajax_cffrtb-reset-all', array( $this, 'ajax_reset_all' ) );
|
38 |
+
|
39 |
+
// Load "pointers" (help tooltips on initial load)
|
40 |
+
require_once( RTB_PLUGIN_DIR . '/includes/custom_fields_pointers.php' );
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Add the booking form editor page to the admin menu
|
45 |
+
*
|
46 |
+
* @since 0.1
|
47 |
+
*/
|
48 |
+
public function add_menu_page() {
|
49 |
+
$this->hook_suffix = add_submenu_page(
|
50 |
+
'rtb-bookings',
|
51 |
+
_x( 'Custom Fields', 'Title of the Custom Fields editor page', 'restaurant-reservations' ),
|
52 |
+
_x( 'Custom Fields', 'Title of Custom Fields editor link in the admin menu', 'restaurant-reservations' ),
|
53 |
+
'manage_options',
|
54 |
+
'cffrtb-editor',
|
55 |
+
array( $this, 'display_editor_page' )
|
56 |
+
);
|
57 |
+
|
58 |
+
// Print the error modal and enqueue assets
|
59 |
+
add_action( 'load-' . $this->hook_suffix, array( $this, 'enqueue_admin_assets' ) );
|
60 |
+
add_action( 'admin_footer-' . $this->hook_suffix, array( $this, 'print_modals' ) );
|
61 |
+
}
|
62 |
+
|
63 |
+
/**
|
64 |
+
* Enqueue assets on the editor page
|
65 |
+
*
|
66 |
+
* @since 0.1
|
67 |
+
*/
|
68 |
+
public function enqueue_admin_assets() {
|
69 |
+
global $rtb_controller;
|
70 |
+
|
71 |
+
if ( ! $rtb_controller->permissions->check_permission( 'custom_fields' ) ) { return; }
|
72 |
+
|
73 |
+
// Retrieve pointers (admin tooltips)
|
74 |
+
$pointers = apply_filters( 'cffrtb_pointers', array() );
|
75 |
+
|
76 |
+
// Determine editor dependencies
|
77 |
+
$editor_css_deps = array( 'rtb-booking-form' );
|
78 |
+
$editor_js_deps = array( 'jquery-ui-sortable', 'rtb-booking-form' );
|
79 |
+
if ( !empty( $pointers ) ) {
|
80 |
+
$editor_css_deps[] = 'wp-pointer';
|
81 |
+
$editor_js_deps[] = 'wp-pointer';
|
82 |
+
}
|
83 |
+
|
84 |
+
// Booking form assets
|
85 |
+
$rtb_controller->register_assets();
|
86 |
+
rtb_enqueue_assets();
|
87 |
+
|
88 |
+
// Editor assets
|
89 |
+
wp_enqueue_style( 'cffrtb-editor', RTB_PLUGIN_URL . '/assets/css/editor.css', $editor_css_deps, false );
|
90 |
+
wp_enqueue_script( 'cffrtb-editor', RTB_PLUGIN_URL . '/assets/js/editor.js', $editor_js_deps, false, true );
|
91 |
+
|
92 |
+
// Pass the fields array to the script
|
93 |
+
$field_controller = $rtb_controller->fields;
|
94 |
+
$field_controller->get_valid_field_types();
|
95 |
+
wp_localize_script(
|
96 |
+
'cffrtb-editor',
|
97 |
+
'cffrtb_editor',
|
98 |
+
array(
|
99 |
+
'ajax_nonce' => wp_create_nonce( 'cffrtb-editor' ),
|
100 |
+
'default_type' => key( $field_controller->valid_field_types ),
|
101 |
+
'default_subtype' => key( $field_controller->valid_field_types[ key( $field_controller->valid_field_types ) ]['subtypes'] ),
|
102 |
+
'pointers' => $pointers,
|
103 |
+
'strings' => array(
|
104 |
+
'save' => __( 'Save', 'restaurant-reservations' ),
|
105 |
+
'editor_add_field' => __( 'Add Field', 'restaurant-reservations' ),
|
106 |
+
'editor_edit_field' => __( 'Edit Field', 'restaurant-reservations' ),
|
107 |
+
'editor_save_field' => __( 'Save Field', 'restaurant-reservations' ),
|
108 |
+
'editor_add_fieldset' => __( 'Add Fieldset', 'restaurant-reservations' ),
|
109 |
+
'editor_save_fieldset' => __( 'Save Fieldset', 'restaurant-reservations' ),
|
110 |
+
'field_missing_title' => __( 'Please enter a label for this field.', 'restaurant-reservations' ),
|
111 |
+
'field_missing_options' => __( 'To add an Option field you must add at least one option below.', 'restaurant-reservations' ),
|
112 |
+
'fieldset_not_empty' => __( 'This fieldset can not be deleted until all of its attached fields are removed or assigned to another fieldset.', 'restaurant-reservations' ),
|
113 |
+
'confirm_reset_all' => __( 'Are you sure you want to reset the booking form? All of your changes and custom fields will be removed. This action can not be undone.', 'restaurant-reservations' ),
|
114 |
+
'unknown_error' => __( 'An unspecified error occurred. Please try again. If the problem persists, try logging out and logging back in.', 'restaurant-reservations' ),
|
115 |
+
),
|
116 |
+
)
|
117 |
+
);
|
118 |
+
}
|
119 |
+
|
120 |
+
/**
|
121 |
+
* Display the booking form editor page
|
122 |
+
*
|
123 |
+
* @since 0.1
|
124 |
+
*/
|
125 |
+
public function display_editor_page() {
|
126 |
+
global $rtb_controller;
|
127 |
+
|
128 |
+
$custom_fields_permission = $rtb_controller->permissions->check_permission( 'custom_fields' )
|
129 |
+
|
130 |
+
?>
|
131 |
+
|
132 |
+
<div class="wrap">
|
133 |
+
<h2>
|
134 |
+
<?php _e( 'Custom Fields Editor', 'restaurant-reservations' ); ?>
|
135 |
+
<a href="#" class="add-new-h2 add-field">Add New</a>
|
136 |
+
</h2>
|
137 |
+
<?php if ( $custom_fields_permission ) { ?>
|
138 |
+
<div id="cffrtb-editor">
|
139 |
+
<?php $this->print_booking_form_fields(); ?>
|
140 |
+
</div>
|
141 |
+
<?php } else { ?>
|
142 |
+
<div class='rtb-premium-locked'>
|
143 |
+
<a href="https://www.fivestarplugins.com/license-payment/?Selected=RTB&Quantity=1" target="_blank">Upgrade</a> to the premium version to use this feature
|
144 |
+
</div>
|
145 |
+
<?php } ?>
|
146 |
+
</div>
|
147 |
+
|
148 |
+
<?php
|
149 |
+
}
|
150 |
+
|
151 |
+
/**
|
152 |
+
* Print the booking form fields for editing
|
153 |
+
*
|
154 |
+
* @since 0.0.1
|
155 |
+
*/
|
156 |
+
public function print_booking_form_fields() {
|
157 |
+
global $rtb_controller;
|
158 |
+
|
159 |
+
// Retrieve the form fields
|
160 |
+
$fields = $rtb_controller->settings->get_booking_form_fields();
|
161 |
+
|
162 |
+
// Retrieve system fields with limited editing abilities
|
163 |
+
$field_controller = $rtb_controller->fields;
|
164 |
+
$field_controller->get_system_fields();
|
165 |
+
?>
|
166 |
+
|
167 |
+
<div class="cffrtb-lft">
|
168 |
+
|
169 |
+
<ul id="cffrtb-list" class="cffrtb-list">
|
170 |
+
|
171 |
+
<?php
|
172 |
+
foreach( $fields as $fieldset => $contents ) {
|
173 |
+
echo $this->print_field( $fieldset, $contents, 'fieldset' );
|
174 |
+
}
|
175 |
+
?>
|
176 |
+
|
177 |
+
</ul>
|
178 |
+
|
179 |
+
<a href="#" class="add-field">
|
180 |
+
<span class="dashicons dashicons-plus-alt"></span>
|
181 |
+
<?php _e( 'Add New', 'restaurant-reservations' ); ?>
|
182 |
+
</a>
|
183 |
+
|
184 |
+
</div>
|
185 |
+
<div class="cffrtb-rgt">
|
186 |
+
|
187 |
+
<div id="cffrtb-disabled" class="cffrtb-list">
|
188 |
+
<h3><?php _e( 'Disabled Fields', 'restaurant-reservations' ); ?></h3>
|
189 |
+
|
190 |
+
<?php
|
191 |
+
$modified = get_option( $rtb_controller->custom_fields->modified_option_key );
|
192 |
+
if ( $modified ) :
|
193 |
+
|
194 |
+
$list = '';
|
195 |
+
foreach( $modified as $slug => $field ) {
|
196 |
+
|
197 |
+
if ( !empty( $field['disabled'] ) ) {
|
198 |
+
|
199 |
+
$default_field = $slug == $field['fieldset'] ? $field_controller->default_fields[ $slug ] : $field_controller->get_nested_field( $slug, $field_controller->default_fields );
|
200 |
+
if ( empty( $default_field ) || !is_array( $default_field ) ) {
|
201 |
+
continue;
|
202 |
+
}
|
203 |
+
|
204 |
+
$type = $slug == $field['fieldset'] ? 'fieldset' : 'field';
|
205 |
+
|
206 |
+
$list .= $this->print_field( $slug, array_merge( $default_field, $field ), $type );
|
207 |
+
}
|
208 |
+
}
|
209 |
+
|
210 |
+
if ( !empty( $list ) ) :
|
211 |
+
?>
|
212 |
+
|
213 |
+
<ul class="fields">
|
214 |
+
<?php echo $list; ?>
|
215 |
+
</ul>
|
216 |
+
|
217 |
+
<?php else : ?>
|
218 |
+
|
219 |
+
<p class="description no-disabled-fields"><?php _e( 'You have not disabled any default fields yet.', 'restaurant-reservations' ); ?></p>
|
220 |
+
|
221 |
+
<?php
|
222 |
+
endif;
|
223 |
+
endif;
|
224 |
+
?>
|
225 |
+
|
226 |
+
<div class="reset<?php echo !empty( $modified ) ? ' is-visible' : ''; ?>">
|
227 |
+
<a href="#" class="button reset-all">
|
228 |
+
<?php _e( 'Revert to default', 'restaurant-reservations' ); ?>
|
229 |
+
</a>
|
230 |
+
<a href="#" class="learn-more">
|
231 |
+
<?php _e( 'Learn more', 'restaurant-reservations' ); ?>
|
232 |
+
</a>
|
233 |
+
<p class="description learn-more-details">
|
234 |
+
<?php _e( 'All of your changes and custom fields will be discarded if you revert to default. This is not advised unless you want to remove all of your changes and restore the default booking form.', 'restaurant-reservations' ); ?>
|
235 |
+
</p>
|
236 |
+
</div>
|
237 |
+
</div>
|
238 |
+
|
239 |
+
</div>
|
240 |
+
|
241 |
+
<?php
|
242 |
+
}
|
243 |
+
|
244 |
+
/**
|
245 |
+
* Print a single field in the fields list
|
246 |
+
*
|
247 |
+
* @since 0.1
|
248 |
+
*/
|
249 |
+
public function print_field( $slug, $field, $type = 'field' ) {
|
250 |
+
global $rtb_controller;
|
251 |
+
|
252 |
+
$field_controller = $rtb_controller->fields;
|
253 |
+
if ( empty( $field_controller->system_fields ) ) {
|
254 |
+
$field_controller->get_system_fields();
|
255 |
+
}
|
256 |
+
|
257 |
+
$title = '';
|
258 |
+
if ( $type == 'fieldset' && !empty( $field['legend'] ) ) {
|
259 |
+
$title = $field['legend'];
|
260 |
+
} elseif ( !empty( $field['title'] ) ) {
|
261 |
+
$title = $field['title'];
|
262 |
+
}
|
263 |
+
|
264 |
+
ob_start();
|
265 |
+
?>
|
266 |
+
|
267 |
+
<li class="<?php echo $type; echo !empty( $field['disabled'] ) ? ' disabled' : ''; ?>" data-slug="<?php echo esc_attr( $slug ); ?>"<?php echo empty( $field['ID'] ) ? '' : ' data-id="' . (int) $field['ID'] . '"'; ?>>
|
268 |
+
|
269 |
+
<div class="title">
|
270 |
+
<div class="view">
|
271 |
+
<span class="value">
|
272 |
+
<?php echo esc_html( $title ); ?>
|
273 |
+
</span>
|
274 |
+
|
275 |
+
<div class="controls">
|
276 |
+
<?php if ( empty( $field['disabled'] ) ) : ?>
|
277 |
+
<a href="#" class="label" title="<?php _e( 'Edit title', 'restaurant-reservations' ); ?>">
|
278 |
+
<span class="dashicons dashicons-edit"></span>
|
279 |
+
</a>
|
280 |
+
<?php endif; ?>
|
281 |
+
<?php if ( !empty( $field['disabled'] ) ) : ?>
|
282 |
+
<a href="#" class="enable" title="<?php _e( 'Enable field', 'restaurant-reservations' ); ?>">
|
283 |
+
<span class="dashicons dashicons-visibility"></span>
|
284 |
+
</a>
|
285 |
+
<?php elseif ( ( $type == 'field' && !in_array( $slug, $field_controller->system_fields ) ) || ( $type == 'fieldset' && !in_array( $slug, $field_controller->system_fieldsets ) ) ) : ?>
|
286 |
+
<?php if ( $type == 'field' && !empty( $field['ID'] ) ) : ?>
|
287 |
+
<a href="#" class="options" title="<?php _e( 'Edit field', 'restaurant-reservations' ); ?>">
|
288 |
+
<span class="dashicons dashicons-admin-tools"></span>
|
289 |
+
</a>
|
290 |
+
<?php endif; ?>
|
291 |
+
<a href="#" class="delete" title="<?php _e( 'Delete field', 'restaurant-reservations' ); ?>">
|
292 |
+
<span class="dashicons dashicons-no"></span>
|
293 |
+
</a>
|
294 |
+
<?php endif; ?>
|
295 |
+
</div>
|
296 |
+
</div>
|
297 |
+
|
298 |
+
<?php if ( empty( $field['disabled'] ) ) : ?>
|
299 |
+
<div class="edit">
|
300 |
+
<input type="text" name="title" value="<?php echo esc_html( $title ); ?>" tabindex="-1">
|
301 |
+
|
302 |
+
<div class="controls">
|
303 |
+
<a href="#" class="save" tabindex="-1">
|
304 |
+
<?php _e( 'Save', 'restaurant-reservations' ); ?>
|
305 |
+
</a>
|
306 |
+
</div>
|
307 |
+
|
308 |
+
<div class="status">
|
309 |
+
<span class="load-spinner"></span>
|
310 |
+
</div>
|
311 |
+
</div>
|
312 |
+
<?php endif; ?>
|
313 |
+
|
314 |
+
</div>
|
315 |
+
|
316 |
+
<?php if ( $type == 'fieldset' && empty( $field['disabled'] ) && empty( $field['exclude_fields'] ) ) : ?>
|
317 |
+
<ul class="fields">
|
318 |
+
<?php
|
319 |
+
if( !empty( $field['fields'] ) ) :
|
320 |
+
foreach( $field['fields'] as $field_slug => $sub_field ) :
|
321 |
+
echo $this->print_field( $field_slug, $sub_field );
|
322 |
+
endforeach;
|
323 |
+
endif;
|
324 |
+
?>
|
325 |
+
</ul>
|
326 |
+
<?php endif; ?>
|
327 |
+
</li>
|
328 |
+
<?php
|
329 |
+
|
330 |
+
return ob_get_clean();
|
331 |
+
}
|
332 |
+
|
333 |
+
/**
|
334 |
+
* Print a label field for the editing form
|
335 |
+
*
|
336 |
+
* @since 0.1
|
337 |
+
*/
|
338 |
+
public function print_label_input( $slug, $label ) {
|
339 |
+
?>
|
340 |
+
|
341 |
+
<div class="label">
|
342 |
+
<label for="<?php echo esc_attr( $slug ); ?>_label">
|
343 |
+
<?php _e( 'Label', 'restaurant-reservations' ); ?>
|
344 |
+
</label>
|
345 |
+
<input type="text" name="label" id="<?php echo esc_attr( $slug ); ?>_label" value="<?php echo esc_attr( $label ); ?>">
|
346 |
+
</div>
|
347 |
+
|
348 |
+
<?php
|
349 |
+
}
|
350 |
+
|
351 |
+
/**
|
352 |
+
* Print the error modal in the footer. This re-uses the error modal
|
353 |
+
* markup and styling from Restaurant Reservations
|
354 |
+
*
|
355 |
+
* @since 0.1
|
356 |
+
*/
|
357 |
+
public function print_modals() {
|
358 |
+
global $rtb_controller;
|
359 |
+
|
360 |
+
$field_controller = $rtb_controller->fields;
|
361 |
+
$field_controller->get_valid_field_types();
|
362 |
+
|
363 |
+
$default_type = key( $field_controller->valid_field_types );
|
364 |
+
$default_subtype = key( $field_controller->valid_field_types[ $default_type ]['subtypes'] );
|
365 |
+
?>
|
366 |
+
<div id="cffrtb-field-editor" class="rtb-admin-modal">
|
367 |
+
|
368 |
+
<form id="cffrtb-field-editor-form" class="rtb-container">
|
369 |
+
<input type="hidden" name="id" value="">
|
370 |
+
<input type="hidden" name="type" value="<?php echo esc_attr( $default_type ); ?>">
|
371 |
+
<input type="hidden" name="subtype" value="<?php echo esc_attr( $default_type ); ?>">
|
372 |
+
|
373 |
+
<div class="title">
|
374 |
+
<h2><?php _e( 'Add Field', 'restaurant-reservations' ); ?></h2>
|
375 |
+
</div>
|
376 |
+
|
377 |
+
<div class="type">
|
378 |
+
<label>
|
379 |
+
<?php _e( 'Field Type', 'restaurant-reservations' ); ?>
|
380 |
+
</label>
|
381 |
+
|
382 |
+
<div class="selector">
|
383 |
+
<ul class="types">
|
384 |
+
<?php foreach( $field_controller->valid_field_types as $slug => $type ) : ?>
|
385 |
+
<li>
|
386 |
+
<a href="#" class="<?php echo esc_attr( $slug ); if ( $default_type == $slug ) : ?> current<?php endif; ?>" data-type="<?php echo esc_attr( $slug ); ?>">
|
387 |
+
<?php echo $type['title']; ?>
|
388 |
+
</a>
|
389 |
+
</li>
|
390 |
+
<?php endforeach; ?>
|
391 |
+
</ul>
|
392 |
+
|
393 |
+
<?php foreach( $field_controller->valid_field_types as $slug => $type ) : ?>
|
394 |
+
<ul class="subtypes <?php echo $slug; if ( $default_type == $slug ) : ?> current<?php endif; ?>">
|
395 |
+
<?php foreach( $type['subtypes'] as $sub_slug => $subtype ) : ?>
|
396 |
+
<li>
|
397 |
+
<a href="#" class="<?php echo esc_attr( $sub_slug ); if ( $default_type == $sub_slug ) : ?> current<?php endif; ?>" data-subtype="<?php echo esc_attr( $sub_slug ); ?>">
|
398 |
+
<?php echo $subtype['title']; ?>
|
399 |
+
</a>
|
400 |
+
</li>
|
401 |
+
<?php endforeach; ?>
|
402 |
+
</ul>
|
403 |
+
<?php endforeach; ?>
|
404 |
+
</div>
|
405 |
+
</div>
|
406 |
+
<div class="settings">
|
407 |
+
|
408 |
+
<div class="item">
|
409 |
+
<label for="title">
|
410 |
+
<?php _e( 'Label', 'restaurant-reservations' ); ?>
|
411 |
+
</label>
|
412 |
+
<input type="text" name="title" id="title">
|
413 |
+
</div>
|
414 |
+
|
415 |
+
<div class="settings-panel options">
|
416 |
+
<div class="item">
|
417 |
+
<label for="options-options">
|
418 |
+
<?php _e( 'Options', 'restaurant-reservations' ); ?>
|
419 |
+
</label>
|
420 |
+
<div class="add">
|
421 |
+
<input type="text" name="options" id="options-options">
|
422 |
+
<a href="#">
|
423 |
+
<span class="dashicons dashicons-plus-alt"></span>
|
424 |
+
<?php _e( 'Add', 'restaurant-reservations' ); ?>
|
425 |
+
</a>
|
426 |
+
</div>
|
427 |
+
<ul class="options">
|
428 |
+
</ul>
|
429 |
+
</div>
|
430 |
+
</div>
|
431 |
+
|
432 |
+
<?php do_action( 'cffrtb_field_editor_settings_panel' ); ?>
|
433 |
+
|
434 |
+
</div>
|
435 |
+
|
436 |
+
<div class="required">
|
437 |
+
<label>
|
438 |
+
<input type="checkbox" name="required" value="1">
|
439 |
+
<?php _e( 'Required', 'restaurant-reservations' ); ?>
|
440 |
+
</label>
|
441 |
+
</div>
|
442 |
+
|
443 |
+
<div class="actions">
|
444 |
+
<a href="#" class="button-primary save">
|
445 |
+
<?php _e( 'Add Field', 'restaurant-reservations' ); ?>
|
446 |
+
</a>
|
447 |
+
<a href="#" class="button cancel">
|
448 |
+
<?php _e( 'Cancel', 'restaurant-reservations' ); ?>
|
449 |
+
</a>
|
450 |
+
<div class="status">
|
451 |
+
<span class="load-spinner"></span>
|
452 |
+
</div>
|
453 |
+
</div>
|
454 |
+
</form>
|
455 |
+
</div>
|
456 |
+
|
457 |
+
<div id="cffrtb-field-editor-option" class="rtb-admin-modal">
|
458 |
+
<div class="rtb-container">
|
459 |
+
<div class="option">
|
460 |
+
<a href="#" class="field button-primary">
|
461 |
+
<?php _e( 'Add Field', 'restaurant-reservations' ); ?>
|
462 |
+
</a>
|
463 |
+
<p class="description">
|
464 |
+
<?php _e( 'Fields prompt the user to enter information or select from options.' ); ?>
|
465 |
+
</p>
|
466 |
+
</div>
|
467 |
+
<div class="option">
|
468 |
+
<a href="#" class="fieldset button">
|
469 |
+
<?php _e( 'Add Fieldset', 'restaurant-reservations' ); ?>
|
470 |
+
</a>
|
471 |
+
<p class="description">
|
472 |
+
<?php _e( 'Fieldsets group other fields under a common label.' ); ?>
|
473 |
+
</p>
|
474 |
+
</div>
|
475 |
+
</div>
|
476 |
+
</div>
|
477 |
+
|
478 |
+
<div id="rtb-error-modal" class="rtb-admin-modal">
|
479 |
+
<div class="rtb-error rtb-container"">
|
480 |
+
<div class="rtb-error-msg"></div>
|
481 |
+
<a href="#" class="button"><?php _e( 'Close', 'restaurant-reservations' ); ?></a>
|
482 |
+
</div>
|
483 |
+
</div>
|
484 |
+
<?php
|
485 |
+
}
|
486 |
+
|
487 |
+
/**
|
488 |
+
* Handle ajax requests from logged out users
|
489 |
+
*
|
490 |
+
* @since 0.1
|
491 |
+
*/
|
492 |
+
public function ajax_nopriv() {
|
493 |
+
|
494 |
+
wp_send_json_error(
|
495 |
+
array(
|
496 |
+
'error' => 'loggedout',
|
497 |
+
'msg' => sprintf( __( 'You have been logged out. Please %slogin again%s.', 'restaurant-reservations' ), '<a href="' . wp_login_url( admin_url( 'admin.php?page=cffrtb-editor' ) ) . '">', '</a>' ),
|
498 |
+
)
|
499 |
+
);
|
500 |
+
}
|
501 |
+
|
502 |
+
/**
|
503 |
+
* Handle ajax request to save a field
|
504 |
+
*
|
505 |
+
* @since 0.1
|
506 |
+
*/
|
507 |
+
public function ajax_save_field() {
|
508 |
+
global $rtb_controller;
|
509 |
+
|
510 |
+
// Authenticate request
|
511 |
+
if ( !check_ajax_referer( 'cffrtb-editor', 'nonce' ) || !current_user_can( 'manage_options' ) ) {
|
512 |
+
$this->nopriv_ajax();
|
513 |
+
}
|
514 |
+
|
515 |
+
// Missing data
|
516 |
+
if ( empty( $_POST['field'] ) ) {
|
517 |
+
wp_send_json_error(
|
518 |
+
array(
|
519 |
+
'error' => 'no_field_data',
|
520 |
+
'msg' => __( 'No field data was received with your request.', 'restaurant-reservations' ) . ' ' . $rtb_controller->custom_fields->common_error_msg
|
521 |
+
)
|
522 |
+
);
|
523 |
+
}
|
524 |
+
|
525 |
+
// Missing request specification
|
526 |
+
if ( empty( $_POST['request'] ) ) {
|
527 |
+
wp_send_json_error(
|
528 |
+
array(
|
529 |
+
'error' => 'no_request',
|
530 |
+
'msg' => __( 'Internal data that was supposed to be passed with your request was not received.', 'restaurant-reservations' ) . ' ' . $rtb_controller->custom_fields->common_error_msg
|
531 |
+
)
|
532 |
+
);
|
533 |
+
}
|
534 |
+
|
535 |
+
$field = new cffrtbField( $_POST['field'] );
|
536 |
+
|
537 |
+
// Just the label
|
538 |
+
if ( $_POST['request'] == 'save_label' ) {
|
539 |
+
$this->send_ajax_response( $field->save_label() );
|
540 |
+
|
541 |
+
// The whole field
|
542 |
+
} elseif ( $_POST['request'] == 'save_field' ) {
|
543 |
+
$this->send_ajax_response( $field->save_field() );
|
544 |
+
}
|
545 |
+
|
546 |
+
wp_send_json_error(
|
547 |
+
array(
|
548 |
+
'error' => 'unknown',
|
549 |
+
'msg' => __( 'An unknown error has occurred.', 'restaurant-reservations' ) . ' ' . $rtb_controller->custom_fields->common_error_msg
|
550 |
+
)
|
551 |
+
);
|
552 |
+
}
|
553 |
+
|
554 |
+
/**
|
555 |
+
* Handle ajax request to save the order of fields and fieldsets
|
556 |
+
*
|
557 |
+
* @since 0.1
|
558 |
+
*/
|
559 |
+
public function ajax_save_order() {
|
560 |
+
global $rtb_controller;
|
561 |
+
|
562 |
+
// Authenticate request
|
563 |
+
if ( !check_ajax_referer( 'cffrtb-editor', 'nonce' ) || !current_user_can( 'manage_options' ) ) {
|
564 |
+
$this->nopriv_ajax();
|
565 |
+
}
|
566 |
+
|
567 |
+
// Missing data
|
568 |
+
if ( empty( $_POST['order'] ) ) {
|
569 |
+
wp_send_json_error(
|
570 |
+
array(
|
571 |
+
'error' => 'no_fields_data',
|
572 |
+
'msg' => __( 'No fields data was received with your request.', 'restaurant-reservations' ) . ' ' . $rtb_controller->custom_fields->common_error_msg
|
573 |
+
)
|
574 |
+
);
|
575 |
+
}
|
576 |
+
|
577 |
+
$modified = get_option( $rtb_controller->custom_fields->modified_option_key );
|
578 |
+
$orig_modified = $modified;
|
579 |
+
|
580 |
+
$custom_fields_error = array();
|
581 |
+
$has_custom_fields = false;
|
582 |
+
foreach( $_POST['order'] as $field ) {
|
583 |
+
|
584 |
+
if ( !isset( $field['order'] ) || empty( $field['fieldset'] ) ) {
|
585 |
+
continue; // @todo this indicates some kind of data error, though
|
586 |
+
}
|
587 |
+
|
588 |
+
// Default fields
|
589 |
+
if ( empty( $field['ID'] ) ) {
|
590 |
+
|
591 |
+
if ( empty( $modified[ $field['slug'] ] ) ) {
|
592 |
+
$modified[ $field['slug'] ] = array();
|
593 |
+
}
|
594 |
+
|
595 |
+
// Skip if the order isn't changing
|
596 |
+
if ( isset( $modified[ $field['slug'] ]['order'] ) && $modified[ $field['slug'] ]['order'] == $field['order'] && isset( $modified[ $field['slug'] ]['fieldset'] ) && $modified[ $field['slug'] ]['fieldset'] == $field['fieldset'] ) {
|
597 |
+
continue;
|
598 |
+
}
|
599 |
+
|
600 |
+
$modified[ $field['slug'] ]['order'] = (int) $field['order'];
|
601 |
+
$modified[ $field['slug'] ]['fieldset'] = sanitize_key( $field['fieldset'] );
|
602 |
+
|
603 |
+
// Custom fields
|
604 |
+
} else {
|
605 |
+
|
606 |
+
$custom_field = new cffrtbField( $field );
|
607 |
+
$result = $custom_field->save_field();
|
608 |
+
|
609 |
+
if ( !$result[0] ) {
|
610 |
+
array_push( $custom_fields_error, $result[1] );
|
611 |
+
}
|
612 |
+
|
613 |
+
$has_custom_fields = true;
|
614 |
+
}
|
615 |
+
}
|
616 |
+
|
617 |
+
update_option( $rtb_controller->custom_fields->modified_option_key, $modified );
|
618 |
+
|
619 |
+
if ( !$has_custom_fields || ( $has_custom_fields && empty( $custom_fields_error ) ) ) {
|
620 |
+
wp_send_json_success();
|
621 |
+
|
622 |
+
} else {
|
623 |
+
wp_send_json_error(
|
624 |
+
array(
|
625 |
+
'error' => 'save_order_failed',
|
626 |
+
'msg' => __( 'An error occurred while saving the new field order. Please try again.', 'restaurant-reservations' ),
|
627 |
+
'fields' => sanitize_text_field( $_POST['order'] ),
|
628 |
+
'custom_fields_error' => $custom_fields_error,
|
629 |
+
)
|
630 |
+
);
|
631 |
+
}
|
632 |
+
|
633 |
+
wp_send_json_error(
|
634 |
+
array(
|
635 |
+
'error' => 'unknown',
|
636 |
+
'msg' => __( 'An unknown error has occurred.', 'restaurant-reservations' ) . ' ' . $rtb_controller->custom_fields->common_error_msg
|
637 |
+
)
|
638 |
+
);
|
639 |
+
}
|
640 |
+
|
641 |
+
/**
|
642 |
+
* Handle ajax request to load a field
|
643 |
+
*
|
644 |
+
* @since 0.1
|
645 |
+
*/
|
646 |
+
public function ajax_load_field() {
|
647 |
+
global $rtb_controller;
|
648 |
+
|
649 |
+
// Authenticate request
|
650 |
+
if ( !check_ajax_referer( 'cffrtb-editor', 'nonce' ) || !current_user_can( 'manage_options' ) ) {
|
651 |
+
$this->nopriv_ajax();
|
652 |
+
}
|
653 |
+
|
654 |
+
// Missing data
|
655 |
+
if ( empty( $_POST['ID'] ) ) {
|
656 |
+
wp_send_json_error(
|
657 |
+
array(
|
658 |
+
'error' => 'no_id',
|
659 |
+
'msg' => __( 'The requested field could not be loaded because no ID was received with your request.', 'restaurant-reservations' ) . ' ' . $rtb_controller->custom_fields->common_error_msg
|
660 |
+
)
|
661 |
+
);
|
662 |
+
}
|
663 |
+
|
664 |
+
$field = new cffrtbField( array( 'ID' => (int) $_POST['ID'] ) );
|
665 |
+
|
666 |
+
wp_send_json_success(
|
667 |
+
array(
|
668 |
+
'field' => $field,
|
669 |
+
)
|
670 |
+
);
|
671 |
+
}
|
672 |
+
|
673 |
+
/**
|
674 |
+
* Handle ajax request to delete or disable a field
|
675 |
+
*
|
676 |
+
* @since 0.1
|
677 |
+
*/
|
678 |
+
public function ajax_delete_field() {
|
679 |
+
global $rtb_controller;
|
680 |
+
|
681 |
+
// Authenticate request
|
682 |
+
if ( !check_ajax_referer( 'cffrtb-editor', 'nonce' ) || !current_user_can( 'manage_options' ) ) {
|
683 |
+
$this->nopriv_ajax();
|
684 |
+
}
|
685 |
+
|
686 |
+
// Missing data
|
687 |
+
if ( empty( $_POST['ID'] ) && ( empty( $_POST['slug'] ) || empty( $_POST['fieldset'] ) ) ) {
|
688 |
+
wp_send_json_error(
|
689 |
+
array(
|
690 |
+
'error' => 'no_id',
|
691 |
+
'msg' => __( 'The requested field could not be deleted because no ID or slug was received with your request.', 'restaurant-reservations' ) . ' ' . $rtb_controller->custom_fields->common_error_msg
|
692 |
+
)
|
693 |
+
);
|
694 |
+
}
|
695 |
+
|
696 |
+
if ( !empty( $_POST['ID'] ) ) {
|
697 |
+
$post = wp_delete_post( (int) $_POST['ID'] );
|
698 |
+
|
699 |
+
if ( !$post ) {
|
700 |
+
wp_send_json_error(
|
701 |
+
array(
|
702 |
+
'error' => 'delete_field_failed',
|
703 |
+
'msg' => __( 'An error occurred while deleting this field.', 'restaurant-reservations' ) . ' ' . $rtb_controller->custom_fields->common_error_msg
|
704 |
+
)
|
705 |
+
);
|
706 |
+
}
|
707 |
+
|
708 |
+
wp_send_json_success();
|
709 |
+
|
710 |
+
} else {
|
711 |
+
$modified = get_option( $rtb_controller->custom_fields->modified_option_key );
|
712 |
+
$slug = sanitize_key( $_POST['slug'] );
|
713 |
+
$fieldset = sanitize_key( $_POST['fieldset'] );
|
714 |
+
$modified[ $slug ]['disabled'] = 1;
|
715 |
+
$modified[ $slug ]['fieldset'] = $fieldset;
|
716 |
+
update_option( $rtb_controller->custom_fields->modified_option_key, $modified );
|
717 |
+
|
718 |
+
global $rtb_controller;
|
719 |
+
$fields = $rtb_controller->settings->get_booking_form_fields();
|
720 |
+
$field = $slug == $fieldset ? $rtb_controller->fields->default_fields[ $slug ] : $rtb_controller->fields->get_nested_field( $slug, $rtb_controller->fields->default_fields );
|
721 |
+
$field['disabled'] = 1;
|
722 |
+
|
723 |
+
$type = $slug == $fieldset ? 'fieldset' : 'field';
|
724 |
+
|
725 |
+
wp_send_json_success(
|
726 |
+
array(
|
727 |
+
'field' => $this->print_field( $slug, $field, $type ),
|
728 |
+
)
|
729 |
+
);
|
730 |
+
}
|
731 |
+
}
|
732 |
+
|
733 |
+
/**
|
734 |
+
* Handle an ajax request to enable a field that has been disabled
|
735 |
+
*
|
736 |
+
* @since 0.1
|
737 |
+
*/
|
738 |
+
public function ajax_enable_field() {
|
739 |
+
global $rtb_controller;
|
740 |
+
|
741 |
+
// Authenticate request
|
742 |
+
if ( !check_ajax_referer( 'cffrtb-editor', 'nonce' ) || !current_user_can( 'manage_options' ) ) {
|
743 |
+
$this->nopriv_ajax();
|
744 |
+
}
|
745 |
+
|
746 |
+
// Missing data
|
747 |
+
if ( empty( $_POST['slug'] ) ) {
|
748 |
+
wp_send_json_error(
|
749 |
+
array(
|
750 |
+
'error' => 'no_id',
|
751 |
+
'msg' => __( 'The requested field could not be enabled because no identifying slug was received with your request.', 'restaurant-reservations' ) . ' ' . $rtb_controller->custom_fields->common_error_msg
|
752 |
+
)
|
753 |
+
);
|
754 |
+
}
|
755 |
+
|
756 |
+
$slug = sanitize_key( $_POST['slug'] );
|
757 |
+
$modified = get_option( $rtb_controller->custom_fields->modified_option_key );
|
758 |
+
|
759 |
+
if ( empty( $modified ) || !is_array( $modified ) || !array_key_exists( $slug, $modified ) ) {
|
760 |
+
wp_send_json_error(
|
761 |
+
array(
|
762 |
+
'error' => 'field_not_found',
|
763 |
+
'msg' => __( 'The requested field could not be enabled because it did not appear to be disabled.', 'restaurant-reservations' ) . ' ' . $rtb_controller->custom_fields->common_error_msg
|
764 |
+
)
|
765 |
+
);
|
766 |
+
}
|
767 |
+
|
768 |
+
if ( isset( $modified[ $slug ]['disabled'] ) ) {
|
769 |
+
unset( $modified[ $slug ]['disabled'] );
|
770 |
+
update_option( $rtb_controller->custom_fields->modified_option_key, $modified );
|
771 |
+
}
|
772 |
+
|
773 |
+
global $rtb_controller;
|
774 |
+
$fields = $rtb_controller->settings->get_booking_form_fields();
|
775 |
+
$field = $_POST['type'] == 'fieldset' ? $rtb_controller->fields->default_fields[ $slug ] : $rtb_controller->fields->get_nested_field( $slug, $fields );
|
776 |
+
|
777 |
+
$type = $_POST['type'] == 'fieldset' ? 'fieldset' : 'field';
|
778 |
+
|
779 |
+
if ( $type == 'fieldset' ) {
|
780 |
+
$field['exclude_fields'] = true;
|
781 |
+
}
|
782 |
+
|
783 |
+
wp_send_json_success(
|
784 |
+
array(
|
785 |
+
'field' => $this->print_field( $slug, $field, $type )
|
786 |
+
)
|
787 |
+
);
|
788 |
+
}
|
789 |
+
|
790 |
+
/**
|
791 |
+
* Handle an ajax request to reset all changes and delete custom
|
792 |
+
* fields
|
793 |
+
*
|
794 |
+
* @since 0.1
|
795 |
+
*/
|
796 |
+
public function ajax_reset_all( $authorized = false) {
|
797 |
+
global $rtb_controller;
|
798 |
+
|
799 |
+
// Authenticate request
|
800 |
+
if ( ( !check_ajax_referer( 'cffrtb-editor', 'nonce' ) || !current_user_can( 'manage_options' ) ) and ! $authorized ) {
|
801 |
+
$this->nopriv_ajax();
|
802 |
+
}
|
803 |
+
|
804 |
+
// Delete modifications to default fields
|
805 |
+
delete_option( $rtb_controller->custom_fields->modified_option_key );
|
806 |
+
|
807 |
+
// Delete all custom posts
|
808 |
+
$posts = new WP_Query(
|
809 |
+
array(
|
810 |
+
'post_type' => 'cffrtb_field',
|
811 |
+
'posts_per_page' => 1000, // Very large upper limit
|
812 |
+
)
|
813 |
+
);
|
814 |
+
|
815 |
+
while( $posts->have_posts() ) {
|
816 |
+
$posts->the_post();
|
817 |
+
wp_delete_post( get_the_ID() );
|
818 |
+
}
|
819 |
+
|
820 |
+
wp_send_json_success();
|
821 |
+
}
|
822 |
+
|
823 |
+
/**
|
824 |
+
* Send an ajax response
|
825 |
+
*
|
826 |
+
* This is a generic response sender which will search for an error
|
827 |
+
* in the response and make the appropriate wp_send_json_*() call.
|
828 |
+
*
|
829 |
+
* @since 0.1
|
830 |
+
*/
|
831 |
+
public function send_ajax_response( $response = array() ) {
|
832 |
+
|
833 |
+
$response[1] = empty( $response[1] ) ? '' : $response[1];
|
834 |
+
|
835 |
+
if ( !empty( $response[0] ) ) {
|
836 |
+
wp_send_json_success( $response[1] );
|
837 |
+
|
838 |
+
} else {
|
839 |
+
wp_send_json_error( $response[1] );
|
840 |
+
}
|
841 |
+
|
842 |
+
}
|
843 |
+
|
844 |
+
}
|
845 |
+
} // endif;
|
includes/Export.CSV.class.php
CHANGED
@@ -1,381 +1,381 @@
|
|
1 |
-
<?php
|
2 |
-
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
-
|
4 |
-
if ( !class_exists( 'ebfrtbExportCSV' ) ) {
|
5 |
-
/**
|
6 |
-
* Handle CSV exports
|
7 |
-
*
|
8 |
-
* PHP's fputcsv() function is used to stream a
|
9 |
-
* CSV file to the browser.
|
10 |
-
*
|
11 |
-
* @since 0.2
|
12 |
-
*/
|
13 |
-
class ebfrtbExportCSV extends ebfrtbExport {
|
14 |
-
|
15 |
-
/**
|
16 |
-
* Arguments for the query used to fetch
|
17 |
-
* bookings for this export
|
18 |
-
*
|
19 |
-
* @since 0.1
|
20 |
-
*/
|
21 |
-
public $query_args;
|
22 |
-
|
23 |
-
/**
|
24 |
-
* Insantiate the CSV export
|
25 |
-
*
|
26 |
-
* @since 0.1
|
27 |
-
*/
|
28 |
-
public function __construct( $bookings, $args = array() ) {
|
29 |
-
|
30 |
-
$this->bookings = $bookings;
|
31 |
-
|
32 |
-
// Date range
|
33 |
-
if ( !empty( $args['query_args'] ) ) {
|
34 |
-
$this->query_args = $args['query_args'];
|
35 |
-
}
|
36 |
-
|
37 |
-
// Locations
|
38 |
-
global $rtb_controller;
|
39 |
-
if ( !empty( $rtb_controller->locations ) && !empty( $rtb_controller->locations->post_type ) ) {
|
40 |
-
add_filter( 'ebfrtb_export_csv_booking_headers', array( $this, 'add_location_header' ) );
|
41 |
-
add_filter( 'ebfrtb_export_csv_booking', array( $this, 'add_location' ), 10, 2 );
|
42 |
-
}
|
43 |
-
|
44 |
-
// Privacy consent
|
45 |
-
if ( $rtb_controller->settings->get_setting( 'require-consent' ) ) {
|
46 |
-
add_filter( 'ebfrtb_export_csv_booking_headers', array( $this, 'add_privacy_header' ) );
|
47 |
-
add_filter( 'ebfrtb_export_csv_booking', array( $this, 'add_privacy' ), 10, 2 );
|
48 |
-
}
|
49 |
-
|
50 |
-
if ( $rtb_controller->settings->get_setting( 'enable-tables' ) ) {
|
51 |
-
add_filter( 'ebfrtb_export_csv_booking_headers', array( $this, 'add_table_header' ), 9 );
|
52 |
-
add_filter( 'ebfrtb_export_csv_booking', array( $this, 'add_table' ), 9, 2 );
|
53 |
-
}
|
54 |
-
|
55 |
-
if ( $rtb_controller->settings->get_setting( 'require-deposit' ) ) {
|
56 |
-
add_filter( 'ebfrtb_export_csv_booking_headers', array( $this, 'add_deposit_header' ) );
|
57 |
-
add_filter( 'ebfrtb_export_csv_booking', array( $this, 'add_deposit' ), 10, 2 );
|
58 |
-
}
|
59 |
-
|
60 |
-
add_filter( 'ebfrtb_export_csv_booking_headers', array( $this, 'add_custom_fields_header' ) );
|
61 |
-
add_filter( 'ebfrtb_export_csv_booking', array( $this, 'add_custom_fields' ), 10, 2 );
|
62 |
-
}
|
63 |
-
|
64 |
-
/**
|
65 |
-
* Compile an array for the CSV file
|
66 |
-
*
|
67 |
-
* @since 0.1
|
68 |
-
*/
|
69 |
-
public function export() {
|
70 |
-
global $rtb_controller;
|
71 |
-
|
72 |
-
$date_format = $rtb_controller->settings->get_setting( 'ebfrtb-csv-date-format' );
|
73 |
-
$time_format = get_option( 'time_format' );
|
74 |
-
|
75 |
-
// Compile bookings arrayarray headers
|
76 |
-
$arr = apply_filters( 'ebfrtb_export_csv_booking_headers', array(
|
77 |
-
array(
|
78 |
-
'ID' => __( 'Booking ID', 'restaurant-reservations' ),
|
79 |
-
'date' => __( 'Date', 'restaurant-reservations' ),
|
80 |
-
'time' => __( 'Time', 'restaurant-reservations' ),
|
81 |
-
'name' => __( 'Name', 'restaurant-reservations' ),
|
82 |
-
'party' => __( 'Party', 'restaurant-reservations' ),
|
83 |
-
'email' => __( 'Email', 'restaurant-reservations' ),
|
84 |
-
'phone' => __( 'Phone', 'restaurant-reservations' ),
|
85 |
-
'message' => __( 'Message', 'restaurant-reservations' ),
|
86 |
-
'date_submission' => __( 'Date the request was made', 'restaurant-reservations' ),
|
87 |
-
'status' => __( 'Booking Status', 'restaurant-reservations' ),
|
88 |
-
)
|
89 |
-
) );
|
90 |
-
|
91 |
-
$tmzn = wp_timezone();
|
92 |
-
|
93 |
-
// Compile bookings array
|
94 |
-
foreach( $this->bookings as $booking ) {
|
95 |
-
$arr[] = apply_filters( 'ebfrtb_export_csv_booking', array(
|
96 |
-
'ID' => $booking->ID,
|
97 |
-
'date' => date_i18n( $date_format, ( new DateTime( $booking->date, $tmzn ) )->format( 'U' ) ),
|
98 |
-
'time' => date_i18n( $time_format, ( new DateTime( $booking->date, $tmzn ) )->format( 'U' ) ),
|
99 |
-
'name' => $booking->name,
|
100 |
-
'party' => $booking->party,
|
101 |
-
'email' => $booking->email,
|
102 |
-
'phone' => $booking->phone,
|
103 |
-
'message' => str_replace( array( "\r\n", "\n", "\r", '<br />', '<br>', '<br/>' ), ' ', $booking->message ),
|
104 |
-
'date_submission' => date_i18n( $date_format . ' ' . $time_format, $booking->date_submission ),
|
105 |
-
'status' => $booking->post_status,
|
106 |
-
), $booking );
|
107 |
-
}
|
108 |
-
|
109 |
-
$this->export = apply_filters( 'ebfrtb_export_csv_bookings', $arr );
|
110 |
-
|
111 |
-
return $this->export;
|
112 |
-
}
|
113 |
-
|
114 |
-
/**
|
115 |
-
* Deliver the CSV file to the browser
|
116 |
-
*
|
117 |
-
* @since 0.1
|
118 |
-
*/
|
119 |
-
public function deliver() {
|
120 |
-
|
121 |
-
// Generate the export if it's not been done yet
|
122 |
-
if ( empty( $this->export ) ) {
|
123 |
-
$this->export();
|
124 |
-
}
|
125 |
-
|
126 |
-
$filename = apply_filters( 'ebfrtb_export_csv_filename', sanitize_file_name( $this->get_date_phrase() ) . '.csv' );
|
127 |
-
$delimiter = apply_filters( 'ebfrtb_export_csv_delimiter', ',' );
|
128 |
-
|
129 |
-
header( 'Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0' );
|
130 |
-
header( 'Content-Description: File Transfer' );
|
131 |
-
header( 'Content-type: text/csv' );
|
132 |
-
header( 'Content-Disposition: attachment; filename=' . $filename );
|
133 |
-
header( 'Expires: 0' );
|
134 |
-
header( 'Pragma: no-cache' );
|
135 |
-
|
136 |
-
$output = @fopen( 'php://output', 'w' );
|
137 |
-
|
138 |
-
foreach( $this->export as $booking ) {
|
139 |
-
fputcsv( $output, $booking, $delimiter );
|
140 |
-
}
|
141 |
-
|
142 |
-
fclose( $output );
|
143 |
-
|
144 |
-
exit();
|
145 |
-
}
|
146 |
-
|
147 |
-
/**
|
148 |
-
* Add a location header if locations are active
|
149 |
-
*
|
150 |
-
* @param array $headers Key/value of spreadsheet header rows id/label
|
151 |
-
* @since 1.1
|
152 |
-
*/
|
153 |
-
public function add_location_header( $headers ) {
|
154 |
-
|
155 |
-
$headers[0] = array_merge(
|
156 |
-
array( 'location' => __( 'Location', 'restaurant-reservations' ) ),
|
157 |
-
$headers[0]
|
158 |
-
);
|
159 |
-
|
160 |
-
return $headers;
|
161 |
-
}
|
162 |
-
|
163 |
-
/**
|
164 |
-
* Add location data from a booking to the array for conversion to csv
|
165 |
-
*
|
166 |
-
* @param array $arr Assoc array of booking data compiled for conversion to
|
167 |
-
* csv
|
168 |
-
* @param rtbBooking $booking Original booking object
|
169 |
-
* @since 1.1
|
170 |
-
*/
|
171 |
-
public function add_location( $arr, $booking ) {
|
172 |
-
|
173 |
-
$location = '';
|
174 |
-
if ( !empty( $booking->location ) ) {
|
175 |
-
$term = get_term( $booking->location );
|
176 |
-
if ( is_a( $term, 'WP_Term' ) ) {
|
177 |
-
$location = $term->name;
|
178 |
-
}
|
179 |
-
}
|
180 |
-
|
181 |
-
$arr = array_merge(
|
182 |
-
array( 'location' => $location ),
|
183 |
-
$arr
|
184 |
-
);
|
185 |
-
|
186 |
-
return $arr;
|
187 |
-
}
|
188 |
-
|
189 |
-
/**
|
190 |
-
* Add a header for the privacy consent if it's active
|
191 |
-
*
|
192 |
-
* @param array $headers Key/value of spreadsheet header rows id/label
|
193 |
-
* @since 1.1.1
|
194 |
-
*/
|
195 |
-
public function add_privacy_header( $headers ) {
|
196 |
-
|
197 |
-
$headers[0] = array_merge(
|
198 |
-
$headers[0],
|
199 |
-
array( 'consent_acquired' => __( 'Data Privacy Consent', 'restaurant-reservations' ) )
|
200 |
-
);
|
201 |
-
|
202 |
-
return $headers;
|
203 |
-
}
|
204 |
-
|
205 |
-
/**
|
206 |
-
* Add privacy consent collection status to the array for conversion to csv
|
207 |
-
*
|
208 |
-
* @param array $arr Assoc array of booking data compiled for conversion to
|
209 |
-
* csv
|
210 |
-
* @param rtbBooking $booking Original booking object
|
211 |
-
* @since 1.1
|
212 |
-
*/
|
213 |
-
public function add_privacy( $arr, $booking ) {
|
214 |
-
|
215 |
-
$consent_acquired = !empty( $booking->consent_acquired ) ? __( 'Yes', 'restaurant-reservations' ) : __( 'No', 'restaurant-reservations' );
|
216 |
-
$arr = array_merge(
|
217 |
-
$arr,
|
218 |
-
array( 'consent_acquired' => $consent_acquired )
|
219 |
-
);
|
220 |
-
|
221 |
-
return $arr;
|
222 |
-
}
|
223 |
-
|
224 |
-
/**
|
225 |
-
* Add a header for the table(s) feature if it's active
|
226 |
-
*
|
227 |
-
* @param array $headers Key/value of spreadsheet header rows id/label
|
228 |
-
* @since 2.2.0
|
229 |
-
*/
|
230 |
-
public function add_table_header( $headers ) {
|
231 |
-
|
232 |
-
$headers[0] = array_merge(
|
233 |
-
$headers[0],
|
234 |
-
array( 'table' => __( 'Table(s)', 'restaurant-reservations' ) )
|
235 |
-
);
|
236 |
-
|
237 |
-
return $headers;
|
238 |
-
}
|
239 |
-
|
240 |
-
/**
|
241 |
-
* Add the selected table(s) to the array for conversion to csv
|
242 |
-
*
|
243 |
-
* @param array $arr Assoc array of booking data compiled for conversion to
|
244 |
-
* csv
|
245 |
-
* @param rtbBooking $booking Original booking object
|
246 |
-
* @since 2.2.0
|
247 |
-
*/
|
248 |
-
public function add_table( $arr, $booking ) {
|
249 |
-
|
250 |
-
$arr = array_merge(
|
251 |
-
$arr,
|
252 |
-
array( 'table' => implode( ',', $booking->table ) )
|
253 |
-
);
|
254 |
-
|
255 |
-
return $arr;
|
256 |
-
}
|
257 |
-
|
258 |
-
/**
|
259 |
-
* Add a header for the payment(s) feature if it's active
|
260 |
-
*
|
261 |
-
* @param array $headers Key/value of spreadsheet header rows id/label
|
262 |
-
* @since 2.2.8
|
263 |
-
*/
|
264 |
-
public function add_deposit_header( $headers ) {
|
265 |
-
|
266 |
-
$headers[0] = array_merge(
|
267 |
-
$headers[0],
|
268 |
-
array(
|
269 |
-
'deposit' => __( 'Deposit', 'restaurant-reservations' ),
|
270 |
-
'receipt_id' => __( 'Receipt ID', 'restaurant-reservations' )
|
271 |
-
)
|
272 |
-
);
|
273 |
-
|
274 |
-
return $headers;
|
275 |
-
}
|
276 |
-
|
277 |
-
/**
|
278 |
-
* Add the deposit(s) to the array for conversion to csv
|
279 |
-
*
|
280 |
-
* @param array $arr Assoc array of booking data compiled for conversion to
|
281 |
-
* csv
|
282 |
-
* @param rtbBooking $booking Original booking object
|
283 |
-
* @since 2.2.0
|
284 |
-
*/
|
285 |
-
public function add_deposit( $arr, $booking ) {
|
286 |
-
|
287 |
-
global $rtb_controller;
|
288 |
-
|
289 |
-
$arr = array_merge(
|
290 |
-
$arr,
|
291 |
-
array(
|
292 |
-
'deposit' => $rtb_controller->settings->get_setting( 'rtb-currency' ) .' '. $booking->deposit,
|
293 |
-
'receipt_id' => $booking->receipt_id
|
294 |
-
)
|
295 |
-
);
|
296 |
-
|
297 |
-
return $arr;
|
298 |
-
}
|
299 |
-
|
300 |
-
/**
|
301 |
-
* Add custom fields to CSV headers
|
302 |
-
*
|
303 |
-
* @param array $headers Key/value of spreadsheet header rows id/label
|
304 |
-
* @since 2.0
|
305 |
-
*/
|
306 |
-
public function add_custom_fields_header( $headers ) {
|
307 |
-
|
308 |
-
$fields = rtb_get_custom_fields();
|
309 |
-
|
310 |
-
foreach( $fields as $field ) {
|
311 |
-
|
312 |
-
if ( $field->type == 'fieldset' ) {
|
313 |
-
continue;
|
314 |
-
}
|
315 |
-
|
316 |
-
$headers[0][ 'cf-' . $field->slug ] = $field->title;
|
317 |
-
}
|
318 |
-
|
319 |
-
return $headers;
|
320 |
-
}
|
321 |
-
|
322 |
-
/**
|
323 |
-
* Add custom fields to CSV headers
|
324 |
-
*
|
325 |
-
* @param array $headers Key/value of spreadsheet header rows id/label
|
326 |
-
* @since 2.0
|
327 |
-
*/
|
328 |
-
public function add_custom_fields( $row, $booking ) {
|
329 |
-
global $rtb_controller;
|
330 |
-
|
331 |
-
$fields = rtb_get_custom_fields();
|
332 |
-
|
333 |
-
$cf = isset( $booking->custom_fields ) ? $booking->custom_fields : array();
|
334 |
-
|
335 |
-
foreach( $fields as $field ) {
|
336 |
-
|
337 |
-
if ( $field->type == 'fieldset' ) {
|
338 |
-
continue;
|
339 |
-
}
|
340 |
-
|
341 |
-
$val = isset( $cf[ $field->slug ] ) ? $cf[ $field->slug ] : '';
|
342 |
-
$display_val = apply_filters( 'cffrtb_display_value_csv', $rtb_controller->fields->get_display_value( $val, $field, '', false ), $val, $field, $booking );
|
343 |
-
|
344 |
-
$row[ 'cf-' . $field->slug ] = $display_val;
|
345 |
-
}
|
346 |
-
|
347 |
-
return $row;
|
348 |
-
}
|
349 |
-
|
350 |
-
/**
|
351 |
-
* Add custom fields to CSV data row
|
352 |
-
*
|
353 |
-
* @param array $row Assoc array of booking data compiled for conversion to
|
354 |
-
* csv
|
355 |
-
* @param rtbBooking $booking Original booking object
|
356 |
-
* @since 2.0
|
357 |
-
*/
|
358 |
-
public function cffrtb_ebfrtb_add_csv_row( $row, $booking ) {
|
359 |
-
global $rtb_controller;
|
360 |
-
|
361 |
-
$fields = rtb_get_custom_fields();
|
362 |
-
|
363 |
-
$cf = isset( $booking->custom_fields ) ? $booking->custom_fields : array();
|
364 |
-
|
365 |
-
foreach( $fields as $field ) {
|
366 |
-
|
367 |
-
if ( $field->type == 'fieldset' ) {
|
368 |
-
continue;
|
369 |
-
}
|
370 |
-
|
371 |
-
$val = isset( $cf[ $field->slug ] ) ? $cf[ $field->slug ] : '';
|
372 |
-
$display_val = apply_filters( 'cffrtb_display_value_csv', $rtb_controller->fields->get_display_value( $val, $field, '', false ), $val, $field, $booking );
|
373 |
-
|
374 |
-
$row[ 'cf-' . $field->slug ] = $display_val;
|
375 |
-
}
|
376 |
-
|
377 |
-
return $row;
|
378 |
-
}
|
379 |
-
|
380 |
-
}
|
381 |
-
} // endif
|
1 |
+
<?php
|
2 |
+
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
+
|
4 |
+
if ( !class_exists( 'ebfrtbExportCSV' ) ) {
|
5 |
+
/**
|
6 |
+
* Handle CSV exports
|
7 |
+
*
|
8 |
+
* PHP's fputcsv() function is used to stream a
|
9 |
+
* CSV file to the browser.
|
10 |
+
*
|
11 |
+
* @since 0.2
|
12 |
+
*/
|
13 |
+
class ebfrtbExportCSV extends ebfrtbExport {
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Arguments for the query used to fetch
|
17 |
+
* bookings for this export
|
18 |
+
*
|
19 |
+
* @since 0.1
|
20 |
+
*/
|
21 |
+
public $query_args;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Insantiate the CSV export
|
25 |
+
*
|
26 |
+
* @since 0.1
|
27 |
+
*/
|
28 |
+
public function __construct( $bookings, $args = array() ) {
|
29 |
+
|
30 |
+
$this->bookings = $bookings;
|
31 |
+
|
32 |
+
// Date range
|
33 |
+
if ( !empty( $args['query_args'] ) ) {
|
34 |
+
$this->query_args = $args['query_args'];
|
35 |
+
}
|
36 |
+
|
37 |
+
// Locations
|
38 |
+
global $rtb_controller;
|
39 |
+
if ( !empty( $rtb_controller->locations ) && !empty( $rtb_controller->locations->post_type ) ) {
|
40 |
+
add_filter( 'ebfrtb_export_csv_booking_headers', array( $this, 'add_location_header' ) );
|
41 |
+
add_filter( 'ebfrtb_export_csv_booking', array( $this, 'add_location' ), 10, 2 );
|
42 |
+
}
|
43 |
+
|
44 |
+
// Privacy consent
|
45 |
+
if ( $rtb_controller->settings->get_setting( 'require-consent' ) ) {
|
46 |
+
add_filter( 'ebfrtb_export_csv_booking_headers', array( $this, 'add_privacy_header' ) );
|
47 |
+
add_filter( 'ebfrtb_export_csv_booking', array( $this, 'add_privacy' ), 10, 2 );
|
48 |
+
}
|
49 |
+
|
50 |
+
if ( $rtb_controller->settings->get_setting( 'enable-tables' ) ) {
|
51 |
+
add_filter( 'ebfrtb_export_csv_booking_headers', array( $this, 'add_table_header' ), 9 );
|
52 |
+
add_filter( 'ebfrtb_export_csv_booking', array( $this, 'add_table' ), 9, 2 );
|
53 |
+
}
|
54 |
+
|
55 |
+
if ( $rtb_controller->settings->get_setting( 'require-deposit' ) ) {
|
56 |
+
add_filter( 'ebfrtb_export_csv_booking_headers', array( $this, 'add_deposit_header' ) );
|
57 |
+
add_filter( 'ebfrtb_export_csv_booking', array( $this, 'add_deposit' ), 10, 2 );
|
58 |
+
}
|
59 |
+
|
60 |
+
add_filter( 'ebfrtb_export_csv_booking_headers', array( $this, 'add_custom_fields_header' ) );
|
61 |
+
add_filter( 'ebfrtb_export_csv_booking', array( $this, 'add_custom_fields' ), 10, 2 );
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Compile an array for the CSV file
|
66 |
+
*
|
67 |
+
* @since 0.1
|
68 |
+
*/
|
69 |
+
public function export() {
|
70 |
+
global $rtb_controller;
|
71 |
+
|
72 |
+
$date_format = $rtb_controller->settings->get_setting( 'ebfrtb-csv-date-format' );
|
73 |
+
$time_format = get_option( 'time_format' );
|
74 |
+
|
75 |
+
// Compile bookings arrayarray headers
|
76 |
+
$arr = apply_filters( 'ebfrtb_export_csv_booking_headers', array(
|
77 |
+
array(
|
78 |
+
'ID' => __( 'Booking ID', 'restaurant-reservations' ),
|
79 |
+
'date' => __( 'Date', 'restaurant-reservations' ),
|
80 |
+
'time' => __( 'Time', 'restaurant-reservations' ),
|
81 |
+
'name' => __( 'Name', 'restaurant-reservations' ),
|
82 |
+
'party' => __( 'Party', 'restaurant-reservations' ),
|
83 |
+
'email' => __( 'Email', 'restaurant-reservations' ),
|
84 |
+
'phone' => __( 'Phone', 'restaurant-reservations' ),
|
85 |
+
'message' => __( 'Message', 'restaurant-reservations' ),
|
86 |
+
'date_submission' => __( 'Date the request was made', 'restaurant-reservations' ),
|
87 |
+
'status' => __( 'Booking Status', 'restaurant-reservations' ),
|
88 |
+
)
|
89 |
+
) );
|
90 |
+
|
91 |
+
$tmzn = wp_timezone();
|
92 |
+
|
93 |
+
// Compile bookings array
|
94 |
+
foreach( $this->bookings as $booking ) {
|
95 |
+
$arr[] = apply_filters( 'ebfrtb_export_csv_booking', array(
|
96 |
+
'ID' => $booking->ID,
|
97 |
+
'date' => date_i18n( $date_format, ( new DateTime( $booking->date, $tmzn ) )->format( 'U' ) ),
|
98 |
+
'time' => date_i18n( $time_format, ( new DateTime( $booking->date, $tmzn ) )->format( 'U' ) ),
|
99 |
+
'name' => $booking->name,
|
100 |
+
'party' => $booking->party,
|
101 |
+
'email' => $booking->email,
|
102 |
+
'phone' => $booking->phone,
|
103 |
+
'message' => str_replace( array( "\r\n", "\n", "\r", '<br />', '<br>', '<br/>' ), ' ', $booking->message ),
|
104 |
+
'date_submission' => date_i18n( $date_format . ' ' . $time_format, $booking->date_submission ),
|
105 |
+
'status' => $booking->post_status,
|
106 |
+
), $booking );
|
107 |
+
}
|
108 |
+
|
109 |
+
$this->export = apply_filters( 'ebfrtb_export_csv_bookings', $arr );
|
110 |
+
|
111 |
+
return $this->export;
|
112 |
+
}
|
113 |
+
|
114 |
+
/**
|
115 |
+
* Deliver the CSV file to the browser
|
116 |
+
*
|
117 |
+
* @since 0.1
|
118 |
+
*/
|
119 |
+
public function deliver() {
|
120 |
+
|
121 |
+
// Generate the export if it's not been done yet
|
122 |
+
if ( empty( $this->export ) ) {
|
123 |
+
$this->export();
|
124 |
+
}
|
125 |
+
|
126 |
+
$filename = apply_filters( 'ebfrtb_export_csv_filename', sanitize_file_name( $this->get_date_phrase() ) . '.csv' );
|
127 |
+
$delimiter = apply_filters( 'ebfrtb_export_csv_delimiter', ',' );
|
128 |
+
|
129 |
+
header( 'Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0' );
|
130 |
+
header( 'Content-Description: File Transfer' );
|
131 |
+
header( 'Content-type: text/csv' );
|
132 |
+
header( 'Content-Disposition: attachment; filename=' . $filename );
|
133 |
+
header( 'Expires: 0' );
|
134 |
+
header( 'Pragma: no-cache' );
|
135 |
+
|
136 |
+
$output = @fopen( 'php://output', 'w' );
|
137 |
+
|
138 |
+
foreach( $this->export as $booking ) {
|
139 |
+
fputcsv( $output, $booking, $delimiter );
|
140 |
+
}
|
141 |
+
|
142 |
+
fclose( $output );
|
143 |
+
|
144 |
+
exit();
|
145 |
+
}
|
146 |
+
|
147 |
+
/**
|
148 |
+
* Add a location header if locations are active
|
149 |
+
*
|
150 |
+
* @param array $headers Key/value of spreadsheet header rows id/label
|
151 |
+
* @since 1.1
|
152 |
+
*/
|
153 |
+
public function add_location_header( $headers ) {
|
154 |
+
|
155 |
+
$headers[0] = array_merge(
|
156 |
+
array( 'location' => __( 'Location', 'restaurant-reservations' ) ),
|
157 |
+
$headers[0]
|
158 |
+
);
|
159 |
+
|
160 |
+
return $headers;
|
161 |
+
}
|
162 |
+
|
163 |
+
/**
|
164 |
+
* Add location data from a booking to the array for conversion to csv
|
165 |
+
*
|
166 |
+
* @param array $arr Assoc array of booking data compiled for conversion to
|
167 |
+
* csv
|
168 |
+
* @param rtbBooking $booking Original booking object
|
169 |
+
* @since 1.1
|
170 |
+
*/
|
171 |
+
public function add_location( $arr, $booking ) {
|
172 |
+
|
173 |
+
$location = '';
|
174 |
+
if ( !empty( $booking->location ) ) {
|
175 |
+
$term = get_term( $booking->location );
|
176 |
+
if ( is_a( $term, 'WP_Term' ) ) {
|
177 |
+
$location = $term->name;
|
178 |
+
}
|
179 |
+
}
|
180 |
+
|
181 |
+
$arr = array_merge(
|
182 |
+
array( 'location' => $location ),
|
183 |
+
$arr
|
184 |
+
);
|
185 |
+
|
186 |
+
return $arr;
|
187 |
+
}
|
188 |
+
|
189 |
+
/**
|
190 |
+
* Add a header for the privacy consent if it's active
|
191 |
+
*
|
192 |
+
* @param array $headers Key/value of spreadsheet header rows id/label
|
193 |
+
* @since 1.1.1
|
194 |
+
*/
|
195 |
+
public function add_privacy_header( $headers ) {
|
196 |
+
|
197 |
+
$headers[0] = array_merge(
|
198 |
+
$headers[0],
|
199 |
+
array( 'consent_acquired' => __( 'Data Privacy Consent', 'restaurant-reservations' ) )
|
200 |
+
);
|
201 |
+
|
202 |
+
return $headers;
|
203 |
+
}
|
204 |
+
|
205 |
+
/**
|
206 |
+
* Add privacy consent collection status to the array for conversion to csv
|
207 |
+
*
|
208 |
+
* @param array $arr Assoc array of booking data compiled for conversion to
|
209 |
+
* csv
|
210 |
+
* @param rtbBooking $booking Original booking object
|
211 |
+
* @since 1.1
|
212 |
+
*/
|
213 |
+
public function add_privacy( $arr, $booking ) {
|
214 |
+
|
215 |
+
$consent_acquired = !empty( $booking->consent_acquired ) ? __( 'Yes', 'restaurant-reservations' ) : __( 'No', 'restaurant-reservations' );
|
216 |
+
$arr = array_merge(
|
217 |
+
$arr,
|
218 |
+
array( 'consent_acquired' => $consent_acquired )
|
219 |
+
);
|
220 |
+
|
221 |
+
return $arr;
|
222 |
+
}
|
223 |
+
|
224 |
+
/**
|
225 |
+
* Add a header for the table(s) feature if it's active
|
226 |
+
*
|
227 |
+
* @param array $headers Key/value of spreadsheet header rows id/label
|
228 |
+
* @since 2.2.0
|
229 |
+
*/
|
230 |
+
public function add_table_header( $headers ) {
|
231 |
+
|
232 |
+
$headers[0] = array_merge(
|
233 |
+
$headers[0],
|
234 |
+
array( 'table' => __( 'Table(s)', 'restaurant-reservations' ) )
|
235 |
+
);
|
236 |
+
|
237 |
+
return $headers;
|
238 |
+
}
|
239 |
+
|
240 |
+
/**
|
241 |
+
* Add the selected table(s) to the array for conversion to csv
|
242 |
+
*
|
243 |
+
* @param array $arr Assoc array of booking data compiled for conversion to
|
244 |
+
* csv
|
245 |
+
* @param rtbBooking $booking Original booking object
|
246 |
+
* @since 2.2.0
|
247 |
+
*/
|
248 |
+
public function add_table( $arr, $booking ) {
|
249 |
+
|
250 |
+
$arr = array_merge(
|
251 |
+
$arr,
|
252 |
+
array( 'table' => implode( ',', $booking->table ) )
|
253 |
+
);
|
254 |
+
|
255 |
+
return $arr;
|
256 |
+
}
|
257 |
+
|
258 |
+
/**
|
259 |
+
* Add a header for the payment(s) feature if it's active
|
260 |
+
*
|
261 |
+
* @param array $headers Key/value of spreadsheet header rows id/label
|
262 |
+
* @since 2.2.8
|
263 |
+
*/
|
264 |
+
public function add_deposit_header( $headers ) {
|
265 |
+
|
266 |
+
$headers[0] = array_merge(
|
267 |
+
$headers[0],
|
268 |
+
array(
|
269 |
+
'deposit' => __( 'Deposit', 'restaurant-reservations' ),
|
270 |
+
'receipt_id' => __( 'Receipt ID', 'restaurant-reservations' )
|
271 |
+
)
|
272 |
+
);
|
273 |
+
|
274 |
+
return $headers;
|
275 |
+
}
|
276 |
+
|
277 |
+
/**
|
278 |
+
* Add the deposit(s) to the array for conversion to csv
|
279 |
+
*
|
280 |
+
* @param array $arr Assoc array of booking data compiled for conversion to
|
281 |
+
* csv
|
282 |
+
* @param rtbBooking $booking Original booking object
|
283 |
+
* @since 2.2.0
|
284 |
+
*/
|
285 |
+
public function add_deposit( $arr, $booking ) {
|
286 |
+
|
287 |
+
global $rtb_controller;
|
288 |
+
|
289 |
+
$arr = array_merge(
|
290 |
+
$arr,
|
291 |
+
array(
|
292 |
+
'deposit' => $rtb_controller->settings->get_setting( 'rtb-currency' ) .' '. $booking->deposit,
|
293 |
+
'receipt_id' => $booking->receipt_id
|
294 |
+
)
|
295 |
+
);
|
296 |
+
|
297 |
+
return $arr;
|
298 |
+
}
|
299 |
+
|
300 |
+
/**
|
301 |
+
* Add custom fields to CSV headers
|
302 |
+
*
|
303 |
+
* @param array $headers Key/value of spreadsheet header rows id/label
|
304 |
+
* @since 2.0
|
305 |
+
*/
|
306 |
+
public function add_custom_fields_header( $headers ) {
|
307 |
+
|
308 |
+
$fields = rtb_get_custom_fields();
|
309 |
+
|
310 |
+
foreach( $fields as $field ) {
|
311 |
+
|
312 |
+
if ( $field->type == 'fieldset' ) {
|
313 |
+
continue;
|
314 |
+
}
|
315 |
+
|
316 |
+
$headers[0][ 'cf-' . $field->slug ] = $field->title;
|
317 |
+
}
|
318 |
+
|
319 |
+
return $headers;
|
320 |
+
}
|
321 |
+
|
322 |
+
/**
|
323 |
+
* Add custom fields to CSV headers
|
324 |
+
*
|
325 |
+
* @param array $headers Key/value of spreadsheet header rows id/label
|
326 |
+
* @since 2.0
|
327 |
+
*/
|
328 |
+
public function add_custom_fields( $row, $booking ) {
|
329 |
+
global $rtb_controller;
|
330 |
+
|
331 |
+
$fields = rtb_get_custom_fields();
|
332 |
+
|
333 |
+
$cf = isset( $booking->custom_fields ) ? $booking->custom_fields : array();
|
334 |
+
|
335 |
+
foreach( $fields as $field ) {
|
336 |
+
|
337 |
+
if ( $field->type == 'fieldset' ) {
|
338 |
+
continue;
|
339 |
+
}
|
340 |
+
|
341 |
+
$val = isset( $cf[ $field->slug ] ) ? $cf[ $field->slug ] : '';
|
342 |
+
$display_val = apply_filters( 'cffrtb_display_value_csv', $rtb_controller->fields->get_display_value( $val, $field, '', false ), $val, $field, $booking );
|
343 |
+
|
344 |
+
$row[ 'cf-' . $field->slug ] = $display_val;
|
345 |
+
}
|
346 |
+
|
347 |
+
return $row;
|
348 |
+
}
|
349 |
+
|
350 |
+
/**
|
351 |
+
* Add custom fields to CSV data row
|
352 |
+
*
|
353 |
+
* @param array $row Assoc array of booking data compiled for conversion to
|
354 |
+
* csv
|
355 |
+
* @param rtbBooking $booking Original booking object
|
356 |
+
* @since 2.0
|
357 |
+
*/
|
358 |
+
public function cffrtb_ebfrtb_add_csv_row( $row, $booking ) {
|
359 |
+
global $rtb_controller;
|
360 |
+
|
361 |
+
$fields = rtb_get_custom_fields();
|
362 |
+
|
363 |
+
$cf = isset( $booking->custom_fields ) ? $booking->custom_fields : array();
|
364 |
+
|
365 |
+
foreach( $fields as $field ) {
|
366 |
+
|
367 |
+
if ( $field->type == 'fieldset' ) {
|
368 |
+
continue;
|
369 |
+
}
|
370 |
+
|
371 |
+
$val = isset( $cf[ $field->slug ] ) ? $cf[ $field->slug ] : '';
|
372 |
+
$display_val = apply_filters( 'cffrtb_display_value_csv', $rtb_controller->fields->get_display_value( $val, $field, '', false ), $val, $field, $booking );
|
373 |
+
|
374 |
+
$row[ 'cf-' . $field->slug ] = $display_val;
|
375 |
+
}
|
376 |
+
|
377 |
+
return $row;
|
378 |
+
}
|
379 |
+
|
380 |
+
}
|
381 |
+
} // endif
|
includes/Export.PDF.class.php
CHANGED
@@ -1,365 +1,365 @@
|
|
1 |
-
<?php
|
2 |
-
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
-
|
4 |
-
if ( !class_exists( 'ebfrtbExportPDF' ) ) {
|
5 |
-
/**
|
6 |
-
* Handle PDF exports
|
7 |
-
*
|
8 |
-
* mPDF or TCPDF is used to render the PDFs.
|
9 |
-
*
|
10 |
-
* mPDF has better HTML/CSS support, but may not
|
11 |
-
* be compatible with all servers.
|
12 |
-
*
|
13 |
-
* http://www.mpdf1.com/
|
14 |
-
*
|
15 |
-
* TCPDF should have better server compatiblity,
|
16 |
-
* so it is a fallback in case the main renderer
|
17 |
-
* is not compatible.
|
18 |
-
*
|
19 |
-
* http://www.tcpdf.org/
|
20 |
-
*
|
21 |
-
* @since 1.4.1
|
22 |
-
*/
|
23 |
-
class ebfrtbExportPDF extends ebfrtbExport {
|
24 |
-
|
25 |
-
/**
|
26 |
-
* PDF Rendering Library
|
27 |
-
*
|
28 |
-
* @since 0.1
|
29 |
-
*/
|
30 |
-
public $lib;
|
31 |
-
|
32 |
-
/**
|
33 |
-
* Paper size to use
|
34 |
-
*
|
35 |
-
* @since 0.1
|
36 |
-
*/
|
37 |
-
public $paper_size;
|
38 |
-
|
39 |
-
/**
|
40 |
-
* Arguments for the query used to fetch
|
41 |
-
* bookings for this export
|
42 |
-
*
|
43 |
-
* @since 0.1
|
44 |
-
*/
|
45 |
-
public $query_args;
|
46 |
-
|
47 |
-
/**
|
48 |
-
* The reservation and guest numbers by date
|
49 |
-
*
|
50 |
-
* @since 0.1
|
51 |
-
*/
|
52 |
-
public $bookings_summary = array();
|
53 |
-
|
54 |
-
/**
|
55 |
-
* Insantiate the PDF export
|
56 |
-
*
|
57 |
-
* @since 0.1
|
58 |
-
*/
|
59 |
-
public function __construct( $bookings, $args = array() ) {
|
60 |
-
|
61 |
-
$this->bookings = $bookings;
|
62 |
-
|
63 |
-
global $rtb_controller;
|
64 |
-
$this->lib = !empty( $args['pdf-lib'] ) ? sanitize_key( $args['pdf-lib'] ) : $rtb_controller->settings->get_setting( 'ebfrtb-pdf-lib' );
|
65 |
-
|
66 |
-
// Get paper size
|
67 |
-
$this->paper_size = !empty( $args['paper-size'] ) ? sanitize_key( $args['paper-size'] ) : $rtb_controller->settings->get_setting( 'ebfrtb-paper-size' );
|
68 |
-
|
69 |
-
// Query arguments
|
70 |
-
if ( !empty( $args['query_args'] ) ) {
|
71 |
-
$this->query_args = $args['query_args'];
|
72 |
-
}
|
73 |
-
|
74 |
-
// Maybe add table hooks
|
75 |
-
add_filter( 'ebfrtb_mpdf_after_details', array($this, 'add_booking_table_to_pdf' ) );
|
76 |
-
add_filter( 'ebfrtb_tcpdf_after_details', array($this, 'add_booking_table_to_pdf' ) );
|
77 |
-
|
78 |
-
// Add custom field hooks
|
79 |
-
add_filter( 'ebfrtb_mpdf_after_details', array($this, 'add_custom_fields_to_mpdf' ) );
|
80 |
-
add_filter( 'ebfrtb_tcpdf_after_details', array($this, 'add_custom_fields_to_tcpdf' ) );
|
81 |
-
}
|
82 |
-
|
83 |
-
/**
|
84 |
-
* Set document information
|
85 |
-
*
|
86 |
-
* Sets document meta data like title, creator, etc. Both
|
87 |
-
* TCPDF and mPDF use the same methods for this.
|
88 |
-
*
|
89 |
-
* @since 0.1
|
90 |
-
*/
|
91 |
-
public function set_doc_info( $pdf ) {
|
92 |
-
|
93 |
-
$pdf->SetCreator( get_bloginfo( 'sitename' ) );
|
94 |
-
$pdf->SetAuthor( get_bloginfo( 'sitename' ) );
|
95 |
-
$pdf->SetTitle( sprintf( _x( 'Bookings at %s', 'Title of PDF documents', 'restaurant-reservations' ), get_bloginfo( 'sitename' ) ) );
|
96 |
-
$pdf->SetSubject( $this->get_date_phrase() );
|
97 |
-
|
98 |
-
return $pdf;
|
99 |
-
}
|
100 |
-
|
101 |
-
/**
|
102 |
-
* Compile the PDF file
|
103 |
-
*
|
104 |
-
* This routes to the appropriate export method
|
105 |
-
* depending on the PDF library being used.
|
106 |
-
*
|
107 |
-
* @since 0.1
|
108 |
-
*/
|
109 |
-
public function export() {
|
110 |
-
|
111 |
-
$this->create_bookings_summary();
|
112 |
-
|
113 |
-
if ( $this->lib === 'tcpdf' ) {
|
114 |
-
$this->export_tcpdf();
|
115 |
-
|
116 |
-
} elseif ( $this->lib === 'mpdf' ) {
|
117 |
-
$this->export_mpdf();
|
118 |
-
|
119 |
-
} else {
|
120 |
-
do_action( 'ebcfrtb_pdf_export_' . $this->lib, $this );
|
121 |
-
}
|
122 |
-
|
123 |
-
return $this->export;
|
124 |
-
}
|
125 |
-
|
126 |
-
/**
|
127 |
-
* Deliver the PDF to the browser
|
128 |
-
*
|
129 |
-
* @since 0.1
|
130 |
-
*/
|
131 |
-
public function deliver() {
|
132 |
-
|
133 |
-
// Generate the export if it's not been done yet
|
134 |
-
if ( empty( $this->export ) ) {
|
135 |
-
$this->export();
|
136 |
-
}
|
137 |
-
|
138 |
-
$filename = apply_filters( 'ebfrtb_export_pdf_filename', sanitize_file_name( $this->get_date_phrase() ) . '.pdf' );
|
139 |
-
|
140 |
-
// Clean any stray errors, warnings or notices that may have been
|
141 |
-
// printed to the buffer
|
142 |
-
ob_get_clean();
|
143 |
-
|
144 |
-
if ( $this->lib === 'tcpdf' || $this->lib === 'mpdf' ) {
|
145 |
-
$this->export->Output( $filename, 'I');
|
146 |
-
exit();
|
147 |
-
|
148 |
-
} else {
|
149 |
-
do_action( 'ebcfrtb_pdf_deliver_' . $this->lib, $this );
|
150 |
-
}
|
151 |
-
|
152 |
-
// Just in case we forget...
|
153 |
-
wp_die( __( 'An unexpected error occurred and your export request could not be fulfilled.', 'restaurant-reservations' ) );
|
154 |
-
}
|
155 |
-
|
156 |
-
/**
|
157 |
-
* Compile PDF file with TCPDF library
|
158 |
-
*
|
159 |
-
* @since 0.1
|
160 |
-
*/
|
161 |
-
public function export_tcpdf() {
|
162 |
-
|
163 |
-
// Load TCPDF library
|
164 |
-
require_once( RTB_PLUGIN_DIR . '/lib/tcpdf/config/tcpdf_config.php' );
|
165 |
-
require_once( RTB_PLUGIN_DIR . '/lib/tcpdf/tcpdf.php' );
|
166 |
-
|
167 |
-
// TCPDF uses a different identifier for U.S. Letter format
|
168 |
-
if ( $this->paper_size === 'LETTER' ) {
|
169 |
-
$this->paper_size = 'ANSI_A';
|
170 |
-
}
|
171 |
-
|
172 |
-
$tcpdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, $this->paper_size, true, 'UTF-8', false);
|
173 |
-
$tcpdf = $this->set_doc_info( $tcpdf );
|
174 |
-
|
175 |
-
// set default header data
|
176 |
-
$header_title = get_bloginfo( 'sitename' );
|
177 |
-
if ( !empty( $this->query_args['location'] ) ) {
|
178 |
-
$term = get_term( $this->query_args['location'] );
|
179 |
-
if ( is_a( $term, 'WP_Term' ) ) {
|
180 |
-
$header_title = $term->name;
|
181 |
-
}
|
182 |
-
}
|
183 |
-
$tcpdf->SetHeaderData('', '', $header_title, $this->get_date_phrase(), array( 117, 117, 117 ), array( 117, 117, 117 ) );
|
184 |
-
$tcpdf->setFooterData(array(0,64,0), array(0,64,128));
|
185 |
-
|
186 |
-
// set header and footer fonts
|
187 |
-
$tcpdf->setHeaderFont(Array(PDF_FONT_NAME_MAIN, '', PDF_FONT_SIZE_MAIN));
|
188 |
-
$tcpdf->setFooterFont(Array(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA));
|
189 |
-
|
190 |
-
// set margins
|
191 |
-
$tcpdf->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT);
|
192 |
-
$tcpdf->SetHeaderMargin(PDF_MARGIN_HEADER);
|
193 |
-
$tcpdf->SetFooterMargin(PDF_MARGIN_FOOTER);
|
194 |
-
|
195 |
-
// set auto page breaks
|
196 |
-
$tcpdf->SetAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM);
|
197 |
-
|
198 |
-
// set image scale factor
|
199 |
-
$tcpdf->setImageScale(PDF_IMAGE_SCALE_RATIO);
|
200 |
-
|
201 |
-
// Subset fonts for smaller doc sizes
|
202 |
-
$tcpdf->setFontSubsetting(true);
|
203 |
-
|
204 |
-
// Add a page
|
205 |
-
$tcpdf->AddPage();
|
206 |
-
$tcpdf->SetCellPadding(0);
|
207 |
-
|
208 |
-
// Generate the bookings HTML
|
209 |
-
ob_start();
|
210 |
-
$this->include_template( 'tcpdf.php' );
|
211 |
-
$html = ob_get_clean();
|
212 |
-
|
213 |
-
// Print HTML
|
214 |
-
$tcpdf->writeHTML($html, false, true, false, false, '');
|
215 |
-
|
216 |
-
$this->export = $tcpdf;
|
217 |
-
|
218 |
-
return $this->export;
|
219 |
-
}
|
220 |
-
|
221 |
-
/**
|
222 |
-
*Maybe add the table for a booking to PDF output
|
223 |
-
*
|
224 |
-
* @since 2.2
|
225 |
-
*/
|
226 |
-
public function add_booking_table_to_pdf( $booking ) {
|
227 |
-
global $rtb_controller;
|
228 |
-
|
229 |
-
if ( $rtb_controller->settings->get_setting( 'enable-tables' ) ) { ?>
|
230 |
-
|
231 |
-
<p class="booking-table">
|
232 |
-
<span class="label"><?php echo __( 'Table(s)' ) . esc_html_x( ': ', 'Appears after table label in PDF exports', 'custom-fields-for-rtb' ); ?></span>
|
233 |
-
<?php echo implode( ',', $booking->table ); ?>
|
234 |
-
</p>
|
235 |
-
|
236 |
-
<?php }
|
237 |
-
}
|
238 |
-
|
239 |
-
/**
|
240 |
-
* Add custom fields to TCPDF output
|
241 |
-
*
|
242 |
-
* @since 2.0
|
243 |
-
*/
|
244 |
-
public function add_custom_fields_to_tcpdf( $booking ) {
|
245 |
-
global $rtb_controller;
|
246 |
-
|
247 |
-
$fields = rtb_get_custom_fields();
|
248 |
-
$cf = isset( $booking->custom_fields ) ? $booking->custom_fields : array();
|
249 |
-
|
250 |
-
foreach( $fields as $field ) {
|
251 |
-
|
252 |
-
if ( $field->type == 'fieldset' || !isset( $cf[ $field->slug ] ) ) {
|
253 |
-
continue;
|
254 |
-
}
|
255 |
-
|
256 |
-
$val = $cf[ $field->slug ];
|
257 |
-
$display_val = apply_filters( 'cffrtb_display_value_tcpdf', $rtb_controller->fields->get_display_value( $val, $field, '', true ), $val, $field, $booking );
|
258 |
-
|
259 |
-
?>
|
260 |
-
|
261 |
-
<div class="custom-field <?php echo esc_attr( $field->type . ' ' . $field->slug ); ?>">
|
262 |
-
<?php echo esc_html( $field->title ) . esc_html_x( ': ', 'Appears after field label in TCPDF exports', 'custom-fields-for-rtb' ); ?>
|
263 |
-
<?php echo $display_val ?>
|
264 |
-
</div>
|
265 |
-
|
266 |
-
<?php
|
267 |
-
}
|
268 |
-
}
|
269 |
-
|
270 |
-
/**
|
271 |
-
* Compile PDF file with mPDF library
|
272 |
-
*
|
273 |
-
* @since 0.1
|
274 |
-
*/
|
275 |
-
public function export_mpdf() {
|
276 |
-
|
277 |
-
if ( !extension_loaded( 'mbstring' ) ) {
|
278 |
-
$rtb_settings_link = '<a href="' . admin_url( 'admin.php?page=rtb-settings&tab=rtb-export' ) . '">' . _x( 'export settings', 'Name of a link to the Export tab on the settings page', 'restaurant-reservations' ) . '</a>';
|
279 |
-
wp_die( sprintf( __( 'Your server has not loaded the mbstring PHP extension, or has disabled the mbregex PHP extension. mPDF requires both to output a PDF file. Please contact your website host and ask them to enable these PHP extensions. Or switch to the TCPDF library, which has fewer server requirements. You can change the PDF Renderer in the %s.', 'restaurant-reservations' ), $rtb_settings_link ) );
|
280 |
-
}
|
281 |
-
|
282 |
-
// Load mPDF library
|
283 |
-
require_once( RTB_PLUGIN_DIR . '/lib/mpdf/vendor/autoload.php' );
|
284 |
-
|
285 |
-
$mpdf = new \Mpdf\Mpdf( [], $this->paper_size, '', '', 15, 15, 25 );
|
286 |
-
$mpdf = $this->set_doc_info( $mpdf );
|
287 |
-
|
288 |
-
// Support languages automatically
|
289 |
-
$mpdf->autoScriptToLang = true;
|
290 |
-
$mpdf->baseScript = 1;
|
291 |
-
$mpdf->autoVietnamese = true;
|
292 |
-
$mpdf->autoArabic = true;
|
293 |
-
$mpdf->autoLangToFont = true;
|
294 |
-
|
295 |
-
// Generate the bookings HTML
|
296 |
-
ob_start();
|
297 |
-
$this->include_template( 'mpdf.php' );
|
298 |
-
$html = ob_get_clean();
|
299 |
-
|
300 |
-
$mpdf->WriteHTML($html);
|
301 |
-
|
302 |
-
$this->export = $mpdf;
|
303 |
-
|
304 |
-
return $this->export;
|
305 |
-
}
|
306 |
-
|
307 |
-
/**
|
308 |
-
* Add custom fields to mPDF output
|
309 |
-
*
|
310 |
-
* @since 2.0
|
311 |
-
*/
|
312 |
-
public function add_custom_fields_to_mpdf( $booking ) {
|
313 |
-
global $rtb_controller;
|
314 |
-
|
315 |
-
$fields = rtb_get_custom_fields();
|
316 |
-
$cf = isset( $booking->custom_fields ) ? $booking->custom_fields : array();
|
317 |
-
|
318 |
-
foreach( $fields as $field ) {
|
319 |
-
|
320 |
-
if ( $field->type == 'fieldset' || !isset( $cf[ $field->slug ] ) ) {
|
321 |
-
continue;
|
322 |
-
}
|
323 |
-
|
324 |
-
$val = $cf[ $field->slug ];
|
325 |
-
$display_val = apply_filters( 'cffrtb_display_value_mpdf', $rtb_controller->fields->get_display_value( $val, $field, '', true ), $val, $field, $booking );
|
326 |
-
|
327 |
-
?>
|
328 |
-
|
329 |
-
<p class="custom-field <?php echo esc_attr( $field->type . ' ' . $field->slug ); ?>">
|
330 |
-
<span class="label"><?php echo esc_html( $field->title ) . esc_html_x( ': ', 'Appears after field label in mPDF exports', 'custom-fields-for-rtb' ); ?></span>
|
331 |
-
<?php echo $display_val ?>
|
332 |
-
</p>
|
333 |
-
|
334 |
-
<?php
|
335 |
-
}
|
336 |
-
}
|
337 |
-
|
338 |
-
/**
|
339 |
-
* Calcualte the reservation and guests number by date
|
340 |
-
*
|
341 |
-
* @since 2.5.2
|
342 |
-
*/
|
343 |
-
public function create_bookings_summary() {
|
344 |
-
global $rtb_controller;
|
345 |
-
|
346 |
-
foreach( $this->bookings as $booking ) {
|
347 |
-
|
348 |
-
$booking_date_idx = mysql2date( 'Y-m-d', $booking->date );
|
349 |
-
|
350 |
-
if ( ! isset( $this->bookings_summary[ $booking_date_idx ] ) ) {
|
351 |
-
|
352 |
-
$this->bookings_summary[ $booking_date_idx ] = array(
|
353 |
-
'reservations' => 0,
|
354 |
-
'seats' => 0
|
355 |
-
);
|
356 |
-
}
|
357 |
-
|
358 |
-
$this->bookings_summary[ $booking_date_idx ]['reservations']++;
|
359 |
-
|
360 |
-
$this->bookings_summary[ $booking_date_idx ]['seats'] += $booking->party;
|
361 |
-
}
|
362 |
-
}
|
363 |
-
|
364 |
-
}
|
365 |
-
} // endif
|
1 |
+
<?php
|
2 |
+
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
+
|
4 |
+
if ( !class_exists( 'ebfrtbExportPDF' ) ) {
|
5 |
+
/**
|
6 |
+
* Handle PDF exports
|
7 |
+
*
|
8 |
+
* mPDF or TCPDF is used to render the PDFs.
|
9 |
+
*
|
10 |
+
* mPDF has better HTML/CSS support, but may not
|
11 |
+
* be compatible with all servers.
|
12 |
+
*
|
13 |
+
* http://www.mpdf1.com/
|
14 |
+
*
|
15 |
+
* TCPDF should have better server compatiblity,
|
16 |
+
* so it is a fallback in case the main renderer
|
17 |
+
* is not compatible.
|
18 |
+
*
|
19 |
+
* http://www.tcpdf.org/
|
20 |
+
*
|
21 |
+
* @since 1.4.1
|
22 |
+
*/
|
23 |
+
class ebfrtbExportPDF extends ebfrtbExport {
|
24 |
+
|
25 |
+
/**
|
26 |
+
* PDF Rendering Library
|
27 |
+
*
|
28 |
+
* @since 0.1
|
29 |
+
*/
|
30 |
+
public $lib;
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Paper size to use
|
34 |
+
*
|
35 |
+
* @since 0.1
|
36 |
+
*/
|
37 |
+
public $paper_size;
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Arguments for the query used to fetch
|
41 |
+
* bookings for this export
|
42 |
+
*
|
43 |
+
* @since 0.1
|
44 |
+
*/
|
45 |
+
public $query_args;
|
46 |
+
|
47 |
+
/**
|
48 |
+
* The reservation and guest numbers by date
|
49 |
+
*
|
50 |
+
* @since 0.1
|
51 |
+
*/
|
52 |
+
public $bookings_summary = array();
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Insantiate the PDF export
|
56 |
+
*
|
57 |
+
* @since 0.1
|
58 |
+
*/
|
59 |
+
public function __construct( $bookings, $args = array() ) {
|
60 |
+
|
61 |
+
$this->bookings = $bookings;
|
62 |
+
|
63 |
+
global $rtb_controller;
|
64 |
+
$this->lib = !empty( $args['pdf-lib'] ) ? sanitize_key( $args['pdf-lib'] ) : $rtb_controller->settings->get_setting( 'ebfrtb-pdf-lib' );
|
65 |
+
|
66 |
+
// Get paper size
|
67 |
+
$this->paper_size = !empty( $args['paper-size'] ) ? sanitize_key( $args['paper-size'] ) : $rtb_controller->settings->get_setting( 'ebfrtb-paper-size' );
|
68 |
+
|
69 |
+
// Query arguments
|
70 |
+
if ( !empty( $args['query_args'] ) ) {
|
71 |
+
$this->query_args = $args['query_args'];
|
72 |
+
}
|
73 |
+
|
74 |
+
// Maybe add table hooks
|
75 |
+
add_filter( 'ebfrtb_mpdf_after_details', array($this, 'add_booking_table_to_pdf' ) );
|
76 |
+
add_filter( 'ebfrtb_tcpdf_after_details', array($this, 'add_booking_table_to_pdf' ) );
|
77 |
+
|
78 |
+
// Add custom field hooks
|
79 |
+
add_filter( 'ebfrtb_mpdf_after_details', array($this, 'add_custom_fields_to_mpdf' ) );
|
80 |
+
add_filter( 'ebfrtb_tcpdf_after_details', array($this, 'add_custom_fields_to_tcpdf' ) );
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* Set document information
|
85 |
+
*
|
86 |
+
* Sets document meta data like title, creator, etc. Both
|
87 |
+
* TCPDF and mPDF use the same methods for this.
|
88 |
+
*
|
89 |
+
* @since 0.1
|
90 |
+
*/
|
91 |
+
public function set_doc_info( $pdf ) {
|
92 |
+
|
93 |
+
$pdf->SetCreator( get_bloginfo( 'sitename' ) );
|
94 |
+
$pdf->SetAuthor( get_bloginfo( 'sitename' ) );
|
95 |
+
$pdf->SetTitle( sprintf( _x( 'Bookings at %s', 'Title of PDF documents', 'restaurant-reservations' ), get_bloginfo( 'sitename' ) ) );
|
96 |
+
$pdf->SetSubject( $this->get_date_phrase() );
|
97 |
+
|
98 |
+
return $pdf;
|
99 |
+
}
|
100 |
+
|
101 |
+
/**
|
102 |
+
* Compile the PDF file
|
103 |
+
*
|
104 |
+
* This routes to the appropriate export method
|
105 |
+
* depending on the PDF library being used.
|
106 |
+
*
|
107 |
+
* @since 0.1
|
108 |
+
*/
|
109 |
+
public function export() {
|
110 |
+
|
111 |
+
$this->create_bookings_summary();
|
112 |
+
|
113 |
+
if ( $this->lib === 'tcpdf' ) {
|
114 |
+
$this->export_tcpdf();
|
115 |
+
|
116 |
+
} elseif ( $this->lib === 'mpdf' ) {
|
117 |
+
$this->export_mpdf();
|
118 |
+
|
119 |
+
} else {
|
120 |
+
do_action( 'ebcfrtb_pdf_export_' . $this->lib, $this );
|
121 |
+
}
|
122 |
+
|
123 |
+
return $this->export;
|
124 |
+
}
|
125 |
+
|
126 |
+
/**
|
127 |
+
* Deliver the PDF to the browser
|
128 |
+
*
|
129 |
+
* @since 0.1
|
130 |
+
*/
|
131 |
+
public function deliver() {
|
132 |
+
|
133 |
+
// Generate the export if it's not been done yet
|
134 |
+
if ( empty( $this->export ) ) {
|
135 |
+
$this->export();
|
136 |
+
}
|
137 |
+
|
138 |
+
$filename = apply_filters( 'ebfrtb_export_pdf_filename', sanitize_file_name( $this->get_date_phrase() ) . '.pdf' );
|
139 |
+
|
140 |
+
// Clean any stray errors, warnings or notices that may have been
|
141 |
+
// printed to the buffer
|
142 |
+
ob_get_clean();
|
143 |
+
|
144 |
+
if ( $this->lib === 'tcpdf' || $this->lib === 'mpdf' ) {
|
145 |
+
$this->export->Output( $filename, 'I');
|
146 |
+
exit();
|
147 |
+
|
148 |
+
} else {
|
149 |
+
do_action( 'ebcfrtb_pdf_deliver_' . $this->lib, $this );
|
150 |
+
}
|
151 |
+
|
152 |
+
// Just in case we forget...
|
153 |
+
wp_die( __( 'An unexpected error occurred and your export request could not be fulfilled.', 'restaurant-reservations' ) );
|
154 |
+
}
|
155 |
+
|
156 |
+
/**
|
157 |
+
* Compile PDF file with TCPDF library
|
158 |
+
*
|
159 |
+
* @since 0.1
|
160 |
+
*/
|
161 |
+
public function export_tcpdf() {
|
162 |
+
|
163 |
+
// Load TCPDF library
|
164 |
+
require_once( RTB_PLUGIN_DIR . '/lib/tcpdf/config/tcpdf_config.php' );
|
165 |
+
require_once( RTB_PLUGIN_DIR . '/lib/tcpdf/tcpdf.php' );
|
166 |
+
|
167 |
+
// TCPDF uses a different identifier for U.S. Letter format
|
168 |
+
if ( $this->paper_size === 'LETTER' ) {
|
169 |
+
$this->paper_size = 'ANSI_A';
|
170 |
+
}
|
171 |
+
|
172 |
+
$tcpdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, $this->paper_size, true, 'UTF-8', false);
|
173 |
+
$tcpdf = $this->set_doc_info( $tcpdf );
|
174 |
+
|
175 |
+
// set default header data
|
176 |
+
$header_title = get_bloginfo( 'sitename' );
|
177 |
+
if ( !empty( $this->query_args['location'] ) ) {
|
178 |
+
$term = get_term( $this->query_args['location'] );
|
179 |
+
if ( is_a( $term, 'WP_Term' ) ) {
|
180 |
+
$header_title = $term->name;
|
181 |
+
}
|
182 |
+
}
|
183 |
+
$tcpdf->SetHeaderData('', '', $header_title, $this->get_date_phrase(), array( 117, 117, 117 ), array( 117, 117, 117 ) );
|
184 |
+
$tcpdf->setFooterData(array(0,64,0), array(0,64,128));
|
185 |
+
|
186 |
+
// set header and footer fonts
|
187 |
+
$tcpdf->setHeaderFont(Array(PDF_FONT_NAME_MAIN, '', PDF_FONT_SIZE_MAIN));
|
188 |
+
$tcpdf->setFooterFont(Array(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA));
|
189 |
+
|
190 |
+
// set margins
|
191 |
+
$tcpdf->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT);
|
192 |
+
$tcpdf->SetHeaderMargin(PDF_MARGIN_HEADER);
|
193 |
+
$tcpdf->SetFooterMargin(PDF_MARGIN_FOOTER);
|
194 |
+
|
195 |
+
// set auto page breaks
|
196 |
+
$tcpdf->SetAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM);
|
197 |
+
|
198 |
+
// set image scale factor
|
199 |
+
$tcpdf->setImageScale(PDF_IMAGE_SCALE_RATIO);
|
200 |
+
|
201 |
+
// Subset fonts for smaller doc sizes
|
202 |
+
$tcpdf->setFontSubsetting(true);
|
203 |
+
|
204 |
+
// Add a page
|
205 |
+
$tcpdf->AddPage();
|
206 |
+
$tcpdf->SetCellPadding(0);
|
207 |
+
|
208 |
+
// Generate the bookings HTML
|
209 |
+
ob_start();
|
210 |
+
$this->include_template( 'tcpdf.php' );
|
211 |
+
$html = ob_get_clean();
|
212 |
+
|
213 |
+
// Print HTML
|
214 |
+
$tcpdf->writeHTML($html, false, true, false, false, '');
|
215 |
+
|
216 |
+
$this->export = $tcpdf;
|
217 |
+
|
218 |
+
return $this->export;
|
219 |
+
}
|
220 |
+
|
221 |
+
/**
|
222 |
+
*Maybe add the table for a booking to PDF output
|
223 |
+
*
|
224 |
+
* @since 2.2
|
225 |
+
*/
|
226 |
+
public function add_booking_table_to_pdf( $booking ) {
|
227 |
+
global $rtb_controller;
|
228 |
+
|
229 |
+
if ( $rtb_controller->settings->get_setting( 'enable-tables' ) ) { ?>
|
230 |
+
|
231 |
+
<p class="booking-table">
|
232 |
+
<span class="label"><?php echo __( 'Table(s)' ) . esc_html_x( ': ', 'Appears after table label in PDF exports', 'custom-fields-for-rtb' ); ?></span>
|
233 |
+
<?php echo implode( ',', $booking->table ); ?>
|
234 |
+
</p>
|
235 |
+
|
236 |
+
<?php }
|
237 |
+
}
|
238 |
+
|
239 |
+
/**
|
240 |
+
* Add custom fields to TCPDF output
|
241 |
+
*
|
242 |
+
* @since 2.0
|
243 |
+
*/
|
244 |
+
public function add_custom_fields_to_tcpdf( $booking ) {
|
245 |
+
global $rtb_controller;
|
246 |
+
|
247 |
+
$fields = rtb_get_custom_fields();
|
248 |
+
$cf = isset( $booking->custom_fields ) ? $booking->custom_fields : array();
|
249 |
+
|
250 |
+
foreach( $fields as $field ) {
|
251 |
+
|
252 |
+
if ( $field->type == 'fieldset' || !isset( $cf[ $field->slug ] ) ) {
|
253 |
+
continue;
|
254 |
+
}
|
255 |
+
|
256 |
+
$val = $cf[ $field->slug ];
|
257 |
+
$display_val = apply_filters( 'cffrtb_display_value_tcpdf', $rtb_controller->fields->get_display_value( $val, $field, '', true ), $val, $field, $booking );
|
258 |
+
|
259 |
+
?>
|
260 |
+
|
261 |
+
<div class="custom-field <?php echo esc_attr( $field->type . ' ' . $field->slug ); ?>">
|
262 |
+
<?php echo esc_html( $field->title ) . esc_html_x( ': ', 'Appears after field label in TCPDF exports', 'custom-fields-for-rtb' ); ?>
|
263 |
+
<?php echo $display_val ?>
|
264 |
+
</div>
|
265 |
+
|
266 |
+
<?php
|
267 |
+
}
|
268 |
+
}
|
269 |
+
|
270 |
+
/**
|
271 |
+
* Compile PDF file with mPDF library
|
272 |
+
*
|
273 |
+
* @since 0.1
|
274 |
+
*/
|
275 |
+
public function export_mpdf() {
|
276 |
+
|
277 |
+
if ( !extension_loaded( 'mbstring' ) ) {
|
278 |
+
$rtb_settings_link = '<a href="' . admin_url( 'admin.php?page=rtb-settings&tab=rtb-export' ) . '">' . _x( 'export settings', 'Name of a link to the Export tab on the settings page', 'restaurant-reservations' ) . '</a>';
|
279 |
+
wp_die( sprintf( __( 'Your server has not loaded the mbstring PHP extension, or has disabled the mbregex PHP extension. mPDF requires both to output a PDF file. Please contact your website host and ask them to enable these PHP extensions. Or switch to the TCPDF library, which has fewer server requirements. You can change the PDF Renderer in the %s.', 'restaurant-reservations' ), $rtb_settings_link ) );
|
280 |
+
}
|
281 |
+
|
282 |
+
// Load mPDF library
|
283 |
+
require_once( RTB_PLUGIN_DIR . '/lib/mpdf/vendor/autoload.php' );
|
284 |
+
|
285 |
+
$mpdf = new \Mpdf\Mpdf( [], $this->paper_size, '', '', 15, 15, 25 );
|
286 |
+
$mpdf = $this->set_doc_info( $mpdf );
|
287 |
+
|
288 |
+
// Support languages automatically
|
289 |
+
$mpdf->autoScriptToLang = true;
|
290 |
+
$mpdf->baseScript = 1;
|
291 |
+
$mpdf->autoVietnamese = true;
|
292 |
+
$mpdf->autoArabic = true;
|
293 |
+
$mpdf->autoLangToFont = true;
|
294 |
+
|
295 |
+
// Generate the bookings HTML
|
296 |
+
ob_start();
|
297 |
+
$this->include_template( 'mpdf.php' );
|
298 |
+
$html = ob_get_clean();
|
299 |
+
|
300 |
+
$mpdf->WriteHTML($html);
|
301 |
+
|
302 |
+
$this->export = $mpdf;
|
303 |
+
|
304 |
+
return $this->export;
|
305 |
+
}
|
306 |
+
|
307 |
+
/**
|
308 |
+
* Add custom fields to mPDF output
|
309 |
+
*
|
310 |
+
* @since 2.0
|
311 |
+
*/
|
312 |
+
public function add_custom_fields_to_mpdf( $booking ) {
|
313 |
+
global $rtb_controller;
|
314 |
+
|
315 |
+
$fields = rtb_get_custom_fields();
|
316 |
+
$cf = isset( $booking->custom_fields ) ? $booking->custom_fields : array();
|
317 |
+
|
318 |
+
foreach( $fields as $field ) {
|
319 |
+
|
320 |
+
if ( $field->type == 'fieldset' || !isset( $cf[ $field->slug ] ) ) {
|
321 |
+
continue;
|
322 |
+
}
|
323 |
+
|
324 |
+
$val = $cf[ $field->slug ];
|
325 |
+
$display_val = apply_filters( 'cffrtb_display_value_mpdf', $rtb_controller->fields->get_display_value( $val, $field, '', true ), $val, $field, $booking );
|
326 |
+
|
327 |
+
?>
|
328 |
+
|
329 |
+
<p class="custom-field <?php echo esc_attr( $field->type . ' ' . $field->slug ); ?>">
|
330 |
+
<span class="label"><?php echo esc_html( $field->title ) . esc_html_x( ': ', 'Appears after field label in mPDF exports', 'custom-fields-for-rtb' ); ?></span>
|
331 |
+
<?php echo $display_val ?>
|
332 |
+
</p>
|
333 |
+
|
334 |
+
<?php
|
335 |
+
}
|
336 |
+
}
|
337 |
+
|
338 |
+
/**
|
339 |
+
* Calcualte the reservation and guests number by date
|
340 |
+
*
|
341 |
+
* @since 2.5.2
|
342 |
+
*/
|
343 |
+
public function create_bookings_summary() {
|
344 |
+
global $rtb_controller;
|
345 |
+
|
346 |
+
foreach( $this->bookings as $booking ) {
|
347 |
+
|
348 |
+
$booking_date_idx = mysql2date( 'Y-m-d', $booking->date );
|
349 |
+
|
350 |
+
if ( ! isset( $this->bookings_summary[ $booking_date_idx ] ) ) {
|
351 |
+
|
352 |
+
$this->bookings_summary[ $booking_date_idx ] = array(
|
353 |
+
'reservations' => 0,
|
354 |
+
'seats' => 0
|
355 |
+
);
|
356 |
+
}
|
357 |
+
|
358 |
+
$this->bookings_summary[ $booking_date_idx ]['reservations']++;
|
359 |
+
|
360 |
+
$this->bookings_summary[ $booking_date_idx ]['seats'] += $booking->party;
|
361 |
+
}
|
362 |
+
}
|
363 |
+
|
364 |
+
}
|
365 |
+
} // endif
|
includes/ExportHandler.class.php
CHANGED
@@ -1,309 +1,309 @@
|
|
1 |
-
<?php
|
2 |
-
if ( ! defined( 'ABSPATH' ) )
|
3 |
-
exit;
|
4 |
-
|
5 |
-
if ( !class_exists( 'rtbExportHandler' ) ) {
|
6 |
-
class rtbExportHandler {
|
7 |
-
|
8 |
-
/**
|
9 |
-
* Registered exports
|
10 |
-
*
|
11 |
-
* @since 0.1
|
12 |
-
*/
|
13 |
-
public $export_types;
|
14 |
-
|
15 |
-
|
16 |
-
/**
|
17 |
-
* Initialize the class and register hooks
|
18 |
-
*/
|
19 |
-
public function __construct() {
|
20 |
-
|
21 |
-
// Admin notices
|
22 |
-
add_action( 'admin_notices', array( $this, 'make_mpdf_dir_writable' ) );
|
23 |
-
|
24 |
-
// Register available exports
|
25 |
-
add_action( 'admin_init', array( $this, 'register_exports' ) );
|
26 |
-
|
27 |
-
// Load an export file
|
28 |
-
add_action( 'admin_init', array( $this, 'load_export' ) );
|
29 |
-
|
30 |
-
// Load bookings page and assets
|
31 |
-
add_action( 'rtb_bookings_table_actions', array( $this, 'print_button' ) );
|
32 |
-
add_action( 'admin_footer-toplevel_page_rtb-bookings', array( $this, 'print_export_options_modal' ), 9 );
|
33 |
-
|
34 |
-
}
|
35 |
-
|
36 |
-
/**
|
37 |
-
* Add an admin notice if the font directory for mpdf is not
|
38 |
-
* writeable
|
39 |
-
*
|
40 |
-
* @since 0.1
|
41 |
-
*/
|
42 |
-
public function make_mpdf_dir_writable() {
|
43 |
-
|
44 |
-
global $rtb_controller;
|
45 |
-
if ( empty( $rtb_controller ) or ! $rtb_controller->permissions->check_permission( 'export' ) ) {
|
46 |
-
return;
|
47 |
-
}
|
48 |
-
|
49 |
-
// Only trigger a warning when using the mpdf library
|
50 |
-
if ( $rtb_controller->settings->get_setting( 'ebfrtb-pdf-lib' ) !== 'mpdf' ) {
|
51 |
-
return;
|
52 |
-
}
|
53 |
-
|
54 |
-
|
55 |
-
// No warning needed if the directory is writable
|
56 |
-
if ( wp_is_writable( RTB_PLUGIN_DIR . '/lib/mpdf/vendor/mpdf/mpdf/tmp/ttfontdata/' ) ) {
|
57 |
-
return;
|
58 |
-
}
|
59 |
-
|
60 |
-
|
61 |
-
$rtb_settings_link = '<a href="' . admin_url( 'admin.php?page=rtb-settings&tab=rtb-export' ) . '">' . _x( 'export settings', 'Name of a link to the Export tab on the settings page', 'restaurant-reservations' ) . '</a>';
|
62 |
-
?>
|
63 |
-
|
64 |
-
<div class="error">
|
65 |
-
<p>
|
66 |
-
<?php printf( __( 'Warning from Export Bookings for Restaurant Reservations: The server is not able to write to the font directory for the mPDF generator. Your PDF exports may not work properly until you change the file permissions for the directory /wp-content/plugins/export-for-rtb/lib/mpdf/ttfontdata/. Your web host can help you change the file permissions to be compatible. Or you can switch to the TCPDF renderer in the %s.', 'restaurant-reservations' ), $rtb_settings_link ); ?>
|
67 |
-
</p>
|
68 |
-
</div>
|
69 |
-
|
70 |
-
<?php
|
71 |
-
}
|
72 |
-
|
73 |
-
/**
|
74 |
-
* Register supported exports
|
75 |
-
*
|
76 |
-
* @since 0.1
|
77 |
-
*/
|
78 |
-
public function register_exports() {
|
79 |
-
|
80 |
-
// Load the export classes
|
81 |
-
require_once( RTB_PLUGIN_DIR . '/includes/Export.class.php' );
|
82 |
-
require_once( RTB_PLUGIN_DIR . '/includes/Export.PDF.class.php' );
|
83 |
-
require_once( RTB_PLUGIN_DIR . '/includes/Export.CSV.class.php' );
|
84 |
-
|
85 |
-
// Array of export types and the class which should be used
|
86 |
-
// to generate them. All classes should extend ebfrtbExport
|
87 |
-
// @todo Excel
|
88 |
-
$this->export_types = apply_filters(
|
89 |
-
'ebfrtb_export_types',
|
90 |
-
array(
|
91 |
-
'pdf' => array(
|
92 |
-
'label' => __( 'PDF', 'restaurant-reservations' ),
|
93 |
-
'class' => 'ebfrtbExportPDF',
|
94 |
-
),
|
95 |
-
'csv' => array(
|
96 |
-
'label' => __( 'Excel/CSV', 'restaurant-reservations' ),
|
97 |
-
'class' => 'ebfrtbExportCSV',
|
98 |
-
),
|
99 |
-
)
|
100 |
-
);
|
101 |
-
|
102 |
-
}
|
103 |
-
|
104 |
-
/**
|
105 |
-
* Load the requested export
|
106 |
-
*
|
107 |
-
* @since 0.1
|
108 |
-
*/
|
109 |
-
public function load_export() {
|
110 |
-
global $rtb_controller;
|
111 |
-
|
112 |
-
if (! $rtb_controller->permissions->check_permission( 'export' ) ) { return; }
|
113 |
-
|
114 |
-
if ( !isset( $_GET['action'] ) || $_GET['action'] !== 'ebfrtb-export' ) {
|
115 |
-
return;
|
116 |
-
}
|
117 |
-
|
118 |
-
if ( !current_user_can( 'manage_bookings' ) ) {
|
119 |
-
wp_die( __( 'You do not have the required permissions to export bookings.', 'restaurant-reservations' ) );
|
120 |
-
}
|
121 |
-
|
122 |
-
if ( isset( $_GET['type'] ) && !empty( $this->export_types[ $_GET['type'] ] ) ) {
|
123 |
-
$export_class = $this->export_types[ $_GET['type'] ]['class'];
|
124 |
-
}
|
125 |
-
|
126 |
-
if ( !isset( $export_class ) || !class_exists( $export_class ) ) {
|
127 |
-
wp_die( __( 'Unable to create export to match your request.', 'restaurant-reservations' ) );
|
128 |
-
}
|
129 |
-
|
130 |
-
// Prepare query args
|
131 |
-
$args = array(
|
132 |
-
'orderby' => 'date',
|
133 |
-
'order' => 'ASC',
|
134 |
-
);
|
135 |
-
|
136 |
-
$query = new rtbQuery( $args, 'export' );
|
137 |
-
$query->parse_request_args();
|
138 |
-
$query->prepare_args();
|
139 |
-
$query->args['posts_per_page'] = -1;
|
140 |
-
|
141 |
-
// Show an error if they forgot to enter dates
|
142 |
-
if ( isset( $query->args['date_range'] ) && $query->args['date_range'] == 'dates' && !isset( $query->args['start_date'] ) && empty( $query->args['end_date'] ) ) {
|
143 |
-
wp_die( __( "You selected a date range but didn't enter a start or end date. Please return and enter a start or end date.", 'restaurant-reservations' ) );
|
144 |
-
}
|
145 |
-
|
146 |
-
// Retrieve bookings
|
147 |
-
$bookings = $query->get_bookings();
|
148 |
-
|
149 |
-
if ( empty( $bookings ) ) {
|
150 |
-
wp_die( __( 'There are no bookings which match your export request.', 'restaurant-reservations' ) );
|
151 |
-
}
|
152 |
-
|
153 |
-
$export = new $export_class( $bookings, array( 'query_args' => $query->args ) );
|
154 |
-
$export->deliver(); // calls wp_die()
|
155 |
-
}
|
156 |
-
|
157 |
-
/**
|
158 |
-
* Print the export button above and below the table
|
159 |
-
*
|
160 |
-
* @since 0.1.0
|
161 |
-
*/
|
162 |
-
public function print_button( $pos ) {
|
163 |
-
global $rtb_controller;
|
164 |
-
|
165 |
-
if (! $rtb_controller->permissions->check_permission( 'export' ) ) { return; }
|
166 |
-
|
167 |
-
?>
|
168 |
-
|
169 |
-
<div class="alignleft actions ebfrtb-actions">
|
170 |
-
<a href="#" class="button ebfrtb-export-button">
|
171 |
-
<span class="dashicons dashicons-media-spreadsheet"></span>
|
172 |
-
<?php esc_html_e( 'Export Bookings', 'restaurant-reservations' ); ?>
|
173 |
-
</a>
|
174 |
-
</div>
|
175 |
-
|
176 |
-
<?php
|
177 |
-
}
|
178 |
-
|
179 |
-
/**
|
180 |
-
* Print the export options modal in the footer
|
181 |
-
* of the bookings page
|
182 |
-
*
|
183 |
-
* @since 0.1
|
184 |
-
*/
|
185 |
-
public function print_export_options_modal() {
|
186 |
-
|
187 |
-
global $rtb_controller;
|
188 |
-
|
189 |
-
if (! $rtb_controller->permissions->check_permission( 'export' ) ) { return; }
|
190 |
-
?>
|
191 |
-
|
192 |
-
<!-- Export bookings options modal -->
|
193 |
-
<div id="ebfrtb-options-modal" class="rtb-admin-modal">
|
194 |
-
<div class="ebfrtb-options-form rtb-container">
|
195 |
-
<form>
|
196 |
-
|
197 |
-
<div class="title">
|
198 |
-
<h2>
|
199 |
-
<?php esc_html_e( 'Export Bookings', 'restaurant-reservations' ); ?>
|
200 |
-
</h2>
|
201 |
-
</div>
|
202 |
-
|
203 |
-
<fieldset>
|
204 |
-
<div class="type">
|
205 |
-
<label for="type" class="hidden-label">
|
206 |
-
<?php esc_html_e( 'Type', 'restaurant-reservations' ); ?>
|
207 |
-
</label>
|
208 |
-
<select name="type">
|
209 |
-
<?php foreach( $this->export_types as $type => $export ) : ?>
|
210 |
-
<option value="<?php echo esc_attr( $type ); ?>">
|
211 |
-
<?php echo esc_html( $export['label'] ); ?>
|
212 |
-
</option>
|
213 |
-
<?php endforeach; ?>
|
214 |
-
</select>
|
215 |
-
</div>
|
216 |
-
|
217 |
-
<?php if ( !empty( $rtb_controller->locations ) && !empty( $rtb_controller->locations->post_type ) ) : ?>
|
218 |
-
<div class="location">
|
219 |
-
<label for="location" class="hidden-label">
|
220 |
-
<?php esc_html_e( 'Location', 'restaurant-reservations' ); ?>
|
221 |
-
</label>
|
222 |
-
<select name="location">
|
223 |
-
<option value=""><?php esc_html_e( 'All locations', 'restaurant-reservations' ); ?></option>
|
224 |
-
<?php
|
225 |
-
$locations = $rtb_controller->locations->get_location_options();
|
226 |
-
foreach( $locations as $id => $name ) :
|
227 |
-
?>
|
228 |
-
<option value="<?php echo absint( $id ); ?>"><?php echo esc_attr( $name ); ?></option>
|
229 |
-
<?php
|
230 |
-
endforeach;
|
231 |
-
?>
|
232 |
-
</select>
|
233 |
-
</div>
|
234 |
-
<?php endif; ?>
|
235 |
-
|
236 |
-
<div class="date-range">
|
237 |
-
<input type="hidden" name="date_range">
|
238 |
-
<label for="date-range" class="hidden-label">
|
239 |
-
<?php esc_html_e( 'Date Range', 'restaurant-reservations' ); ?>
|
240 |
-
</label>
|
241 |
-
<div class="selector">
|
242 |
-
<ul class="options">
|
243 |
-
<li>
|
244 |
-
<a href="#" data-type="today">
|
245 |
-
<?php esc_html_e( 'Today', 'restaurant-reservations' ); ?>
|
246 |
-
</a>
|
247 |
-
</li>
|
248 |
-
<li>
|
249 |
-
<a href="#" data-type="upcoming">
|
250 |
-
<?php esc_html_e( 'Upcoming', 'restaurant-reservations' ); ?>
|
251 |
-
</a>
|
252 |
-
</li>
|
253 |
-
<li>
|
254 |
-
<a href="#" data-type="dates">
|
255 |
-
<?php esc_html_e( 'Date Range', 'restaurant-reservations' ); ?>
|
256 |
-
</a>
|
257 |
-
</li>
|
258 |
-
</ul>
|
259 |
-
<div class="today">
|
260 |
-
<?php esc_html_e( "Today's bookings", 'restaurant-reservations' ); ?>
|
261 |
-
</div>
|
262 |
-
<div class="upcoming">
|
263 |
-
<?php esc_html_e( 'All upcoming bookings', 'restaurant-reservations' ); ?>
|
264 |
-
</div>
|
265 |
-
<div class="dates">
|
266 |
-
<div class="date-start">
|
267 |
-
<label for="ebfrtb-start-date">
|
268 |
-
<?php esc_html_e( 'Start', 'restaurant-reservations' ); ?>
|
269 |
-
</label>
|
270 |
-
<input type="text" name="start_date" id="ebfrtb-start-date" value="<?php echo ! empty( $_GET['start_date'] ) ? esc_attr( $_GET['start_date'] ) : ''; ?>">
|
271 |
-
</div>
|
272 |
-
<div class="date-end">
|
273 |
-
<label for="ebfrtb-end-date">
|
274 |
-
<?php esc_html_e( 'End', 'restaurant-reservations' ); ?>
|
275 |
-
</label>
|
276 |
-
<input type="text" name="end_date" id="ebfrtb-end-date" value="<?php echo ! empty( $_GET['end_date'] ) ? esc_attr( $_GET['end_date'] ) : ''; ?>">
|
277 |
-
</div>
|
278 |
-
</div>
|
279 |
-
</div>
|
280 |
-
</div>
|
281 |
-
|
282 |
-
<div class="status">
|
283 |
-
<?php foreach( $rtb_controller->cpts->booking_statuses as $key => $status ) : ?>
|
284 |
-
<label>
|
285 |
-
<input type="checkbox" name="status" value="<?php echo esc_attr( $key ); ?>" <?php checked( $key, 'confirmed' ); ?>>
|
286 |
-
<?php echo esc_html( $status['label'] ); ?>
|
287 |
-
</label>
|
288 |
-
<?php endforeach; ?>
|
289 |
-
</div>
|
290 |
-
|
291 |
-
</fieldset>
|
292 |
-
<button type="submit" class="button button-primary">
|
293 |
-
<?php esc_html_e( 'Export', 'restaurant-reservations' ); ?>
|
294 |
-
</button>
|
295 |
-
<a href="#" class="button" id="ebfrtb-cancel-export-modal">
|
296 |
-
<?php esc_html_e( 'Cancel', 'restaurant-reservations' ); ?>
|
297 |
-
</a>
|
298 |
-
<a href="<?php echo admin_url( 'admin.php?page=rtb-settings&tab=rtb-export-tab' ); ?>" class="settings">
|
299 |
-
<?php esc_html_e( 'Settings', 'restaurant-reservations' ); ?>
|
300 |
-
</a>
|
301 |
-
</form>
|
302 |
-
</div>
|
303 |
-
</div>
|
304 |
-
|
305 |
-
<?php
|
306 |
-
}
|
307 |
-
|
308 |
-
}
|
309 |
-
} // endif;
|
1 |
+
<?php
|
2 |
+
if ( ! defined( 'ABSPATH' ) )
|
3 |
+
exit;
|
4 |
+
|
5 |
+
if ( !class_exists( 'rtbExportHandler' ) ) {
|
6 |
+
class rtbExportHandler {
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Registered exports
|
10 |
+
*
|
11 |
+
* @since 0.1
|
12 |
+
*/
|
13 |
+
public $export_types;
|
14 |
+
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Initialize the class and register hooks
|
18 |
+
*/
|
19 |
+
public function __construct() {
|
20 |
+
|
21 |
+
// Admin notices
|
22 |
+
add_action( 'admin_notices', array( $this, 'make_mpdf_dir_writable' ) );
|
23 |
+
|
24 |
+
// Register available exports
|
25 |
+
add_action( 'admin_init', array( $this, 'register_exports' ) );
|
26 |
+
|
27 |
+
// Load an export file
|
28 |
+
add_action( 'admin_init', array( $this, 'load_export' ) );
|
29 |
+
|
30 |
+
// Load bookings page and assets
|
31 |
+
add_action( 'rtb_bookings_table_actions', array( $this, 'print_button' ) );
|
32 |
+
add_action( 'admin_footer-toplevel_page_rtb-bookings', array( $this, 'print_export_options_modal' ), 9 );
|
33 |
+
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Add an admin notice if the font directory for mpdf is not
|
38 |
+
* writeable
|
39 |
+
*
|
40 |
+
* @since 0.1
|
41 |
+
*/
|
42 |
+
public function make_mpdf_dir_writable() {
|
43 |
+
|
44 |
+
global $rtb_controller;
|
45 |
+
if ( empty( $rtb_controller ) or ! $rtb_controller->permissions->check_permission( 'export' ) ) {
|
46 |
+
return;
|
47 |
+
}
|
48 |
+
|
49 |
+
// Only trigger a warning when using the mpdf library
|
50 |
+
if ( $rtb_controller->settings->get_setting( 'ebfrtb-pdf-lib' ) !== 'mpdf' ) {
|
51 |
+
return;
|
52 |
+
}
|
53 |
+
|
54 |
+
|
55 |
+
// No warning needed if the directory is writable
|
56 |
+
if ( wp_is_writable( RTB_PLUGIN_DIR . '/lib/mpdf/vendor/mpdf/mpdf/tmp/ttfontdata/' ) ) {
|
57 |
+
return;
|
58 |
+
}
|
59 |
+
|
60 |
+
|
61 |
+
$rtb_settings_link = '<a href="' . admin_url( 'admin.php?page=rtb-settings&tab=rtb-export' ) . '">' . _x( 'export settings', 'Name of a link to the Export tab on the settings page', 'restaurant-reservations' ) . '</a>';
|
62 |
+
?>
|
63 |
+
|
64 |
+
<div class="error">
|
65 |
+
<p>
|
66 |
+
<?php printf( __( 'Warning from Export Bookings for Restaurant Reservations: The server is not able to write to the font directory for the mPDF generator. Your PDF exports may not work properly until you change the file permissions for the directory /wp-content/plugins/export-for-rtb/lib/mpdf/ttfontdata/. Your web host can help you change the file permissions to be compatible. Or you can switch to the TCPDF renderer in the %s.', 'restaurant-reservations' ), $rtb_settings_link ); ?>
|
67 |
+
</p>
|
68 |
+
</div>
|
69 |
+
|
70 |
+
<?php
|
71 |
+
}
|
72 |
+
|
73 |
+
/**
|
74 |
+
* Register supported exports
|
75 |
+
*
|
76 |
+
* @since 0.1
|
77 |
+
*/
|
78 |
+
public function register_exports() {
|
79 |
+
|
80 |
+
// Load the export classes
|
81 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Export.class.php' );
|
82 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Export.PDF.class.php' );
|
83 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Export.CSV.class.php' );
|
84 |
+
|
85 |
+
// Array of export types and the class which should be used
|
86 |
+
// to generate them. All classes should extend ebfrtbExport
|
87 |
+
// @todo Excel
|
88 |
+
$this->export_types = apply_filters(
|
89 |
+
'ebfrtb_export_types',
|
90 |
+
array(
|
91 |
+
'pdf' => array(
|
92 |
+
'label' => __( 'PDF', 'restaurant-reservations' ),
|
93 |
+
'class' => 'ebfrtbExportPDF',
|
94 |
+
),
|
95 |
+
'csv' => array(
|
96 |
+
'label' => __( 'Excel/CSV', 'restaurant-reservations' ),
|
97 |
+
'class' => 'ebfrtbExportCSV',
|
98 |
+
),
|
99 |
+
)
|
100 |
+
);
|
101 |
+
|
102 |
+
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Load the requested export
|
106 |
+
*
|
107 |
+
* @since 0.1
|
108 |
+
*/
|
109 |
+
public function load_export() {
|
110 |
+
global $rtb_controller;
|
111 |
+
|
112 |
+
if (! $rtb_controller->permissions->check_permission( 'export' ) ) { return; }
|
113 |
+
|
114 |
+
if ( !isset( $_GET['action'] ) || $_GET['action'] !== 'ebfrtb-export' ) {
|
115 |
+
return;
|
116 |
+
}
|
117 |
+
|
118 |
+
if ( !current_user_can( 'manage_bookings' ) ) {
|
119 |
+
wp_die( __( 'You do not have the required permissions to export bookings.', 'restaurant-reservations' ) );
|
120 |
+
}
|
121 |
+
|
122 |
+
if ( isset( $_GET['type'] ) && !empty( $this->export_types[ $_GET['type'] ] ) ) {
|
123 |
+
$export_class = $this->export_types[ $_GET['type'] ]['class'];
|
124 |
+
}
|
125 |
+
|
126 |
+
if ( !isset( $export_class ) || !class_exists( $export_class ) ) {
|
127 |
+
wp_die( __( 'Unable to create export to match your request.', 'restaurant-reservations' ) );
|
128 |
+
}
|
129 |
+
|
130 |
+
// Prepare query args
|
131 |
+
$args = array(
|
132 |
+
'orderby' => 'date',
|
133 |
+
'order' => 'ASC',
|
134 |
+
);
|
135 |
+
|
136 |
+
$query = new rtbQuery( $args, 'export' );
|
137 |
+
$query->parse_request_args();
|
138 |
+
$query->prepare_args();
|
139 |
+
$query->args['posts_per_page'] = -1;
|
140 |
+
|
141 |
+
// Show an error if they forgot to enter dates
|
142 |
+
if ( isset( $query->args['date_range'] ) && $query->args['date_range'] == 'dates' && !isset( $query->args['start_date'] ) && empty( $query->args['end_date'] ) ) {
|
143 |
+
wp_die( __( "You selected a date range but didn't enter a start or end date. Please return and enter a start or end date.", 'restaurant-reservations' ) );
|
144 |
+
}
|
145 |
+
|
146 |
+
// Retrieve bookings
|
147 |
+
$bookings = $query->get_bookings();
|
148 |
+
|
149 |
+
if ( empty( $bookings ) ) {
|
150 |
+
wp_die( __( 'There are no bookings which match your export request.', 'restaurant-reservations' ) );
|
151 |
+
}
|
152 |
+
|
153 |
+
$export = new $export_class( $bookings, array( 'query_args' => $query->args ) );
|
154 |
+
$export->deliver(); // calls wp_die()
|
155 |
+
}
|
156 |
+
|
157 |
+
/**
|
158 |
+
* Print the export button above and below the table
|
159 |
+
*
|
160 |
+
* @since 0.1.0
|
161 |
+
*/
|
162 |
+
public function print_button( $pos ) {
|
163 |
+
global $rtb_controller;
|
164 |
+
|
165 |
+
if (! $rtb_controller->permissions->check_permission( 'export' ) ) { return; }
|
166 |
+
|
167 |
+
?>
|
168 |
+
|
169 |
+
<div class="alignleft actions ebfrtb-actions">
|
170 |
+
<a href="#" class="button ebfrtb-export-button">
|
171 |
+
<span class="dashicons dashicons-media-spreadsheet"></span>
|
172 |
+
<?php esc_html_e( 'Export Bookings', 'restaurant-reservations' ); ?>
|
173 |
+
</a>
|
174 |
+
</div>
|
175 |
+
|
176 |
+
<?php
|
177 |
+
}
|
178 |
+
|
179 |
+
/**
|
180 |
+
* Print the export options modal in the footer
|
181 |
+
* of the bookings page
|
182 |
+
*
|
183 |
+
* @since 0.1
|
184 |
+
*/
|
185 |
+
public function print_export_options_modal() {
|
186 |
+
|
187 |
+
global $rtb_controller;
|
188 |
+
|
189 |
+
if (! $rtb_controller->permissions->check_permission( 'export' ) ) { return; }
|
190 |
+
?>
|
191 |
+
|
192 |
+
<!-- Export bookings options modal -->
|
193 |
+
<div id="ebfrtb-options-modal" class="rtb-admin-modal">
|
194 |
+
<div class="ebfrtb-options-form rtb-container">
|
195 |
+
<form>
|
196 |
+
|
197 |
+
<div class="title">
|
198 |
+
<h2>
|
199 |
+
<?php esc_html_e( 'Export Bookings', 'restaurant-reservations' ); ?>
|
200 |
+
</h2>
|
201 |
+
</div>
|
202 |
+
|
203 |
+
<fieldset>
|
204 |
+
<div class="type">
|
205 |
+
<label for="type" class="hidden-label">
|
206 |
+
<?php esc_html_e( 'Type', 'restaurant-reservations' ); ?>
|
207 |
+
</label>
|
208 |
+
<select name="type">
|
209 |
+
<?php foreach( $this->export_types as $type => $export ) : ?>
|
210 |
+
<option value="<?php echo esc_attr( $type ); ?>">
|
211 |
+
<?php echo esc_html( $export['label'] ); ?>
|
212 |
+
</option>
|
213 |
+
<?php endforeach; ?>
|
214 |
+
</select>
|
215 |
+
</div>
|
216 |
+
|
217 |
+
<?php if ( !empty( $rtb_controller->locations ) && !empty( $rtb_controller->locations->post_type ) ) : ?>
|
218 |
+
<div class="location">
|
219 |
+
<label for="location" class="hidden-label">
|
220 |
+
<?php esc_html_e( 'Location', 'restaurant-reservations' ); ?>
|
221 |
+
</label>
|
222 |
+
<select name="location">
|
223 |
+
<option value=""><?php esc_html_e( 'All locations', 'restaurant-reservations' ); ?></option>
|
224 |
+
<?php
|
225 |
+
$locations = $rtb_controller->locations->get_location_options();
|
226 |
+
foreach( $locations as $id => $name ) :
|
227 |
+
?>
|
228 |
+
<option value="<?php echo absint( $id ); ?>"><?php echo esc_attr( $name ); ?></option>
|
229 |
+
<?php
|
230 |
+
endforeach;
|
231 |
+
?>
|
232 |
+
</select>
|
233 |
+
</div>
|
234 |
+
<?php endif; ?>
|
235 |
+
|
236 |
+
<div class="date-range">
|
237 |
+
<input type="hidden" name="date_range">
|
238 |
+
<label for="date-range" class="hidden-label">
|
239 |
+
<?php esc_html_e( 'Date Range', 'restaurant-reservations' ); ?>
|
240 |
+
</label>
|
241 |
+
<div class="selector">
|
242 |
+
<ul class="options">
|
243 |
+
<li>
|
244 |
+
<a href="#" data-type="today">
|
245 |
+
<?php esc_html_e( 'Today', 'restaurant-reservations' ); ?>
|
246 |
+
</a>
|
247 |
+
</li>
|
248 |
+
<li>
|
249 |
+
<a href="#" data-type="upcoming">
|
250 |
+
<?php esc_html_e( 'Upcoming', 'restaurant-reservations' ); ?>
|
251 |
+
</a>
|
252 |
+
</li>
|
253 |
+
<li>
|
254 |
+
<a href="#" data-type="dates">
|
255 |
+
<?php esc_html_e( 'Date Range', 'restaurant-reservations' ); ?>
|
256 |
+
</a>
|
257 |
+
</li>
|
258 |
+
</ul>
|
259 |
+
<div class="today">
|
260 |
+
<?php esc_html_e( "Today's bookings", 'restaurant-reservations' ); ?>
|
261 |
+
</div>
|
262 |
+
<div class="upcoming">
|
263 |
+
<?php esc_html_e( 'All upcoming bookings', 'restaurant-reservations' ); ?>
|
264 |
+
</div>
|
265 |
+
<div class="dates">
|
266 |
+
<div class="date-start">
|
267 |
+
<label for="ebfrtb-start-date">
|
268 |
+
<?php esc_html_e( 'Start', 'restaurant-reservations' ); ?>
|
269 |
+
</label>
|
270 |
+
<input type="text" name="start_date" id="ebfrtb-start-date" value="<?php echo ! empty( $_GET['start_date'] ) ? esc_attr( $_GET['start_date'] ) : ''; ?>">
|
271 |
+
</div>
|
272 |
+
<div class="date-end">
|
273 |
+
<label for="ebfrtb-end-date">
|
274 |
+
<?php esc_html_e( 'End', 'restaurant-reservations' ); ?>
|
275 |
+
</label>
|
276 |
+
<input type="text" name="end_date" id="ebfrtb-end-date" value="<?php echo ! empty( $_GET['end_date'] ) ? esc_attr( $_GET['end_date'] ) : ''; ?>">
|
277 |
+
</div>
|
278 |
+
</div>
|
279 |
+
</div>
|
280 |
+
</div>
|
281 |
+
|
282 |
+
<div class="status">
|
283 |
+
<?php foreach( $rtb_controller->cpts->booking_statuses as $key => $status ) : ?>
|
284 |
+
<label>
|
285 |
+
<input type="checkbox" name="status" value="<?php echo esc_attr( $key ); ?>" <?php checked( $key, 'confirmed' ); ?>>
|
286 |
+
<?php echo esc_html( $status['label'] ); ?>
|
287 |
+
</label>
|
288 |
+
<?php endforeach; ?>
|
289 |
+
</div>
|
290 |
+
|
291 |
+
</fieldset>
|
292 |
+
<button type="submit" class="button button-primary">
|
293 |
+
<?php esc_html_e( 'Export', 'restaurant-reservations' ); ?>
|
294 |
+
</button>
|
295 |
+
<a href="#" class="button" id="ebfrtb-cancel-export-modal">
|
296 |
+
<?php esc_html_e( 'Cancel', 'restaurant-reservations' ); ?>
|
297 |
+
</a>
|
298 |
+
<a href="<?php echo admin_url( 'admin.php?page=rtb-settings&tab=rtb-export-tab' ); ?>" class="settings">
|
299 |
+
<?php esc_html_e( 'Settings', 'restaurant-reservations' ); ?>
|
300 |
+
</a>
|
301 |
+
</form>
|
302 |
+
</div>
|
303 |
+
</div>
|
304 |
+
|
305 |
+
<?php
|
306 |
+
}
|
307 |
+
|
308 |
+
}
|
309 |
+
} // endif;
|
includes/Field.class.php
CHANGED
@@ -1,611 +1,611 @@
|
|
1 |
-
<?php
|
2 |
-
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
-
|
4 |
-
if ( !class_exists( 'cffrtbField' ) ) {
|
5 |
-
/**
|
6 |
-
* Field class to add, edit, save, update, validate and process a
|
7 |
-
* custom field added to the booking form.
|
8 |
-
*
|
9 |
-
* @since 0.1
|
10 |
-
*/
|
11 |
-
class cffrtbField {
|
12 |
-
|
13 |
-
/**
|
14 |
-
* ID
|
15 |
-
*
|
16 |
-
* @since 0.1
|
17 |
-
*/
|
18 |
-
public $ID;
|
19 |
-
|
20 |
-
/**
|
21 |
-
* Type
|
22 |
-
*
|
23 |
-
* eg - text, options, confirm
|
24 |
-
*
|
25 |
-
* @since 0.1
|
26 |
-
*/
|
27 |
-
public $type;
|
28 |
-
|
29 |
-
/**
|
30 |
-
* Subtype
|
31 |
-
*
|
32 |
-
* eg - text/textarea, select/checkbox/radio, confirm
|
33 |
-
*
|
34 |
-
* @since 0.1
|
35 |
-
*/
|
36 |
-
public $subtype;
|
37 |
-
|
38 |
-
/**
|
39 |
-
* Label
|
40 |
-
*
|
41 |
-
* @since 0.1
|
42 |
-
*/
|
43 |
-
public $title;
|
44 |
-
|
45 |
-
/**
|
46 |
-
* Slug
|
47 |
-
*
|
48 |
-
* @since 0.1
|
49 |
-
*/
|
50 |
-
public $slug;
|
51 |
-
|
52 |
-
/**
|
53 |
-
* Required
|
54 |
-
*
|
55 |
-
* @since 0.1
|
56 |
-
*/
|
57 |
-
public $required;
|
58 |
-
|
59 |
-
/**
|
60 |
-
* Fieldset
|
61 |
-
*
|
62 |
-
* @since 0.1
|
63 |
-
*/
|
64 |
-
public $fieldset;
|
65 |
-
|
66 |
-
/**
|
67 |
-
* Order of the field in its fieldset
|
68 |
-
*
|
69 |
-
* @since 0.1
|
70 |
-
*/
|
71 |
-
public $order;
|
72 |
-
|
73 |
-
/**
|
74 |
-
* Options (used in some field types)
|
75 |
-
*
|
76 |
-
* @since 0.1
|
77 |
-
*/
|
78 |
-
public $options;
|
79 |
-
|
80 |
-
/**
|
81 |
-
* Post status
|
82 |
-
*
|
83 |
-
* @since 0.1
|
84 |
-
*/
|
85 |
-
public $status;
|
86 |
-
|
87 |
-
/**
|
88 |
-
* Get it started
|
89 |
-
*
|
90 |
-
* @since 0.1
|
91 |
-
*/
|
92 |
-
public function __construct( $args = array() ) {
|
93 |
-
|
94 |
-
// Load from the db if an ID is passed
|
95 |
-
if ( !empty( $args['ID'] ) ) {
|
96 |
-
$args = array_merge( $this->load_from_db( $args['ID'] ), $args );
|
97 |
-
}
|
98 |
-
|
99 |
-
// Set up the field object
|
100 |
-
$this->setup_field( $args );
|
101 |
-
|
102 |
-
}
|
103 |
-
|
104 |
-
/**
|
105 |
-
* Load an object based on the arguments passed
|
106 |
-
*
|
107 |
-
* @since 0.1
|
108 |
-
*/
|
109 |
-
public function load_from_db( $id ) {
|
110 |
-
|
111 |
-
$post = get_post( $id, 'ARRAY_A' );
|
112 |
-
|
113 |
-
$post['title'] = $post['post_title'];
|
114 |
-
$post['slug'] = $post['post_name'];
|
115 |
-
$post['order'] = $post['menu_order'];
|
116 |
-
$post['status'] = $post['post_status'];
|
117 |
-
|
118 |
-
$post_meta = get_post_meta( $id, 'cffrtb', true );
|
119 |
-
|
120 |
-
if( !empty( $post_meta['type'] ) ) { $post['type'] = $post_meta['type']; }
|
121 |
-
if( !empty( $post_meta['subtype'] ) ) { $post['subtype'] = $post_meta['subtype']; }
|
122 |
-
if( !empty( $post_meta['required'] ) ) { $post['required'] = $post_meta['required']; }
|
123 |
-
if( !empty( $post_meta['fieldset'] ) ) { $post['fieldset'] = $post_meta['fieldset']; }
|
124 |
-
if( !empty( $post_meta['options'] ) ) { $post['options'] = $post_meta['options']; }
|
125 |
-
|
126 |
-
return $post;
|
127 |
-
}
|
128 |
-
|
129 |
-
/**
|
130 |
-
* Set up a new field object
|
131 |
-
*
|
132 |
-
* @since 0.1
|
133 |
-
*/
|
134 |
-
public function setup_field( $args ) {
|
135 |
-
global $rtb_controller;
|
136 |
-
|
137 |
-
// Use defaults for missing args
|
138 |
-
$args = array_merge( $rtb_controller->fields->get_default_field_values(), $args );
|
139 |
-
|
140 |
-
// Set up the object
|
141 |
-
$this->ID = empty( $args['ID'] ) ? null : (int) $args['ID'];
|
142 |
-
$this->type = empty( $args['type'] ) ? $this->get_valid_type( '' ) : $this->get_valid_type( $args['type'] );
|
143 |
-
$this->subtype = empty( $args['subtype'] ) ? $this->get_valid_subtype( '' ) : $this->get_valid_subtype( $args['subtype'] );
|
144 |
-
$this->title = empty( $args['title'] ) ? null : sanitize_text_field( $args['title'] );
|
145 |
-
$this->slug = empty( $args['slug'] ) ? null : sanitize_key( $args['slug'] );
|
146 |
-
$this->required = empty( $args['required'] ) ? false : (bool) $args['required'];
|
147 |
-
$this->fieldset = empty( $args['fieldset'] ) ? null : sanitize_key( $args['fieldset'] );
|
148 |
-
$this->order = empty( $args['order'] ) ? null : (int) $args['order'];
|
149 |
-
$this->options = empty( $args['options'] ) ? null : $this->get_valid_options( $args['options'] );
|
150 |
-
$this->status = empty( $args['status'] ) ? 'publish' : sanitize_key( $args['status'] );
|
151 |
-
|
152 |
-
do_action( 'cffrtb_setup_field_object', $this, $args );
|
153 |
-
}
|
154 |
-
|
155 |
-
/**
|
156 |
-
* Check if type is valid
|
157 |
-
*
|
158 |
-
* @since 0.1
|
159 |
-
*/
|
160 |
-
public function is_valid_type( $type ) {
|
161 |
-
global $rtb_controller;
|
162 |
-
|
163 |
-
$types = $rtb_controller->fields->get_valid_field_types();
|
164 |
-
return !empty( $type ) && array_key_exists( $type, $types );
|
165 |
-
}
|
166 |
-
|
167 |
-
/**
|
168 |
-
* Return a valid type. Falls back to default if specified type is
|
169 |
-
* not valid. Also allows a `fieldset` type to pass
|
170 |
-
*
|
171 |
-
* @since 0.1
|
172 |
-
*/
|
173 |
-
public function get_valid_type( $type ) {
|
174 |
-
global $rtb_controller;
|
175 |
-
|
176 |
-
if ( $type == 'fieldset' ) {
|
177 |
-
return $type;
|
178 |
-
}
|
179 |
-
|
180 |
-
if ( $this->is_valid_type( $type ) ) {
|
181 |
-
return $type;
|
182 |
-
}
|
183 |
-
|
184 |
-
$defaults = $rtb_controller->fields->get_default_field_values();
|
185 |
-
return $defaults['type'];
|
186 |
-
}
|
187 |
-
|
188 |
-
/**
|
189 |
-
* Return a valid subtype. Falls back to first subtype of type if
|
190 |
-
* subtype is not valid.
|
191 |
-
*
|
192 |
-
* @since 0.1
|
193 |
-
*/
|
194 |
-
public function get_valid_subtype( $subtype ) {
|
195 |
-
global $rtb_controller;
|
196 |
-
|
197 |
-
if( $subtype == 'fieldset' ) {
|
198 |
-
return $subtype;
|
199 |
-
}
|
200 |
-
|
201 |
-
$types = $rtb_controller->fields->get_valid_field_types();
|
202 |
-
if ( array_key_exists( $subtype, $types[ $this->type ]['subtypes'] ) ) {
|
203 |
-
return $subtype;
|
204 |
-
}
|
205 |
-
|
206 |
-
reset( $types[ $this->type ][ 'subtypes' ] );
|
207 |
-
|
208 |
-
return key( $types[ $this->type ][ 'subtypes' ] );
|
209 |
-
}
|
210 |
-
|
211 |
-
/**
|
212 |
-
* Sanitize options
|
213 |
-
*
|
214 |
-
* @since 0.1
|
215 |
-
*/
|
216 |
-
public function get_valid_options( $options ) {
|
217 |
-
global $rtb_controller;
|
218 |
-
|
219 |
-
$new_options = array();
|
220 |
-
foreach( $options as $key => $option ) {
|
221 |
-
|
222 |
-
// Store key for fields that were generated before the change in
|
223 |
-
// version 1.1, when the key represented the field id
|
224 |
-
if ( !isset( $option['id'] ) ) {
|
225 |
-
$option['id'] = $key;
|
226 |
-
}
|
227 |
-
|
228 |
-
$new_options[] = array(
|
229 |
-
'id' => substr( $option['id'], 0, 3 ) == 'new' ? sanitize_text_field( $option['id'] ) : absint( $option['id'] ),
|
230 |
-
'value' => isset( $option['value'] ) ? sanitize_text_field( $option['value'] ) : '',
|
231 |
-
'disabled' => empty( $option['disabled'] ) ? false : true,
|
232 |
-
// An order param may not exist if the option was created in an
|
233 |
-
// earlier version of the plugin
|
234 |
-
'order' => isset( $option['order'] ) ? absint( $option['order'] ) : 0,
|
235 |
-
);
|
236 |
-
}
|
237 |
-
|
238 |
-
usort( $new_options, array( $rtb_controller->fields, 'sort_by_order' ) );
|
239 |
-
|
240 |
-
return $new_options;
|
241 |
-
}
|
242 |
-
|
243 |
-
/**
|
244 |
-
* Prepare an options array for input as `post_meta`
|
245 |
-
*
|
246 |
-
* Each option must have a unique, permanent ID. If no ID exists, create a
|
247 |
-
* unique ID
|
248 |
-
*
|
249 |
-
* @since 0.1
|
250 |
-
*/
|
251 |
-
public function prepare_options_input() {
|
252 |
-
|
253 |
-
// Load existing options
|
254 |
-
$options = array();
|
255 |
-
if ( !empty( $this->ID ) ) {
|
256 |
-
$meta = get_post_meta( $this->ID, 'cffrtb', true );
|
257 |
-
if ( isset( $meta['options'] ) ) {
|
258 |
-
$options = $meta['options'];
|
259 |
-
}
|
260 |
-
}
|
261 |
-
|
262 |
-
// Disable options that are no longer available
|
263 |
-
foreach( $options as $i => $option ) {
|
264 |
-
|
265 |
-
// Store key for fields that were generated before the change in
|
266 |
-
// version 1.1, when the key represented the field id
|
267 |
-
if ( !isset( $option['id'] ) ) {
|
268 |
-
$option['id'] = $i;
|
269 |
-
}
|
270 |
-
|
271 |
-
$exists = false;
|
272 |
-
foreach( $this->options as $new_option ) {
|
273 |
-
if ( $new_option['id'] == $option['id'] ) {
|
274 |
-
$exists = true;
|
275 |
-
break;
|
276 |
-
}
|
277 |
-
}
|
278 |
-
|
279 |
-
if ( !$exists ) {
|
280 |
-
$options[$i]['disabled'] = true;
|
281 |
-
}
|
282 |
-
}
|
283 |
-
|
284 |
-
// Generate new options array with properly assigned IDs for new options
|
285 |
-
$i = count( $options );
|
286 |
-
foreach( $this->options as $key => $option ) {
|
287 |
-
if ( substr( $option['id'], 0, 3 ) == 'new' ) {
|
288 |
-
$option['id'] = $i;
|
289 |
-
$i++;
|
290 |
-
}
|
291 |
-
$options[$key] = $option;
|
292 |
-
}
|
293 |
-
|
294 |
-
return $options;
|
295 |
-
}
|
296 |
-
|
297 |
-
/**
|
298 |
-
* Check if this field has an id
|
299 |
-
*
|
300 |
-
* @since 0.1
|
301 |
-
*/
|
302 |
-
public function has_id() {
|
303 |
-
return !empty( $this->ID );
|
304 |
-
}
|
305 |
-
|
306 |
-
/**
|
307 |
-
* Check if an option is valid
|
308 |
-
*
|
309 |
-
* @since 0.1
|
310 |
-
*/
|
311 |
-
public function is_valid_option( $value ) {
|
312 |
-
|
313 |
-
if ( !is_array( $this->options ) ) {
|
314 |
-
return false;
|
315 |
-
}
|
316 |
-
|
317 |
-
return array_key_exists( $value, $this->options );
|
318 |
-
}
|
319 |
-
|
320 |
-
/**
|
321 |
-
* Save the label of this field
|
322 |
-
*
|
323 |
-
* @since 0.1
|
324 |
-
*/
|
325 |
-
public function save_label() {
|
326 |
-
global $rtb_controller;
|
327 |
-
|
328 |
-
// A default field shouldn't be saved as a custom field post
|
329 |
-
// type. Instead we need to overwrite the label for the slug.
|
330 |
-
if ( !$this->has_id() ) {
|
331 |
-
|
332 |
-
if ( empty( $this->slug ) ) {
|
333 |
-
return array(
|
334 |
-
false,
|
335 |
-
array(
|
336 |
-
'error' => 'missing_slug',
|
337 |
-
'msg' => __( 'No field slug was sent with this request to update a default field.', 'custom-fields-for-rtb' ) . ' ' . $rtb_controller->custom_fields->common_error_msg,
|
338 |
-
'data' => array( 'field' => $field, 'post_vars' => $_POST )
|
339 |
-
)
|
340 |
-
);
|
341 |
-
}
|
342 |
-
|
343 |
-
$modified = get_option( $rtb_controller->custom_fields->modified_option_key );
|
344 |
-
|
345 |
-
$new = $modified;
|
346 |
-
|
347 |
-
if ( empty( $new ) ) {
|
348 |
-
$new = array();
|
349 |
-
}
|
350 |
-
|
351 |
-
if ( empty( $new[ $this->slug ] ) ) {
|
352 |
-
$new[ $this->slug ] = array();
|
353 |
-
}
|
354 |
-
|
355 |
-
$new[ $this->slug ][ 'title'] = $this->title;
|
356 |
-
$new[ $this->slug ][ 'fieldset'] = $this->fieldset;
|
357 |
-
|
358 |
-
if ( update_option( $rtb_controller->custom_fields->modified_option_key, $new ) ) {
|
359 |
-
return array(
|
360 |
-
true,
|
361 |
-
array(
|
362 |
-
'field' => $this
|
363 |
-
)
|
364 |
-
);
|
365 |
-
|
366 |
-
} else {
|
367 |
-
|
368 |
-
/**
|
369 |
-
* update_option() returns false if the new option is
|
370 |
-
* the same as the old option, so let's check if that's
|
371 |
-
* why we got a false response and send out a true
|
372 |
-
* response if there's no error with the save process.
|
373 |
-
*/
|
374 |
-
$same = true;
|
375 |
-
|
376 |
-
if ( empty( $modified ) || empty( $modified[ $this->slug ] ) ) {
|
377 |
-
$same = false;
|
378 |
-
}
|
379 |
-
|
380 |
-
if ( ( isset( $modified[ $this->slug ]['title'] ) && $modified[ $this->slug ]['title'] !== $this->title ) ||
|
381 |
-
( !isset( $modified[ $this->slug ]['title'] ) && !empty( $this->title ) ) ) {
|
382 |
-
$same = false;
|
383 |
-
}
|
384 |
-
|
385 |
-
if ( empty( $modified[ $this->slug ]['fieldset'] ) || $modified[ $this->slug ]['fieldset'] !== $this->fieldset ) {
|
386 |
-
$same = false;
|
387 |
-
}
|
388 |
-
|
389 |
-
if ( $same ) {
|
390 |
-
return array(
|
391 |
-
true,
|
392 |
-
array(
|
393 |
-
'field' => $this
|
394 |
-
)
|
395 |
-
);
|
396 |
-
|
397 |
-
} else {
|
398 |
-
return array(
|
399 |
-
false,
|
400 |
-
array(
|
401 |
-
'error' => 'save_failed',
|
402 |
-
'msg' => __( 'An error occurred while updating the label for this default field. Please try again.', 'custom-fields-for-rtb' ),
|
403 |
-
'field' => $this,
|
404 |
-
'option' => $new,
|
405 |
-
)
|
406 |
-
);
|
407 |
-
}
|
408 |
-
}
|
409 |
-
|
410 |
-
// Custom field
|
411 |
-
} else {
|
412 |
-
|
413 |
-
$field = array(
|
414 |
-
'ID' => $this->ID,
|
415 |
-
'post_title' => $this->title,
|
416 |
-
);
|
417 |
-
|
418 |
-
if ( wp_update_post( $field ) ) {
|
419 |
-
return array(
|
420 |
-
true,
|
421 |
-
array(
|
422 |
-
'title' => get_the_title( $this->ID ),
|
423 |
-
)
|
424 |
-
);
|
425 |
-
|
426 |
-
} else {
|
427 |
-
return array(
|
428 |
-
false,
|
429 |
-
array(
|
430 |
-
'error' => 'save_failed',
|
431 |
-
'msg' => __( 'An error occurred while updating the label for this custom field. Please try again.', 'custom-fields-for-rtb' ),
|
432 |
-
'field' => $this,
|
433 |
-
)
|
434 |
-
);
|
435 |
-
}
|
436 |
-
}
|
437 |
-
}
|
438 |
-
|
439 |
-
/**
|
440 |
-
* Save a field
|
441 |
-
*
|
442 |
-
* @since 0.1
|
443 |
-
*/
|
444 |
-
public function save_field() {
|
445 |
-
global $rtb_controller;
|
446 |
-
|
447 |
-
if ( empty( $this->title ) || empty( $this->type ) || ( $this->type !== 'fieldset' && empty( $this->subtype ) ) ) {
|
448 |
-
return array(
|
449 |
-
false,
|
450 |
-
array(
|
451 |
-
'error' => 'missing_data',
|
452 |
-
'msg' => __( 'This field could not be saved because required data was missing.', 'custom-fields-for-rtb' ),
|
453 |
-
'field' => $this,
|
454 |
-
)
|
455 |
-
);
|
456 |
-
}
|
457 |
-
|
458 |
-
$post = array(
|
459 |
-
'post_title' => $this->title,
|
460 |
-
'post_type' => 'cffrtb_field',
|
461 |
-
'post_status' => $this->status,
|
462 |
-
);
|
463 |
-
if ( $this->has_id() ) { $post['ID'] = $this->ID; }
|
464 |
-
if ( !empty( $this->slug ) ) { $post['post_name'] = $this->slug; }
|
465 |
-
if ( !empty( $this->order ) ) { $post['menu_order'] = $this->order; }
|
466 |
-
|
467 |
-
$post = apply_filters( 'cffrtb_insert_field_data', $post, $this );
|
468 |
-
|
469 |
-
$id = wp_insert_post( $post );
|
470 |
-
if ( is_wp_error( $id ) || empty( $id ) ) {
|
471 |
-
return array(
|
472 |
-
false,
|
473 |
-
array(
|
474 |
-
'error' => 'save_post_failed',
|
475 |
-
'msg' => __( 'An error occurred while saving this field. Please try again. If the problem persists, please refresh the page.', 'custom-fields-for-rtb' ),
|
476 |
-
'field' => $this,
|
477 |
-
'wp_error' => $id
|
478 |
-
)
|
479 |
-
);
|
480 |
-
}
|
481 |
-
|
482 |
-
$is_new_field = !$this->has_id();
|
483 |
-
|
484 |
-
$this->ID = $id;
|
485 |
-
|
486 |
-
$post_item = get_post( $id );
|
487 |
-
$this->title = get_the_title( $id );
|
488 |
-
$this->slug = $post_item->post_name;
|
489 |
-
|
490 |
-
$post_meta = array(
|
491 |
-
'type' => $this->type,
|
492 |
-
);
|
493 |
-
if ( !empty( $this->subtype ) ) { $post_meta['subtype'] = $this->subtype; }
|
494 |
-
if ( !empty( $this->required ) ) { $post_meta['required'] = $this->required; }
|
495 |
-
if ( !empty( $this->fieldset ) ) { $post_meta['fieldset'] = $this->fieldset; }
|
496 |
-
if ( !empty( $this->options ) && $this->type == 'options' ) { $post_meta['options'] = $this->prepare_options_input(); }
|
497 |
-
|
498 |
-
$post_meta = apply_filters( 'cffrtb_insert_field_metadata', $post_meta, $this );
|
499 |
-
|
500 |
-
update_post_meta( $id, 'cffrtb', $post_meta );
|
501 |
-
|
502 |
-
$field = array(
|
503 |
-
'ID' => $this->ID,
|
504 |
-
'title' => $this->title,
|
505 |
-
);
|
506 |
-
|
507 |
-
if ( $this->type == 'fieldset' ) {
|
508 |
-
$type = 'fieldset';
|
509 |
-
$field['legend'] = $this->title;
|
510 |
-
} else {
|
511 |
-
$type = 'field';
|
512 |
-
}
|
513 |
-
|
514 |
-
return array(
|
515 |
-
true,
|
516 |
-
array(
|
517 |
-
'ID' => $this->ID,
|
518 |
-
'is_new_field' => $is_new_field,
|
519 |
-
'field' => $rtb_controller->editor->print_field( $this->slug, $field, $type ),
|
520 |
-
'type' => $this->type,
|
521 |
-
)
|
522 |
-
);
|
523 |
-
}
|
524 |
-
|
525 |
-
/**
|
526 |
-
* Validate the input for a field
|
527 |
-
*
|
528 |
-
* @since 0.1
|
529 |
-
*/
|
530 |
-
public function validate_input( $booking ) {
|
531 |
-
|
532 |
-
// Don't validate fieldsets
|
533 |
-
if ( $this->type === 'fieldset' ) {
|
534 |
-
return;
|
535 |
-
}
|
536 |
-
|
537 |
-
// Instantiate the custom fields array if it's missing
|
538 |
-
if ( !isset( $booking->custom_fields ) ) {
|
539 |
-
$booking->custom_fields = array();
|
540 |
-
}
|
541 |
-
|
542 |
-
$input = isset( $_POST['rtb-' . $this->slug ] )
|
543 |
-
? ( is_array( $_POST['rtb-' . $this->slug ] )
|
544 |
-
? array_map( 'absint', $_POST['rtb-' . $this->slug ] )
|
545 |
-
: sanitize_text_field( $_POST['rtb-' . $this->slug] ) )
|
546 |
-
: '';
|
547 |
-
|
548 |
-
// Skip empty fields but do not skip checkboxes.
|
549 |
-
// required checks are performed by base plugin validation
|
550 |
-
|
551 |
-
$checkbox = 'options' === $this->type && 'checkbox' === $this->subtype;
|
552 |
-
|
553 |
-
if ( ( is_string( $input ) && trim( $input ) == '' ) ||
|
554 |
-
( is_array( $input ) && empty( $input ) ) ) {
|
555 |
-
|
556 |
-
// When a checkbox is unselected, it will not override the previously selected
|
557 |
-
// value because HTML Form will not submit empty checkboxes
|
558 |
-
if($checkbox) {
|
559 |
-
$input = [];
|
560 |
-
}
|
561 |
-
else {
|
562 |
-
return;
|
563 |
-
}
|
564 |
-
}
|
565 |
-
|
566 |
-
// Option fields
|
567 |
-
if ( $this->type == 'options' ) {
|
568 |
-
|
569 |
-
if ( !is_array( $input ) ) {
|
570 |
-
$input = array( $input );
|
571 |
-
}
|
572 |
-
|
573 |
-
foreach( $input as $input_i ) {
|
574 |
-
if ( !$this->is_valid_option( $input_i ) ) {
|
575 |
-
$booking->validation_errors[] = array(
|
576 |
-
'field' => $this->slug,
|
577 |
-
'post_variable' => $input,
|
578 |
-
'message' => __( 'The option you selected is not valid. Please make another choice.', 'custom-fields-for-rtb' )
|
579 |
-
);
|
580 |
-
|
581 |
-
return;
|
582 |
-
}
|
583 |
-
}
|
584 |
-
|
585 |
-
if ( $this->subtype === 'select' || $this->subtype === 'radio' ) {
|
586 |
-
$val = absint( $input[0] );
|
587 |
-
if ( isset( $this->options[ $val ] ) ) {
|
588 |
-
$booking->custom_fields[ $this->slug ] = $val;
|
589 |
-
}
|
590 |
-
} elseif ( $this->subtype === 'checkbox' ) {
|
591 |
-
$val = array();
|
592 |
-
foreach( $input as $input_i ) {
|
593 |
-
if ( isset( $this->options[ $input_i ] ) ) {
|
594 |
-
$val[] = $input_i;
|
595 |
-
}
|
596 |
-
}
|
597 |
-
$booking->custom_fields[ $this->slug ] = $val;
|
598 |
-
}
|
599 |
-
|
600 |
-
// Confirm fields (always true if we've reached this stage)
|
601 |
-
} elseif ( $this->type === 'confirm' ) {
|
602 |
-
$booking->custom_fields[ $this->slug ] = true;
|
603 |
-
|
604 |
-
// Text fields just need to be sanitized
|
605 |
-
} elseif ( $this->type === 'text' ) {
|
606 |
-
$booking->custom_fields[ $this->slug ] = sanitize_text_field( $input );
|
607 |
-
}
|
608 |
-
}
|
609 |
-
|
610 |
-
}
|
611 |
-
} // endif;
|
1 |
+
<?php
|
2 |
+
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
+
|
4 |
+
if ( !class_exists( 'cffrtbField' ) ) {
|
5 |
+
/**
|
6 |
+
* Field class to add, edit, save, update, validate and process a
|
7 |
+
* custom field added to the booking form.
|
8 |
+
*
|
9 |
+
* @since 0.1
|
10 |
+
*/
|
11 |
+
class cffrtbField {
|
12 |
+
|
13 |
+
/**
|
14 |
+
* ID
|
15 |
+
*
|
16 |
+
* @since 0.1
|
17 |
+
*/
|
18 |
+
public $ID;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Type
|
22 |
+
*
|
23 |
+
* eg - text, options, confirm
|
24 |
+
*
|
25 |
+
* @since 0.1
|
26 |
+
*/
|
27 |
+
public $type;
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Subtype
|
31 |
+
*
|
32 |
+
* eg - text/textarea, select/checkbox/radio, confirm
|
33 |
+
*
|
34 |
+
* @since 0.1
|
35 |
+
*/
|
36 |
+
public $subtype;
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Label
|
40 |
+
*
|
41 |
+
* @since 0.1
|
42 |
+
*/
|
43 |
+
public $title;
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Slug
|
47 |
+
*
|
48 |
+
* @since 0.1
|
49 |
+
*/
|
50 |
+
public $slug;
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Required
|
54 |
+
*
|
55 |
+
* @since 0.1
|
56 |
+
*/
|
57 |
+
public $required;
|
58 |
+
|
59 |
+
/**
|
60 |
+
* Fieldset
|
61 |
+
*
|
62 |
+
* @since 0.1
|
63 |
+
*/
|
64 |
+
public $fieldset;
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Order of the field in its fieldset
|
68 |
+
*
|
69 |
+
* @since 0.1
|
70 |
+
*/
|
71 |
+
public $order;
|
72 |
+
|
73 |
+
/**
|
74 |
+
* Options (used in some field types)
|
75 |
+
*
|
76 |
+
* @since 0.1
|
77 |
+
*/
|
78 |
+
public $options;
|
79 |
+
|
80 |
+
/**
|
81 |
+
* Post status
|
82 |
+
*
|
83 |
+
* @since 0.1
|
84 |
+
*/
|
85 |
+
public $status;
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Get it started
|
89 |
+
*
|
90 |
+
* @since 0.1
|
91 |
+
*/
|
92 |
+
public function __construct( $args = array() ) {
|
93 |
+
|
94 |
+
// Load from the db if an ID is passed
|
95 |
+
if ( !empty( $args['ID'] ) ) {
|
96 |
+
$args = array_merge( $this->load_from_db( $args['ID'] ), $args );
|
97 |
+
}
|
98 |
+
|
99 |
+
// Set up the field object
|
100 |
+
$this->setup_field( $args );
|
101 |
+
|
102 |
+
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Load an object based on the arguments passed
|
106 |
+
*
|
107 |
+
* @since 0.1
|
108 |
+
*/
|
109 |
+
public function load_from_db( $id ) {
|
110 |
+
|
111 |
+
$post = get_post( $id, 'ARRAY_A' );
|
112 |
+
|
113 |
+
$post['title'] = $post['post_title'];
|
114 |
+
$post['slug'] = $post['post_name'];
|
115 |
+
$post['order'] = $post['menu_order'];
|
116 |
+
$post['status'] = $post['post_status'];
|
117 |
+
|
118 |
+
$post_meta = get_post_meta( $id, 'cffrtb', true );
|
119 |
+
|
120 |
+
if( !empty( $post_meta['type'] ) ) { $post['type'] = $post_meta['type']; }
|
121 |
+
if( !empty( $post_meta['subtype'] ) ) { $post['subtype'] = $post_meta['subtype']; }
|
122 |
+
if( !empty( $post_meta['required'] ) ) { $post['required'] = $post_meta['required']; }
|
123 |
+
if( !empty( $post_meta['fieldset'] ) ) { $post['fieldset'] = $post_meta['fieldset']; }
|
124 |
+
if( !empty( $post_meta['options'] ) ) { $post['options'] = $post_meta['options']; }
|
125 |
+
|
126 |
+
return $post;
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* Set up a new field object
|
131 |
+
*
|
132 |
+
* @since 0.1
|
133 |
+
*/
|
134 |
+
public function setup_field( $args ) {
|
135 |
+
global $rtb_controller;
|
136 |
+
|
137 |
+
// Use defaults for missing args
|
138 |
+
$args = array_merge( $rtb_controller->fields->get_default_field_values(), $args );
|
139 |
+
|
140 |
+
// Set up the object
|
141 |
+
$this->ID = empty( $args['ID'] ) ? null : (int) $args['ID'];
|
142 |
+
$this->type = empty( $args['type'] ) ? $this->get_valid_type( '' ) : $this->get_valid_type( $args['type'] );
|
143 |
+
$this->subtype = empty( $args['subtype'] ) ? $this->get_valid_subtype( '' ) : $this->get_valid_subtype( $args['subtype'] );
|
144 |
+
$this->title = empty( $args['title'] ) ? null : sanitize_text_field( $args['title'] );
|
145 |
+
$this->slug = empty( $args['slug'] ) ? null : sanitize_key( $args['slug'] );
|
146 |
+
$this->required = empty( $args['required'] ) ? false : (bool) $args['required'];
|
147 |
+
$this->fieldset = empty( $args['fieldset'] ) ? null : sanitize_key( $args['fieldset'] );
|
148 |
+
$this->order = empty( $args['order'] ) ? null : (int) $args['order'];
|
149 |
+
$this->options = empty( $args['options'] ) ? null : $this->get_valid_options( $args['options'] );
|
150 |
+
$this->status = empty( $args['status'] ) ? 'publish' : sanitize_key( $args['status'] );
|
151 |
+
|
152 |
+
do_action( 'cffrtb_setup_field_object', $this, $args );
|
153 |
+
}
|
154 |
+
|
155 |
+
/**
|
156 |
+
* Check if type is valid
|
157 |
+
*
|
158 |
+
* @since 0.1
|
159 |
+
*/
|
160 |
+
public function is_valid_type( $type ) {
|
161 |
+
global $rtb_controller;
|
162 |
+
|
163 |
+
$types = $rtb_controller->fields->get_valid_field_types();
|
164 |
+
return !empty( $type ) && array_key_exists( $type, $types );
|
165 |
+
}
|
166 |
+
|
167 |
+
/**
|
168 |
+
* Return a valid type. Falls back to default if specified type is
|
169 |
+
* not valid. Also allows a `fieldset` type to pass
|
170 |
+
*
|
171 |
+
* @since 0.1
|
172 |
+
*/
|
173 |
+
public function get_valid_type( $type ) {
|
174 |
+
global $rtb_controller;
|
175 |
+
|
176 |
+
if ( $type == 'fieldset' ) {
|
177 |
+
return $type;
|
178 |
+
}
|
179 |
+
|
180 |
+
if ( $this->is_valid_type( $type ) ) {
|
181 |
+
return $type;
|
182 |
+
}
|
183 |
+
|
184 |
+
$defaults = $rtb_controller->fields->get_default_field_values();
|
185 |
+
return $defaults['type'];
|
186 |
+
}
|
187 |
+
|
188 |
+
/**
|
189 |
+
* Return a valid subtype. Falls back to first subtype of type if
|
190 |
+
* subtype is not valid.
|
191 |
+
*
|
192 |
+
* @since 0.1
|
193 |
+
*/
|
194 |
+
public function get_valid_subtype( $subtype ) {
|
195 |
+
global $rtb_controller;
|
196 |
+
|
197 |
+
if( $subtype == 'fieldset' ) {
|
198 |
+
return $subtype;
|
199 |
+
}
|
200 |
+
|
201 |
+
$types = $rtb_controller->fields->get_valid_field_types();
|
202 |
+
if ( array_key_exists( $subtype, $types[ $this->type ]['subtypes'] ) ) {
|
203 |
+
return $subtype;
|
204 |
+
}
|
205 |
+
|
206 |
+
reset( $types[ $this->type ][ 'subtypes' ] );
|
207 |
+
|
208 |
+
return key( $types[ $this->type ][ 'subtypes' ] );
|
209 |
+
}
|
210 |
+
|
211 |
+
/**
|
212 |
+
* Sanitize options
|
213 |
+
*
|
214 |
+
* @since 0.1
|
215 |
+
*/
|
216 |
+
public function get_valid_options( $options ) {
|
217 |
+
global $rtb_controller;
|
218 |
+
|
219 |
+
$new_options = array();
|
220 |
+
foreach( $options as $key => $option ) {
|
221 |
+
|
222 |
+
// Store key for fields that were generated before the change in
|
223 |
+
// version 1.1, when the key represented the field id
|
224 |
+
if ( !isset( $option['id'] ) ) {
|
225 |
+
$option['id'] = $key;
|
226 |
+
}
|
227 |
+
|
228 |
+
$new_options[] = array(
|
229 |
+
'id' => substr( $option['id'], 0, 3 ) == 'new' ? sanitize_text_field( $option['id'] ) : absint( $option['id'] ),
|
230 |
+
'value' => isset( $option['value'] ) ? sanitize_text_field( $option['value'] ) : '',
|
231 |
+
'disabled' => empty( $option['disabled'] ) ? false : true,
|
232 |
+
// An order param may not exist if the option was created in an
|
233 |
+
// earlier version of the plugin
|
234 |
+
'order' => isset( $option['order'] ) ? absint( $option['order'] ) : 0,
|
235 |
+
);
|
236 |
+
}
|
237 |
+
|
238 |
+
usort( $new_options, array( $rtb_controller->fields, 'sort_by_order' ) );
|
239 |
+
|
240 |
+
return $new_options;
|
241 |
+
}
|
242 |
+
|
243 |
+
/**
|
244 |
+
* Prepare an options array for input as `post_meta`
|
245 |
+
*
|
246 |
+
* Each option must have a unique, permanent ID. If no ID exists, create a
|
247 |
+
* unique ID
|
248 |
+
*
|
249 |
+
* @since 0.1
|
250 |
+
*/
|
251 |
+
public function prepare_options_input() {
|
252 |
+
|
253 |
+
// Load existing options
|
254 |
+
$options = array();
|
255 |
+
if ( !empty( $this->ID ) ) {
|
256 |
+
$meta = get_post_meta( $this->ID, 'cffrtb', true );
|
257 |
+
if ( isset( $meta['options'] ) ) {
|
258 |
+
$options = $meta['options'];
|
259 |
+
}
|
260 |
+
}
|
261 |
+
|
262 |
+
// Disable options that are no longer available
|
263 |
+
foreach( $options as $i => $option ) {
|
264 |
+
|
265 |
+
// Store key for fields that were generated before the change in
|
266 |
+
// version 1.1, when the key represented the field id
|
267 |
+
if ( !isset( $option['id'] ) ) {
|
268 |
+
$option['id'] = $i;
|
269 |
+
}
|
270 |
+
|
271 |
+
$exists = false;
|
272 |
+
foreach( $this->options as $new_option ) {
|
273 |
+
if ( $new_option['id'] == $option['id'] ) {
|
274 |
+
$exists = true;
|
275 |
+
break;
|
276 |
+
}
|
277 |
+
}
|
278 |
+
|
279 |
+
if ( !$exists ) {
|
280 |
+
$options[$i]['disabled'] = true;
|
281 |
+
}
|
282 |
+
}
|
283 |
+
|
284 |
+
// Generate new options array with properly assigned IDs for new options
|
285 |
+
$i = count( $options );
|
286 |
+
foreach( $this->options as $key => $option ) {
|
287 |
+
if ( substr( $option['id'], 0, 3 ) == 'new' ) {
|
288 |
+
$option['id'] = $i;
|
289 |
+
$i++;
|
290 |
+
}
|
291 |
+
$options[$key] = $option;
|
292 |
+
}
|
293 |
+
|
294 |
+
return $options;
|
295 |
+
}
|
296 |
+
|
297 |
+
/**
|
298 |
+
* Check if this field has an id
|
299 |
+
*
|
300 |
+
* @since 0.1
|
301 |
+
*/
|
302 |
+
public function has_id() {
|
303 |
+
return !empty( $this->ID );
|
304 |
+
}
|
305 |
+
|
306 |
+
/**
|
307 |
+
* Check if an option is valid
|
308 |
+
*
|
309 |
+
* @since 0.1
|
310 |
+
*/
|
311 |
+
public function is_valid_option( $value ) {
|
312 |
+
|
313 |
+
if ( !is_array( $this->options ) ) {
|
314 |
+
return false;
|
315 |
+
}
|
316 |
+
|
317 |
+
return array_key_exists( $value, $this->options );
|
318 |
+
}
|
319 |
+
|
320 |
+
/**
|
321 |
+
* Save the label of this field
|
322 |
+
*
|
323 |
+
* @since 0.1
|
324 |
+
*/
|
325 |
+
public function save_label() {
|
326 |
+
global $rtb_controller;
|
327 |
+
|
328 |
+
// A default field shouldn't be saved as a custom field post
|
329 |
+
// type. Instead we need to overwrite the label for the slug.
|
330 |
+
if ( !$this->has_id() ) {
|
331 |
+
|
332 |
+
if ( empty( $this->slug ) ) {
|
333 |
+
return array(
|
334 |
+
false,
|
335 |
+
array(
|
336 |
+
'error' => 'missing_slug',
|
337 |
+
'msg' => __( 'No field slug was sent with this request to update a default field.', 'custom-fields-for-rtb' ) . ' ' . $rtb_controller->custom_fields->common_error_msg,
|
338 |
+
'data' => array( 'field' => $field, 'post_vars' => $_POST )
|
339 |
+
)
|
340 |
+
);
|
341 |
+
}
|
342 |
+
|
343 |
+
$modified = get_option( $rtb_controller->custom_fields->modified_option_key );
|
344 |
+
|
345 |
+
$new = $modified;
|
346 |
+
|
347 |
+
if ( empty( $new ) ) {
|
348 |
+
$new = array();
|
349 |
+
}
|
350 |
+
|
351 |
+
if ( empty( $new[ $this->slug ] ) ) {
|
352 |
+
$new[ $this->slug ] = array();
|
353 |
+
}
|
354 |
+
|
355 |
+
$new[ $this->slug ][ 'title'] = $this->title;
|
356 |
+
$new[ $this->slug ][ 'fieldset'] = $this->fieldset;
|
357 |
+
|
358 |
+
if ( update_option( $rtb_controller->custom_fields->modified_option_key, $new ) ) {
|
359 |
+
return array(
|
360 |
+
true,
|
361 |
+
array(
|
362 |
+
'field' => $this
|
363 |
+
)
|
364 |
+
);
|
365 |
+
|
366 |
+
} else {
|
367 |
+
|
368 |
+
/**
|
369 |
+
* update_option() returns false if the new option is
|
370 |
+
* the same as the old option, so let's check if that's
|
371 |
+
* why we got a false response and send out a true
|
372 |
+
* response if there's no error with the save process.
|
373 |
+
*/
|
374 |
+
$same = true;
|
375 |
+
|
376 |
+
if ( empty( $modified ) || empty( $modified[ $this->slug ] ) ) {
|
377 |
+
$same = false;
|
378 |
+
}
|
379 |
+
|
380 |
+
if ( ( isset( $modified[ $this->slug ]['title'] ) && $modified[ $this->slug ]['title'] !== $this->title ) ||
|
381 |
+
( !isset( $modified[ $this->slug ]['title'] ) && !empty( $this->title ) ) ) {
|
382 |
+
$same = false;
|
383 |
+
}
|
384 |
+
|
385 |
+
if ( empty( $modified[ $this->slug ]['fieldset'] ) || $modified[ $this->slug ]['fieldset'] !== $this->fieldset ) {
|
386 |
+
$same = false;
|
387 |
+
}
|
388 |
+
|
389 |
+
if ( $same ) {
|
390 |
+
return array(
|
391 |
+
true,
|
392 |
+
array(
|
393 |
+
'field' => $this
|
394 |
+
)
|
395 |
+
);
|
396 |
+
|
397 |
+
} else {
|
398 |
+
return array(
|
399 |
+
false,
|
400 |
+
array(
|
401 |
+
'error' => 'save_failed',
|
402 |
+
'msg' => __( 'An error occurred while updating the label for this default field. Please try again.', 'custom-fields-for-rtb' ),
|
403 |
+
'field' => $this,
|
404 |
+
'option' => $new,
|
405 |
+
)
|
406 |
+
);
|
407 |
+
}
|
408 |
+
}
|
409 |
+
|
410 |
+
// Custom field
|
411 |
+
} else {
|
412 |
+
|
413 |
+
$field = array(
|
414 |
+
'ID' => $this->ID,
|
415 |
+
'post_title' => $this->title,
|
416 |
+
);
|
417 |
+
|
418 |
+
if ( wp_update_post( $field ) ) {
|
419 |
+
return array(
|
420 |
+
true,
|
421 |
+
array(
|
422 |
+
'title' => get_the_title( $this->ID ),
|
423 |
+
)
|
424 |
+
);
|
425 |
+
|
426 |
+
} else {
|
427 |
+
return array(
|
428 |
+
false,
|
429 |
+
array(
|
430 |
+
'error' => 'save_failed',
|
431 |
+
'msg' => __( 'An error occurred while updating the label for this custom field. Please try again.', 'custom-fields-for-rtb' ),
|
432 |
+
'field' => $this,
|
433 |
+
)
|
434 |
+
);
|
435 |
+
}
|
436 |
+
}
|
437 |
+
}
|
438 |
+
|
439 |
+
/**
|
440 |
+
* Save a field
|
441 |
+
*
|
442 |
+
* @since 0.1
|
443 |
+
*/
|
444 |
+
public function save_field() {
|
445 |
+
global $rtb_controller;
|
446 |
+
|
447 |
+
if ( empty( $this->title ) || empty( $this->type ) || ( $this->type !== 'fieldset' && empty( $this->subtype ) ) ) {
|
448 |
+
return array(
|
449 |
+
false,
|
450 |
+
array(
|
451 |
+
'error' => 'missing_data',
|
452 |
+
'msg' => __( 'This field could not be saved because required data was missing.', 'custom-fields-for-rtb' ),
|
453 |
+
'field' => $this,
|
454 |
+
)
|
455 |
+
);
|
456 |
+
}
|
457 |
+
|
458 |
+
$post = array(
|
459 |
+
'post_title' => $this->title,
|
460 |
+
'post_type' => 'cffrtb_field',
|
461 |
+
'post_status' => $this->status,
|
462 |
+
);
|
463 |
+
if ( $this->has_id() ) { $post['ID'] = $this->ID; }
|
464 |
+
if ( !empty( $this->slug ) ) { $post['post_name'] = $this->slug; }
|
465 |
+
if ( !empty( $this->order ) ) { $post['menu_order'] = $this->order; }
|
466 |
+
|
467 |
+
$post = apply_filters( 'cffrtb_insert_field_data', $post, $this );
|
468 |
+
|
469 |
+
$id = wp_insert_post( $post );
|
470 |
+
if ( is_wp_error( $id ) || empty( $id ) ) {
|
471 |
+
return array(
|
472 |
+
false,
|
473 |
+
array(
|
474 |
+
'error' => 'save_post_failed',
|
475 |
+
'msg' => __( 'An error occurred while saving this field. Please try again. If the problem persists, please refresh the page.', 'custom-fields-for-rtb' ),
|
476 |
+
'field' => $this,
|
477 |
+
'wp_error' => $id
|
478 |
+
)
|
479 |
+
);
|
480 |
+
}
|
481 |
+
|
482 |
+
$is_new_field = !$this->has_id();
|
483 |
+
|
484 |
+
$this->ID = $id;
|
485 |
+
|
486 |
+
$post_item = get_post( $id );
|
487 |
+
$this->title = get_the_title( $id );
|
488 |
+
$this->slug = $post_item->post_name;
|
489 |
+
|
490 |
+
$post_meta = array(
|
491 |
+
'type' => $this->type,
|
492 |
+
);
|
493 |
+
if ( !empty( $this->subtype ) ) { $post_meta['subtype'] = $this->subtype; }
|
494 |
+
if ( !empty( $this->required ) ) { $post_meta['required'] = $this->required; }
|
495 |
+
if ( !empty( $this->fieldset ) ) { $post_meta['fieldset'] = $this->fieldset; }
|
496 |
+
if ( !empty( $this->options ) && $this->type == 'options' ) { $post_meta['options'] = $this->prepare_options_input(); }
|
497 |
+
|
498 |
+
$post_meta = apply_filters( 'cffrtb_insert_field_metadata', $post_meta, $this );
|
499 |
+
|
500 |
+
update_post_meta( $id, 'cffrtb', $post_meta );
|
501 |
+
|
502 |
+
$field = array(
|
503 |
+
'ID' => $this->ID,
|
504 |
+
'title' => $this->title,
|
505 |
+
);
|
506 |
+
|
507 |
+
if ( $this->type == 'fieldset' ) {
|
508 |
+
$type = 'fieldset';
|
509 |
+
$field['legend'] = $this->title;
|
510 |
+
} else {
|
511 |
+
$type = 'field';
|
512 |
+
}
|
513 |
+
|
514 |
+
return array(
|
515 |
+
true,
|
516 |
+
array(
|
517 |
+
'ID' => $this->ID,
|
518 |
+
'is_new_field' => $is_new_field,
|
519 |
+
'field' => $rtb_controller->editor->print_field( $this->slug, $field, $type ),
|
520 |
+
'type' => $this->type,
|
521 |
+
)
|
522 |
+
);
|
523 |
+
}
|
524 |
+
|
525 |
+
/**
|
526 |
+
* Validate the input for a field
|
527 |
+
*
|
528 |
+
* @since 0.1
|
529 |
+
*/
|
530 |
+
public function validate_input( $booking ) {
|
531 |
+
|
532 |
+
// Don't validate fieldsets
|
533 |
+
if ( $this->type === 'fieldset' ) {
|
534 |
+
return;
|
535 |
+
}
|
536 |
+
|
537 |
+
// Instantiate the custom fields array if it's missing
|
538 |
+
if ( !isset( $booking->custom_fields ) ) {
|
539 |
+
$booking->custom_fields = array();
|
540 |
+
}
|
541 |
+
|
542 |
+
$input = isset( $_POST['rtb-' . $this->slug ] )
|
543 |
+
? ( is_array( $_POST['rtb-' . $this->slug ] )
|
544 |
+
? array_map( 'absint', $_POST['rtb-' . $this->slug ] )
|
545 |
+
: sanitize_text_field( $_POST['rtb-' . $this->slug] ) )
|
546 |
+
: '';
|
547 |
+
|
548 |
+
// Skip empty fields but do not skip checkboxes.
|
549 |
+
// required checks are performed by base plugin validation
|
550 |
+
|
551 |
+
$checkbox = 'options' === $this->type && 'checkbox' === $this->subtype;
|
552 |
+
|
553 |
+
if ( ( is_string( $input ) && trim( $input ) == '' ) ||
|
554 |
+
( is_array( $input ) && empty( $input ) ) ) {
|
555 |
+
|
556 |
+
// When a checkbox is unselected, it will not override the previously selected
|
557 |
+
// value because HTML Form will not submit empty checkboxes
|
558 |
+
if($checkbox) {
|
559 |
+
$input = [];
|
560 |
+
}
|
561 |
+
else {
|
562 |
+
return;
|
563 |
+
}
|
564 |
+
}
|
565 |
+
|
566 |
+
// Option fields
|
567 |
+
if ( $this->type == 'options' ) {
|
568 |
+
|
569 |
+
if ( !is_array( $input ) ) {
|
570 |
+
$input = array( $input );
|
571 |
+
}
|
572 |
+
|
573 |
+
foreach( $input as $input_i ) {
|
574 |
+
if ( !$this->is_valid_option( $input_i ) ) {
|
575 |
+
$booking->validation_errors[] = array(
|
576 |
+
'field' => $this->slug,
|
577 |
+
'post_variable' => $input,
|
578 |
+
'message' => __( 'The option you selected is not valid. Please make another choice.', 'custom-fields-for-rtb' )
|
579 |
+
);
|
580 |
+
|
581 |
+
return;
|
582 |
+
}
|
583 |
+
}
|
584 |
+
|
585 |
+
if ( $this->subtype === 'select' || $this->subtype === 'radio' ) {
|
586 |
+
$val = absint( $input[0] );
|
587 |
+
if ( isset( $this->options[ $val ] ) ) {
|
588 |
+
$booking->custom_fields[ $this->slug ] = $val;
|
589 |
+
}
|
590 |
+
} elseif ( $this->subtype === 'checkbox' ) {
|
591 |
+
$val = array();
|
592 |
+
foreach( $input as $input_i ) {
|
593 |
+
if ( isset( $this->options[ $input_i ] ) ) {
|
594 |
+
$val[] = $input_i;
|
595 |
+
}
|
596 |
+
}
|
597 |
+
$booking->custom_fields[ $this->slug ] = $val;
|
598 |
+
}
|
599 |
+
|
600 |
+
// Confirm fields (always true if we've reached this stage)
|
601 |
+
} elseif ( $this->type === 'confirm' ) {
|
602 |
+
$booking->custom_fields[ $this->slug ] = true;
|
603 |
+
|
604 |
+
// Text fields just need to be sanitized
|
605 |
+
} elseif ( $this->type === 'text' ) {
|
606 |
+
$booking->custom_fields[ $this->slug ] = sanitize_text_field( $input );
|
607 |
+
}
|
608 |
+
}
|
609 |
+
|
610 |
+
}
|
611 |
+
} // endif;
|
includes/Helper.class.php
CHANGED
@@ -1,104 +1,104 @@
|
|
1 |
-
<?php
|
2 |
-
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
-
|
4 |
-
if ( !class_exists( 'rtbHelper' ) ) {
|
5 |
-
/**
|
6 |
-
* Class to to provide helper functions
|
7 |
-
*
|
8 |
-
* @since 2.4.10
|
9 |
-
*/
|
10 |
-
class rtbHelper {
|
11 |
-
|
12 |
-
// Hold the class instance.
|
13 |
-
private static $instance = null;
|
14 |
-
|
15 |
-
/**
|
16 |
-
* The constructor is private
|
17 |
-
* to prevent initiation with outer code.
|
18 |
-
*
|
19 |
-
**/
|
20 |
-
private function __construct() {}
|
21 |
-
|
22 |
-
/**
|
23 |
-
* The object is created from within the class itself
|
24 |
-
* only if the class has no instance.
|
25 |
-
*/
|
26 |
-
public static function getInstance() {
|
27 |
-
|
28 |
-
if ( self::$instance == null ) {
|
29 |
-
|
30 |
-
self::$instance = new rtbHelper();
|
31 |
-
}
|
32 |
-
|
33 |
-
return self::$instance;
|
34 |
-
}
|
35 |
-
|
36 |
-
/**
|
37 |
-
* Handle ajax requests from the admin bookings area from logged out users
|
38 |
-
* @since 2.4.10
|
39 |
-
*/
|
40 |
-
public static function admin_nopriv_ajax() {
|
41 |
-
|
42 |
-
wp_send_json_error(
|
43 |
-
array(
|
44 |
-
'error' => 'loggedout',
|
45 |
-
'msg' => sprintf( __( 'You have been logged out. Please %slogin again%s.', 'restaurant-reservations' ), '<a href="' . wp_login_url( admin_url( 'admin.php?page=rtb-dashboard' ) ) . '">', '</a>' ),
|
46 |
-
)
|
47 |
-
);
|
48 |
-
}
|
49 |
-
|
50 |
-
/**
|
51 |
-
* Handle ajax requests where an invalid nonce is passed with the request
|
52 |
-
* @since 2.4.10
|
53 |
-
*/
|
54 |
-
public static function bad_nonce_ajax() {
|
55 |
-
|
56 |
-
wp_send_json_error(
|
57 |
-
array(
|
58 |
-
'error' => 'badnonce',
|
59 |
-
'msg' => __( 'The request has been rejected because it does not appear to have come from this site.', 'restaurant-reservations' ),
|
60 |
-
)
|
61 |
-
);
|
62 |
-
}
|
63 |
-
|
64 |
-
/**
|
65 |
-
* sanitize_text_field for array's each value, recusivly
|
66 |
-
* @since 2.4.10
|
67 |
-
*/
|
68 |
-
public static function sanitize_text_field_recursive( $input ) {
|
69 |
-
|
70 |
-
if ( is_array( $input ) || is_object( $input ) ) {
|
71 |
-
|
72 |
-
foreach ( $input as $key => $value ) {
|
73 |
-
|
74 |
-
$input[ sanitize_key( $key ) ] = self::sanitize_text_field_recursive( $value );
|
75 |
-
}
|
76 |
-
|
77 |
-
return $input;
|
78 |
-
}
|
79 |
-
|
80 |
-
return sanitize_text_field( $input );
|
81 |
-
}
|
82 |
-
|
83 |
-
/**
|
84 |
-
* sanitize_recursive for array's each value by applying given sanitization
|
85 |
-
* method, recusivly
|
86 |
-
* @since 2.4.10
|
87 |
-
*/
|
88 |
-
public static function sanitize_recursive( $input, $method ) {
|
89 |
-
|
90 |
-
if ( is_array( $input ) || is_object( $input ) ) {
|
91 |
-
|
92 |
-
foreach ( $input as $key => $value ) {
|
93 |
-
|
94 |
-
$input[ sanitize_key( $key ) ] = self::sanitize_recursive( $value, $method);
|
95 |
-
}
|
96 |
-
|
97 |
-
return $input;
|
98 |
-
}
|
99 |
-
|
100 |
-
return $method( $input );
|
101 |
-
}
|
102 |
-
}
|
103 |
-
|
104 |
}
|
1 |
+
<?php
|
2 |
+
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
+
|
4 |
+
if ( !class_exists( 'rtbHelper' ) ) {
|
5 |
+
/**
|
6 |
+
* Class to to provide helper functions
|
7 |
+
*
|
8 |
+
* @since 2.4.10
|
9 |
+
*/
|
10 |
+
class rtbHelper {
|
11 |
+
|
12 |
+
// Hold the class instance.
|
13 |
+
private static $instance = null;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* The constructor is private
|
17 |
+
* to prevent initiation with outer code.
|
18 |
+
*
|
19 |
+
**/
|
20 |
+
private function __construct() {}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* The object is created from within the class itself
|
24 |
+
* only if the class has no instance.
|
25 |
+
*/
|
26 |
+
public static function getInstance() {
|
27 |
+
|
28 |
+
if ( self::$instance == null ) {
|
29 |
+
|
30 |
+
self::$instance = new rtbHelper();
|
31 |
+
}
|
32 |
+
|
33 |
+
return self::$instance;
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Handle ajax requests from the admin bookings area from logged out users
|
38 |
+
* @since 2.4.10
|
39 |
+
*/
|
40 |
+
public static function admin_nopriv_ajax() {
|
41 |
+
|
42 |
+
wp_send_json_error(
|
43 |
+
array(
|
44 |
+
'error' => 'loggedout',
|
45 |
+
'msg' => sprintf( __( 'You have been logged out. Please %slogin again%s.', 'restaurant-reservations' ), '<a href="' . wp_login_url( admin_url( 'admin.php?page=rtb-dashboard' ) ) . '">', '</a>' ),
|
46 |
+
)
|
47 |
+
);
|
48 |
+
}
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Handle ajax requests where an invalid nonce is passed with the request
|
52 |
+
* @since 2.4.10
|
53 |
+
*/
|
54 |
+
public static function bad_nonce_ajax() {
|
55 |
+
|
56 |
+
wp_send_json_error(
|
57 |
+
array(
|
58 |
+
'error' => 'badnonce',
|
59 |
+
'msg' => __( 'The request has been rejected because it does not appear to have come from this site.', 'restaurant-reservations' ),
|
60 |
+
)
|
61 |
+
);
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* sanitize_text_field for array's each value, recusivly
|
66 |
+
* @since 2.4.10
|
67 |
+
*/
|
68 |
+
public static function sanitize_text_field_recursive( $input ) {
|
69 |
+
|
70 |
+
if ( is_array( $input ) || is_object( $input ) ) {
|
71 |
+
|
72 |
+
foreach ( $input as $key => $value ) {
|
73 |
+
|
74 |
+
$input[ sanitize_key( $key ) ] = self::sanitize_text_field_recursive( $value );
|
75 |
+
}
|
76 |
+
|
77 |
+
return $input;
|
78 |
+
}
|
79 |
+
|
80 |
+
return sanitize_text_field( $input );
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* sanitize_recursive for array's each value by applying given sanitization
|
85 |
+
* method, recusivly
|
86 |
+
* @since 2.4.10
|
87 |
+
*/
|
88 |
+
public static function sanitize_recursive( $input, $method ) {
|
89 |
+
|
90 |
+
if ( is_array( $input ) || is_object( $input ) ) {
|
91 |
+
|
92 |
+
foreach ( $input as $key => $value ) {
|
93 |
+
|
94 |
+
$input[ sanitize_key( $key ) ] = self::sanitize_recursive( $value, $method);
|
95 |
+
}
|
96 |
+
|
97 |
+
return $input;
|
98 |
+
}
|
99 |
+
|
100 |
+
return $method( $input );
|
101 |
+
}
|
102 |
+
}
|
103 |
+
|
104 |
}
|
includes/Import.class.php
CHANGED
@@ -1,279 +1,279 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Class to import bookings from a spreadsheet (not working atm)
|
5 |
-
*/
|
6 |
-
|
7 |
-
if ( !defined( 'ABSPATH' ) )
|
8 |
-
exit;
|
9 |
-
|
10 |
-
if (!class_exists('ComposerAutoloaderInit4618f5c41cf5e27cc7908556f031e4d4')) {require_once RTB_PLUGIN_DIR . '/lib/PHPSpreadsheet/vendor/autoload.php';}
|
11 |
-
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
12 |
-
class rtbImport {
|
13 |
-
|
14 |
-
/**
|
15 |
-
* Hook suffix for the page
|
16 |
-
*
|
17 |
-
* @since 0.1
|
18 |
-
*/
|
19 |
-
public $hook_suffix;
|
20 |
-
|
21 |
-
/**
|
22 |
-
* The success/error message content
|
23 |
-
*
|
24 |
-
* @since 0.1
|
25 |
-
*/
|
26 |
-
public $status;
|
27 |
-
|
28 |
-
/**
|
29 |
-
* Whether the operation was a success or not
|
30 |
-
*
|
31 |
-
* @since 0.1
|
32 |
-
*/
|
33 |
-
public $message;
|
34 |
-
|
35 |
-
public function __construct() {
|
36 |
-
add_action( 'admin_menu', array($this, 'register_install_screen' ));
|
37 |
-
|
38 |
-
if ( isset( $_POST['rtbImport'] ) ) { add_action( 'admin_init', array($this, 'import_bookings' )); }
|
39 |
-
|
40 |
-
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_import_scripts' ) );
|
41 |
-
}
|
42 |
-
|
43 |
-
public function register_install_screen() {
|
44 |
-
$this->hook_suffix = add_submenu_page(
|
45 |
-
'rtb-bookings',
|
46 |
-
_x( 'Import', 'Title of the Imports page', 'restaurant-reservations' ),
|
47 |
-
_x( 'Import', 'Title of Imports link in the admin menu', 'restaurant-reservations' ),
|
48 |
-
'manage_options',
|
49 |
-
'rtb-imports',
|
50 |
-
array( $this, 'display_editor_page' )
|
51 |
-
);
|
52 |
-
|
53 |
-
// Print the error modal and enqueue assets
|
54 |
-
add_action( 'load-' . $this->hook_suffix, array( $this, 'enqueue_admin_assets' ) );
|
55 |
-
}
|
56 |
-
|
57 |
-
public function display_import_screen() {
|
58 |
-
global $rtb_controller;
|
59 |
-
|
60 |
-
$import_permission = $rtb_controller->permissions->check_permission( 'import' );
|
61 |
-
?>
|
62 |
-
<div class='wrap'>
|
63 |
-
<h2>Import</h2>
|
64 |
-
<?php if ( $import_permission ) { ?>
|
65 |
-
<form method='post' enctype="multipart/form-data">
|
66 |
-
<p>
|
67 |
-
<label for="rtb_bookings_spreadsheet"><?php _e("Spreadsheet Containing Bookings", 'restaurant-reservations') ?></label><br />
|
68 |
-
<input name="rtb_bookings_spreadsheet" type="file" value=""/>
|
69 |
-
</p>
|
70 |
-
<input type='submit' name='rtbImport' value='Import Bookings' class='button button-primary' />
|
71 |
-
</form>
|
72 |
-
<?php } else { ?>
|
73 |
-
<div class='rtb-premium-locked'>
|
74 |
-
<a href="https://www.fivestarplugins.com/license-payment/?Selected=RTB&Quantity=1" target="_blank">Upgrade</a> to the premium version to use this feature
|
75 |
-
</div>
|
76 |
-
<?php } ?>
|
77 |
-
</div>
|
78 |
-
<?php }
|
79 |
-
|
80 |
-
public function import_bookings() {
|
81 |
-
global $rtb_controller;
|
82 |
-
|
83 |
-
$fields = $rtb_controller->settings->get_booking_form_fields();
|
84 |
-
|
85 |
-
$update = $this->handle_spreadsheet_upload();
|
86 |
-
|
87 |
-
if ( $update['message_type'] != 'Success' ) :
|
88 |
-
$this->status = false;
|
89 |
-
$this->message = $update['message'];
|
90 |
-
|
91 |
-
add_action( 'admin_notices', array( $this, 'display_notice' ) );
|
92 |
-
|
93 |
-
return;
|
94 |
-
endif;
|
95 |
-
|
96 |
-
$excel_url = RTB_PLUGIN_DIR . '/user-sheets/' . $update['filename'];
|
97 |
-
|
98 |
-
// Build the workbook object out of the uploaded spreadsheet
|
99 |
-
$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($excel_url);
|
100 |
-
|
101 |
-
// Create a worksheet object out of the product sheet in the workbook
|
102 |
-
$sheet = $spreadsheet->getActiveSheet();
|
103 |
-
|
104 |
-
$allowable_fields = array();
|
105 |
-
foreach ($fields as $field) {$allowable_fields[] = $field->name;}
|
106 |
-
//List of fields that can be accepted via upload
|
107 |
-
//$allowed_fields = array("ID", "Title", "Description", "Price", "Sections");
|
108 |
-
|
109 |
-
|
110 |
-
// Get column names
|
111 |
-
$highest_column = $sheet->getHighestColumn();
|
112 |
-
$highest_column_index = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($highest_column);
|
113 |
-
for ($column = 1; $column <= $highest_column_index; $column++) {
|
114 |
-
/*if (trim($sheet->getCellByColumnAndRow($column, 1)->getValue()) == "ID") {$ID_column = $column;}
|
115 |
-
if (trim($sheet->getCellByColumnAndRow($column, 1)->getValue()) == "Title") {$title_column = $column;}
|
116 |
-
if (trim($sheet->getCellByColumnAndRow($column, 1)->getValue()) == "Description") {$description_column = $column;}
|
117 |
-
if (trim($sheet->getCellByColumnAndRow($column, 1)->getValue()) == "Price") {$price_column = $column;}
|
118 |
-
if (trim($sheet->getCellByColumnAndRow($column, 1)->getValue()) == "Sections") {$sections_column = $column;}*/
|
119 |
-
|
120 |
-
foreach ($fields as $key => $field) {
|
121 |
-
if (trim($sheet->getCellByColumnAndRow($column, 1)->getValue()) == $field->name) {$field->column = $column;}
|
122 |
-
}
|
123 |
-
}
|
124 |
-
|
125 |
-
|
126 |
-
// Put the spreadsheet data into a multi-dimensional array to facilitate processing
|
127 |
-
$highest_row = $sheet->getHighestRow();
|
128 |
-
for ($row = 2; $row <= $highest_row; $row++) {
|
129 |
-
for ($column = 1; $column <= $highest_column_index; $column++) {
|
130 |
-
$data[$row][$column] = $sheet->getCellByColumnAndRow($column, $row)->getValue();
|
131 |
-
}
|
132 |
-
}
|
133 |
-
|
134 |
-
// Create the query to insert the products one at a time into the database and then run it
|
135 |
-
foreach ($data as $booking) {
|
136 |
-
// Create an array of the values that are being inserted for each order,
|
137 |
-
// edit if it's a current order, otherwise add it
|
138 |
-
foreach ($booking as $col_index => $value) {
|
139 |
-
if ($col_index == $ID_column and $ID_column !== null) {$post['ID'] = esc_sql($value);}
|
140 |
-
if ($col_index == $title_column and $title_column !== null) {$post['post_title'] = esc_sql($value);}
|
141 |
-
if ($col_index == $description_column and $description_column !== null) {$post['post_content'] = esc_sql($value);}
|
142 |
-
if ($col_index == $price_column and $price_column !== null) {$post_prices = explode(",", esc_sql($value));}
|
143 |
-
if (isset($sections_column) and $col_index == $sections_column and $sections_column !== null) {$post_sections = explode(",", esc_sql($value));}
|
144 |
-
}
|
145 |
-
|
146 |
-
if (!is_array($post_prices)) {$post_prices = array();}
|
147 |
-
if (!is_array($post_sections)) {$post_sections = array();}
|
148 |
-
|
149 |
-
if ($post['post_title'] == '') {continue;}
|
150 |
-
|
151 |
-
$post['post_status'] = 'publish';
|
152 |
-
$post['post_type'] = RTB_BOOKING_POST_TYPE;
|
153 |
-
|
154 |
-
if ( isset( $post['ID'] ) and $post['ID'] != '') { $post_id = wp_update_post($post); }
|
155 |
-
else { $post_id = wp_insert_post($post); }
|
156 |
-
|
157 |
-
if ( $post_id != 0 ) {
|
158 |
-
foreach ( $post_sections as $section ) {
|
159 |
-
$menu_section = term_exists($section, 'fdm-menu-section');
|
160 |
-
if ( $menu_section !== 0 && $menu_section !== null ) { $menu_section_ids[] = (int) $menu_section['term_id']; }
|
161 |
-
}
|
162 |
-
if ( isset($menu_section_ids) and is_array($menu_section_ids) ) { wp_set_object_terms($post_id, $menu_section_ids, 'fdm-menu-section'); }
|
163 |
-
|
164 |
-
update_post_meta( $post_id, 'fdm_item_price', implode(",", $post_prices) );
|
165 |
-
|
166 |
-
$field_values = array();
|
167 |
-
foreach ( $fields as $field ) {
|
168 |
-
if (isset($field->column) and isset($menu_item[$field->column])) {
|
169 |
-
$field_values[$field->slug] = esc_sql($menu_item[$field->column]);
|
170 |
-
|
171 |
-
}
|
172 |
-
}
|
173 |
-
update_post_meta($post_id, '_fdm_menu_item_custom_fields', $field_values);
|
174 |
-
}
|
175 |
-
|
176 |
-
unset($post);
|
177 |
-
unset($post_sections);
|
178 |
-
unset($menu_section_ids);
|
179 |
-
unset($post_prices);
|
180 |
-
unset($field_values);
|
181 |
-
}
|
182 |
-
|
183 |
-
$this->status = true;
|
184 |
-
$this->message = __("Reservations added successfully.", 'restaurant-reservations');
|
185 |
-
|
186 |
-
add_action( 'admin_notices', array( $this, 'display_notice' ) );
|
187 |
-
}
|
188 |
-
|
189 |
-
function handle_spreadsheet_upload() {
|
190 |
-
/* Test if there is an error with the uploaded spreadsheet and return that error if there is */
|
191 |
-
if (!empty($_FILES['fdm_menu_items_spreadsheet']['error']))
|
192 |
-
{
|
193 |
-
switch($_FILES['fdm_menu_items_spreadsheet']['error'])
|
194 |
-
{
|
195 |
-
|
196 |
-
case '1':
|
197 |
-
$error = __('The uploaded file exceeds the upload_max_filesize directive in php.ini', 'restaurant-reservations');
|
198 |
-
break;
|
199 |
-
case '2':
|
200 |
-
$error = __('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form', 'restaurant-reservations');
|
201 |
-
break;
|
202 |
-
case '3':
|
203 |
-
$error = __('The uploaded file was only partially uploaded', 'restaurant-reservations');
|
204 |
-
break;
|
205 |
-
case '4':
|
206 |
-
$error = __('No file was uploaded.', 'restaurant-reservations');
|
207 |
-
break;
|
208 |
-
|
209 |
-
case '6':
|
210 |
-
$error = __('Missing a temporary folder', 'restaurant-reservations');
|
211 |
-
break;
|
212 |
-
case '7':
|
213 |
-
$error = __('Failed to write file to disk', 'restaurant-reservations');
|
214 |
-
break;
|
215 |
-
case '8':
|
216 |
-
$error = __('File upload stopped by extension', 'restaurant-reservations');
|
217 |
-
break;
|
218 |
-
case '999':
|
219 |
-
default:
|
220 |
-
$error = __('No error code avaiable', 'restaurant-reservations');
|
221 |
-
}
|
222 |
-
}
|
223 |
-
/* Make sure that the file exists */
|
224 |
-
elseif (empty($_FILES['fdm_menu_items_spreadsheet']['tmp_name']) || $_FILES['fdm_menu_items_spreadsheet']['tmp_name'] == 'none') {
|
225 |
-
$error = __('No file was uploaded here..', 'restaurant-reservations');
|
226 |
-
}
|
227 |
-
/* Move the file and store the URL to pass it onwards*/
|
228 |
-
/* Check that it is a .xls or .xlsx file */
|
229 |
-
if(!isset($_FILES['fdm_menu_items_spreadsheet']['name']) or (!preg_match("/\.(xls.?)$/", $_FILES['fdm_menu_items_spreadsheet']['name']) and !preg_match("/\.(csv.?)$/", $_FILES['fdm_menu_items_spreadsheet']['name']))) {
|
230 |
-
$error = __('File must be .csv, .xls or .xlsx', 'restaurant-reservations');
|
231 |
-
}
|
232 |
-
else {
|
233 |
-
$filename = basename( $_FILES['fdm_menu_items_spreadsheet']['name']);
|
234 |
-
$filename = mb_ereg_replace("([^\w\s\d\-_~,;\[\]\(\).])", '', $filename);
|
235 |
-
$filename = mb_ereg_replace("([\.]{2,})", '', $filename);
|
236 |
-
|
237 |
-
//for security reason, we force to remove all uploaded file
|
238 |
-
$target_path = RTB_PLUGIN_DIR . "/user-sheets/";
|
239 |
-
|
240 |
-
$target_path = $target_path . $filename;
|
241 |
-
|
242 |
-
if (!move_uploaded_file($_FILES['fdm_menu_items_spreadsheet']['tmp_name'], $target_path)) {
|
243 |
-
$error .= "There was an error uploading the file, please try again!";
|
244 |
-
}
|
245 |
-
else {
|
246 |
-
$excel_file_name = $filename;
|
247 |
-
}
|
248 |
-
}
|
249 |
-
|
250 |
-
/* Pass the data to the appropriate function in Update_Admin_Databases.php to create the products */
|
251 |
-
if (!isset($error)) {
|
252 |
-
$update = array("message_type" => "Success", "filename" => $excel_file_name);
|
253 |
-
}
|
254 |
-
else {
|
255 |
-
$update = array("message_type" => "Error", "message" => $error);
|
256 |
-
}
|
257 |
-
return $update;
|
258 |
-
}
|
259 |
-
|
260 |
-
public function enqueue_import_scripts() {
|
261 |
-
$screen = get_current_screen();
|
262 |
-
if($screen->id == 'rtb-menu_page_rtb-import'){
|
263 |
-
wp_enqueue_style( 'rtb-admin-css', RTB_PLUGIN_URL . '/assets/css/admin.css', array(), RTB_VERSION );
|
264 |
-
wp_enqueue_script( 'rtb-admin-js', RTB_PLUGIN_URL . '/assets/js/admin.js', array( 'jquery' ), RTB_VERSION, true );
|
265 |
-
}
|
266 |
-
}
|
267 |
-
|
268 |
-
public function display_notice() {
|
269 |
-
if ( $this->status ) {
|
270 |
-
echo "<div class='updated'><p>" . $this->message . "</p></div>";
|
271 |
-
}
|
272 |
-
else {
|
273 |
-
echo "<div class='error'><p>" . $this->message . "</p></div>";
|
274 |
-
}
|
275 |
-
}
|
276 |
-
|
277 |
-
}
|
278 |
-
|
279 |
-
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Class to import bookings from a spreadsheet (not working atm)
|
5 |
+
*/
|
6 |
+
|
7 |
+
if ( !defined( 'ABSPATH' ) )
|
8 |
+
exit;
|
9 |
+
|
10 |
+
if (!class_exists('ComposerAutoloaderInit4618f5c41cf5e27cc7908556f031e4d4')) {require_once RTB_PLUGIN_DIR . '/lib/PHPSpreadsheet/vendor/autoload.php';}
|
11 |
+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
12 |
+
class rtbImport {
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Hook suffix for the page
|
16 |
+
*
|
17 |
+
* @since 0.1
|
18 |
+
*/
|
19 |
+
public $hook_suffix;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* The success/error message content
|
23 |
+
*
|
24 |
+
* @since 0.1
|
25 |
+
*/
|
26 |
+
public $status;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Whether the operation was a success or not
|
30 |
+
*
|
31 |
+
* @since 0.1
|
32 |
+
*/
|
33 |
+
public $message;
|
34 |
+
|
35 |
+
public function __construct() {
|
36 |
+
add_action( 'admin_menu', array($this, 'register_install_screen' ));
|
37 |
+
|
38 |
+
if ( isset( $_POST['rtbImport'] ) ) { add_action( 'admin_init', array($this, 'import_bookings' )); }
|
39 |
+
|
40 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_import_scripts' ) );
|
41 |
+
}
|
42 |
+
|
43 |
+
public function register_install_screen() {
|
44 |
+
$this->hook_suffix = add_submenu_page(
|
45 |
+
'rtb-bookings',
|
46 |
+
_x( 'Import', 'Title of the Imports page', 'restaurant-reservations' ),
|
47 |
+
_x( 'Import', 'Title of Imports link in the admin menu', 'restaurant-reservations' ),
|
48 |
+
'manage_options',
|
49 |
+
'rtb-imports',
|
50 |
+
array( $this, 'display_editor_page' )
|
51 |
+
);
|
52 |
+
|
53 |
+
// Print the error modal and enqueue assets
|
54 |
+
add_action( 'load-' . $this->hook_suffix, array( $this, 'enqueue_admin_assets' ) );
|
55 |
+
}
|
56 |
+
|
57 |
+
public function display_import_screen() {
|
58 |
+
global $rtb_controller;
|
59 |
+
|
60 |
+
$import_permission = $rtb_controller->permissions->check_permission( 'import' );
|
61 |
+
?>
|
62 |
+
<div class='wrap'>
|
63 |
+
<h2>Import</h2>
|
64 |
+
<?php if ( $import_permission ) { ?>
|
65 |
+
<form method='post' enctype="multipart/form-data">
|
66 |
+
<p>
|
67 |
+
<label for="rtb_bookings_spreadsheet"><?php _e("Spreadsheet Containing Bookings", 'restaurant-reservations') ?></label><br />
|
68 |
+
<input name="rtb_bookings_spreadsheet" type="file" value=""/>
|
69 |
+
</p>
|
70 |
+
<input type='submit' name='rtbImport' value='Import Bookings' class='button button-primary' />
|
71 |
+
</form>
|
72 |
+
<?php } else { ?>
|
73 |
+
<div class='rtb-premium-locked'>
|
74 |
+
<a href="https://www.fivestarplugins.com/license-payment/?Selected=RTB&Quantity=1" target="_blank">Upgrade</a> to the premium version to use this feature
|
75 |
+
</div>
|
76 |
+
<?php } ?>
|
77 |
+
</div>
|
78 |
+
<?php }
|
79 |
+
|
80 |
+
public function import_bookings() {
|
81 |
+
global $rtb_controller;
|
82 |
+
|
83 |
+
$fields = $rtb_controller->settings->get_booking_form_fields();
|
84 |
+
|
85 |
+
$update = $this->handle_spreadsheet_upload();
|
86 |
+
|
87 |
+
if ( $update['message_type'] != 'Success' ) :
|
88 |
+
$this->status = false;
|
89 |
+
$this->message = $update['message'];
|
90 |
+
|
91 |
+
add_action( 'admin_notices', array( $this, 'display_notice' ) );
|
92 |
+
|
93 |
+
return;
|
94 |
+
endif;
|
95 |
+
|
96 |
+
$excel_url = RTB_PLUGIN_DIR . '/user-sheets/' . $update['filename'];
|
97 |
+
|
98 |
+
// Build the workbook object out of the uploaded spreadsheet
|
99 |
+
$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($excel_url);
|
100 |
+
|
101 |
+
// Create a worksheet object out of the product sheet in the workbook
|
102 |
+
$sheet = $spreadsheet->getActiveSheet();
|
103 |
+
|
104 |
+
$allowable_fields = array();
|
105 |
+
foreach ($fields as $field) {$allowable_fields[] = $field->name;}
|
106 |
+
//List of fields that can be accepted via upload
|
107 |
+
//$allowed_fields = array("ID", "Title", "Description", "Price", "Sections");
|
108 |
+
|
109 |
+
|
110 |
+
// Get column names
|
111 |
+
$highest_column = $sheet->getHighestColumn();
|
112 |
+
$highest_column_index = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($highest_column);
|
113 |
+
for ($column = 1; $column <= $highest_column_index; $column++) {
|
114 |
+
/*if (trim($sheet->getCellByColumnAndRow($column, 1)->getValue()) == "ID") {$ID_column = $column;}
|
115 |
+
if (trim($sheet->getCellByColumnAndRow($column, 1)->getValue()) == "Title") {$title_column = $column;}
|
116 |
+
if (trim($sheet->getCellByColumnAndRow($column, 1)->getValue()) == "Description") {$description_column = $column;}
|
117 |
+
if (trim($sheet->getCellByColumnAndRow($column, 1)->getValue()) == "Price") {$price_column = $column;}
|
118 |
+
if (trim($sheet->getCellByColumnAndRow($column, 1)->getValue()) == "Sections") {$sections_column = $column;}*/
|
119 |
+
|
120 |
+
foreach ($fields as $key => $field) {
|
121 |
+
if (trim($sheet->getCellByColumnAndRow($column, 1)->getValue()) == $field->name) {$field->column = $column;}
|
122 |
+
}
|
123 |
+
}
|
124 |
+
|
125 |
+
|
126 |
+
// Put the spreadsheet data into a multi-dimensional array to facilitate processing
|
127 |
+
$highest_row = $sheet->getHighestRow();
|
128 |
+
for ($row = 2; $row <= $highest_row; $row++) {
|
129 |
+
for ($column = 1; $column <= $highest_column_index; $column++) {
|
130 |
+
$data[$row][$column] = $sheet->getCellByColumnAndRow($column, $row)->getValue();
|
131 |
+
}
|
132 |
+
}
|
133 |
+
|
134 |
+
// Create the query to insert the products one at a time into the database and then run it
|
135 |
+
foreach ($data as $booking) {
|
136 |
+
// Create an array of the values that are being inserted for each order,
|
137 |
+
// edit if it's a current order, otherwise add it
|
138 |
+
foreach ($booking as $col_index => $value) {
|
139 |
+
if ($col_index == $ID_column and $ID_column !== null) {$post['ID'] = esc_sql($value);}
|
140 |
+
if ($col_index == $title_column and $title_column !== null) {$post['post_title'] = esc_sql($value);}
|
141 |
+
if ($col_index == $description_column and $description_column !== null) {$post['post_content'] = esc_sql($value);}
|
142 |
+
if ($col_index == $price_column and $price_column !== null) {$post_prices = explode(",", esc_sql($value));}
|
143 |
+
if (isset($sections_column) and $col_index == $sections_column and $sections_column !== null) {$post_sections = explode(",", esc_sql($value));}
|
144 |
+
}
|
145 |
+
|
146 |
+
if (!is_array($post_prices)) {$post_prices = array();}
|
147 |
+
if (!is_array($post_sections)) {$post_sections = array();}
|
148 |
+
|
149 |
+
if ($post['post_title'] == '') {continue;}
|
150 |
+
|
151 |
+
$post['post_status'] = 'publish';
|
152 |
+
$post['post_type'] = RTB_BOOKING_POST_TYPE;
|
153 |
+
|
154 |
+
if ( isset( $post['ID'] ) and $post['ID'] != '') { $post_id = wp_update_post($post); }
|
155 |
+
else { $post_id = wp_insert_post($post); }
|
156 |
+
|
157 |
+
if ( $post_id != 0 ) {
|
158 |
+
foreach ( $post_sections as $section ) {
|
159 |
+
$menu_section = term_exists($section, 'fdm-menu-section');
|
160 |
+
if ( $menu_section !== 0 && $menu_section !== null ) { $menu_section_ids[] = (int) $menu_section['term_id']; }
|
161 |
+
}
|
162 |
+
if ( isset($menu_section_ids) and is_array($menu_section_ids) ) { wp_set_object_terms($post_id, $menu_section_ids, 'fdm-menu-section'); }
|
163 |
+
|
164 |
+
update_post_meta( $post_id, 'fdm_item_price', implode(",", $post_prices) );
|
165 |
+
|
166 |
+
$field_values = array();
|
167 |
+
foreach ( $fields as $field ) {
|
168 |
+
if (isset($field->column) and isset($menu_item[$field->column])) {
|
169 |
+
$field_values[$field->slug] = esc_sql($menu_item[$field->column]);
|
170 |
+
|
171 |
+
}
|
172 |
+
}
|
173 |
+
update_post_meta($post_id, '_fdm_menu_item_custom_fields', $field_values);
|
174 |
+
}
|
175 |
+
|
176 |
+
unset($post);
|
177 |
+
unset($post_sections);
|
178 |
+
unset($menu_section_ids);
|
179 |
+
unset($post_prices);
|
180 |
+
unset($field_values);
|
181 |
+
}
|
182 |
+
|
183 |
+
$this->status = true;
|
184 |
+
$this->message = __("Reservations added successfully.", 'restaurant-reservations');
|
185 |
+
|
186 |
+
add_action( 'admin_notices', array( $this, 'display_notice' ) );
|
187 |
+
}
|
188 |
+
|
189 |
+
function handle_spreadsheet_upload() {
|
190 |
+
/* Test if there is an error with the uploaded spreadsheet and return that error if there is */
|
191 |
+
if (!empty($_FILES['fdm_menu_items_spreadsheet']['error']))
|
192 |
+
{
|
193 |
+
switch($_FILES['fdm_menu_items_spreadsheet']['error'])
|
194 |
+
{
|
195 |
+
|
196 |
+
case '1':
|
197 |
+
$error = __('The uploaded file exceeds the upload_max_filesize directive in php.ini', 'restaurant-reservations');
|
198 |
+
break;
|
199 |
+
case '2':
|
200 |
+
$error = __('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form', 'restaurant-reservations');
|
201 |
+
break;
|
202 |
+
case '3':
|
203 |
+
$error = __('The uploaded file was only partially uploaded', 'restaurant-reservations');
|
204 |
+
break;
|
205 |
+
case '4':
|
206 |
+
$error = __('No file was uploaded.', 'restaurant-reservations');
|
207 |
+
break;
|
208 |
+
|
209 |
+
case '6':
|
210 |
+
$error = __('Missing a temporary folder', 'restaurant-reservations');
|
211 |
+
break;
|
212 |
+
case '7':
|
213 |
+
$error = __('Failed to write file to disk', 'restaurant-reservations');
|
214 |
+
break;
|
215 |
+
case '8':
|
216 |
+
$error = __('File upload stopped by extension', 'restaurant-reservations');
|
217 |
+
break;
|
218 |
+
case '999':
|
219 |
+
default:
|
220 |
+
$error = __('No error code avaiable', 'restaurant-reservations');
|
221 |
+
}
|
222 |
+
}
|
223 |
+
/* Make sure that the file exists */
|
224 |
+
elseif (empty($_FILES['fdm_menu_items_spreadsheet']['tmp_name']) || $_FILES['fdm_menu_items_spreadsheet']['tmp_name'] == 'none') {
|
225 |
+
$error = __('No file was uploaded here..', 'restaurant-reservations');
|
226 |
+
}
|
227 |
+
/* Move the file and store the URL to pass it onwards*/
|
228 |
+
/* Check that it is a .xls or .xlsx file */
|
229 |
+
if(!isset($_FILES['fdm_menu_items_spreadsheet']['name']) or (!preg_match("/\.(xls.?)$/", $_FILES['fdm_menu_items_spreadsheet']['name']) and !preg_match("/\.(csv.?)$/", $_FILES['fdm_menu_items_spreadsheet']['name']))) {
|
230 |
+
$error = __('File must be .csv, .xls or .xlsx', 'restaurant-reservations');
|
231 |
+
}
|
232 |
+
else {
|
233 |
+
$filename = basename( $_FILES['fdm_menu_items_spreadsheet']['name']);
|
234 |
+
$filename = mb_ereg_replace("([^\w\s\d\-_~,;\[\]\(\).])", '', $filename);
|
235 |
+
$filename = mb_ereg_replace("([\.]{2,})", '', $filename);
|
236 |
+
|
237 |
+
//for security reason, we force to remove all uploaded file
|
238 |
+
$target_path = RTB_PLUGIN_DIR . "/user-sheets/";
|
239 |
+
|
240 |
+
$target_path = $target_path . $filename;
|
241 |
+
|
242 |
+
if (!move_uploaded_file($_FILES['fdm_menu_items_spreadsheet']['tmp_name'], $target_path)) {
|
243 |
+
$error .= "There was an error uploading the file, please try again!";
|
244 |
+
}
|
245 |
+
else {
|
246 |
+
$excel_file_name = $filename;
|
247 |
+
}
|
248 |
+
}
|
249 |
+
|
250 |
+
/* Pass the data to the appropriate function in Update_Admin_Databases.php to create the products */
|
251 |
+
if (!isset($error)) {
|
252 |
+
$update = array("message_type" => "Success", "filename" => $excel_file_name);
|
253 |
+
}
|
254 |
+
else {
|
255 |
+
$update = array("message_type" => "Error", "message" => $error);
|
256 |
+
}
|
257 |
+
return $update;
|
258 |
+
}
|
259 |
+
|
260 |
+
public function enqueue_import_scripts() {
|
261 |
+
$screen = get_current_screen();
|
262 |
+
if($screen->id == 'rtb-menu_page_rtb-import'){
|
263 |
+
wp_enqueue_style( 'rtb-admin-css', RTB_PLUGIN_URL . '/assets/css/admin.css', array(), RTB_VERSION );
|
264 |
+
wp_enqueue_script( 'rtb-admin-js', RTB_PLUGIN_URL . '/assets/js/admin.js', array( 'jquery' ), RTB_VERSION, true );
|
265 |
+
}
|
266 |
+
}
|
267 |
+
|
268 |
+
public function display_notice() {
|
269 |
+
if ( $this->status ) {
|
270 |
+
echo "<div class='updated'><p>" . $this->message . "</p></div>";
|
271 |
+
}
|
272 |
+
else {
|
273 |
+
echo "<div class='error'><p>" . $this->message . "</p></div>";
|
274 |
+
}
|
275 |
+
}
|
276 |
+
|
277 |
+
}
|
278 |
+
|
279 |
+
|
includes/InstallationWalkthrough.class.php
CHANGED
@@ -1,425 +1,425 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Class to handle everything related to the walk-through that runs on plugin activation
|
5 |
-
*/
|
6 |
-
|
7 |
-
if ( !defined( 'ABSPATH' ) )
|
8 |
-
exit;
|
9 |
-
|
10 |
-
class rtbInstallationWalkthrough {
|
11 |
-
|
12 |
-
public function __construct() {
|
13 |
-
add_action( 'admin_menu', array( $this, 'register_install_screen' ) );
|
14 |
-
add_action( 'admin_head', array( $this, 'hide_install_screen_menu_item' ) );
|
15 |
-
add_action( 'admin_init', array( $this, 'redirect' ), 9999 );
|
16 |
-
|
17 |
-
add_action( 'admin_head', array( $this, 'admin_enqueue') );
|
18 |
-
|
19 |
-
add_action( 'wp_ajax_nopriv_rtb-welcome-add-menu-page' , array( 'rtbHelper' , 'admin_nopriv_ajax' ) );
|
20 |
-
add_action( 'wp_ajax_rtb-welcome-add-menu-page', array( $this, 'add_reservations_page' ) );
|
21 |
-
add_action( 'wp_ajax_nopriv_rtb-welcome-set-schedule' , array( 'rtbHelper' , 'admin_nopriv_ajax' ) );
|
22 |
-
add_action( 'wp_ajax_rtb-welcome-set-schedule', array( $this, 'set_schedule' ) );
|
23 |
-
add_action( 'wp_ajax_nopriv_rtb-welcome-set-options' , array( 'rtbHelper' , 'admin_nopriv_ajax' ) );
|
24 |
-
add_action( 'wp_ajax_rtb-welcome-set-options', array( $this, 'set_options' ) );
|
25 |
-
}
|
26 |
-
|
27 |
-
public function redirect() {
|
28 |
-
if ( ! get_transient( 'rtb-getting-started' ) )
|
29 |
-
return;
|
30 |
-
|
31 |
-
delete_transient( 'rtb-getting-started' );
|
32 |
-
|
33 |
-
if ( is_network_admin() || isset( $_GET['activate-multi'] ) )
|
34 |
-
return;
|
35 |
-
|
36 |
-
$bookings = get_posts( array( 'post_type' => 'rtb-booking', 'post_status' => 'any' ) );
|
37 |
-
if ( ! empty( $bookings ) ) {
|
38 |
-
set_transient( 'rtb-admin-install-notice', true, 5 );
|
39 |
-
return;
|
40 |
-
}
|
41 |
-
|
42 |
-
wp_safe_redirect( admin_url( 'index.php?page=rtb-getting-started' ) );
|
43 |
-
exit;
|
44 |
-
}
|
45 |
-
|
46 |
-
public function register_install_screen() {
|
47 |
-
add_dashboard_page(
|
48 |
-
esc_html__( 'Five-Star Restaurant Reservations - Welcome!', 'restaurant-reservations' ),
|
49 |
-
esc_html__( 'Five-Star Restaurant Reservations - Welcome!', 'restaurant-reservations' ),
|
50 |
-
'manage_options',
|
51 |
-
'rtb-getting-started',
|
52 |
-
array($this, 'display_install_screen')
|
53 |
-
);
|
54 |
-
}
|
55 |
-
|
56 |
-
public function hide_install_screen_menu_item() {
|
57 |
-
remove_submenu_page( 'index.php', 'rtb-getting-started' );
|
58 |
-
}
|
59 |
-
|
60 |
-
public function add_reservations_page() {
|
61 |
-
|
62 |
-
// Authenticate request
|
63 |
-
if ( !check_ajax_referer( 'rtb-getting-started', 'nonce' ) || !current_user_can( 'manage_bookings' ) ) {
|
64 |
-
rtbHelper::admin_nopriv_ajax();
|
65 |
-
}
|
66 |
-
|
67 |
-
$reservations_page = wp_insert_post(array(
|
68 |
-
'post_title' => (isset($_POST['reservations_page_title']) ? sanitize_text_field( $_POST['reservations_page_title'] ) : ''),
|
69 |
-
'post_content' => '',
|
70 |
-
'post_status' => 'publish',
|
71 |
-
'post_type' => 'page'
|
72 |
-
));
|
73 |
-
|
74 |
-
if ( $reservations_page ) {
|
75 |
-
$rtb_options = get_option( 'rtb-settings' );
|
76 |
-
$rtb_options['booking-page'] = $reservations_page;
|
77 |
-
update_option( 'rtb-settings', $rtb_options );
|
78 |
-
}
|
79 |
-
|
80 |
-
exit();
|
81 |
-
}
|
82 |
-
|
83 |
-
public function set_schedule() {
|
84 |
-
|
85 |
-
// Authenticate request
|
86 |
-
if ( !check_ajax_referer( 'rtb-getting-started', 'nonce' ) || !current_user_can( 'manage_bookings' ) ) {
|
87 |
-
rtbHelper::admin_nopriv_ajax();
|
88 |
-
}
|
89 |
-
|
90 |
-
$rtb_options = get_option( 'rtb-settings' );
|
91 |
-
|
92 |
-
$rtb_options['schedule-open'] = rtbHelper::sanitize_text_field_recursive( $_POST['schedule_open'] );
|
93 |
-
|
94 |
-
update_option( 'rtb-settings', $rtb_options );
|
95 |
-
|
96 |
-
exit();
|
97 |
-
}
|
98 |
-
|
99 |
-
public function set_options() {
|
100 |
-
|
101 |
-
// Authenticate request
|
102 |
-
if ( !check_ajax_referer( 'rtb-getting-started', 'nonce' ) || !current_user_can( 'manage_bookings' ) ) {
|
103 |
-
rtbHelper::admin_nopriv_ajax();
|
104 |
-
}
|
105 |
-
|
106 |
-
$rtb_options = get_option( 'rtb-settings' );
|
107 |
-
$rtb_options['party-size-min'] = sanitize_text_field( $_POST['party_size_min'] );
|
108 |
-
$rtb_options['party-size'] = sanitize_text_field( $_POST['party_size'] );
|
109 |
-
$rtb_options['early-bookings'] = sanitize_text_field( $_POST['early_bookings'] );
|
110 |
-
$rtb_options['late-bookings'] = sanitize_text_field( $_POST['late_bookings'] );
|
111 |
-
$rtb_options['time-interval'] = sanitize_text_field( $_POST['time_interval'] );
|
112 |
-
update_option( 'rtb-settings', $rtb_options );
|
113 |
-
|
114 |
-
exit();
|
115 |
-
}
|
116 |
-
|
117 |
-
function admin_enqueue() {
|
118 |
-
|
119 |
-
if ( ! isset( $_GET['page'] ) or $_GET['page'] != 'rtb-getting-started' ) { return; }
|
120 |
-
|
121 |
-
wp_enqueue_style( 'rtb-admin-css', RTB_PLUGIN_URL . '/lib/simple-admin-pages/css/admin.css', array(), RTB_VERSION );
|
122 |
-
wp_enqueue_style( 'rtb-welcome-screen', RTB_PLUGIN_URL . '/assets/css/admin-rtb-welcome-screen.css', array(), RTB_VERSION );
|
123 |
-
wp_enqueue_style( 'pickadate-default', RTB_PLUGIN_URL . '/lib/simple-admin-pages/lib/pickadate/themes/default.css', array(), RTB_VERSION );
|
124 |
-
wp_enqueue_style( 'pickadate-date', RTB_PLUGIN_URL . '/lib/simple-admin-pages/lib/pickadate/themes/default.date.css', array(), RTB_VERSION );
|
125 |
-
wp_enqueue_style( 'pickadate-time', RTB_PLUGIN_URL . '/lib/simple-admin-pages/lib/pickadate/themes/default.time.css', array(), RTB_VERSION );
|
126 |
-
|
127 |
-
wp_enqueue_script( 'rtb-getting-started', RTB_PLUGIN_URL . '/assets/js/admin-rtb-welcome-screen.js', array('jquery'), RTB_VERSION );
|
128 |
-
wp_localize_script(
|
129 |
-
'rtb-getting-started',
|
130 |
-
'rtb_getting_started',
|
131 |
-
array(
|
132 |
-
'nonce' => wp_create_nonce( 'rtb-getting-started' )
|
133 |
-
)
|
134 |
-
);
|
135 |
-
|
136 |
-
wp_enqueue_script( 'pickadate', RTB_PLUGIN_URL . '/lib/simple-admin-pages/lib/pickadate/picker.js', array('jquery'), RTB_VERSION, true );
|
137 |
-
wp_enqueue_script( 'pickadate-date', RTB_PLUGIN_URL . '/lib/simple-admin-pages/lib/pickadate/picker.date.js', array('jquery'), RTB_VERSION, true );
|
138 |
-
wp_enqueue_script( 'pickadate-time', RTB_PLUGIN_URL . '/lib/simple-admin-pages/lib/pickadate/picker.time.js', array('jquery'), RTB_VERSION, true );
|
139 |
-
wp_enqueue_script( 'pickadate-legacy', RTB_PLUGIN_URL . '/lib/simple-admin-pages/lib/pickadate/legacy.js', array('jquery'), RTB_VERSION, true );
|
140 |
-
wp_enqueue_script( 'sap-scheduler', RTB_PLUGIN_URL . '/lib/simple-admin-pages/js/scheduler.js', array('jquery'), RTB_VERSION, true );
|
141 |
-
|
142 |
-
$sap_scheduler_settings[ 'schedule-open' ] = array(
|
143 |
-
'time_interval' => 15,
|
144 |
-
'time_format' => 'h:i A',
|
145 |
-
'date_format' => 'd mmmm, yyyy',
|
146 |
-
'template' => $this->get_template(),
|
147 |
-
'weekdays' => array(
|
148 |
-
'monday' => 'Mo',
|
149 |
-
'tuesday' => 'Tu',
|
150 |
-
'wednesday' => 'We',
|
151 |
-
'thursday' => 'Th',
|
152 |
-
'friday' => 'Fr',
|
153 |
-
'saturday' => 'Sa',
|
154 |
-
'sunday' => 'Su',
|
155 |
-
),
|
156 |
-
'weeks' => array(
|
157 |
-
'first' => '1st',
|
158 |
-
'second' => '2nd',
|
159 |
-
'third' => '3rd',
|
160 |
-
'fourth' => '4th',
|
161 |
-
'last' => 'last',
|
162 |
-
),
|
163 |
-
'disable_weekdays' => false,
|
164 |
-
'disable_weeks' => true,
|
165 |
-
'disable_date' => true,
|
166 |
-
'disable_time' => false,
|
167 |
-
'disable_multiple' => false,
|
168 |
-
'summaries' => array(
|
169 |
-
'never' => __( 'Never', 'restaurant-reservations' ),
|
170 |
-
'weekly_always' => __( 'Every day', 'restaurant-reservations' ),
|
171 |
-
'monthly_weekdays' => sprintf( __( '%s on the %s week of the month', 'restaurant-reservations' ), '{days}', '{weeks}' ),
|
172 |
-
'monthly_weeks' => sprintf( __( '%s week of the month', 'restaurant-reservations' ), '{weeks}' ),
|
173 |
-
'all_day' => __( 'All day', 'restaurant-reservations' ),
|
174 |
-
'before' => __( 'Ends at', 'restaurant-reservations' ),
|
175 |
-
'after' => __( 'Starts at', 'restaurant-reservations' ),
|
176 |
-
'separator' => __( '&mdash', 'restaurant-reservations' ),
|
177 |
-
),
|
178 |
-
);
|
179 |
-
|
180 |
-
// This gets called multiple times, but only the last call is actually
|
181 |
-
// pushed to the script.
|
182 |
-
wp_localize_script(
|
183 |
-
'sap-scheduler',
|
184 |
-
'sap_scheduler',
|
185 |
-
array(
|
186 |
-
'settings' => $sap_scheduler_settings
|
187 |
-
)
|
188 |
-
);
|
189 |
-
}
|
190 |
-
|
191 |
-
public function display_install_screen() { ?>
|
192 |
-
<div class='rtb-welcome-screen'>
|
193 |
-
<?php if (!isset($_GET['exclude'])) { ?>
|
194 |
-
<div class='rtb-welcome-screen-header'>
|
195 |
-
<h1><?php _e('Welcome to the Five-Star Restaurant Reservations Plugin', 'restaurant-reservations'); ?></h1>
|
196 |
-
<p><?php _e('Thanks for choosing the Five-Star Restaurant Reservations! The following will help you get started with the setup of your reservations system by creating your reservations page, setting times when bookings are allowed as well as configuring a few key options.', 'restaurant-reservations'); ?></p>
|
197 |
-
</div>
|
198 |
-
<?php } ?>
|
199 |
-
<?php if (!isset($_GET['exclude'])) { ?>
|
200 |
-
<div class='rtb-welcome-screen-box rtb-welcome-screen-reservations_page rtb-welcome-screen-open' data-screen='reservations_page'>
|
201 |
-
<h2><?php _e('Add a Reservations Page', 'restaurant-reservations'); ?></h2>
|
202 |
-
<div class='rtb-welcome-screen-box-content'>
|
203 |
-
<p><?php _e('You can create a dedicated reservations booking page below, or skip this step and add your reservations to a page you\'ve already created manually.', 'restaurant-reservations'); ?></p>
|
204 |
-
<div class='rtb-welcome-screen-menu-page'>
|
205 |
-
<div class='rtb-welcome-screen-add-reservations-page-name rtb-welcome-screen-box-content-divs'><label><?php _e('Page Title:', 'restaurant-reservations'); ?></label><input type='text' value='Reservations' /></div>
|
206 |
-
<div class='rtb-welcome-screen-add-reservations-page-button' data-nextaction='schedule_open'><?php _e('Create Page', 'restaurant-reservations'); ?></div>
|
207 |
-
</div>
|
208 |
-
<div class='rtb-welcome-screen-next-button rtb-welcome-screen-next-button-not-top-margin' data-nextaction='schedule_open'><?php _e('Next Step', 'restaurant-reservations'); ?></div>
|
209 |
-
<div class='clear'></div>
|
210 |
-
</div>
|
211 |
-
</div>
|
212 |
-
<?php } ?>
|
213 |
-
<div class='rtb-welcome-screen-box rtb-welcome-screen-schedule_open' data-screen='schedule_open'>
|
214 |
-
<h2><?php echo (isset($_GET['exclude']) ? '1.' : '2.') . __(' Create Booking Schedule', 'restaurant-reservations'); ?></h2>
|
215 |
-
<div class='rtb-welcome-screen-box-content'>
|
216 |
-
<p><?php _e('Choose what times each week your restaurant is available to book reservations.', 'restaurant-reservations'); ?></p>
|
217 |
-
<div class='rtb-welcome-screen-created-schedule-open'>
|
218 |
-
<div class="sap-scheduler" id="schedule-open"></div>
|
219 |
-
<div class="sap-add-scheduler">
|
220 |
-
<a href="#" class="button">
|
221 |
-
<?php _e('Add new scheduling rule', 'restaurant-reservations' ); ?>
|
222 |
-
</a>
|
223 |
-
</div>
|
224 |
-
</div>
|
225 |
-
<div class='rtb-welcome-screen-save-schedule-open-button'><?php _e('Save Schedule', 'restaurant-reservations'); ?></div>
|
226 |
-
<div class="rtb-welcome-clear"></div>
|
227 |
-
<div class='rtb-welcome-screen-next-button' data-nextaction='options'><?php _e('Next Step', 'restaurant-reservations'); ?></div>
|
228 |
-
<div class='rtb-welcome-screen-previous-button' data-previousaction='reservations_page'><?php _e('Previous Step', 'restaurant-reservations'); ?></div>
|
229 |
-
<div class='clear'></div>
|
230 |
-
</div>
|
231 |
-
</div>
|
232 |
-
|
233 |
-
<div class='rtb-welcome-screen-box rtb-welcome-screen-options' data-screen='options'>
|
234 |
-
<h2><?php echo (isset($_GET['exclude']) ? '2.' : '3.') . __(' Set Key Options', 'restaurant-reservations'); ?></h2>
|
235 |
-
<div class='rtb-welcome-screen-box-content'>
|
236 |
-
<p><?php _e('Set a min/max party size for bookings, choose how early and late bookings can be made, and pick the time interval between different booking options.', 'restaurant-reservations'); ?></p>
|
237 |
-
<div class='rtb-welcome-screen-option'>
|
238 |
-
<label><?php _e('Min Party Size:', 'restaurant-reservations'); ?></label>
|
239 |
-
<select name='min-party-size'>
|
240 |
-
<?php for ( $i = 1; $i <= 100; $i++ ) { ?>
|
241 |
-
<option value='<?php echo $i; ?>'><?php echo $i; ?></option>
|
242 |
-
<?php } ?>
|
243 |
-
</select>
|
244 |
-
</div>
|
245 |
-
<div class='rtb-welcome-screen-option'>
|
246 |
-
<label><?php _e('Max Party Size:', 'restaurant-reservations'); ?></label>
|
247 |
-
<select name='party-size'>
|
248 |
-
<option value='0'><?php _e('Any Size', 'restaurant-reservations' ); ?></option>
|
249 |
-
<?php for ( $i = 1; $i <= 100; $i++ ) { ?>
|
250 |
-
<option value='<?php echo $i; ?>'><?php echo $i; ?></option>
|
251 |
-
<?php } ?>
|
252 |
-
</select>
|
253 |
-
</div>
|
254 |
-
<div class='rtb-welcome-screen-option'>
|
255 |
-
<label><?php _e('Early Bookings:', 'restaurant-reservations'); ?></label>
|
256 |
-
<select name='early-bookings'>
|
257 |
-
<option><?php _e('Any Time', 'restaurant-reservations' ); ?></option>
|
258 |
-
<option value='1'><?php _e('From 1 day in advance', 'restaurant-reservations' ); ?></option>
|
259 |
-
<option value='7'><?php _e('From 1 week in advance', 'restaurant-reservations' ); ?></option>
|
260 |
-
<option value='14'><?php _e('From 2 weeks in advance', 'restaurant-reservations' ); ?></option>
|
261 |
-
<option value='30'><?php _e('From 30 days in advance', 'restaurant-reservations' ); ?></option>
|
262 |
-
<option value='90'><?php _e('From 90 days in advance', 'restaurant-reservations' ); ?></option>
|
263 |
-
</select>
|
264 |
-
</div>
|
265 |
-
<div class='rtb-welcome-screen-option'>
|
266 |
-
<label><?php _e('Late Bookings:', 'restaurant-reservations'); ?></label>
|
267 |
-
<select name='late-bookings'>
|
268 |
-
<option><?php _e('Up to the last minute', 'restaurant-reservations' ); ?></option>
|
269 |
-
<option value='15'><?php _e('At least 15 minutes in advance', 'restaurant-reservations' ); ?></option>
|
270 |
-
<option value='30'><?php _e('At least 30 minutes in advance', 'restaurant-reservations' ); ?></option>
|
271 |
-
<option value='45'><?php _e('At least 45 minutes in advance', 'restaurant-reservations' ); ?></option>
|
272 |
-
<option value='60'><?php _e('At least 1 hour in advance', 'restaurant-reservations' ); ?></option>
|
273 |
-
<option value='240'><?php _e('At least 4 hours in advance', 'restaurant-reservations' ); ?></option>
|
274 |
-
<option value='1440'><?php _e('At least 24 hours in advance', 'restaurant-reservations' ); ?></option>
|
275 |
-
<option value='same_day'><?php _e('Block same-day-bookings', 'restaurant-reservations' ); ?></option>
|
276 |
-
</select>
|
277 |
-
</div>
|
278 |
-
<div class='rtb-welcome-screen-option'>
|
279 |
-
<label><?php _e('Time Interval:', 'restaurant-reservations'); ?></label>
|
280 |
-
<select name='time-interval'>
|
281 |
-
<option value='30'><?php _e('Every 30 minutes', 'restaurant-reservations' ); ?></option>
|
282 |
-
<option value='15'><?php _e('Every 15 minutes', 'restaurant-reservations' ); ?></option>
|
283 |
-
<option value='10'><?php _e('Every 10 minutes', 'restaurant-reservations' ); ?></option>
|
284 |
-
<option value='5'><?php _e('Every 5 minutes', 'restaurant-reservations' ); ?></option>
|
285 |
-
</select>
|
286 |
-
</div>
|
287 |
-
<div class='rtb-welcome-screen-save-options-button'><?php _e('Save Options', 'restaurant-reservations'); ?></div>
|
288 |
-
<div class="rtb-welcome-clear"></div>
|
289 |
-
<div class='rtb-welcome-screen-previous-button' data-previousaction='schedule_open'><?php _e('Previous Step', 'restaurant-reservations'); ?></div>
|
290 |
-
<div class='rtb-welcome-screen-finish-button'><a href='admin.php?page=rtb-dashboard'><?php _e('Finish', 'restaurant-reservations'); ?></a></div>
|
291 |
-
<div class='clear'></div>
|
292 |
-
</div>
|
293 |
-
</div>
|
294 |
-
|
295 |
-
<div class='rtb-welcome-screen-skip-container'>
|
296 |
-
<a href='admin.php?page=rtb-dashboard'><div class='rtb-welcome-screen-skip-button'><?php _e('Skip Setup', 'restaurant-reservations'); ?></div></a>
|
297 |
-
</div>
|
298 |
-
</div>
|
299 |
-
|
300 |
-
<?php }
|
301 |
-
|
302 |
-
/**
|
303 |
-
* Retrieve the template for a scheduling rule
|
304 |
-
* @since 2.0
|
305 |
-
*/
|
306 |
-
public function get_template( $id = 0, $values = array(), $list = false ) {
|
307 |
-
|
308 |
-
$date_format = 'weekly';
|
309 |
-
$time_format = 'all-day';
|
310 |
-
|
311 |
-
$weekdays = array(
|
312 |
-
'monday' => 'Mo',
|
313 |
-
'tuesday' => 'Tu',
|
314 |
-
'wednesday' => 'We',
|
315 |
-
'thursday' => 'Th',
|
316 |
-
'friday' => 'Fr',
|
317 |
-
'saturday' => 'Sa',
|
318 |
-
'sunday' => 'Su',
|
319 |
-
);
|
320 |
-
|
321 |
-
ob_start();
|
322 |
-
?>
|
323 |
-
|
324 |
-
<div class="sap-scheduler-rule clearfix<?php echo $list ? ' list' : ''; ?>">
|
325 |
-
<div class="sap-scheduler-date weekly">
|
326 |
-
<ul class="sap-selector">
|
327 |
-
|
328 |
-
<li>
|
329 |
-
<div class="dashicons dashicons-calendar"></div>
|
330 |
-
<?php _e( 'Weekly', 'restaurant-reservations' ); ?>
|
331 |
-
</li>
|
332 |
-
|
333 |
-
</ul>
|
334 |
-
|
335 |
-
<ul class="sap-scheduler-weekdays">
|
336 |
-
<li class="label">
|
337 |
-
<?php _e( 'Days of the week', 'restaurant-reservations' ); ?>
|
338 |
-
</li>
|
339 |
-
<?php
|
340 |
-
foreach ( $weekdays as $slug => $label ) :
|
341 |
-
$input_name = 'rtb-setting[schedule_open][' . $id . '][weekdays][' . esc_attr( $slug ) . ']';
|
342 |
-
?>
|
343 |
-
<li>
|
344 |
-
<input type="checkbox" name="<?php echo $input_name; ?>" id="<?php echo $input_name; ?>" value="1" data-day="<?php echo esc_attr( $slug ); ?>"><label for="<?php echo $input_name; ?>"><?php echo ucfirst( $label ); ?></label>
|
345 |
-
</li>
|
346 |
-
<?php endforeach; ?>
|
347 |
-
</ul>
|
348 |
-
|
349 |
-
</div>
|
350 |
-
|
351 |
-
<div class="sap-scheduler-time all-day">
|
352 |
-
|
353 |
-
<ul class="sap-selector">
|
354 |
-
<li>
|
355 |
-
<div class="dashicons dashicons-clock"></div>
|
356 |
-
<a href="#" data-format="time-slot">
|
357 |
-
<?php _e( 'Time', 'restaurant-reservations' ); ?>
|
358 |
-
</a>
|
359 |
-
</li>
|
360 |
-
<li>
|
361 |
-
<a href="#" data-format="all-day" class="selected">
|
362 |
-
<?php _e( 'All day', 'restaurant-reservations' ); ?>
|
363 |
-
</a>
|
364 |
-
</li>
|
365 |
-
</ul>
|
366 |
-
|
367 |
-
<div class="sap-scheduler-time-input clearfix">
|
368 |
-
|
369 |
-
<div class="start">
|
370 |
-
<label for="rtb-setting[schedule_open][<?php echo $id; ?>][time][start]">
|
371 |
-
<?php _e( 'Start', 'restaurant-reservations' ); ?>
|
372 |
-
</label>
|
373 |
-
<input type="text" name="<?php echo 'rtb-setting[schedule_open][' . $id . '][time][start]'; ?>" id="<?php echo 'rtb-setting[schedule_open][' . $id . '][time][start]'; ?>">
|
374 |
-
</div>
|
375 |
-
|
376 |
-
<div class="end">
|
377 |
-
<label for="rtb-setting[schedule_open][<?php echo $id; ?>][time][end]">
|
378 |
-
<?php _e( 'End', 'restaurant-reservations' ); ?>
|
379 |
-
</label>
|
380 |
-
<input type="text" name="<?php echo'rtb-setting[schedule_open][' . $id . '][time][end]'; ?>" id="<?php echo 'rtb-setting[schedule_open][' . $id . '][time][end]'; ?>">
|
381 |
-
</div>
|
382 |
-
|
383 |
-
</div>
|
384 |
-
|
385 |
-
<div class="sap-scheduler-all-day">
|
386 |
-
<?php printf( __( 'All day long. Want to %sset a time slot%s?', 'restaurant-reservations' ), '<a href="#" data-format="time-slot">', '</a>' ); ?>
|
387 |
-
</div>
|
388 |
-
|
389 |
-
</div>
|
390 |
-
|
391 |
-
<div class="sap-scheduler-brief">
|
392 |
-
<div class="date">
|
393 |
-
<div class="dashicons dashicons-calendar"></div>
|
394 |
-
<span class="value"></span>
|
395 |
-
</div>
|
396 |
-
<div class="time">
|
397 |
-
<div class="dashicons dashicons-clock"></div>
|
398 |
-
<span class="value"></span>
|
399 |
-
</div>
|
400 |
-
</div>
|
401 |
-
<div class="sap-scheduler-control">
|
402 |
-
<a href="#" class="toggle" title="<?php _e( 'Open and close this rule', 'restaurant-reservations' ); ?>">
|
403 |
-
<div class="dashicons dashicons-<?php echo $list ? 'edit' : 'arrow-up-alt2'; ?>"></div>
|
404 |
-
<span class="screen-reader-text">
|
405 |
-
<?php _e( 'Open and close this rule', 'restaurant-reservations' ); ?>
|
406 |
-
</span>
|
407 |
-
</a>
|
408 |
-
<a href="#" class="delete" title="<?php _e( 'Delete rule', 'restaurant-reservations' ); ?>">
|
409 |
-
<div class="dashicons dashicons-dismiss"></div>
|
410 |
-
<span class="screen-reader-text">
|
411 |
-
<?php _e( 'Delete scheduling rule', 'restaurant-reservations' ); ?>
|
412 |
-
</span>
|
413 |
-
</a>
|
414 |
-
</div>
|
415 |
-
</div>
|
416 |
-
|
417 |
-
<?php
|
418 |
-
$output = ob_get_clean();
|
419 |
-
|
420 |
-
return $output;
|
421 |
-
}
|
422 |
-
}
|
423 |
-
|
424 |
-
|
425 |
?>
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Class to handle everything related to the walk-through that runs on plugin activation
|
5 |
+
*/
|
6 |
+
|
7 |
+
if ( !defined( 'ABSPATH' ) )
|
8 |
+
exit;
|
9 |
+
|
10 |
+
class rtbInstallationWalkthrough {
|
11 |
+
|
12 |
+
public function __construct() {
|
13 |
+
add_action( 'admin_menu', array( $this, 'register_install_screen' ) );
|
14 |
+
add_action( 'admin_head', array( $this, 'hide_install_screen_menu_item' ) );
|
15 |
+
add_action( 'admin_init', array( $this, 'redirect' ), 9999 );
|
16 |
+
|
17 |
+
add_action( 'admin_head', array( $this, 'admin_enqueue') );
|
18 |
+
|
19 |
+
add_action( 'wp_ajax_nopriv_rtb-welcome-add-menu-page' , array( 'rtbHelper' , 'admin_nopriv_ajax' ) );
|
20 |
+
add_action( 'wp_ajax_rtb-welcome-add-menu-page', array( $this, 'add_reservations_page' ) );
|
21 |
+
add_action( 'wp_ajax_nopriv_rtb-welcome-set-schedule' , array( 'rtbHelper' , 'admin_nopriv_ajax' ) );
|
22 |
+
add_action( 'wp_ajax_rtb-welcome-set-schedule', array( $this, 'set_schedule' ) );
|
23 |
+
add_action( 'wp_ajax_nopriv_rtb-welcome-set-options' , array( 'rtbHelper' , 'admin_nopriv_ajax' ) );
|
24 |
+
add_action( 'wp_ajax_rtb-welcome-set-options', array( $this, 'set_options' ) );
|
25 |
+
}
|
26 |
+
|
27 |
+
public function redirect() {
|
28 |
+
if ( ! get_transient( 'rtb-getting-started' ) )
|
29 |
+
return;
|
30 |
+
|
31 |
+
delete_transient( 'rtb-getting-started' );
|
32 |
+
|
33 |
+
if ( is_network_admin() || isset( $_GET['activate-multi'] ) )
|
34 |
+
return;
|
35 |
+
|
36 |
+
$bookings = get_posts( array( 'post_type' => 'rtb-booking', 'post_status' => 'any' ) );
|
37 |
+
if ( ! empty( $bookings ) ) {
|
38 |
+
set_transient( 'rtb-admin-install-notice', true, 5 );
|
39 |
+
return;
|
40 |
+
}
|
41 |
+
|
42 |
+
wp_safe_redirect( admin_url( 'index.php?page=rtb-getting-started' ) );
|
43 |
+
exit;
|
44 |
+
}
|
45 |
+
|
46 |
+
public function register_install_screen() {
|
47 |
+
add_dashboard_page(
|
48 |
+
esc_html__( 'Five-Star Restaurant Reservations - Welcome!', 'restaurant-reservations' ),
|
49 |
+
esc_html__( 'Five-Star Restaurant Reservations - Welcome!', 'restaurant-reservations' ),
|
50 |
+
'manage_options',
|
51 |
+
'rtb-getting-started',
|
52 |
+
array($this, 'display_install_screen')
|
53 |
+
);
|
54 |
+
}
|
55 |
+
|
56 |
+
public function hide_install_screen_menu_item() {
|
57 |
+
remove_submenu_page( 'index.php', 'rtb-getting-started' );
|
58 |
+
}
|
59 |
+
|
60 |
+
public function add_reservations_page() {
|
61 |
+
|
62 |
+
// Authenticate request
|
63 |
+
if ( !check_ajax_referer( 'rtb-getting-started', 'nonce' ) || !current_user_can( 'manage_bookings' ) ) {
|
64 |
+
rtbHelper::admin_nopriv_ajax();
|
65 |
+
}
|
66 |
+
|
67 |
+
$reservations_page = wp_insert_post(array(
|
68 |
+
'post_title' => (isset($_POST['reservations_page_title']) ? sanitize_text_field( $_POST['reservations_page_title'] ) : ''),
|
69 |
+
'post_content' => '',
|
70 |
+
'post_status' => 'publish',
|
71 |
+
'post_type' => 'page'
|
72 |
+
));
|
73 |
+
|
74 |
+
if ( $reservations_page ) {
|
75 |
+
$rtb_options = get_option( 'rtb-settings' );
|
76 |
+
$rtb_options['booking-page'] = $reservations_page;
|
77 |
+
update_option( 'rtb-settings', $rtb_options );
|
78 |
+
}
|
79 |
+
|
80 |
+
exit();
|
81 |
+
}
|
82 |
+
|
83 |
+
public function set_schedule() {
|
84 |
+
|
85 |
+
// Authenticate request
|
86 |
+
if ( !check_ajax_referer( 'rtb-getting-started', 'nonce' ) || !current_user_can( 'manage_bookings' ) ) {
|
87 |
+
rtbHelper::admin_nopriv_ajax();
|
88 |
+
}
|
89 |
+
|
90 |
+
$rtb_options = get_option( 'rtb-settings' );
|
91 |
+
|
92 |
+
$rtb_options['schedule-open'] = rtbHelper::sanitize_text_field_recursive( $_POST['schedule_open'] );
|
93 |
+
|
94 |
+
update_option( 'rtb-settings', $rtb_options );
|
95 |
+
|
96 |
+
exit();
|
97 |
+
}
|
98 |
+
|
99 |
+
public function set_options() {
|
100 |
+
|
101 |
+
// Authenticate request
|
102 |
+
if ( !check_ajax_referer( 'rtb-getting-started', 'nonce' ) || !current_user_can( 'manage_bookings' ) ) {
|
103 |
+
rtbHelper::admin_nopriv_ajax();
|
104 |
+
}
|
105 |
+
|
106 |
+
$rtb_options = get_option( 'rtb-settings' );
|
107 |
+
$rtb_options['party-size-min'] = sanitize_text_field( $_POST['party_size_min'] );
|
108 |
+
$rtb_options['party-size'] = sanitize_text_field( $_POST['party_size'] );
|
109 |
+
$rtb_options['early-bookings'] = sanitize_text_field( $_POST['early_bookings'] );
|
110 |
+
$rtb_options['late-bookings'] = sanitize_text_field( $_POST['late_bookings'] );
|
111 |
+
$rtb_options['time-interval'] = sanitize_text_field( $_POST['time_interval'] );
|
112 |
+
update_option( 'rtb-settings', $rtb_options );
|
113 |
+
|
114 |
+
exit();
|
115 |
+
}
|
116 |
+
|
117 |
+
function admin_enqueue() {
|
118 |
+
|
119 |
+
if ( ! isset( $_GET['page'] ) or $_GET['page'] != 'rtb-getting-started' ) { return; }
|
120 |
+
|
121 |
+
wp_enqueue_style( 'rtb-admin-css', RTB_PLUGIN_URL . '/lib/simple-admin-pages/css/admin.css', array(), RTB_VERSION );
|
122 |
+
wp_enqueue_style( 'rtb-welcome-screen', RTB_PLUGIN_URL . '/assets/css/admin-rtb-welcome-screen.css', array(), RTB_VERSION );
|
123 |
+
wp_enqueue_style( 'pickadate-default', RTB_PLUGIN_URL . '/lib/simple-admin-pages/lib/pickadate/themes/default.css', array(), RTB_VERSION );
|
124 |
+
wp_enqueue_style( 'pickadate-date', RTB_PLUGIN_URL . '/lib/simple-admin-pages/lib/pickadate/themes/default.date.css', array(), RTB_VERSION );
|
125 |
+
wp_enqueue_style( 'pickadate-time', RTB_PLUGIN_URL . '/lib/simple-admin-pages/lib/pickadate/themes/default.time.css', array(), RTB_VERSION );
|
126 |
+
|
127 |
+
wp_enqueue_script( 'rtb-getting-started', RTB_PLUGIN_URL . '/assets/js/admin-rtb-welcome-screen.js', array('jquery'), RTB_VERSION );
|
128 |
+
wp_localize_script(
|
129 |
+
'rtb-getting-started',
|
130 |
+
'rtb_getting_started',
|
131 |
+
array(
|
132 |
+
'nonce' => wp_create_nonce( 'rtb-getting-started' )
|
133 |
+
)
|
134 |
+
);
|
135 |
+
|
136 |
+
wp_enqueue_script( 'pickadate', RTB_PLUGIN_URL . '/lib/simple-admin-pages/lib/pickadate/picker.js', array('jquery'), RTB_VERSION, true );
|
137 |
+
wp_enqueue_script( 'pickadate-date', RTB_PLUGIN_URL . '/lib/simple-admin-pages/lib/pickadate/picker.date.js', array('jquery'), RTB_VERSION, true );
|
138 |
+
wp_enqueue_script( 'pickadate-time', RTB_PLUGIN_URL . '/lib/simple-admin-pages/lib/pickadate/picker.time.js', array('jquery'), RTB_VERSION, true );
|
139 |
+
wp_enqueue_script( 'pickadate-legacy', RTB_PLUGIN_URL . '/lib/simple-admin-pages/lib/pickadate/legacy.js', array('jquery'), RTB_VERSION, true );
|
140 |
+
wp_enqueue_script( 'sap-scheduler', RTB_PLUGIN_URL . '/lib/simple-admin-pages/js/scheduler.js', array('jquery'), RTB_VERSION, true );
|
141 |
+
|
142 |
+
$sap_scheduler_settings[ 'schedule-open' ] = array(
|
143 |
+
'time_interval' => 15,
|
144 |
+
'time_format' => 'h:i A',
|
145 |
+
'date_format' => 'd mmmm, yyyy',
|
146 |
+
'template' => $this->get_template(),
|
147 |
+
'weekdays' => array(
|
148 |
+
'monday' => 'Mo',
|
149 |
+
'tuesday' => 'Tu',
|
150 |
+
'wednesday' => 'We',
|
151 |
+
'thursday' => 'Th',
|
152 |
+
'friday' => 'Fr',
|
153 |
+
'saturday' => 'Sa',
|
154 |
+
'sunday' => 'Su',
|
155 |
+
),
|
156 |
+
'weeks' => array(
|
157 |
+
'first' => '1st',
|
158 |
+
'second' => '2nd',
|
159 |
+
'third' => '3rd',
|
160 |
+
'fourth' => '4th',
|
161 |
+
'last' => 'last',
|
162 |
+
),
|
163 |
+
'disable_weekdays' => false,
|
164 |
+
'disable_weeks' => true,
|
165 |
+
'disable_date' => true,
|
166 |
+
'disable_time' => false,
|
167 |
+
'disable_multiple' => false,
|
168 |
+
'summaries' => array(
|
169 |
+
'never' => __( 'Never', 'restaurant-reservations' ),
|
170 |
+
'weekly_always' => __( 'Every day', 'restaurant-reservations' ),
|
171 |
+
'monthly_weekdays' => sprintf( __( '%s on the %s week of the month', 'restaurant-reservations' ), '{days}', '{weeks}' ),
|
172 |
+
'monthly_weeks' => sprintf( __( '%s week of the month', 'restaurant-reservations' ), '{weeks}' ),
|
173 |
+
'all_day' => __( 'All day', 'restaurant-reservations' ),
|
174 |
+
'before' => __( 'Ends at', 'restaurant-reservations' ),
|
175 |
+
'after' => __( 'Starts at', 'restaurant-reservations' ),
|
176 |
+
'separator' => __( '&mdash', 'restaurant-reservations' ),
|
177 |
+
),
|
178 |
+
);
|
179 |
+
|
180 |
+
// This gets called multiple times, but only the last call is actually
|
181 |
+
// pushed to the script.
|
182 |
+
wp_localize_script(
|
183 |
+
'sap-scheduler',
|
184 |
+
'sap_scheduler',
|
185 |
+
array(
|
186 |
+
'settings' => $sap_scheduler_settings
|
187 |
+
)
|
188 |
+
);
|
189 |
+
}
|
190 |
+
|
191 |
+
public function display_install_screen() { ?>
|
192 |
+
<div class='rtb-welcome-screen'>
|
193 |
+
<?php if (!isset($_GET['exclude'])) { ?>
|
194 |
+
<div class='rtb-welcome-screen-header'>
|
195 |
+
<h1><?php _e('Welcome to the Five-Star Restaurant Reservations Plugin', 'restaurant-reservations'); ?></h1>
|
196 |
+
<p><?php _e('Thanks for choosing the Five-Star Restaurant Reservations! The following will help you get started with the setup of your reservations system by creating your reservations page, setting times when bookings are allowed as well as configuring a few key options.', 'restaurant-reservations'); ?></p>
|
197 |
+
</div>
|
198 |
+
<?php } ?>
|
199 |
+
<?php if (!isset($_GET['exclude'])) { ?>
|
200 |
+
<div class='rtb-welcome-screen-box rtb-welcome-screen-reservations_page rtb-welcome-screen-open' data-screen='reservations_page'>
|
201 |
+
<h2><?php _e('Add a Reservations Page', 'restaurant-reservations'); ?></h2>
|
202 |
+
<div class='rtb-welcome-screen-box-content'>
|
203 |
+
<p><?php _e('You can create a dedicated reservations booking page below, or skip this step and add your reservations to a page you\'ve already created manually.', 'restaurant-reservations'); ?></p>
|
204 |
+
<div class='rtb-welcome-screen-menu-page'>
|
205 |
+
<div class='rtb-welcome-screen-add-reservations-page-name rtb-welcome-screen-box-content-divs'><label><?php _e('Page Title:', 'restaurant-reservations'); ?></label><input type='text' value='Reservations' /></div>
|
206 |
+
<div class='rtb-welcome-screen-add-reservations-page-button' data-nextaction='schedule_open'><?php _e('Create Page', 'restaurant-reservations'); ?></div>
|
207 |
+
</div>
|
208 |
+
<div class='rtb-welcome-screen-next-button rtb-welcome-screen-next-button-not-top-margin' data-nextaction='schedule_open'><?php _e('Next Step', 'restaurant-reservations'); ?></div>
|
209 |
+
<div class='clear'></div>
|
210 |
+
</div>
|
211 |
+
</div>
|
212 |
+
<?php } ?>
|
213 |
+
<div class='rtb-welcome-screen-box rtb-welcome-screen-schedule_open' data-screen='schedule_open'>
|
214 |
+
<h2><?php echo (isset($_GET['exclude']) ? '1.' : '2.') . __(' Create Booking Schedule', 'restaurant-reservations'); ?></h2>
|
215 |
+
<div class='rtb-welcome-screen-box-content'>
|
216 |
+
<p><?php _e('Choose what times each week your restaurant is available to book reservations.', 'restaurant-reservations'); ?></p>
|
217 |
+
<div class='rtb-welcome-screen-created-schedule-open'>
|
218 |
+
<div class="sap-scheduler" id="schedule-open"></div>
|
219 |
+
<div class="sap-add-scheduler">
|
220 |
+
<a href="#" class="button">
|
221 |
+
<?php _e('Add new scheduling rule', 'restaurant-reservations' ); ?>
|
222 |
+
</a>
|
223 |
+
</div>
|
224 |
+
</div>
|
225 |
+
<div class='rtb-welcome-screen-save-schedule-open-button'><?php _e('Save Schedule', 'restaurant-reservations'); ?></div>
|
226 |
+
<div class="rtb-welcome-clear"></div>
|
227 |
+
<div class='rtb-welcome-screen-next-button' data-nextaction='options'><?php _e('Next Step', 'restaurant-reservations'); ?></div>
|
228 |
+
<div class='rtb-welcome-screen-previous-button' data-previousaction='reservations_page'><?php _e('Previous Step', 'restaurant-reservations'); ?></div>
|
229 |
+
<div class='clear'></div>
|
230 |
+
</div>
|
231 |
+
</div>
|
232 |
+
|
233 |
+
<div class='rtb-welcome-screen-box rtb-welcome-screen-options' data-screen='options'>
|
234 |
+
<h2><?php echo (isset($_GET['exclude']) ? '2.' : '3.') . __(' Set Key Options', 'restaurant-reservations'); ?></h2>
|
235 |
+
<div class='rtb-welcome-screen-box-content'>
|
236 |
+
<p><?php _e('Set a min/max party size for bookings, choose how early and late bookings can be made, and pick the time interval between different booking options.', 'restaurant-reservations'); ?></p>
|
237 |
+
<div class='rtb-welcome-screen-option'>
|
238 |
+
<label><?php _e('Min Party Size:', 'restaurant-reservations'); ?></label>
|
239 |
+
<select name='min-party-size'>
|
240 |
+
<?php for ( $i = 1; $i <= 100; $i++ ) { ?>
|
241 |
+
<option value='<?php echo $i; ?>'><?php echo $i; ?></option>
|
242 |
+
<?php } ?>
|
243 |
+
</select>
|
244 |
+
</div>
|
245 |
+
<div class='rtb-welcome-screen-option'>
|
246 |
+
<label><?php _e('Max Party Size:', 'restaurant-reservations'); ?></label>
|
247 |
+
<select name='party-size'>
|
248 |
+
<option value='0'><?php _e('Any Size', 'restaurant-reservations' ); ?></option>
|
249 |
+
<?php for ( $i = 1; $i <= 100; $i++ ) { ?>
|
250 |
+
<option value='<?php echo $i; ?>'><?php echo $i; ?></option>
|
251 |
+
<?php } ?>
|
252 |
+
</select>
|
253 |
+
</div>
|
254 |
+
<div class='rtb-welcome-screen-option'>
|
255 |
+
<label><?php _e('Early Bookings:', 'restaurant-reservations'); ?></label>
|
256 |
+
<select name='early-bookings'>
|
257 |
+
<option><?php _e('Any Time', 'restaurant-reservations' ); ?></option>
|
258 |
+
<option value='1'><?php _e('From 1 day in advance', 'restaurant-reservations' ); ?></option>
|
259 |
+
<option value='7'><?php _e('From 1 week in advance', 'restaurant-reservations' ); ?></option>
|
260 |
+
<option value='14'><?php _e('From 2 weeks in advance', 'restaurant-reservations' ); ?></option>
|
261 |
+
<option value='30'><?php _e('From 30 days in advance', 'restaurant-reservations' ); ?></option>
|
262 |
+
<option value='90'><?php _e('From 90 days in advance', 'restaurant-reservations' ); ?></option>
|
263 |
+
</select>
|
264 |
+
</div>
|
265 |
+
<div class='rtb-welcome-screen-option'>
|
266 |
+
<label><?php _e('Late Bookings:', 'restaurant-reservations'); ?></label>
|
267 |
+
<select name='late-bookings'>
|
268 |
+
<option><?php _e('Up to the last minute', 'restaurant-reservations' ); ?></option>
|
269 |
+
<option value='15'><?php _e('At least 15 minutes in advance', 'restaurant-reservations' ); ?></option>
|
270 |
+
<option value='30'><?php _e('At least 30 minutes in advance', 'restaurant-reservations' ); ?></option>
|
271 |
+
<option value='45'><?php _e('At least 45 minutes in advance', 'restaurant-reservations' ); ?></option>
|
272 |
+
<option value='60'><?php _e('At least 1 hour in advance', 'restaurant-reservations' ); ?></option>
|
273 |
+
<option value='240'><?php _e('At least 4 hours in advance', 'restaurant-reservations' ); ?></option>
|
274 |
+
<option value='1440'><?php _e('At least 24 hours in advance', 'restaurant-reservations' ); ?></option>
|
275 |
+
<option value='same_day'><?php _e('Block same-day-bookings', 'restaurant-reservations' ); ?></option>
|
276 |
+
</select>
|
277 |
+
</div>
|
278 |
+
<div class='rtb-welcome-screen-option'>
|
279 |
+
<label><?php _e('Time Interval:', 'restaurant-reservations'); ?></label>
|
280 |
+
<select name='time-interval'>
|
281 |
+
<option value='30'><?php _e('Every 30 minutes', 'restaurant-reservations' ); ?></option>
|
282 |
+
<option value='15'><?php _e('Every 15 minutes', 'restaurant-reservations' ); ?></option>
|
283 |
+
<option value='10'><?php _e('Every 10 minutes', 'restaurant-reservations' ); ?></option>
|
284 |
+
<option value='5'><?php _e('Every 5 minutes', 'restaurant-reservations' ); ?></option>
|
285 |
+
</select>
|
286 |
+
</div>
|
287 |
+
<div class='rtb-welcome-screen-save-options-button'><?php _e('Save Options', 'restaurant-reservations'); ?></div>
|
288 |
+
<div class="rtb-welcome-clear"></div>
|
289 |
+
<div class='rtb-welcome-screen-previous-button' data-previousaction='schedule_open'><?php _e('Previous Step', 'restaurant-reservations'); ?></div>
|
290 |
+
<div class='rtb-welcome-screen-finish-button'><a href='admin.php?page=rtb-dashboard'><?php _e('Finish', 'restaurant-reservations'); ?></a></div>
|
291 |
+
<div class='clear'></div>
|
292 |
+
</div>
|
293 |
+
</div>
|
294 |
+
|
295 |
+
<div class='rtb-welcome-screen-skip-container'>
|
296 |
+
<a href='admin.php?page=rtb-dashboard'><div class='rtb-welcome-screen-skip-button'><?php _e('Skip Setup', 'restaurant-reservations'); ?></div></a>
|
297 |
+
</div>
|
298 |
+
</div>
|
299 |
+
|
300 |
+
<?php }
|
301 |
+
|
302 |
+
/**
|
303 |
+
* Retrieve the template for a scheduling rule
|
304 |
+
* @since 2.0
|
305 |
+
*/
|
306 |
+
public function get_template( $id = 0, $values = array(), $list = false ) {
|
307 |
+
|
308 |
+
$date_format = 'weekly';
|
309 |
+
$time_format = 'all-day';
|
310 |
+
|
311 |
+
$weekdays = array(
|
312 |
+
'monday' => 'Mo',
|
313 |
+
'tuesday' => 'Tu',
|
314 |
+
'wednesday' => 'We',
|
315 |
+
'thursday' => 'Th',
|
316 |
+
'friday' => 'Fr',
|
317 |
+
'saturday' => 'Sa',
|
318 |
+
'sunday' => 'Su',
|
319 |
+
);
|
320 |
+
|
321 |
+
ob_start();
|
322 |
+
?>
|
323 |
+
|
324 |
+
<div class="sap-scheduler-rule clearfix<?php echo $list ? ' list' : ''; ?>">
|
325 |
+
<div class="sap-scheduler-date weekly">
|
326 |
+
<ul class="sap-selector">
|
327 |
+
|
328 |
+
<li>
|
329 |
+
<div class="dashicons dashicons-calendar"></div>
|
330 |
+
<?php _e( 'Weekly', 'restaurant-reservations' ); ?>
|
331 |
+
</li>
|
332 |
+
|
333 |
+
</ul>
|
334 |
+
|
335 |
+
<ul class="sap-scheduler-weekdays">
|
336 |
+
<li class="label">
|
337 |
+
<?php _e( 'Days of the week', 'restaurant-reservations' ); ?>
|
338 |
+
</li>
|
339 |
+
<?php
|
340 |
+
foreach ( $weekdays as $slug => $label ) :
|
341 |
+
$input_name = 'rtb-setting[schedule_open][' . $id . '][weekdays][' . esc_attr( $slug ) . ']';
|
342 |
+
?>
|
343 |
+
<li>
|
344 |
+
<input type="checkbox" name="<?php echo $input_name; ?>" id="<?php echo $input_name; ?>" value="1" data-day="<?php echo esc_attr( $slug ); ?>"><label for="<?php echo $input_name; ?>"><?php echo ucfirst( $label ); ?></label>
|
345 |
+
</li>
|
346 |
+
<?php endforeach; ?>
|
347 |
+
</ul>
|
348 |
+
|
349 |
+
</div>
|
350 |
+
|
351 |
+
<div class="sap-scheduler-time all-day">
|
352 |
+
|
353 |
+
<ul class="sap-selector">
|
354 |
+
<li>
|
355 |
+
<div class="dashicons dashicons-clock"></div>
|
356 |
+
<a href="#" data-format="time-slot">
|
357 |
+
<?php _e( 'Time', 'restaurant-reservations' ); ?>
|
358 |
+
</a>
|
359 |
+
</li>
|
360 |
+
<li>
|
361 |
+
<a href="#" data-format="all-day" class="selected">
|
362 |
+
<?php _e( 'All day', 'restaurant-reservations' ); ?>
|
363 |
+
</a>
|
364 |
+
</li>
|
365 |
+
</ul>
|
366 |
+
|
367 |
+
<div class="sap-scheduler-time-input clearfix">
|
368 |
+
|
369 |
+
<div class="start">
|
370 |
+
<label for="rtb-setting[schedule_open][<?php echo $id; ?>][time][start]">
|
371 |
+
<?php _e( 'Start', 'restaurant-reservations' ); ?>
|
372 |
+
</label>
|
373 |
+
<input type="text" name="<?php echo 'rtb-setting[schedule_open][' . $id . '][time][start]'; ?>" id="<?php echo 'rtb-setting[schedule_open][' . $id . '][time][start]'; ?>">
|
374 |
+
</div>
|
375 |
+
|
376 |
+
<div class="end">
|
377 |
+
<label for="rtb-setting[schedule_open][<?php echo $id; ?>][time][end]">
|
378 |
+
<?php _e( 'End', 'restaurant-reservations' ); ?>
|
379 |
+
</label>
|
380 |
+
<input type="text" name="<?php echo'rtb-setting[schedule_open][' . $id . '][time][end]'; ?>" id="<?php echo 'rtb-setting[schedule_open][' . $id . '][time][end]'; ?>">
|
381 |
+
</div>
|
382 |
+
|
383 |
+
</div>
|
384 |
+
|
385 |
+
<div class="sap-scheduler-all-day">
|
386 |
+
<?php printf( __( 'All day long. Want to %sset a time slot%s?', 'restaurant-reservations' ), '<a href="#" data-format="time-slot">', '</a>' ); ?>
|
387 |
+
</div>
|
388 |
+
|
389 |
+
</div>
|
390 |
+
|
391 |
+
<div class="sap-scheduler-brief">
|
392 |
+
<div class="date">
|
393 |
+
<div class="dashicons dashicons-calendar"></div>
|
394 |
+
<span class="value"></span>
|
395 |
+
</div>
|
396 |
+
<div class="time">
|
397 |
+
<div class="dashicons dashicons-clock"></div>
|
398 |
+
<span class="value"></span>
|
399 |
+
</div>
|
400 |
+
</div>
|
401 |
+
<div class="sap-scheduler-control">
|
402 |
+
<a href="#" class="toggle" title="<?php _e( 'Open and close this rule', 'restaurant-reservations' ); ?>">
|
403 |
+
<div class="dashicons dashicons-<?php echo $list ? 'edit' : 'arrow-up-alt2'; ?>"></div>
|
404 |
+
<span class="screen-reader-text">
|
405 |
+
<?php _e( 'Open and close this rule', 'restaurant-reservations' ); ?>
|
406 |
+
</span>
|
407 |
+
</a>
|
408 |
+
<a href="#" class="delete" title="<?php _e( 'Delete rule', 'restaurant-reservations' ); ?>">
|
409 |
+
<div class="dashicons dashicons-dismiss"></div>
|
410 |
+
<span class="screen-reader-text">
|
411 |
+
<?php _e( 'Delete scheduling rule', 'restaurant-reservations' ); ?>
|
412 |
+
</span>
|
413 |
+
</a>
|
414 |
+
</div>
|
415 |
+
</div>
|
416 |
+
|
417 |
+
<?php
|
418 |
+
$output = ob_get_clean();
|
419 |
+
|
420 |
+
return $output;
|
421 |
+
}
|
422 |
+
}
|
423 |
+
|
424 |
+
|
425 |
?>
|
includes/Licenses.class.php
CHANGED
@@ -1,270 +1,270 @@
|
|
1 |
-
<?php
|
2 |
-
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
-
|
4 |
-
if ( !class_exists( 'rtbLicenses' ) ) {
|
5 |
-
/**
|
6 |
-
* Class to handle license keys when an addon
|
7 |
-
* is enabled.
|
8 |
-
*
|
9 |
-
* This will add a tab to the settings page as long as at least
|
10 |
-
* one addon has enabled it by setting its $enabled parameter
|
11 |
-
* to `true`. It will also perform the license check and plugin
|
12 |
-
* update procedures.
|
13 |
-
*
|
14 |
-
* If no addons are enabled, it does not phone home or perform
|
15 |
-
* any additional actions.
|
16 |
-
*
|
17 |
-
* @since 1.4.1
|
18 |
-
*/
|
19 |
-
class rtbLicenses {
|
20 |
-
|
21 |
-
/**
|
22 |
-
* Array of licensed products to manage
|
23 |
-
*
|
24 |
-
* @since 1.4.1
|
25 |
-
*/
|
26 |
-
public $licensed_products = array();
|
27 |
-
|
28 |
-
/**
|
29 |
-
* Path to load license setting class file
|
30 |
-
*
|
31 |
-
* @since 1.4.1
|
32 |
-
*/
|
33 |
-
public $sap_extension_path;
|
34 |
-
|
35 |
-
/**
|
36 |
-
* Filename of the setting class to handle a
|
37 |
-
* license key input field.
|
38 |
-
*
|
39 |
-
* @since 1.4.1
|
40 |
-
*/
|
41 |
-
public $sap_setting_file;
|
42 |
-
|
43 |
-
/**
|
44 |
-
* Class name of the setting file to load
|
45 |
-
* when handling a license key input field.
|
46 |
-
*
|
47 |
-
* Should contain the class referenced in
|
48 |
-
* $sap_setting_class.
|
49 |
-
*
|
50 |
-
* @since 1.4.1
|
51 |
-
*/
|
52 |
-
public $sap_setting_class;
|
53 |
-
|
54 |
-
/**
|
55 |
-
* Initialize the license handling
|
56 |
-
*
|
57 |
-
* @since 1.4.1
|
58 |
-
*/
|
59 |
-
public function __construct() {
|
60 |
-
|
61 |
-
$this->sap_extension_path = RTB_PLUGIN_DIR . '/includes/';
|
62 |
-
$this->sap_setting_file = 'AdminPageSettingLicenseKey.class.php';
|
63 |
-
$this->sap_setting_class = 'rtbAdminPageSettingLicenseKey';
|
64 |
-
|
65 |
-
// Check and process updates
|
66 |
-
add_action( 'admin_init', array( $this, 'load_plugin_updater' ), 20 );
|
67 |
-
|
68 |
-
// Add a licenses tab as the last tab in the settings page
|
69 |
-
add_filter( 'rtb_settings_page', array( $this, 'add_licenses_tab' ), 100 );
|
70 |
-
|
71 |
-
// Enqueue assets on licenses page
|
72 |
-
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
|
73 |
-
|
74 |
-
// Show a success/failed message on license activation/deactivation
|
75 |
-
add_action( 'admin_notices', array( $this, 'admin_notices' ) );
|
76 |
-
}
|
77 |
-
|
78 |
-
/**
|
79 |
-
* Check if a licensing system should be enabled
|
80 |
-
*
|
81 |
-
* @since 1.4.1
|
82 |
-
*/
|
83 |
-
public function is_enabled() {
|
84 |
-
|
85 |
-
if ( count( $this->licensed_products ) ) {
|
86 |
-
return true;
|
87 |
-
}
|
88 |
-
|
89 |
-
return false;
|
90 |
-
}
|
91 |
-
|
92 |
-
/**
|
93 |
-
* Add a licensed product to manage
|
94 |
-
*
|
95 |
-
* This should be called in admin_init (before priority 20) so that
|
96 |
-
* the updater knows what products to check.
|
97 |
-
*
|
98 |
-
* @since 1.4.1
|
99 |
-
*/
|
100 |
-
public function add_licensed_product( $product ) {
|
101 |
-
$this->licensed_products[ $product['id'] ] = $product;
|
102 |
-
}
|
103 |
-
|
104 |
-
/**
|
105 |
-
* Add a licenses tab as the last tab in the settings page
|
106 |
-
*
|
107 |
-
* @since 1.4.1
|
108 |
-
*/
|
109 |
-
public function add_licenses_tab( $sap ) {
|
110 |
-
|
111 |
-
if ( !$this->is_enabled() ) {
|
112 |
-
return $sap;
|
113 |
-
}
|
114 |
-
|
115 |
-
$sap->add_section(
|
116 |
-
'rtb-settings',
|
117 |
-
array(
|
118 |
-
'id' => 'rtb-licenses',
|
119 |
-
'title' => __( 'Licenses', 'restaurant-reservations' ),
|
120 |
-
'description' => sprintf(
|
121 |
-
__( 'Activate license keys for any commercial addons you have purchased. %sView all addons%s.', 'restaurant-reservations' ),
|
122 |
-
'<a href="' . admin_url( 'admin.php?page=rtb-addons' ) . '">',
|
123 |
-
'</a>'
|
124 |
-
),
|
125 |
-
'is_tab' => true,
|
126 |
-
)
|
127 |
-
);
|
128 |
-
|
129 |
-
$sap->lib_extension_path = $this->sap_extension_path;
|
130 |
-
|
131 |
-
global $rtb_controller;
|
132 |
-
foreach( $this->licensed_products as $product ) {
|
133 |
-
$sap->add_setting(
|
134 |
-
'rtb-settings',
|
135 |
-
'rtb-licenses',
|
136 |
-
array(
|
137 |
-
'id' => 'rtb-license-key',
|
138 |
-
'filename' => $rtb_controller->licenses->sap_setting_file,
|
139 |
-
'class' => $rtb_controller->licenses->sap_setting_class,
|
140 |
-
),
|
141 |
-
$product
|
142 |
-
);
|
143 |
-
}
|
144 |
-
|
145 |
-
do_action( 'rtb_settings_licenses', $sap );
|
146 |
-
|
147 |
-
|
148 |
-
return $sap;
|
149 |
-
}
|
150 |
-
|
151 |
-
/**
|
152 |
-
* Check if we're on the licenses page
|
153 |
-
*
|
154 |
-
* @since 1.4.1
|
155 |
-
*/
|
156 |
-
public function is_license_page() {
|
157 |
-
|
158 |
-
global $rtb_controller;
|
159 |
-
|
160 |
-
// Use the page reference in $admin_page_hooks because
|
161 |
-
// it changes in SOME hooks when it is translated.
|
162 |
-
// https://core.trac.wordpress.org/ticket/18857
|
163 |
-
global $admin_page_hooks;
|
164 |
-
|
165 |
-
$screen = get_current_screen();
|
166 |
-
if ( empty( $screen ) || empty( $admin_page_hooks['rtb-bookings'] ) ) {
|
167 |
-
return false;
|
168 |
-
}
|
169 |
-
|
170 |
-
if ( $screen->base != $admin_page_hooks['rtb-bookings'] . '_page_rtb-settings' || empty( $_GET['tab'] ) || $_GET['tab'] !== 'rtb-licenses' ) {
|
171 |
-
return false;
|
172 |
-
}
|
173 |
-
|
174 |
-
return true;
|
175 |
-
}
|
176 |
-
|
177 |
-
/**
|
178 |
-
* Enqueue JavaScript and CSS files on the licenses page
|
179 |
-
*
|
180 |
-
* @since 1.4.1
|
181 |
-
*/
|
182 |
-
public function enqueue_assets() {
|
183 |
-
|
184 |
-
if ( !$this->is_license_page() ) {
|
185 |
-
return;
|
186 |
-
}
|
187 |
-
|
188 |
-
wp_enqueue_style( 'rtb-admin', RTB_PLUGIN_URL . '/assets/css/admin.css', array(), RTB_VERSION );
|
189 |
-
}
|
190 |
-
|
191 |
-
/**
|
192 |
-
* Show admin notices on license activation/deactivation attempts
|
193 |
-
*
|
194 |
-
* @since 1.4.1
|
195 |
-
*/
|
196 |
-
public function admin_notices() {
|
197 |
-
|
198 |
-
if ( !$this->is_license_page() || !isset( $_GET['license_result'] ) || $_GET['license_result'] != 0 || empty( $_GET['action'] ) ) {
|
199 |
-
return;
|
200 |
-
}
|
201 |
-
|
202 |
-
$error = empty( $_GET['result_error'] ) ? '' : $_GET['result_error'];
|
203 |
-
|
204 |
-
if ( $_GET['action'] == 'deactivate' ) {
|
205 |
-
$msg = __( 'Your attempt to deactivate a license key failed. Please try again later or contact support for help.', 'restaurant-reservations' );
|
206 |
-
} else {
|
207 |
-
|
208 |
-
if ( $error == 'no_activations_left' ) {
|
209 |
-
$msg = sprintf( __( 'You have reached the activation limit for this license. If you have the license activated on other sites you will need to deactivate them or purchase more license keys from %sTheme of the Crop%s.', 'restaurant-reservations' ), '<a href="http://themeofthecrop.com/">', '</a>' );
|
210 |
-
} else {
|
211 |
-
$msg = __( 'Your attempt to activate a license key failed. Please check the license key and try again.', 'restaurant-reservations' );
|
212 |
-
}
|
213 |
-
}
|
214 |
-
|
215 |
-
?>
|
216 |
-
|
217 |
-
<div class="error">
|
218 |
-
<p><?php echo esc_html( $msg ); ?></p>
|
219 |
-
</div>
|
220 |
-
|
221 |
-
<?php
|
222 |
-
|
223 |
-
}
|
224 |
-
|
225 |
-
/**
|
226 |
-
* Load plugin updater library for Easy Digital Downloads Software
|
227 |
-
* Licensing API
|
228 |
-
*
|
229 |
-
* @since 0.3
|
230 |
-
*/
|
231 |
-
public function load_plugin_updater() {
|
232 |
-
|
233 |
-
if ( !$this->is_enabled() ) {
|
234 |
-
return;
|
235 |
-
}
|
236 |
-
|
237 |
-
if ( !class_exists( 'RTB_EDD_SL_Plugin_Updater' ) ) {
|
238 |
-
|
239 |
-
if ( !file_exists( RTB_PLUGIN_DIR . '/lib/EDD_SL_Plugin_Updater.class.php' ) ) {
|
240 |
-
return;
|
241 |
-
}
|
242 |
-
|
243 |
-
require_once( RTB_PLUGIN_DIR . '/lib/EDD_SL_Plugin_Updater.class.php' );
|
244 |
-
}
|
245 |
-
|
246 |
-
global $rtb_controller;
|
247 |
-
|
248 |
-
$this->updaters = array();
|
249 |
-
foreach( $this->licensed_products as $product ) {
|
250 |
-
|
251 |
-
$license = $rtb_controller->settings->get_setting( $product['id'] );
|
252 |
-
|
253 |
-
if ( empty( $license ) || empty( $license['api_key']) ) {
|
254 |
-
continue;
|
255 |
-
}
|
256 |
-
|
257 |
-
new RTB_EDD_SL_Plugin_Updater(
|
258 |
-
$product['store_url'],
|
259 |
-
$product['plugin_path'],
|
260 |
-
array(
|
261 |
-
'version' => $product['version'],
|
262 |
-
'license' => $license['api_key'],
|
263 |
-
'item_name' => $product['product'],
|
264 |
-
'author' => $product['author'],
|
265 |
-
)
|
266 |
-
);
|
267 |
-
}
|
268 |
-
}
|
269 |
-
}
|
270 |
-
} // endif
|
1 |
+
<?php
|
2 |
+
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
+
|
4 |
+
if ( !class_exists( 'rtbLicenses' ) ) {
|
5 |
+
/**
|
6 |
+
* Class to handle license keys when an addon
|
7 |
+
* is enabled.
|
8 |
+
*
|
9 |
+
* This will add a tab to the settings page as long as at least
|
10 |
+
* one addon has enabled it by setting its $enabled parameter
|
11 |
+
* to `true`. It will also perform the license check and plugin
|
12 |
+
* update procedures.
|
13 |
+
*
|
14 |
+
* If no addons are enabled, it does not phone home or perform
|
15 |
+
* any additional actions.
|
16 |
+
*
|
17 |
+
* @since 1.4.1
|
18 |
+
*/
|
19 |
+
class rtbLicenses {
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Array of licensed products to manage
|
23 |
+
*
|
24 |
+
* @since 1.4.1
|
25 |
+
*/
|
26 |
+
public $licensed_products = array();
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Path to load license setting class file
|
30 |
+
*
|
31 |
+
* @since 1.4.1
|
32 |
+
*/
|
33 |
+
public $sap_extension_path;
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Filename of the setting class to handle a
|
37 |
+
* license key input field.
|
38 |
+
*
|
39 |
+
* @since 1.4.1
|
40 |
+
*/
|
41 |
+
public $sap_setting_file;
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Class name of the setting file to load
|
45 |
+
* when handling a license key input field.
|
46 |
+
*
|
47 |
+
* Should contain the class referenced in
|
48 |
+
* $sap_setting_class.
|
49 |
+
*
|
50 |
+
* @since 1.4.1
|
51 |
+
*/
|
52 |
+
public $sap_setting_class;
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Initialize the license handling
|
56 |
+
*
|
57 |
+
* @since 1.4.1
|
58 |
+
*/
|
59 |
+
public function __construct() {
|
60 |
+
|
61 |
+
$this->sap_extension_path = RTB_PLUGIN_DIR . '/includes/';
|
62 |
+
$this->sap_setting_file = 'AdminPageSettingLicenseKey.class.php';
|
63 |
+
$this->sap_setting_class = 'rtbAdminPageSettingLicenseKey';
|
64 |
+
|
65 |
+
// Check and process updates
|
66 |
+
add_action( 'admin_init', array( $this, 'load_plugin_updater' ), 20 );
|
67 |
+
|
68 |
+
// Add a licenses tab as the last tab in the settings page
|
69 |
+
add_filter( 'rtb_settings_page', array( $this, 'add_licenses_tab' ), 100 );
|
70 |
+
|
71 |
+
// Enqueue assets on licenses page
|
72 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
|
73 |
+
|
74 |
+
// Show a success/failed message on license activation/deactivation
|
75 |
+
add_action( 'admin_notices', array( $this, 'admin_notices' ) );
|
76 |
+
}
|
77 |
+
|
78 |
+
/**
|
79 |
+
* Check if a licensing system should be enabled
|
80 |
+
*
|
81 |
+
* @since 1.4.1
|
82 |
+
*/
|
83 |
+
public function is_enabled() {
|
84 |
+
|
85 |
+
if ( count( $this->licensed_products ) ) {
|
86 |
+
return true;
|
87 |
+
}
|
88 |
+
|
89 |
+
return false;
|
90 |
+
}
|
91 |
+
|
92 |
+
/**
|
93 |
+
* Add a licensed product to manage
|
94 |
+
*
|
95 |
+
* This should be called in admin_init (before priority 20) so that
|
96 |
+
* the updater knows what products to check.
|
97 |
+
*
|
98 |
+
* @since 1.4.1
|
99 |
+
*/
|
100 |
+
public function add_licensed_product( $product ) {
|
101 |
+
$this->licensed_products[ $product['id'] ] = $product;
|
102 |
+
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Add a licenses tab as the last tab in the settings page
|
106 |
+
*
|
107 |
+
* @since 1.4.1
|
108 |
+
*/
|
109 |
+
public function add_licenses_tab( $sap ) {
|
110 |
+
|
111 |
+
if ( !$this->is_enabled() ) {
|
112 |
+
return $sap;
|
113 |
+
}
|
114 |
+
|
115 |
+
$sap->add_section(
|
116 |
+
'rtb-settings',
|
117 |
+
array(
|
118 |
+
'id' => 'rtb-licenses',
|
119 |
+
'title' => __( 'Licenses', 'restaurant-reservations' ),
|
120 |
+
'description' => sprintf(
|
121 |
+
__( 'Activate license keys for any commercial addons you have purchased. %sView all addons%s.', 'restaurant-reservations' ),
|
122 |
+
'<a href="' . admin_url( 'admin.php?page=rtb-addons' ) . '">',
|
123 |
+
'</a>'
|
124 |
+
),
|
125 |
+
'is_tab' => true,
|
126 |
+
)
|
127 |
+
);
|
128 |
+
|
129 |
+
$sap->lib_extension_path = $this->sap_extension_path;
|
130 |
+
|
131 |
+
global $rtb_controller;
|
132 |
+
foreach( $this->licensed_products as $product ) {
|
133 |
+
$sap->add_setting(
|
134 |
+
'rtb-settings',
|
135 |
+
'rtb-licenses',
|
136 |
+
array(
|
137 |
+
'id' => 'rtb-license-key',
|
138 |
+
'filename' => $rtb_controller->licenses->sap_setting_file,
|
139 |
+
'class' => $rtb_controller->licenses->sap_setting_class,
|
140 |
+
),
|
141 |
+
$product
|
142 |
+
);
|
143 |
+
}
|
144 |
+
|
145 |
+
do_action( 'rtb_settings_licenses', $sap );
|
146 |
+
|
147 |
+
|
148 |
+
return $sap;
|
149 |
+
}
|
150 |
+
|
151 |
+
/**
|
152 |
+
* Check if we're on the licenses page
|
153 |
+
*
|
154 |
+
* @since 1.4.1
|
155 |
+
*/
|
156 |
+
public function is_license_page() {
|
157 |
+
|
158 |
+
global $rtb_controller;
|
159 |
+
|
160 |
+
// Use the page reference in $admin_page_hooks because
|
161 |
+
// it changes in SOME hooks when it is translated.
|
162 |
+
// https://core.trac.wordpress.org/ticket/18857
|
163 |
+
global $admin_page_hooks;
|
164 |
+
|
165 |
+
$screen = get_current_screen();
|
166 |
+
if ( empty( $screen ) || empty( $admin_page_hooks['rtb-bookings'] ) ) {
|
167 |
+
return false;
|
168 |
+
}
|
169 |
+
|
170 |
+
if ( $screen->base != $admin_page_hooks['rtb-bookings'] . '_page_rtb-settings' || empty( $_GET['tab'] ) || $_GET['tab'] !== 'rtb-licenses' ) {
|
171 |
+
return false;
|
172 |
+
}
|
173 |
+
|
174 |
+
return true;
|
175 |
+
}
|
176 |
+
|
177 |
+
/**
|
178 |
+
* Enqueue JavaScript and CSS files on the licenses page
|
179 |
+
*
|
180 |
+
* @since 1.4.1
|
181 |
+
*/
|
182 |
+
public function enqueue_assets() {
|
183 |
+
|
184 |
+
if ( !$this->is_license_page() ) {
|
185 |
+
return;
|
186 |
+
}
|
187 |
+
|
188 |
+
wp_enqueue_style( 'rtb-admin', RTB_PLUGIN_URL . '/assets/css/admin.css', array(), RTB_VERSION );
|
189 |
+
}
|
190 |
+
|
191 |
+
/**
|
192 |
+
* Show admin notices on license activation/deactivation attempts
|
193 |
+
*
|
194 |
+
* @since 1.4.1
|
195 |
+
*/
|
196 |
+
public function admin_notices() {
|
197 |
+
|
198 |
+
if ( !$this->is_license_page() || !isset( $_GET['license_result'] ) || $_GET['license_result'] != 0 || empty( $_GET['action'] ) ) {
|
199 |
+
return;
|
200 |
+
}
|
201 |
+
|
202 |
+
$error = empty( $_GET['result_error'] ) ? '' : $_GET['result_error'];
|
203 |
+
|
204 |
+
if ( $_GET['action'] == 'deactivate' ) {
|
205 |
+
$msg = __( 'Your attempt to deactivate a license key failed. Please try again later or contact support for help.', 'restaurant-reservations' );
|
206 |
+
} else {
|
207 |
+
|
208 |
+
if ( $error == 'no_activations_left' ) {
|
209 |
+
$msg = sprintf( __( 'You have reached the activation limit for this license. If you have the license activated on other sites you will need to deactivate them or purchase more license keys from %sTheme of the Crop%s.', 'restaurant-reservations' ), '<a href="http://themeofthecrop.com/">', '</a>' );
|
210 |
+
} else {
|
211 |
+
$msg = __( 'Your attempt to activate a license key failed. Please check the license key and try again.', 'restaurant-reservations' );
|
212 |
+
}
|
213 |
+
}
|
214 |
+
|
215 |
+
?>
|
216 |
+
|
217 |
+
<div class="error">
|
218 |
+
<p><?php echo esc_html( $msg ); ?></p>
|
219 |
+
</div>
|
220 |
+
|
221 |
+
<?php
|
222 |
+
|
223 |
+
}
|
224 |
+
|
225 |
+
/**
|
226 |
+
* Load plugin updater library for Easy Digital Downloads Software
|
227 |
+
* Licensing API
|
228 |
+
*
|
229 |
+
* @since 0.3
|
230 |
+
*/
|
231 |
+
public function load_plugin_updater() {
|
232 |
+
|
233 |
+
if ( !$this->is_enabled() ) {
|
234 |
+
return;
|
235 |
+
}
|
236 |
+
|
237 |
+
if ( !class_exists( 'RTB_EDD_SL_Plugin_Updater' ) ) {
|
238 |
+
|
239 |
+
if ( !file_exists( RTB_PLUGIN_DIR . '/lib/EDD_SL_Plugin_Updater.class.php' ) ) {
|
240 |
+
return;
|
241 |
+
}
|
242 |
+
|
243 |
+
require_once( RTB_PLUGIN_DIR . '/lib/EDD_SL_Plugin_Updater.class.php' );
|
244 |
+
}
|
245 |
+
|
246 |
+
global $rtb_controller;
|
247 |
+
|
248 |
+
$this->updaters = array();
|
249 |
+
foreach( $this->licensed_products as $product ) {
|
250 |
+
|
251 |
+
$license = $rtb_controller->settings->get_setting( $product['id'] );
|
252 |
+
|
253 |
+
if ( empty( $license ) || empty( $license['api_key']) ) {
|
254 |
+
continue;
|
255 |
+
}
|
256 |
+
|
257 |
+
new RTB_EDD_SL_Plugin_Updater(
|
258 |
+
$product['store_url'],
|
259 |
+
$product['plugin_path'],
|
260 |
+
array(
|
261 |
+
'version' => $product['version'],
|
262 |
+
'license' => $license['api_key'],
|
263 |
+
'item_name' => $product['product'],
|
264 |
+
'author' => $product['author'],
|
265 |
+
)
|
266 |
+
);
|
267 |
+
}
|
268 |
+
}
|
269 |
+
}
|
270 |
+
} // endif
|
includes/MailChimp.class.php
CHANGED
@@ -1,615 +1,615 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* Class used to add in MailChimp compatibility
|
4 |
-
*/
|
5 |
-
if ( ! defined( 'ABSPATH' ) )
|
6 |
-
exit;
|
7 |
-
|
8 |
-
if ( !class_exists( 'mcfrtbInit' ) ) {
|
9 |
-
class mcfrtbInit {
|
10 |
-
|
11 |
-
public $api_key = null;
|
12 |
-
|
13 |
-
public $status = null;
|
14 |
-
|
15 |
-
public $api_call_cache = array();
|
16 |
-
|
17 |
-
public $transit_cookie_name = 'rtb-mc-sbscrb';
|
18 |
-
|
19 |
-
public function __construct() {
|
20 |
-
global $rtb_controller;
|
21 |
-
|
22 |
-
add_action( 'init', array( $this, 'init' ) );
|
23 |
-
|
24 |
-
if ( !is_admin() and $rtb_controller->permissions->check_permission( 'mailchimp' ) ) {
|
25 |
-
|
26 |
-
// Add optin checkbox to booking form
|
27 |
-
add_filter( 'rtb_booking_form_fields', array( $this, 'add_optin_field' ), 10, 2 );
|
28 |
-
|
29 |
-
// Validate the optin request data
|
30 |
-
add_action( 'rtb_validate_booking_submission', array( $this, 'validate_optin_request' ) );
|
31 |
-
|
32 |
-
// Enqueue assets to send subscription request
|
33 |
-
add_filter( 'rtb_insert_booking', array( $this, 'enqueue_subscription_call' ) );
|
34 |
-
add_filter( 'init', array( $this, 'enqueue_subscription_call' ) );
|
35 |
-
|
36 |
-
// Save mc-optin user input value
|
37 |
-
add_filter( 'rtb_insert_booking_metadata', array( $this, 'add_booking_meta' ), 10, 2 );
|
38 |
-
// Reload meta information
|
39 |
-
add_action( 'rtb_booking_load_post_data', array( $this, 'reload_booking_meta' ), 20, 2 );
|
40 |
-
}
|
41 |
-
|
42 |
-
}
|
43 |
-
|
44 |
-
/**
|
45 |
-
* Initialize the plugin and register hooks
|
46 |
-
*/
|
47 |
-
public function init() {
|
48 |
-
|
49 |
-
// Initialize the plugin
|
50 |
-
$this->load_config();
|
51 |
-
|
52 |
-
add_action( 'mcfrtb_list_merge_fields', array( $this, 'maybe_add_location_merge_field' ) );
|
53 |
-
add_action( 'mcfrtb_list_merge_fields', array( $this, 'maybe_add_merge_options' ) );
|
54 |
-
|
55 |
-
// Load assets
|
56 |
-
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_assets' ) );
|
57 |
-
|
58 |
-
// Receive ajax calls for mailchimp lists
|
59 |
-
add_action( 'wp_ajax_nopriv_mcfrtb-get-lists' , array( 'rtbHelper' , 'admin_nopriv_ajax' ) );
|
60 |
-
add_action( 'wp_ajax_mcfrtb-get-lists', array( $this, 'ajax_get_lists' ) );
|
61 |
-
|
62 |
-
// Receive ajax calls for merge fields
|
63 |
-
add_action( 'wp_ajax_nopriv_mcfrtb-load-merge-fields' , array( 'rtbHelper' , 'admin_nopriv_ajax' ) );
|
64 |
-
add_action( 'wp_ajax_mcfrtb-load-merge-fields', array( $this, 'ajax_load_merge_fields' ) );
|
65 |
-
|
66 |
-
|
67 |
-
// Process subscription calls
|
68 |
-
add_action( 'wp_ajax_nopriv_mcfrtb-subscribe' , array( $this , 'subscribe' ) );
|
69 |
-
add_action( 'wp_ajax_mcfrtb-subscribe', array( $this, 'subscribe' ) );
|
70 |
-
add_filter( 'mcfrtb_merge_fields_data', array( $this, 'maybe_send_location_merge_field' ), 10, 3 );
|
71 |
-
add_filter( 'mcfrtb_merge_fields_data', array( $this, 'maybe_send_merge_fields' ), 10, 3 );
|
72 |
-
}
|
73 |
-
|
74 |
-
|
75 |
-
/**
|
76 |
-
* Load the configuration parameters
|
77 |
-
*/
|
78 |
-
public function load_config() {
|
79 |
-
|
80 |
-
global $rtb_controller;
|
81 |
-
$api_key = $rtb_controller->settings->get_setting( 'mc-apikey' );
|
82 |
-
|
83 |
-
if( ! is_array( $api_key ) ) {
|
84 |
-
$api_key = [];
|
85 |
-
}
|
86 |
-
|
87 |
-
$this->api_key = array_key_exists('api_key', $api_key) ? $api_key['api_key'] : '';
|
88 |
-
$this->status = array_key_exists('status', $api_key) ? $api_key['status'] : '';
|
89 |
-
|
90 |
-
$this->merge_fields = apply_filters(
|
91 |
-
'mcfrtb_list_merge_fields',
|
92 |
-
array(
|
93 |
-
'datetime' => __( 'Date/Time of Booking', 'restaurant-reservations' ),
|
94 |
-
'name' => __( 'Name', 'restaurant-reservations' ),
|
95 |
-
'party' => __( 'Party Size', 'restaurant-reservations' ),
|
96 |
-
'phone' => __( 'Phone Number', 'restaurant-reservations' ),
|
97 |
-
'message' => __( 'Message', 'restaurant-reservations' ),
|
98 |
-
)
|
99 |
-
);
|
100 |
-
|
101 |
-
}
|
102 |
-
|
103 |
-
/**
|
104 |
-
* Add merge field for location if multi-location support is active
|
105 |
-
*
|
106 |
-
* @param array $fields Key/value list of booking data available for merge
|
107 |
-
* @since 1.2
|
108 |
-
*/
|
109 |
-
public function maybe_add_location_merge_field( $fields ) {
|
110 |
-
|
111 |
-
global $rtb_controller;
|
112 |
-
|
113 |
-
if ( !empty( $rtb_controller->locations ) && !empty( $rtb_controller->locations->post_type ) ) {
|
114 |
-
$fields['location'] = __( 'Location', 'restaurant-reservations' );
|
115 |
-
}
|
116 |
-
|
117 |
-
return $fields;
|
118 |
-
}
|
119 |
-
|
120 |
-
/**
|
121 |
-
* Add merge field options for custom fields
|
122 |
-
*
|
123 |
-
* @param array $fields Key/value list of booking data available for merge
|
124 |
-
* @since 1.3
|
125 |
-
*/
|
126 |
-
public function maybe_add_merge_options( $fields ) {
|
127 |
-
|
128 |
-
$custom_fields = rtb_get_custom_fields();
|
129 |
-
|
130 |
-
$custom_merge_fields = array();
|
131 |
-
foreach( $custom_fields as $custom_field ) {
|
132 |
-
$custom_merge_fields['cf-' . $custom_field->slug] = $custom_field->title;
|
133 |
-
}
|
134 |
-
|
135 |
-
return array_merge( $fields, $custom_merge_fields );
|
136 |
-
}
|
137 |
-
|
138 |
-
/**
|
139 |
-
* Enqueue the admin-only CSS and Javascript
|
140 |
-
* @since 0.0.1
|
141 |
-
*/
|
142 |
-
public function enqueue_admin_assets() {
|
143 |
-
|
144 |
-
global $rtb_controller;
|
145 |
-
|
146 |
-
// Use the page reference in $admin_page_hooks because
|
147 |
-
// it changes in SOME hooks when it is translated.
|
148 |
-
// https://core.trac.wordpress.org/ticket/18857
|
149 |
-
global $admin_page_hooks;
|
150 |
-
|
151 |
-
$screen = get_current_screen();
|
152 |
-
if ( empty( $screen ) || empty( $admin_page_hooks['rtb-bookings'] ) ) {
|
153 |
-
return;
|
154 |
-
}
|
155 |
-
|
156 |
-
if ( $screen->base == 'toplevel_page_rtb-bookings' || $screen->base == $admin_page_hooks['rtb-bookings'] . '_page_rtb-settings' ) {
|
157 |
-
|
158 |
-
wp_enqueue_script( 'rtb-admin-mc', RTB_PLUGIN_URL . '/assets/js/mailchimp-admin.js', array( 'jquery' ), '', true );
|
159 |
-
wp_localize_script(
|
160 |
-
'rtb-admin-mc',
|
161 |
-
'rtb_admin_mc',
|
162 |
-
array(
|
163 |
-
'ajax_nonce' => wp_create_nonce( 'rtb-admin-mc' ),
|
164 |
-
'merge_fields' => $this->merge_fields,
|
165 |
-
'lists' => $rtb_controller->settings->get_setting( 'mc-lists' ),
|
166 |
-
'strings' => array(
|
167 |
-
'merge_booking_data' => __( 'Booking Form Data', 'restaurant-reservations' ),
|
168 |
-
'merge_list_field' => __( 'MailChimp List Field', 'restaurant-reservations' ),
|
169 |
-
'merge_description' => __( 'Connect information from the booking request to <a href="http://kb.mailchimp.com/article/getting-started-with-merge-tags" target="_blank">merge fields</a> in your MailChimp list.', 'restaurant-reservations' ),
|
170 |
-
'api_unknown_error' => __( 'There was an unexpected error when trying to retrieve the list\'s merge fields.', 'restaurant-reservations' ),
|
171 |
-
'merge_email_label' => __( 'Email', 'restaurant-reservations' ),
|
172 |
-
'merge_email_description' => __( 'The email field is automatically merged.', 'restaurant-reservations' ),
|
173 |
-
)
|
174 |
-
)
|
175 |
-
);
|
176 |
-
}
|
177 |
-
}
|
178 |
-
|
179 |
-
/**
|
180 |
-
* Handle ajax request for lists
|
181 |
-
*/
|
182 |
-
public function ajax_get_lists() {
|
183 |
-
|
184 |
-
// Authenticate request
|
185 |
-
if ( !check_ajax_referer( 'rtb-admin-mc', 'nonce' ) || !current_user_can( 'manage_bookings' ) ) {
|
186 |
-
rtbHelper::admin_nopriv_ajax();
|
187 |
-
}
|
188 |
-
|
189 |
-
$this->load_api( $this->api_key );
|
190 |
-
|
191 |
-
$this->api_call( '/lists' )->send_json_response();
|
192 |
-
}
|
193 |
-
|
194 |
-
/**
|
195 |
-
* Handle ajax request for list merge fields
|
196 |
-
*/
|
197 |
-
public function ajax_load_merge_fields() {
|
198 |
-
|
199 |
-
// Authenticate request
|
200 |
-
if ( !check_ajax_referer( 'rtb-admin-mc', 'nonce' ) || !current_user_can( 'manage_bookings' ) || empty( $_POST['list'] ) ) {
|
201 |
-
rtbHelper::admin_nopriv_ajax();
|
202 |
-
}
|
203 |
-
|
204 |
-
$this->load_api( $this->api_key );
|
205 |
-
|
206 |
-
$this->api_call( '/lists/' . sanitize_key( $_POST['list'] ) . '/merge-fields' )->send_json_response();
|
207 |
-
}
|
208 |
-
|
209 |
-
/**
|
210 |
-
* Load the api request class
|
211 |
-
*
|
212 |
-
* @param string $api_key MailChimp API key
|
213 |
-
*/
|
214 |
-
public function load_api( $api_key = '' ) {
|
215 |
-
|
216 |
-
// Don't load it twice
|
217 |
-
if ( !empty( $this->mc ) ) {
|
218 |
-
return;
|
219 |
-
}
|
220 |
-
|
221 |
-
require_once( RTB_PLUGIN_DIR . '/includes/MailChimpRequest.class.php' );
|
222 |
-
|
223 |
-
// Update the api key
|
224 |
-
if ( $api_key ) {
|
225 |
-
$this->api_key = $api_key;
|
226 |
-
}
|
227 |
-
|
228 |
-
// Load the API wrapper library
|
229 |
-
$this->mc = new mcrftbMailChimpRequest( $this->api_key );
|
230 |
-
}
|
231 |
-
|
232 |
-
/**
|
233 |
-
* Make a call to the API or pull results from cache
|
234 |
-
*
|
235 |
-
* @param string $method HTTP method. Only GET and POST supported for now
|
236 |
-
* @param string $endpoint API endpoint to query, eg: /lists
|
237 |
-
* @param array $params Parameters to pass with the API request
|
238 |
-
*/
|
239 |
-
public function api_call( $endpoint = '', $method = 'GET', $params = array() ) {
|
240 |
-
return $this->mc->call( $endpoint, $method, $params );
|
241 |
-
}
|
242 |
-
|
243 |
-
/**
|
244 |
-
* Check if the API key is valid
|
245 |
-
*/
|
246 |
-
public function is_valid_api_key() {
|
247 |
-
|
248 |
-
if ( empty( $this->api_key ) || empty( $this->mc ) ) {
|
249 |
-
return false;
|
250 |
-
}
|
251 |
-
|
252 |
-
// Bad API key if no data center available
|
253 |
-
if ( strpos( $this->api_key, '-' ) === false ) {
|
254 |
-
return false;
|
255 |
-
}
|
256 |
-
|
257 |
-
// Make a test call to the API
|
258 |
-
$result = $this->api_call( '/lists' )->get_response();
|
259 |
-
if ( empty( $result ) || ( is_object( $result ) && get_class( $result ) == 'WP_Error' ) ) {
|
260 |
-
return false;
|
261 |
-
} else {
|
262 |
-
return true;
|
263 |
-
}
|
264 |
-
|
265 |
-
return false;
|
266 |
-
}
|
267 |
-
|
268 |
-
/**
|
269 |
-
* Add the optin checkbox field to the booking form
|
270 |
-
*/
|
271 |
-
public function add_optin_field( $fields, $request ) {
|
272 |
-
|
273 |
-
global $rtb_controller;
|
274 |
-
$optout = $rtb_controller->settings->get_setting( 'mc-optout' );
|
275 |
-
$lists = $rtb_controller->settings->get_setting( 'mc-lists' );
|
276 |
-
|
277 |
-
if ( $optout !== 'no' && !empty( $lists['list'] ) ) {
|
278 |
-
$optprompt = $rtb_controller->settings->get_setting( 'mc-optprompt' );
|
279 |
-
|
280 |
-
$fields['optin'] = array(
|
281 |
-
'fields' => array(
|
282 |
-
'mc-optin' => array(
|
283 |
-
'title' => $optprompt,
|
284 |
-
'request_input' => empty( $request->mc_optin ) ? '' : $request->mc_optin,
|
285 |
-
'callback' => array( $this, 'print_optin_field' ),
|
286 |
-
)
|
287 |
-
),
|
288 |
-
'order' => 1000,
|
289 |
-
);
|
290 |
-
}
|
291 |
-
|
292 |
-
return $fields;
|
293 |
-
}
|
294 |
-
|
295 |
-
/**
|
296 |
-
* Print the optin checkbox field on the booking form
|
297 |
-
*/
|
298 |
-
public function print_optin_field( $slug, $title, $value ) {
|
299 |
-
|
300 |
-
global $rtb_controller;
|
301 |
-
$optout = $rtb_controller->settings->get_setting( 'mc-optout' );
|
302 |
-
$lists = $rtb_controller->settings->get_setting( 'mc-lists' );
|
303 |
-
|
304 |
-
// Check the box if it's been selected or if the setting is
|
305 |
-
// auto-checked and the form hasn't been submitted with it
|
306 |
-
// un-checked
|
307 |
-
$checked = $value ? true : false;
|
308 |
-
if ( !$checked && $optout == 'checked' && ( empty( $_POST['action'] ) || $_POST['action'] !== 'booking_request' ) ) {
|
309 |
-
$checked = true;
|
310 |
-
}
|
311 |
-
|
312 |
-
if ( $optout !== 'no' && !empty( $lists['list'] ) ) {
|
313 |
-
$label = $rtb_controller->settings->get_setting( 'mc-optprompt' );
|
314 |
-
?>
|
315 |
-
|
316 |
-
<div class="mc-optin">
|
317 |
-
<label>
|
318 |
-
<input type="checkbox" name="<?php echo esc_attr( $slug ); ?>" value="1"<?php checked( $checked ); ?>>
|
319 |
-
<?php echo $label; ?>
|
320 |
-
</label>
|
321 |
-
</div>
|
322 |
-
|
323 |
-
<?php
|
324 |
-
}
|
325 |
-
}
|
326 |
-
|
327 |
-
/**
|
328 |
-
* Validate the optin request data and save on booking object
|
329 |
-
* @param rtbBooking $booking Booking object
|
330 |
-
* @return void
|
331 |
-
*/
|
332 |
-
public function validate_optin_request( $booking ) {
|
333 |
-
global $rtb_controller;
|
334 |
-
|
335 |
-
$booking->mc_optin = false;
|
336 |
-
|
337 |
-
if ( $rtb_controller->settings->get_setting( 'mc-optout' ) !== 'no' && isset( $_POST['mc-optin'] ) && $_POST['mc-optin'] == '1' ) {
|
338 |
-
$booking->mc_optin = true;
|
339 |
-
}
|
340 |
-
}
|
341 |
-
|
342 |
-
/**
|
343 |
-
* Save mc-optin user selection to booking for future reference
|
344 |
-
* @param array $meta Booking meta info
|
345 |
-
* @param rtbBooking $booking Booking object
|
346 |
-
*
|
347 |
-
* @return array $meta array of meta info
|
348 |
-
*/
|
349 |
-
public function add_booking_meta( $meta, $booking )
|
350 |
-
{
|
351 |
-
global $rtb_controller;
|
352 |
-
|
353 |
-
// Did they opt out?
|
354 |
-
$optout = $rtb_controller->settings->get_setting( 'mc-optout' );
|
355 |
-
|
356 |
-
if ( $optout != 'no' ) {
|
357 |
-
$meta['mc_optin'] = $booking->mc_optin;
|
358 |
-
}
|
359 |
-
|
360 |
-
return $meta;
|
361 |
-
}
|
362 |
-
|
363 |
-
/**
|
364 |
-
* Reload mailchimp meta data for booking object
|
365 |
-
* @param rtbBooking $booking Booking object
|
366 |
-
* @param wp_post $post Booking post object
|
367 |
-
*/
|
368 |
-
public function reload_booking_meta( $booking, $post )
|
369 |
-
{
|
370 |
-
global $rtb_controller;
|
371 |
-
|
372 |
-
// Did they opt out?
|
373 |
-
$optout = $rtb_controller->settings->get_setting( 'mc-optout' );
|
374 |
-
|
375 |
-
if ( $optout != 'no' ) {
|
376 |
-
$booking->mc_optin = false;
|
377 |
-
$meta = get_post_meta( $booking->ID, 'rtb', true );
|
378 |
-
|
379 |
-
if ( is_array( $meta ) && isset( $meta['mc_optin'] ) ) {
|
380 |
-
$booking->mc_optin = $meta['mc_optin'];
|
381 |
-
}
|
382 |
-
}
|
383 |
-
}
|
384 |
-
|
385 |
-
/**
|
386 |
-
* Enqueue some JavaScript to subscribe the user after they've
|
387 |
-
* booked.
|
388 |
-
*/
|
389 |
-
public function enqueue_subscription_call( $arg = null ) {
|
390 |
-
global $rtb_controller;
|
391 |
-
|
392 |
-
$transit = null;
|
393 |
-
if( null == $arg ) {
|
394 |
-
$rtb_mc_sbscrb = isset( $_COOKIE['rtb-mc-sbscrb'] ) ? $_COOKIE['rtb-mc-sbscrb'] : null;
|
395 |
-
if( null != $rtb_mc_sbscrb ) {
|
396 |
-
$transit = get_transient( $rtb_mc_sbscrb );
|
397 |
-
}
|
398 |
-
|
399 |
-
if( false == $transit ) {
|
400 |
-
return $arg;
|
401 |
-
}
|
402 |
-
|
403 |
-
require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
|
404 |
-
$booking = new rtbBooking();
|
405 |
-
//Invalid booking
|
406 |
-
if( false == $booking->load_post( $transit ) ) {
|
407 |
-
return $arg;
|
408 |
-
}
|
409 |
-
}
|
410 |
-
else {
|
411 |
-
$booking = $arg;
|
412 |
-
}
|
413 |
-
|
414 |
-
// Did they opt out?
|
415 |
-
$optout = $rtb_controller->settings->get_setting( 'mc-optout' );
|
416 |
-
if ( $optout != 'no' && empty( $booking->mc_optin ) ) {
|
417 |
-
return $arg;
|
418 |
-
}
|
419 |
-
|
420 |
-
// Do we have a list and email address to make the subscription
|
421 |
-
$lists = $rtb_controller->settings->get_setting( 'mc-lists' );
|
422 |
-
if ( empty( $lists['list'] ) || empty( $booking->email ) ) {
|
423 |
-
return $arg;
|
424 |
-
}
|
425 |
-
|
426 |
-
// To later subscribe via JS
|
427 |
-
if( null == $transit ) {
|
428 |
-
$hash = $this->get_booking_hash( $booking );
|
429 |
-
setcookie( $this->transit_cookie_name, $hash, time() + DAY_IN_SECONDS, '/' );
|
430 |
-
set_transient( $hash, $booking->ID, DAY_IN_SECONDS );
|
431 |
-
}
|
432 |
-
|
433 |
-
wp_enqueue_script( 'rtb-mc-subscribe', RTB_PLUGIN_URL . '/assets/js/mailchimp-subscribe.js', array( 'jquery' ), '', true );
|
434 |
-
wp_localize_script(
|
435 |
-
'rtb-mc-subscribe',
|
436 |
-
'rtb_subscribe_mc',
|
437 |
-
array(
|
438 |
-
'ajax_nonce' => wp_create_nonce( 'rtb-mc-subscribe' ),
|
439 |
-
'ajax_url' => admin_url( 'admin-ajax.php' ),
|
440 |
-
'booking' => $booking->ID,
|
441 |
-
)
|
442 |
-
);
|
443 |
-
|
444 |
-
return $arg;
|
445 |
-
}
|
446 |
-
|
447 |
-
/**
|
448 |
-
* Process a subscription request
|
449 |
-
*/
|
450 |
-
public function subscribe() {
|
451 |
-
global $rtb_controller;
|
452 |
-
|
453 |
-
if ( !check_ajax_referer( 'rtb-mc-subscribe', 'nonce' ) || empty( $_POST['booking'] ) ) {
|
454 |
-
wp_send_json_error(
|
455 |
-
array(
|
456 |
-
'error' => 'badnonce',
|
457 |
-
'msg' => __( 'The subscription request has been rejected because it does not appear to have come from this site.', 'restaurant-reservations' ),
|
458 |
-
)
|
459 |
-
);
|
460 |
-
return;
|
461 |
-
}
|
462 |
-
|
463 |
-
$booking_id = sanitize_text_field( $_POST['booking'] );
|
464 |
-
|
465 |
-
require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
|
466 |
-
$booking = new rtbBooking();
|
467 |
-
$this->load_api( $this->api_key );
|
468 |
-
|
469 |
-
// invalid booking
|
470 |
-
if( false == $booking->load_post( $booking_id ) ) {
|
471 |
-
$this->mc->send_json_response();
|
472 |
-
return;
|
473 |
-
}
|
474 |
-
|
475 |
-
$booking = (array) $booking;
|
476 |
-
|
477 |
-
// Did they opt out?
|
478 |
-
$optout = $rtb_controller->settings->get_setting( 'mc-optout' );
|
479 |
-
if ( $optout != 'no' && empty( $booking['mc_optin'] ) ) {
|
480 |
-
$this->mc->send_json_response();
|
481 |
-
return;
|
482 |
-
}
|
483 |
-
|
484 |
-
// Do we have a list and email address to make the subscription
|
485 |
-
$lists = $rtb_controller->settings->get_setting( 'mc-lists' );
|
486 |
-
if ( empty( $lists['list'] ) || empty( $booking['email'] ) ) {
|
487 |
-
$this->mc->send_json_response();
|
488 |
-
return;
|
489 |
-
}
|
490 |
-
|
491 |
-
// Prepare post parameters to send
|
492 |
-
$params = array(
|
493 |
-
'email_address' => $booking['email'],
|
494 |
-
'status' => 'pending',
|
495 |
-
'merge_fields' => (object) $this->get_merge_fields_data( $lists['fields'], $booking ),
|
496 |
-
);
|
497 |
-
|
498 |
-
// Pass in the user's IP for geolocation if available
|
499 |
-
if ( !empty( $_SERVER['REMOTE_ADDR'] ) ) {
|
500 |
-
$params['ip_signup'] = sanitize_text_field( $_SERVER['REMOTE_ADDR'] );
|
501 |
-
$params['ip_opt'] = sanitize_text_field( $_SERVER['REMOTE_ADDR'] );
|
502 |
-
}
|
503 |
-
|
504 |
-
$params = apply_filters( 'mcfrtb_mailchimp_subscribe_args', $params, $booking );
|
505 |
-
|
506 |
-
$mc = $this->api_call( '/lists/' . $lists['list'] . '/members', 'POST', $params );
|
507 |
-
|
508 |
-
// Prevent further attempts to subscribe
|
509 |
-
$hash = $this->get_booking_hash( $booking );
|
510 |
-
if( ( $transit = get_transient( $hash ) ) !== false ) {
|
511 |
-
unset( $_COOKIE[ $this->transit_cookie_name ] );
|
512 |
-
setcookie( $this->transit_cookie_name, null, -1, '/' );
|
513 |
-
delete_transient( $hash );
|
514 |
-
}
|
515 |
-
|
516 |
-
$mc->send_json_response();
|
517 |
-
}
|
518 |
-
|
519 |
-
/**
|
520 |
-
* Get merge fields array to send to the MailChimp API
|
521 |
-
*
|
522 |
-
* @merge_fields array Merge fields data pulled locally from settings
|
523 |
-
*/
|
524 |
-
public function get_merge_fields_data( $merge_fields, $booking ) {
|
525 |
-
|
526 |
-
$output = array();
|
527 |
-
|
528 |
-
foreach( $this->merge_fields as $field => $title ) {
|
529 |
-
if ( !empty( $merge_fields[$field] ) ) {
|
530 |
-
|
531 |
-
if ( $field == 'datetime' ) {
|
532 |
-
$output[$merge_fields[$field]] = $booking['date'];
|
533 |
-
}
|
534 |
-
|
535 |
-
if ( $field == 'name' ) {
|
536 |
-
$output[$merge_fields[$field]] = $booking['name'];
|
537 |
-
}
|
538 |
-
|
539 |
-
if ( $field == 'party' ) {
|
540 |
-
$output[$merge_fields[$field]] = $booking['party'];
|
541 |
-
}
|
542 |
-
|
543 |
-
if ( $field == 'phone' ) {
|
544 |
-
$output[$merge_fields[$field]] = $booking['phone'];
|
545 |
-
}
|
546 |
-
|
547 |
-
if ( $field == 'message' ) {
|
548 |
-
$output[$merge_fields[$field]] = $booking['message'];
|
549 |
-
}
|
550 |
-
}
|
551 |
-
}
|
552 |
-
|
553 |
-
return apply_filters( 'mcfrtb_merge_fields_data', $output, $merge_fields, $booking );
|
554 |
-
}
|
555 |
-
|
556 |
-
/**
|
557 |
-
* Add location to the data merge field when appropriate
|
558 |
-
*
|
559 |
-
* @param array $send Key/value array of merge data to be sent
|
560 |
-
* @param array $merge_fields Key/value array of configured merge fields
|
561 |
-
* @param rtbBooking $booking Booking object
|
562 |
-
* @since 1.2
|
563 |
-
*/
|
564 |
-
public function maybe_send_location_merge_field( $send, $merge_fields, $booking ) {
|
565 |
-
|
566 |
-
global $rtb_controller;
|
567 |
-
|
568 |
-
if ( empty( $rtb_controller->locations ) || empty( $rtb_controller->locations->post_type ) ) {
|
569 |
-
return $send;
|
570 |
-
}
|
571 |
-
|
572 |
-
if ( !empty( $booking['location'] ) && !empty( $merge_fields['location'] ) ) {
|
573 |
-
$term = get_term( $booking['location'] );
|
574 |
-
if ( !empty( $term ) && is_a( $term, 'WP_Term' ) ) {
|
575 |
-
$send[$merge_fields['location']] = $term->name;
|
576 |
-
}
|
577 |
-
}
|
578 |
-
|
579 |
-
return $send;
|
580 |
-
}
|
581 |
-
|
582 |
-
/**
|
583 |
-
* Send merge field data for custom fields
|
584 |
-
*
|
585 |
-
* @param array $send Key/value array of merge data to be sent
|
586 |
-
* @param array $merge_fields Key/value array of configured merge fields
|
587 |
-
* @param rtbBooking $booking Booking object
|
588 |
-
* @since 1.3
|
589 |
-
*/
|
590 |
-
public function maybe_send_merge_fields( $send, $merge_fields, $booking ) {
|
591 |
-
global $rtb_controller;
|
592 |
-
|
593 |
-
$custom_fields = rtb_get_custom_fields();
|
594 |
-
|
595 |
-
foreach( $custom_fields as $custom_field ) {
|
596 |
-
if ( !empty( $merge_fields['cf-' . $custom_field->slug] ) && isset( $booking['custom_fields'] ) && isset( $booking['custom_fields'][$custom_field->slug] ) ) {
|
597 |
-
if ( $custom_field->type == 'confirm' ) {
|
598 |
-
$send[$merge_fields['cf-' . $custom_field->slug]] = 'Checked';
|
599 |
-
} else {
|
600 |
-
$send[$merge_fields['cf-' . $custom_field->slug]] = $rtb_controller->fields->get_display_value( $booking['custom_fields'][$custom_field->slug], $custom_field, '', false );
|
601 |
-
}
|
602 |
-
}
|
603 |
-
}
|
604 |
-
|
605 |
-
return $send;
|
606 |
-
}
|
607 |
-
|
608 |
-
// Helper for cookie and transient to re-attempt subsribe
|
609 |
-
public function get_booking_hash( $booking )
|
610 |
-
{
|
611 |
-
$booking = (array) $booking;
|
612 |
-
return md5( $booking['ID'].'-'.$booking['email'] );
|
613 |
-
}
|
614 |
-
}
|
615 |
-
} // endif;
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class used to add in MailChimp compatibility
|
4 |
+
*/
|
5 |
+
if ( ! defined( 'ABSPATH' ) )
|
6 |
+
exit;
|
7 |
+
|
8 |
+
if ( !class_exists( 'mcfrtbInit' ) ) {
|
9 |
+
class mcfrtbInit {
|
10 |
+
|
11 |
+
public $api_key = null;
|
12 |
+
|
13 |
+
public $status = null;
|
14 |
+
|
15 |
+
public $api_call_cache = array();
|
16 |
+
|
17 |
+
public $transit_cookie_name = 'rtb-mc-sbscrb';
|
18 |
+
|
19 |
+
public function __construct() {
|
20 |
+
global $rtb_controller;
|
21 |
+
|
22 |
+
add_action( 'init', array( $this, 'init' ) );
|
23 |
+
|
24 |
+
if ( !is_admin() and $rtb_controller->permissions->check_permission( 'mailchimp' ) ) {
|
25 |
+
|
26 |
+
// Add optin checkbox to booking form
|
27 |
+
add_filter( 'rtb_booking_form_fields', array( $this, 'add_optin_field' ), 10, 2 );
|
28 |
+
|
29 |
+
// Validate the optin request data
|
30 |
+
add_action( 'rtb_validate_booking_submission', array( $this, 'validate_optin_request' ) );
|
31 |
+
|
32 |
+
// Enqueue assets to send subscription request
|
33 |
+
add_filter( 'rtb_insert_booking', array( $this, 'enqueue_subscription_call' ) );
|
34 |
+
add_filter( 'init', array( $this, 'enqueue_subscription_call' ) );
|
35 |
+
|
36 |
+
// Save mc-optin user input value
|
37 |
+
add_filter( 'rtb_insert_booking_metadata', array( $this, 'add_booking_meta' ), 10, 2 );
|
38 |
+
// Reload meta information
|
39 |
+
add_action( 'rtb_booking_load_post_data', array( $this, 'reload_booking_meta' ), 20, 2 );
|
40 |
+
}
|
41 |
+
|
42 |
+
}
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Initialize the plugin and register hooks
|
46 |
+
*/
|
47 |
+
public function init() {
|
48 |
+
|
49 |
+
// Initialize the plugin
|
50 |
+
$this->load_config();
|
51 |
+
|
52 |
+
add_action( 'mcfrtb_list_merge_fields', array( $this, 'maybe_add_location_merge_field' ) );
|
53 |
+
add_action( 'mcfrtb_list_merge_fields', array( $this, 'maybe_add_merge_options' ) );
|
54 |
+
|
55 |
+
// Load assets
|
56 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_assets' ) );
|
57 |
+
|
58 |
+
// Receive ajax calls for mailchimp lists
|
59 |
+
add_action( 'wp_ajax_nopriv_mcfrtb-get-lists' , array( 'rtbHelper' , 'admin_nopriv_ajax' ) );
|
60 |
+
add_action( 'wp_ajax_mcfrtb-get-lists', array( $this, 'ajax_get_lists' ) );
|
61 |
+
|
62 |
+
// Receive ajax calls for merge fields
|
63 |
+
add_action( 'wp_ajax_nopriv_mcfrtb-load-merge-fields' , array( 'rtbHelper' , 'admin_nopriv_ajax' ) );
|
64 |
+
add_action( 'wp_ajax_mcfrtb-load-merge-fields', array( $this, 'ajax_load_merge_fields' ) );
|
65 |
+
|
66 |
+
|
67 |
+
// Process subscription calls
|
68 |
+
add_action( 'wp_ajax_nopriv_mcfrtb-subscribe' , array( $this , 'subscribe' ) );
|
69 |
+
add_action( 'wp_ajax_mcfrtb-subscribe', array( $this, 'subscribe' ) );
|
70 |
+
add_filter( 'mcfrtb_merge_fields_data', array( $this, 'maybe_send_location_merge_field' ), 10, 3 );
|
71 |
+
add_filter( 'mcfrtb_merge_fields_data', array( $this, 'maybe_send_merge_fields' ), 10, 3 );
|
72 |
+
}
|
73 |
+
|
74 |
+
|
75 |
+
/**
|
76 |
+
* Load the configuration parameters
|
77 |
+
*/
|
78 |
+
public function load_config() {
|
79 |
+
|
80 |
+
global $rtb_controller;
|
81 |
+
$api_key = $rtb_controller->settings->get_setting( 'mc-apikey' );
|
82 |
+
|
83 |
+
if( ! is_array( $api_key ) ) {
|
84 |
+
$api_key = [];
|
85 |
+
}
|
86 |
+
|
87 |
+
$this->api_key = array_key_exists('api_key', $api_key) ? $api_key['api_key'] : '';
|
88 |
+
$this->status = array_key_exists('status', $api_key) ? $api_key['status'] : '';
|
89 |
+
|
90 |
+
$this->merge_fields = apply_filters(
|
91 |
+
'mcfrtb_list_merge_fields',
|
92 |
+
array(
|
93 |
+
'datetime' => __( 'Date/Time of Booking', 'restaurant-reservations' ),
|
94 |
+
'name' => __( 'Name', 'restaurant-reservations' ),
|
95 |
+
'party' => __( 'Party Size', 'restaurant-reservations' ),
|
96 |
+
'phone' => __( 'Phone Number', 'restaurant-reservations' ),
|
97 |
+
'message' => __( 'Message', 'restaurant-reservations' ),
|
98 |
+
)
|
99 |
+
);
|
100 |
+
|
101 |
+
}
|
102 |
+
|
103 |
+
/**
|
104 |
+
* Add merge field for location if multi-location support is active
|
105 |
+
*
|
106 |
+
* @param array $fields Key/value list of booking data available for merge
|
107 |
+
* @since 1.2
|
108 |
+
*/
|
109 |
+
public function maybe_add_location_merge_field( $fields ) {
|
110 |
+
|
111 |
+
global $rtb_controller;
|
112 |
+
|
113 |
+
if ( !empty( $rtb_controller->locations ) && !empty( $rtb_controller->locations->post_type ) ) {
|
114 |
+
$fields['location'] = __( 'Location', 'restaurant-reservations' );
|
115 |
+
}
|
116 |
+
|
117 |
+
return $fields;
|
118 |
+
}
|
119 |
+
|
120 |
+
/**
|
121 |
+
* Add merge field options for custom fields
|
122 |
+
*
|
123 |
+
* @param array $fields Key/value list of booking data available for merge
|
124 |
+
* @since 1.3
|
125 |
+
*/
|
126 |
+
public function maybe_add_merge_options( $fields ) {
|
127 |
+
|
128 |
+
$custom_fields = rtb_get_custom_fields();
|
129 |
+
|
130 |
+
$custom_merge_fields = array();
|
131 |
+
foreach( $custom_fields as $custom_field ) {
|
132 |
+
$custom_merge_fields['cf-' . $custom_field->slug] = $custom_field->title;
|
133 |
+
}
|
134 |
+
|
135 |
+
return array_merge( $fields, $custom_merge_fields );
|
136 |
+
}
|
137 |
+
|
138 |
+
/**
|
139 |
+
* Enqueue the admin-only CSS and Javascript
|
140 |
+
* @since 0.0.1
|
141 |
+
*/
|
142 |
+
public function enqueue_admin_assets() {
|
143 |
+
|
144 |
+
global $rtb_controller;
|
145 |
+
|
146 |
+
// Use the page reference in $admin_page_hooks because
|
147 |
+
// it changes in SOME hooks when it is translated.
|
148 |
+
// https://core.trac.wordpress.org/ticket/18857
|
149 |
+
global $admin_page_hooks;
|
150 |
+
|
151 |
+
$screen = get_current_screen();
|
152 |
+
if ( empty( $screen ) || empty( $admin_page_hooks['rtb-bookings'] ) ) {
|
153 |
+
return;
|
154 |
+
}
|
155 |
+
|
156 |
+
if ( $screen->base == 'toplevel_page_rtb-bookings' || $screen->base == $admin_page_hooks['rtb-bookings'] . '_page_rtb-settings' ) {
|
157 |
+
|
158 |
+
wp_enqueue_script( 'rtb-admin-mc', RTB_PLUGIN_URL . '/assets/js/mailchimp-admin.js', array( 'jquery' ), '', true );
|
159 |
+
wp_localize_script(
|
160 |
+
'rtb-admin-mc',
|
161 |
+
'rtb_admin_mc',
|
162 |
+
array(
|
163 |
+
'ajax_nonce' => wp_create_nonce( 'rtb-admin-mc' ),
|
164 |
+
'merge_fields' => $this->merge_fields,
|
165 |
+
'lists' => $rtb_controller->settings->get_setting( 'mc-lists' ),
|
166 |
+
'strings' => array(
|
167 |
+
'merge_booking_data' => __( 'Booking Form Data', 'restaurant-reservations' ),
|
168 |
+
'merge_list_field' => __( 'MailChimp List Field', 'restaurant-reservations' ),
|
169 |
+
'merge_description' => __( 'Connect information from the booking request to <a href="http://kb.mailchimp.com/article/getting-started-with-merge-tags" target="_blank">merge fields</a> in your MailChimp list.', 'restaurant-reservations' ),
|
170 |
+
'api_unknown_error' => __( 'There was an unexpected error when trying to retrieve the list\'s merge fields.', 'restaurant-reservations' ),
|
171 |
+
'merge_email_label' => __( 'Email', 'restaurant-reservations' ),
|
172 |
+
'merge_email_description' => __( 'The email field is automatically merged.', 'restaurant-reservations' ),
|
173 |
+
)
|
174 |
+
)
|
175 |
+
);
|
176 |
+
}
|
177 |
+
}
|
178 |
+
|
179 |
+
/**
|
180 |
+
* Handle ajax request for lists
|
181 |
+
*/
|
182 |
+
public function ajax_get_lists() {
|
183 |
+
|
184 |
+
// Authenticate request
|
185 |
+
if ( !check_ajax_referer( 'rtb-admin-mc', 'nonce' ) || !current_user_can( 'manage_bookings' ) ) {
|
186 |
+
rtbHelper::admin_nopriv_ajax();
|
187 |
+
}
|
188 |
+
|
189 |
+
$this->load_api( $this->api_key );
|
190 |
+
|
191 |
+
$this->api_call( '/lists' )->send_json_response();
|
192 |
+
}
|
193 |
+
|
194 |
+
/**
|
195 |
+
* Handle ajax request for list merge fields
|
196 |
+
*/
|
197 |
+
public function ajax_load_merge_fields() {
|
198 |
+
|
199 |
+
// Authenticate request
|
200 |
+
if ( !check_ajax_referer( 'rtb-admin-mc', 'nonce' ) || !current_user_can( 'manage_bookings' ) || empty( $_POST['list'] ) ) {
|
201 |
+
rtbHelper::admin_nopriv_ajax();
|
202 |
+
}
|
203 |
+
|
204 |
+
$this->load_api( $this->api_key );
|
205 |
+
|
206 |
+
$this->api_call( '/lists/' . sanitize_key( $_POST['list'] ) . '/merge-fields' )->send_json_response();
|
207 |
+
}
|
208 |
+
|
209 |
+
/**
|
210 |
+
* Load the api request class
|
211 |
+
*
|
212 |
+
* @param string $api_key MailChimp API key
|
213 |
+
*/
|
214 |
+
public function load_api( $api_key = '' ) {
|
215 |
+
|
216 |
+
// Don't load it twice
|
217 |
+
if ( !empty( $this->mc ) ) {
|
218 |
+
return;
|
219 |
+
}
|
220 |
+
|
221 |
+
require_once( RTB_PLUGIN_DIR . '/includes/MailChimpRequest.class.php' );
|
222 |
+
|
223 |
+
// Update the api key
|
224 |
+
if ( $api_key ) {
|
225 |
+
$this->api_key = $api_key;
|
226 |
+
}
|
227 |
+
|
228 |
+
// Load the API wrapper library
|
229 |
+
$this->mc = new mcrftbMailChimpRequest( $this->api_key );
|
230 |
+
}
|
231 |
+
|
232 |
+
/**
|
233 |
+
* Make a call to the API or pull results from cache
|
234 |
+
*
|
235 |
+
* @param string $method HTTP method. Only GET and POST supported for now
|
236 |
+
* @param string $endpoint API endpoint to query, eg: /lists
|
237 |
+
* @param array $params Parameters to pass with the API request
|
238 |
+
*/
|
239 |
+
public function api_call( $endpoint = '', $method = 'GET', $params = array() ) {
|
240 |
+
return $this->mc->call( $endpoint, $method, $params );
|
241 |
+
}
|
242 |
+
|
243 |
+
/**
|
244 |
+
* Check if the API key is valid
|
245 |
+
*/
|
246 |
+
public function is_valid_api_key() {
|
247 |
+
|
248 |
+
if ( empty( $this->api_key ) || empty( $this->mc ) ) {
|
249 |
+
return false;
|
250 |
+
}
|
251 |
+
|
252 |
+
// Bad API key if no data center available
|
253 |
+
if ( strpos( $this->api_key, '-' ) === false ) {
|
254 |
+
return false;
|
255 |
+
}
|
256 |
+
|
257 |
+
// Make a test call to the API
|
258 |
+
$result = $this->api_call( '/lists' )->get_response();
|
259 |
+
if ( empty( $result ) || ( is_object( $result ) && get_class( $result ) == 'WP_Error' ) ) {
|
260 |
+
return false;
|
261 |
+
} else {
|
262 |
+
return true;
|
263 |
+
}
|
264 |
+
|
265 |
+
return false;
|
266 |
+
}
|
267 |
+
|
268 |
+
/**
|
269 |
+
* Add the optin checkbox field to the booking form
|
270 |
+
*/
|
271 |
+
public function add_optin_field( $fields, $request ) {
|
272 |
+
|
273 |
+
global $rtb_controller;
|
274 |
+
$optout = $rtb_controller->settings->get_setting( 'mc-optout' );
|
275 |
+
$lists = $rtb_controller->settings->get_setting( 'mc-lists' );
|
276 |
+
|
277 |
+
if ( $optout !== 'no' && !empty( $lists['list'] ) ) {
|
278 |
+
$optprompt = $rtb_controller->settings->get_setting( 'mc-optprompt' );
|
279 |
+
|
280 |
+
$fields['optin'] = array(
|
281 |
+
'fields' => array(
|
282 |
+
'mc-optin' => array(
|
283 |
+
'title' => $optprompt,
|
284 |
+
'request_input' => empty( $request->mc_optin ) ? '' : $request->mc_optin,
|
285 |
+
'callback' => array( $this, 'print_optin_field' ),
|
286 |
+
)
|
287 |
+
),
|
288 |
+
'order' => 1000,
|
289 |
+
);
|
290 |
+
}
|
291 |
+
|
292 |
+
return $fields;
|
293 |
+
}
|
294 |
+
|
295 |
+
/**
|
296 |
+
* Print the optin checkbox field on the booking form
|
297 |
+
*/
|
298 |
+
public function print_optin_field( $slug, $title, $value ) {
|
299 |
+
|
300 |
+
global $rtb_controller;
|
301 |
+
$optout = $rtb_controller->settings->get_setting( 'mc-optout' );
|
302 |
+
$lists = $rtb_controller->settings->get_setting( 'mc-lists' );
|
303 |
+
|
304 |
+
// Check the box if it's been selected or if the setting is
|
305 |
+
// auto-checked and the form hasn't been submitted with it
|
306 |
+
// un-checked
|
307 |
+
$checked = $value ? true : false;
|
308 |
+
if ( !$checked && $optout == 'checked' && ( empty( $_POST['action'] ) || $_POST['action'] !== 'booking_request' ) ) {
|
309 |
+
$checked = true;
|
310 |
+
}
|
311 |
+
|
312 |
+
if ( $optout !== 'no' && !empty( $lists['list'] ) ) {
|
313 |
+
$label = $rtb_controller->settings->get_setting( 'mc-optprompt' );
|
314 |
+
?>
|
315 |
+
|
316 |
+
<div class="mc-optin">
|
317 |
+
<label>
|
318 |
+
<input type="checkbox" name="<?php echo esc_attr( $slug ); ?>" value="1"<?php checked( $checked ); ?>>
|
319 |
+
<?php echo $label; ?>
|
320 |
+
</label>
|
321 |
+
</div>
|
322 |
+
|
323 |
+
<?php
|
324 |
+
}
|
325 |
+
}
|
326 |
+
|
327 |
+
/**
|
328 |
+
* Validate the optin request data and save on booking object
|
329 |
+
* @param rtbBooking $booking Booking object
|
330 |
+
* @return void
|
331 |
+
*/
|
332 |
+
public function validate_optin_request( $booking ) {
|
333 |
+
global $rtb_controller;
|
334 |
+
|
335 |
+
$booking->mc_optin = false;
|
336 |
+
|
337 |
+
if ( $rtb_controller->settings->get_setting( 'mc-optout' ) !== 'no' && isset( $_POST['mc-optin'] ) && $_POST['mc-optin'] == '1' ) {
|
338 |
+
$booking->mc_optin = true;
|
339 |
+
}
|
340 |
+
}
|
341 |
+
|
342 |
+
/**
|
343 |
+
* Save mc-optin user selection to booking for future reference
|
344 |
+
* @param array $meta Booking meta info
|
345 |
+
* @param rtbBooking $booking Booking object
|
346 |
+
*
|
347 |
+
* @return array $meta array of meta info
|
348 |
+
*/
|
349 |
+
public function add_booking_meta( $meta, $booking )
|
350 |
+
{
|
351 |
+
global $rtb_controller;
|
352 |
+
|
353 |
+
// Did they opt out?
|
354 |
+
$optout = $rtb_controller->settings->get_setting( 'mc-optout' );
|
355 |
+
|
356 |
+
if ( $optout != 'no' ) {
|
357 |
+
$meta['mc_optin'] = $booking->mc_optin;
|
358 |
+
}
|
359 |
+
|
360 |
+
return $meta;
|
361 |
+
}
|
362 |
+
|
363 |
+
/**
|
364 |
+
* Reload mailchimp meta data for booking object
|
365 |
+
* @param rtbBooking $booking Booking object
|
366 |
+
* @param wp_post $post Booking post object
|
367 |
+
*/
|
368 |
+
public function reload_booking_meta( $booking, $post )
|
369 |
+
{
|
370 |
+
global $rtb_controller;
|
371 |
+
|
372 |
+
// Did they opt out?
|
373 |
+
$optout = $rtb_controller->settings->get_setting( 'mc-optout' );
|
374 |
+
|
375 |
+
if ( $optout != 'no' ) {
|
376 |
+
$booking->mc_optin = false;
|
377 |
+
$meta = get_post_meta( $booking->ID, 'rtb', true );
|
378 |
+
|
379 |
+
if ( is_array( $meta ) && isset( $meta['mc_optin'] ) ) {
|
380 |
+
$booking->mc_optin = $meta['mc_optin'];
|
381 |
+
}
|
382 |
+
}
|
383 |
+
}
|
384 |
+
|
385 |
+
/**
|
386 |
+
* Enqueue some JavaScript to subscribe the user after they've
|
387 |
+
* booked.
|
388 |
+
*/
|
389 |
+
public function enqueue_subscription_call( $arg = null ) {
|
390 |
+
global $rtb_controller;
|
391 |
+
|
392 |
+
$transit = null;
|
393 |
+
if( null == $arg ) {
|
394 |
+
$rtb_mc_sbscrb = isset( $_COOKIE['rtb-mc-sbscrb'] ) ? $_COOKIE['rtb-mc-sbscrb'] : null;
|
395 |
+
if( null != $rtb_mc_sbscrb ) {
|
396 |
+
$transit = get_transient( $rtb_mc_sbscrb );
|
397 |
+
}
|
398 |
+
|
399 |
+
if( false == $transit ) {
|
400 |
+
return $arg;
|
401 |
+
}
|
402 |
+
|
403 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
|
404 |
+
$booking = new rtbBooking();
|
405 |
+
//Invalid booking
|
406 |
+
if( false == $booking->load_post( $transit ) ) {
|
407 |
+
return $arg;
|
408 |
+
}
|
409 |
+
}
|
410 |
+
else {
|
411 |
+
$booking = $arg;
|
412 |
+
}
|
413 |
+
|
414 |
+
// Did they opt out?
|
415 |
+
$optout = $rtb_controller->settings->get_setting( 'mc-optout' );
|
416 |
+
if ( $optout != 'no' && empty( $booking->mc_optin ) ) {
|
417 |
+
return $arg;
|
418 |
+
}
|
419 |
+
|
420 |
+
// Do we have a list and email address to make the subscription
|
421 |
+
$lists = $rtb_controller->settings->get_setting( 'mc-lists' );
|
422 |
+
if ( empty( $lists['list'] ) || empty( $booking->email ) ) {
|
423 |
+
return $arg;
|
424 |
+
}
|
425 |
+
|
426 |
+
// To later subscribe via JS
|
427 |
+
if( null == $transit ) {
|
428 |
+
$hash = $this->get_booking_hash( $booking );
|
429 |
+
setcookie( $this->transit_cookie_name, $hash, time() + DAY_IN_SECONDS, '/' );
|
430 |
+
set_transient( $hash, $booking->ID, DAY_IN_SECONDS );
|
431 |
+
}
|
432 |
+
|
433 |
+
wp_enqueue_script( 'rtb-mc-subscribe', RTB_PLUGIN_URL . '/assets/js/mailchimp-subscribe.js', array( 'jquery' ), '', true );
|
434 |
+
wp_localize_script(
|
435 |
+
'rtb-mc-subscribe',
|
436 |
+
'rtb_subscribe_mc',
|
437 |
+
array(
|
438 |
+
'ajax_nonce' => wp_create_nonce( 'rtb-mc-subscribe' ),
|
439 |
+
'ajax_url' => admin_url( 'admin-ajax.php' ),
|
440 |
+
'booking' => $booking->ID,
|
441 |
+
)
|
442 |
+
);
|
443 |
+
|
444 |
+
return $arg;
|
445 |
+
}
|
446 |
+
|
447 |
+
/**
|
448 |
+
* Process a subscription request
|
449 |
+
*/
|
450 |
+
public function subscribe() {
|
451 |
+
global $rtb_controller;
|
452 |
+
|
453 |
+
if ( !check_ajax_referer( 'rtb-mc-subscribe', 'nonce' ) || empty( $_POST['booking'] ) ) {
|
454 |
+
wp_send_json_error(
|
455 |
+
array(
|
456 |
+
'error' => 'badnonce',
|
457 |
+
'msg' => __( 'The subscription request has been rejected because it does not appear to have come from this site.', 'restaurant-reservations' ),
|
458 |
+
)
|
459 |
+
);
|
460 |
+
return;
|
461 |
+
}
|
462 |
+
|
463 |
+
$booking_id = sanitize_text_field( $_POST['booking'] );
|
464 |
+
|
465 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
|
466 |
+
$booking = new rtbBooking();
|
467 |
+
$this->load_api( $this->api_key );
|
468 |
+
|
469 |
+
// invalid booking
|
470 |
+
if( false == $booking->load_post( $booking_id ) ) {
|
471 |
+
$this->mc->send_json_response();
|
472 |
+
return;
|
473 |
+
}
|
474 |
+
|
475 |
+
$booking = (array) $booking;
|
476 |
+
|
477 |
+
// Did they opt out?
|
478 |
+
$optout = $rtb_controller->settings->get_setting( 'mc-optout' );
|
479 |
+
if ( $optout != 'no' && empty( $booking['mc_optin'] ) ) {
|
480 |
+
$this->mc->send_json_response();
|
481 |
+
return;
|
482 |
+
}
|
483 |
+
|
484 |
+
// Do we have a list and email address to make the subscription
|
485 |
+
$lists = $rtb_controller->settings->get_setting( 'mc-lists' );
|
486 |
+
if ( empty( $lists['list'] ) || empty( $booking['email'] ) ) {
|
487 |
+
$this->mc->send_json_response();
|
488 |
+
return;
|
489 |
+
}
|
490 |
+
|
491 |
+
// Prepare post parameters to send
|
492 |
+
$params = array(
|
493 |
+
'email_address' => $booking['email'],
|
494 |
+
'status' => 'pending',
|
495 |
+
'merge_fields' => (object) $this->get_merge_fields_data( $lists['fields'], $booking ),
|
496 |
+
);
|
497 |
+
|
498 |
+
// Pass in the user's IP for geolocation if available
|
499 |
+
if ( !empty( $_SERVER['REMOTE_ADDR'] ) ) {
|
500 |
+
$params['ip_signup'] = sanitize_text_field( $_SERVER['REMOTE_ADDR'] );
|
501 |
+
$params['ip_opt'] = sanitize_text_field( $_SERVER['REMOTE_ADDR'] );
|
502 |
+
}
|
503 |
+
|
504 |
+
$params = apply_filters( 'mcfrtb_mailchimp_subscribe_args', $params, $booking );
|
505 |
+
|
506 |
+
$mc = $this->api_call( '/lists/' . $lists['list'] . '/members', 'POST', $params );
|
507 |
+
|
508 |
+
// Prevent further attempts to subscribe
|
509 |
+
$hash = $this->get_booking_hash( $booking );
|
510 |
+
if( ( $transit = get_transient( $hash ) ) !== false ) {
|
511 |
+
unset( $_COOKIE[ $this->transit_cookie_name ] );
|
512 |
+
setcookie( $this->transit_cookie_name, null, -1, '/' );
|
513 |
+
delete_transient( $hash );
|
514 |
+
}
|
515 |
+
|
516 |
+
$mc->send_json_response();
|
517 |
+
}
|
518 |
+
|
519 |
+
/**
|
520 |
+
* Get merge fields array to send to the MailChimp API
|
521 |
+
*
|
522 |
+
* @merge_fields array Merge fields data pulled locally from settings
|
523 |
+
*/
|
524 |
+
public function get_merge_fields_data( $merge_fields, $booking ) {
|
525 |
+
|
526 |
+
$output = array();
|
527 |
+
|
528 |
+
foreach( $this->merge_fields as $field => $title ) {
|
529 |
+
if ( !empty( $merge_fields[$field] ) ) {
|
530 |
+
|
531 |
+
if ( $field == 'datetime' ) {
|
532 |
+
$output[$merge_fields[$field]] = $booking['date'];
|
533 |
+
}
|
534 |
+
|
535 |
+
if ( $field == 'name' ) {
|
536 |
+
$output[$merge_fields[$field]] = $booking['name'];
|
537 |
+
}
|
538 |
+
|
539 |
+
if ( $field == 'party' ) {
|
540 |
+
$output[$merge_fields[$field]] = $booking['party'];
|
541 |
+
}
|
542 |
+
|
543 |
+
if ( $field == 'phone' ) {
|
544 |
+
$output[$merge_fields[$field]] = $booking['phone'];
|
545 |
+
}
|
546 |
+
|
547 |
+
if ( $field == 'message' ) {
|
548 |
+
$output[$merge_fields[$field]] = $booking['message'];
|
549 |
+
}
|
550 |
+
}
|
551 |
+
}
|
552 |
+
|
553 |
+
return apply_filters( 'mcfrtb_merge_fields_data', $output, $merge_fields, $booking );
|
554 |
+
}
|
555 |
+
|
556 |
+
/**
|
557 |
+
* Add location to the data merge field when appropriate
|
558 |
+
*
|
559 |
+
* @param array $send Key/value array of merge data to be sent
|
560 |
+
* @param array $merge_fields Key/value array of configured merge fields
|
561 |
+
* @param rtbBooking $booking Booking object
|
562 |
+
* @since 1.2
|
563 |
+
*/
|
564 |
+
public function maybe_send_location_merge_field( $send, $merge_fields, $booking ) {
|
565 |
+
|
566 |
+
global $rtb_controller;
|
567 |
+
|
568 |
+
if ( empty( $rtb_controller->locations ) || empty( $rtb_controller->locations->post_type ) ) {
|
569 |
+
return $send;
|
570 |
+
}
|
571 |
+
|
572 |
+
if ( !empty( $booking['location'] ) && !empty( $merge_fields['location'] ) ) {
|
573 |
+
$term = get_term( $booking['location'] );
|
574 |
+
if ( !empty( $term ) && is_a( $term, 'WP_Term' ) ) {
|
575 |
+
$send[$merge_fields['location']] = $term->name;
|
576 |
+
}
|
577 |
+
}
|
578 |
+
|
579 |
+
return $send;
|
580 |
+
}
|
581 |
+
|
582 |
+
/**
|
583 |
+
* Send merge field data for custom fields
|
584 |
+
*
|
585 |
+
* @param array $send Key/value array of merge data to be sent
|
586 |
+
* @param array $merge_fields Key/value array of configured merge fields
|
587 |
+
* @param rtbBooking $booking Booking object
|
588 |
+
* @since 1.3
|
589 |
+
*/
|
590 |
+
public function maybe_send_merge_fields( $send, $merge_fields, $booking ) {
|
591 |
+
global $rtb_controller;
|
592 |
+
|
593 |
+
$custom_fields = rtb_get_custom_fields();
|
594 |
+
|
595 |
+
foreach( $custom_fields as $custom_field ) {
|
596 |
+
if ( !empty( $merge_fields['cf-' . $custom_field->slug] ) && isset( $booking['custom_fields'] ) && isset( $booking['custom_fields'][$custom_field->slug] ) ) {
|
597 |
+
if ( $custom_field->type == 'confirm' ) {
|
598 |
+
$send[$merge_fields['cf-' . $custom_field->slug]] = 'Checked';
|
599 |
+
} else {
|
600 |
+
$send[$merge_fields['cf-' . $custom_field->slug]] = $rtb_controller->fields->get_display_value( $booking['custom_fields'][$custom_field->slug], $custom_field, '', false );
|
601 |
+
}
|
602 |
+
}
|
603 |
+
}
|
604 |
+
|
605 |
+
return $send;
|
606 |
+
}
|
607 |
+
|
608 |
+
// Helper for cookie and transient to re-attempt subsribe
|
609 |
+
public function get_booking_hash( $booking )
|
610 |
+
{
|
611 |
+
$booking = (array) $booking;
|
612 |
+
return md5( $booking['ID'].'-'.$booking['email'] );
|
613 |
+
}
|
614 |
+
}
|
615 |
+
} // endif;
|
includes/Migration.class.php
CHANGED
@@ -1,141 +1,141 @@
|
|
1 |
-
<?php
|
2 |
-
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
-
|
4 |
-
if ( !class_exists( 'rtbMigrationManager' ) ) {
|
5 |
-
/**
|
6 |
-
* Class to handle post update migrations for Restaurant Reservations
|
7 |
-
*
|
8 |
-
* @since 2.2.4
|
9 |
-
*/
|
10 |
-
class rtbMigrationManager {
|
11 |
-
|
12 |
-
public function __construct() {
|
13 |
-
|
14 |
-
$this->convert_draft_status_to_payment_pending_status();
|
15 |
-
|
16 |
-
$this->convert_payment_gateway_setting();
|
17 |
-
|
18 |
-
$this->update_invalid_setting_values();
|
19 |
-
|
20 |
-
$this->update_setting_value_types();
|
21 |
-
}
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
/************************* Migration callback ahead *************************/
|
26 |
-
|
27 |
-
/**
|
28 |
-
* Changes rtb-booking post status from draft to payment_pending while a booking has been created but
|
29 |
-
* the payment is yet to be paid. Update all existing rtb-booking posts with status draft to
|
30 |
-
* payment_pending. This change will let admins check and process/delete the existing bookings
|
31 |
-
* without payment, and also let the use pay if they have accidently move elsewhere before
|
32 |
-
* the payment is completed.
|
33 |
-
* Currently users or admins can not do anything with bookings with the status draft. If the same
|
34 |
-
* booking needs to be made again, at least name or phone number or email needs to be cahgned
|
35 |
-
* for the same slot otherwise validation will fail stating there is a duplicate booking
|
36 |
-
*
|
37 |
-
* @removal 2022-01-01
|
38 |
-
*/
|
39 |
-
public function convert_draft_status_to_payment_pending_status() {
|
40 |
-
global $wpdb;
|
41 |
-
|
42 |
-
$wpdb->query("
|
43 |
-
UPDATE
|
44 |
-
{$wpdb->posts}
|
45 |
-
SET
|
46 |
-
`post_status` = 'payment_pending'
|
47 |
-
WHERE
|
48 |
-
`post_status` = 'draft' AND `post_type` = 'rtb-booking'
|
49 |
-
");
|
50 |
-
}
|
51 |
-
|
52 |
-
/**
|
53 |
-
* Update the rtb-payment-gateway setting from string to array
|
54 |
-
* Previously this setting was a radio, now it is a checkbox.
|
55 |
-
*
|
56 |
-
* @since 2.3.0
|
57 |
-
*
|
58 |
-
* @removal 2022-04-01
|
59 |
-
*/
|
60 |
-
public function convert_payment_gateway_setting()
|
61 |
-
{
|
62 |
-
$settings_page = 'rtb-settings';
|
63 |
-
$options = get_option( $settings_page );
|
64 |
-
|
65 |
-
if ( ! is_array( $options ) || ! array_key_exists( 'rtb-payment-gateway', $options ) ) { return; }
|
66 |
-
|
67 |
-
if( ! is_array( $options['rtb-payment-gateway'] ) && version_compare( RTB_VERSION, '2.2.11', '>' ) ) {
|
68 |
-
$options['rtb-payment-gateway'] = [ $options['rtb-payment-gateway'] ];
|
69 |
-
|
70 |
-
update_option( $settings_page, $options );
|
71 |
-
}
|
72 |
-
}
|
73 |
-
|
74 |
-
/**
|
75 |
-
* Update setting of type count, which are 'time-reminder-user' and
|
76 |
-
* 'time-late-user'. When no value is being set in admin, it has underscore
|
77 |
-
* at least. Whereas, its expected to be empty
|
78 |
-
*
|
79 |
-
* @since 2.3.1
|
80 |
-
*
|
81 |
-
* @removal 2022-04-01
|
82 |
-
*/
|
83 |
-
public function update_invalid_setting_values() {
|
84 |
-
$settings_page = 'rtb-settings';
|
85 |
-
$options = get_option( $settings_page );
|
86 |
-
|
87 |
-
if(
|
88 |
-
is_array( $options )
|
89 |
-
&&
|
90 |
-
isset( $options['time-reminder-user'] )
|
91 |
-
&&
|
92 |
-
'_' == $options['time-reminder-user']
|
93 |
-
&&
|
94 |
-
version_compare( RTB_VERSION, '2.2.11', '>' )
|
95 |
-
)
|
96 |
-
{
|
97 |
-
$options['time-reminder-user'] = '';
|
98 |
-
}
|
99 |
-
|
100 |
-
if(
|
101 |
-
is_array( $options )
|
102 |
-
&&
|
103 |
-
isset( $options['time-late-user'] )
|
104 |
-
&&
|
105 |
-
'_' == $options['time-late-user']
|
106 |
-
&&
|
107 |
-
version_compare( RTB_VERSION, '2.2.11', '>' )
|
108 |
-
)
|
109 |
-
{
|
110 |
-
$options['time-late-user'] = '';
|
111 |
-
}
|
112 |
-
|
113 |
-
update_option( $settings_page, $options );
|
114 |
-
}
|
115 |
-
|
116 |
-
/**
|
117 |
-
* Updates a number of settings, which now are using the "select" type instead of the "count"
|
118 |
-
* type, since they have no units to select
|
119 |
-
*
|
120 |
-
* @since 2.3.0
|
121 |
-
*
|
122 |
-
* @removal 2022-07-01
|
123 |
-
*/
|
124 |
-
public function update_setting_value_types() {
|
125 |
-
|
126 |
-
$options = get_option( 'rtb-settings' );
|
127 |
-
|
128 |
-
if ( is_array( $options ) ) {
|
129 |
-
|
130 |
-
$options['rtb-dining-block-length'] = ! empty( $options['rtb-dining-block-length'] ) ? intval( $options['rtb-dining-block-length'] ) : '';
|
131 |
-
$options['rtb-max-tables-count'] = ! empty( $options['rtb-max-tables-count'] ) ? intval( $options['rtb-max-tables-count'] ) : '';
|
132 |
-
$options['rtb-max-people-count'] = ! empty( $options['rtb-max-people-count'] ) ? intval( $options['rtb-max-people-count'] ) : '';
|
133 |
-
$options['auto-confirm-max-reservations'] = ! empty( $options['auto-confirm-max-reservations'] ) ? intval( $options['auto-confirm-max-reservations'] ) : '';
|
134 |
-
$options['auto-confirm-max-seats'] = ! empty( $options['auto-confirm-max-seats'] ) ? intval( $options['auto-confirm-max-seats'] ) : '';
|
135 |
-
|
136 |
-
update_option( 'rtb-settings', $options );
|
137 |
-
}
|
138 |
-
}
|
139 |
-
}
|
140 |
-
|
141 |
}
|
1 |
+
<?php
|
2 |
+
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
+
|
4 |
+
if ( !class_exists( 'rtbMigrationManager' ) ) {
|
5 |
+
/**
|
6 |
+
* Class to handle post update migrations for Restaurant Reservations
|
7 |
+
*
|
8 |
+
* @since 2.2.4
|
9 |
+
*/
|
10 |
+
class rtbMigrationManager {
|
11 |
+
|
12 |
+
public function __construct() {
|
13 |
+
|
14 |
+
$this->convert_draft_status_to_payment_pending_status();
|
15 |
+
|
16 |
+
$this->convert_payment_gateway_setting();
|
17 |
+
|
18 |
+
$this->update_invalid_setting_values();
|
19 |
+
|
20 |
+
$this->update_setting_value_types();
|
21 |
+
}
|
22 |
+
|
23 |
+
|
24 |
+
|
25 |
+
/************************* Migration callback ahead *************************/
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Changes rtb-booking post status from draft to payment_pending while a booking has been created but
|
29 |
+
* the payment is yet to be paid. Update all existing rtb-booking posts with status draft to
|
30 |
+
* payment_pending. This change will let admins check and process/delete the existing bookings
|
31 |
+
* without payment, and also let the use pay if they have accidently move elsewhere before
|
32 |
+
* the payment is completed.
|
33 |
+
* Currently users or admins can not do anything with bookings with the status draft. If the same
|
34 |
+
* booking needs to be made again, at least name or phone number or email needs to be cahgned
|
35 |
+
* for the same slot otherwise validation will fail stating there is a duplicate booking
|
36 |
+
*
|
37 |
+
* @removal 2022-01-01
|
38 |
+
*/
|
39 |
+
public function convert_draft_status_to_payment_pending_status() {
|
40 |
+
global $wpdb;
|
41 |
+
|
42 |
+
$wpdb->query("
|
43 |
+
UPDATE
|
44 |
+
{$wpdb->posts}
|
45 |
+
SET
|
46 |
+
`post_status` = 'payment_pending'
|
47 |
+
WHERE
|
48 |
+
`post_status` = 'draft' AND `post_type` = 'rtb-booking'
|
49 |
+
");
|
50 |
+
}
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Update the rtb-payment-gateway setting from string to array
|
54 |
+
* Previously this setting was a radio, now it is a checkbox.
|
55 |
+
*
|
56 |
+
* @since 2.3.0
|
57 |
+
*
|
58 |
+
* @removal 2022-04-01
|
59 |
+
*/
|
60 |
+
public function convert_payment_gateway_setting()
|
61 |
+
{
|
62 |
+
$settings_page = 'rtb-settings';
|
63 |
+
$options = get_option( $settings_page );
|
64 |
+
|
65 |
+
if ( ! is_array( $options ) || ! array_key_exists( 'rtb-payment-gateway', $options ) ) { return; }
|
66 |
+
|
67 |
+
if( ! is_array( $options['rtb-payment-gateway'] ) && version_compare( RTB_VERSION, '2.2.11', '>' ) ) {
|
68 |
+
$options['rtb-payment-gateway'] = [ $options['rtb-payment-gateway'] ];
|
69 |
+
|
70 |
+
update_option( $settings_page, $options );
|
71 |
+
}
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Update setting of type count, which are 'time-reminder-user' and
|
76 |
+
* 'time-late-user'. When no value is being set in admin, it has underscore
|
77 |
+
* at least. Whereas, its expected to be empty
|
78 |
+
*
|
79 |
+
* @since 2.3.1
|
80 |
+
*
|
81 |
+
* @removal 2022-04-01
|
82 |
+
*/
|
83 |
+
public function update_invalid_setting_values() {
|
84 |
+
$settings_page = 'rtb-settings';
|
85 |
+
$options = get_option( $settings_page );
|
86 |
+
|
87 |
+
if(
|
88 |
+
is_array( $options )
|
89 |
+
&&
|
90 |
+
isset( $options['time-reminder-user'] )
|
91 |
+
&&
|
92 |
+
'_' == $options['time-reminder-user']
|
93 |
+
&&
|
94 |
+
version_compare( RTB_VERSION, '2.2.11', '>' )
|
95 |
+
)
|
96 |
+
{
|
97 |
+
$options['time-reminder-user'] = '';
|
98 |
+
}
|
99 |
+
|
100 |
+
if(
|
101 |
+
is_array( $options )
|
102 |
+
&&
|
103 |
+
isset( $options['time-late-user'] )
|
104 |
+
&&
|
105 |
+
'_' == $options['time-late-user']
|
106 |
+
&&
|
107 |
+
version_compare( RTB_VERSION, '2.2.11', '>' )
|
108 |
+
)
|
109 |
+
{
|
110 |
+
$options['time-late-user'] = '';
|
111 |
+
}
|
112 |
+
|
113 |
+
update_option( $settings_page, $options );
|
114 |
+
}
|
115 |
+
|
116 |
+
/**
|
117 |
+
* Updates a number of settings, which now are using the "select" type instead of the "count"
|
118 |
+
* type, since they have no units to select
|
119 |
+
*
|
120 |
+
* @since 2.3.0
|
121 |
+
*
|
122 |
+
* @removal 2022-07-01
|
123 |
+
*/
|
124 |
+
public function update_setting_value_types() {
|
125 |
+
|
126 |
+
$options = get_option( 'rtb-settings' );
|
127 |
+
|
128 |
+
if ( is_array( $options ) ) {
|
129 |
+
|
130 |
+
$options['rtb-dining-block-length'] = ! empty( $options['rtb-dining-block-length'] ) ? intval( $options['rtb-dining-block-length'] ) : '';
|
131 |
+
$options['rtb-max-tables-count'] = ! empty( $options['rtb-max-tables-count'] ) ? intval( $options['rtb-max-tables-count'] ) : '';
|
132 |
+
$options['rtb-max-people-count'] = ! empty( $options['rtb-max-people-count'] ) ? intval( $options['rtb-max-people-count'] ) : '';
|
133 |
+
$options['auto-confirm-max-reservations'] = ! empty( $options['auto-confirm-max-reservations'] ) ? intval( $options['auto-confirm-max-reservations'] ) : '';
|
134 |
+
$options['auto-confirm-max-seats'] = ! empty( $options['auto-confirm-max-seats'] ) ? intval( $options['auto-confirm-max-seats'] ) : '';
|
135 |
+
|
136 |
+
update_option( 'rtb-settings', $options );
|
137 |
+
}
|
138 |
+
}
|
139 |
+
}
|
140 |
+
|
141 |
}
|
includes/MultipleLocations.class.php
CHANGED
@@ -1,1098 +1,1098 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* Methods for handling multiple locations
|
4 |
-
*
|
5 |
-
* @package RestaurantReservations
|
6 |
-
* @copyright Copyright (c) 2016, Theme of the Crop
|
7 |
-
* @license GPL-2.0+
|
8 |
-
* @since 1.6
|
9 |
-
*/
|
10 |
-
defined( 'ABSPATH' ) || exit;
|
11 |
-
|
12 |
-
if ( ! class_exists( 'rtbMultipleLocations', false ) ) {
|
13 |
-
/**
|
14 |
-
* Class to handle custom post type and post meta fields
|
15 |
-
*
|
16 |
-
* @since 1.6
|
17 |
-
*/
|
18 |
-
class rtbMultipleLocations {
|
19 |
-
|
20 |
-
/**
|
21 |
-
* Post type slug where locations can be found
|
22 |
-
*
|
23 |
-
* @since 1.6
|
24 |
-
*/
|
25 |
-
public $post_type = false;
|
26 |
-
|
27 |
-
/**
|
28 |
-
* Taxonomy to use when assigning bookings to locations
|
29 |
-
*
|
30 |
-
* @since 1.6
|
31 |
-
*/
|
32 |
-
public $location_taxonomy = 'rtb_location';
|
33 |
-
|
34 |
-
/**
|
35 |
-
* Set the loading hook
|
36 |
-
*
|
37 |
-
* @since 1.6
|
38 |
-
*/
|
39 |
-
public function __construct() {
|
40 |
-
add_action( 'plugins_loaded', array( $this, 'load' ), 100 );
|
41 |
-
}
|
42 |
-
|
43 |
-
/**
|
44 |
-
* Load locations support
|
45 |
-
*
|
46 |
-
* @since 1.6
|
47 |
-
*/
|
48 |
-
public function load() {
|
49 |
-
|
50 |
-
/**
|
51 |
-
* Allow third-party plugins to enable multiple locations
|
52 |
-
*
|
53 |
-
* Expects a post type slug pointing to the locations or false if
|
54 |
-
* multiple locations are not enabled.
|
55 |
-
*
|
56 |
-
* @since 1.6
|
57 |
-
*/
|
58 |
-
$this->post_type = apply_filters( 'rtb_set_locations_post_type', false );
|
59 |
-
|
60 |
-
if ( !$this->post_type ) {
|
61 |
-
return;
|
62 |
-
}
|
63 |
-
|
64 |
-
$this->hooks();
|
65 |
-
}
|
66 |
-
|
67 |
-
/**
|
68 |
-
* Set up hooks
|
69 |
-
*
|
70 |
-
* @since 1.6
|
71 |
-
*/
|
72 |
-
public function hooks() {
|
73 |
-
add_action( 'init', array( $this, 'register_taxonomy' ), 1000 ); // after custom post types declared (hopefully!)
|
74 |
-
add_action( 'save_post_' . $this->post_type, array( $this, 'save_location' ), 10, 3 );
|
75 |
-
add_action( 'before_delete_post', array( $this, 'delete_location' ) );
|
76 |
-
add_action( 'rtb_booking_form_fields', array( $this, 'add_location_field' ), 10, 3 );
|
77 |
-
add_action( 'rtb_pre_validate_booking_submission', array( $this, 'validate_location' ) );
|
78 |
-
add_action( 'rtb_insert_booking', array( $this, 'save_booking_location' ) );
|
79 |
-
add_action( 'rtb_update_booking', array( $this, 'save_booking_location' ) );
|
80 |
-
add_action( 'rtb_booking_load_post_data', array( $this, 'load_booking_location' ), 10, 2 );
|
81 |
-
add_filter( 'rtb_query_args', array( $this, 'modify_query' ), 10, 2 );
|
82 |
-
add_filter( 'rtb_bookings_all_table_columns', array( $this, 'add_location_column' ) );
|
83 |
-
add_filter( 'rtb_bookings_table_column', array( $this, 'print_location_column' ), 10, 3 );
|
84 |
-
add_action( 'edit_form_after_title', array( $this, 'add_meta_nonce' ) );
|
85 |
-
add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) );
|
86 |
-
add_filter( 'the_content', array( $this, 'append_to_content' ) );
|
87 |
-
add_filter( 'rtb_notification_email_to_email', array( $this, 'notification_to_email' ), 10, 2 );
|
88 |
-
add_filter( 'rtb_notification_email_from_email', array( $this, 'notification_from_email' ), 10, 2 );
|
89 |
-
add_filter( 'rtb_notification_email_from_name', array( $this, 'notification_from_name' ), 10, 2 );
|
90 |
-
add_filter( 'rtb_notification_template_tags', array( $this, 'notification_template_tags' ), 10, 2 );
|
91 |
-
add_filter( 'rtb_notification_template_tag_descriptions', array( $this, 'notification_template_tag_descriptions' ) );
|
92 |
-
add_action( 'admin_init', array( $this, 'fix_autodraft_term_error' ) );
|
93 |
-
add_filter( 'rtb_settings_page', array( $this, 'maybe_add_location_settings' ) );
|
94 |
-
add_action( 'admin_init', array( $this, 'remove_location_select_setting' ) );
|
95 |
-
add_filter( 'rtb_booking_form_init', array( $this, 'pass_location_data_to_js' ) );
|
96 |
-
add_filter( 'rtb-payment-summary-data', array( $this, 'add_payment_summary' ), 10, 2);
|
97 |
-
}
|
98 |
-
|
99 |
-
/**
|
100 |
-
* Register the location taxonomy
|
101 |
-
*
|
102 |
-
* @since 1.6
|
103 |
-
*/
|
104 |
-
public function register_taxonomy() {
|
105 |
-
|
106 |
-
$args = array(
|
107 |
-
'label' => _x( 'Location', 'Name for grouping bookings', 'restaurant-reservations' ),
|
108 |
-
'hierarchical' => false,
|
109 |
-
'public' => true,
|
110 |
-
'rewrite' => false,
|
111 |
-
);
|
112 |
-
|
113 |
-
/**
|
114 |
-
* Allow third-party plugins to modify the location taxonomy
|
115 |
-
* arguments.
|
116 |
-
*
|
117 |
-
* @since 1.6
|
118 |
-
*/
|
119 |
-
$args = apply_filters( 'rtb_locations_args', $args );
|
120 |
-
|
121 |
-
register_taxonomy( $this->location_taxonomy, RTB_BOOKING_POST_TYPE, $args );
|
122 |
-
}
|
123 |
-
|
124 |
-
/**
|
125 |
-
* Generate taxonomy terms linked to locations and keep them sync'd
|
126 |
-
* with any changes
|
127 |
-
*
|
128 |
-
* @since 1.6
|
129 |
-
*/
|
130 |
-
public function save_location( $post_id, $post, $update ) {
|
131 |
-
|
132 |
-
if (
|
133 |
-
$post->post_status === 'auto-draft' ||
|
134 |
-
( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) ||
|
135 |
-
!current_user_can( 'edit_post', $post_id ) ||
|
136 |
-
!isset( $_POST['rtb_location_meta_nonce'] ) ||
|
137 |
-
!wp_verify_nonce( $_POST['rtb_location_meta_nonce'], 'rtb_location_meta' )
|
138 |
-
) {
|
139 |
-
return $post_id;
|
140 |
-
}
|
141 |
-
|
142 |
-
$term_id = get_post_meta( $post_id, $this->location_taxonomy, true );
|
143 |
-
|
144 |
-
// Create a new term for this location
|
145 |
-
if ( !$term_id ) {
|
146 |
-
|
147 |
-
$term = wp_insert_term(
|
148 |
-
sanitize_text_field( $post->post_title ),
|
149 |
-
$this->location_taxonomy
|
150 |
-
);
|
151 |
-
|
152 |
-
if ( !is_a( $term, 'WP_Error' ) ) {
|
153 |
-
update_post_meta( $post_id, $this->location_taxonomy, $term['term_id'] );
|
154 |
-
$term_id = $term['term_id'];
|
155 |
-
}
|
156 |
-
|
157 |
-
// Update the term for this location
|
158 |
-
} else {
|
159 |
-
wp_update_term(
|
160 |
-
$term_id,
|
161 |
-
$this->location_taxonomy,
|
162 |
-
array(
|
163 |
-
'name' => sanitize_text_field( $post->post_title ),
|
164 |
-
'slug' => sanitize_text_field( $post->post_name ),
|
165 |
-
)
|
166 |
-
);
|
167 |
-
}
|
168 |
-
|
169 |
-
if ( !empty( $_POST['rtb_append_booking_form'] ) ) {
|
170 |
-
update_post_meta( $post_id, 'rtb_append_booking_form', true );
|
171 |
-
} else {
|
172 |
-
delete_post_meta( $post_id, 'rtb_append_booking_form' );
|
173 |
-
}
|
174 |
-
|
175 |
-
if ( $term_id ) {
|
176 |
-
|
177 |
-
if ( !empty( $_POST['rtb_reply_to_name'] ) ) {
|
178 |
-
$reply_to_name = sanitize_text_field( $_POST['rtb_reply_to_name'] );
|
179 |
-
update_term_meta( $term_id, 'rtb_reply_to_name', $reply_to_name );
|
180 |
-
} else {
|
181 |
-
delete_term_meta( $term_id, 'rtb_reply_to_name' );
|
182 |
-
}
|
183 |
-
|
184 |
-
if ( !empty( $_POST['rtb_reply_to_address'] ) ) {
|
185 |
-
$reply_to_address = sanitize_email( $_POST['rtb_reply_to_address'] );
|
186 |
-
update_term_meta( $term_id, 'rtb_reply_to_address', $reply_to_address );
|
187 |
-
} else {
|
188 |
-
delete_term_meta( $term_id, 'rtb_reply_to_address' );
|
189 |
-
}
|
190 |
-
|
191 |
-
if ( !empty( $_POST['rtb_admin_email_address'] ) ) {
|
192 |
-
$email = sanitize_text_field( $_POST['rtb_admin_email_address'] );
|
193 |
-
update_term_meta( $term_id, 'rtb_admin_email_address', $email );
|
194 |
-
} else {
|
195 |
-
delete_term_meta( $term_id, 'rtb_admin_email_address' );
|
196 |
-
}
|
197 |
-
}
|
198 |
-
|
199 |
-
return $post_id;
|
200 |
-
}
|
201 |
-
|
202 |
-
/**
|
203 |
-
* Delete taxonomy terms linked to locations when a location is deleted
|
204 |
-
*
|
205 |
-
* Only does this when no bookings are associated with that term.
|
206 |
-
* Otherwise it may be important to keep the bookings grouped for
|
207 |
-
* historical data.
|
208 |
-
*
|
209 |
-
* @since 1.6
|
210 |
-
*/
|
211 |
-
public function delete_location( $post_id ) {
|
212 |
-
|
213 |
-
if ( !current_user_can( 'delete_posts' ) ) {
|
214 |
-
return $post_id;
|
215 |
-
}
|
216 |
-
|
217 |
-
$term_id = get_post_meta( $post_id, $this->location_taxonomy, true );
|
218 |
-
|
219 |
-
$term = get_term( $term_id, $this->location_taxonomy );
|
220 |
-
|
221 |
-
if ( !$term || is_a( $term, 'WP_Error' ) ) {
|
222 |
-
return;
|
223 |
-
}
|
224 |
-
|
225 |
-
$query = new rtbQuery( array( 'location' => $term_id ), 'delete-location-term-check' );
|
226 |
-
$query->prepare_args();
|
227 |
-
$query->get_bookings();
|
228 |
-
|
229 |
-
// Don't delete taxonomy terms if there are bookings assigned to
|
230 |
-
// this location, so the booking associations can remain as
|
231 |
-
// historical data.
|
232 |
-
if ( count( $query->bookings ) ) {
|
233 |
-
add_term_meta( $term_id, 'rtb_location_removed', true );
|
234 |
-
} else {
|
235 |
-
wp_delete_term( $term_id, $this->location_taxonomy );
|
236 |
-
}
|
237 |
-
|
238 |
-
}
|
239 |
-
|
240 |
-
/**
|
241 |
-
* Get location term id from location post id
|
242 |
-
*
|
243 |
-
* Transforms a location post id into its associated term id. If the
|
244 |
-
* id doesn't match a location post, it will check if the received id
|
245 |
-
* matches a term id and return it if so. Between versions 1.6 and
|
246 |
-
* and 1.6.1, only term ids were accepted as shortcodes, and this
|
247 |
-
* provides a backwards-compatible fallback.
|
248 |
-
*
|
249 |
-
* @param $location_id int The location id (post or term)
|
250 |
-
* @return int The location term id. Default: 0
|
251 |
-
*/
|
252 |
-
public function get_location_term_id( $location_id ) {
|
253 |
-
|
254 |
-
$location_id = absint( $location_id );
|
255 |
-
$term_id = 0;
|
256 |
-
|
257 |
-
if ( get_post_type( $location_id ) === $this->post_type ) {
|
258 |
-
$term_id = get_post_meta( $location_id, $this->location_taxonomy, true );
|
259 |
-
} elseif ( term_exists( $location_id, $this->location_taxonomy ) ) {
|
260 |
-
$term_id = $location_id;
|
261 |
-
}
|
262 |
-
|
263 |
-
return $term_id;
|
264 |
-
}
|
265 |
-
|
266 |
-
|
267 |
-
/**
|
268 |
-
* Add the location selection field to the booking form
|
269 |
-
*
|
270 |
-
* @since 1.6
|
271 |
-
*/
|
272 |
-
public function add_location_field( $fields, $request = null, $args = array() ) {
|
273 |
-
global $rtb_controller;
|
274 |
-
|
275 |
-
// If the location is specified, don't add a field.
|
276 |
-
// A hidden field is added automatically in rtb_print_booking_form()
|
277 |
-
if ( !empty( $args['location'] ) ) {
|
278 |
-
$args['location'] = $this->get_location_term_id( $args['location'] );
|
279 |
-
if ( !empty( $args['location'] ) ) {
|
280 |
-
return $fields;
|
281 |
-
}
|
282 |
-
}
|
283 |
-
|
284 |
-
if ( $request === null ) {
|
285 |
-
global $rtb_controller;
|
286 |
-
$request = $rtb_controller->request;
|
287 |
-
}
|
288 |
-
|
289 |
-
// Select a fieldset in which to place the field
|
290 |
-
$placement = false;
|
291 |
-
if ( isset( $fields['reservation'] ) && isset( $fields['reservation']['fields'] ) ) {
|
292 |
-
$placement = &$fields['reservation']['fields'];
|
293 |
-
} else {
|
294 |
-
$key = key( reset( $fields ) );
|
295 |
-
if ( isset( $fields[$key]['fields'] ) ) {
|
296 |
-
$placement = &$fields[$key]['fields'];
|
297 |
-
}
|
298 |
-
}
|
299 |
-
|
300 |
-
// If we couldn't find any working fieldset, then something odd is
|
301 |
-
// going on. Just pretend we were never here.
|
302 |
-
if ( $placement === false ) {
|
303 |
-
return $fields;
|
304 |
-
}
|
305 |
-
|
306 |
-
$placement = array_merge(
|
307 |
-
array(
|
308 |
-
'location' => array(
|
309 |
-
'title' => esc_html( $rtb_controller->settings->get_setting( 'label-location' ) ),
|
310 |
-
'request_input' => empty( $request->location ) ? '' : $request->location,
|
311 |
-
'callback' => 'rtb_print_form_select_field',
|
312 |
-
'callback_args' => array(
|
313 |
-
'options' => $this->get_location_options(),
|
314 |
-
),
|
315 |
-
'empty_option' => true,
|
316 |
-
'required' => true,
|
317 |
-
)
|
318 |
-
),
|
319 |
-
$placement
|
320 |
-
);
|
321 |
-
|
322 |
-
return $fields;
|
323 |
-
}
|
324 |
-
|
325 |
-
/**
|
326 |
-
* Retrieve a key/value array of location terms and names
|
327 |
-
*
|
328 |
-
* @param bool $active_only Whether or not to retrieve only currently
|
329 |
-
* active locations. Default: true - don't retrieve locations that
|
330 |
-
* have been removed
|
331 |
-
* @since 1.6
|
332 |
-
*/
|
333 |
-
public function get_location_options( $active_only = true ) {
|
334 |
-
|
335 |
-
$terms = get_terms(
|
336 |
-
array(
|
337 |
-
'taxonomy' => $this->location_taxonomy,
|
338 |
-
'hide_empty' => false,
|
339 |
-
)
|
340 |
-
);
|
341 |
-
|
342 |
-
$options = array();
|
343 |
-
foreach( $terms as $term ) {
|
344 |
-
$archived = get_term_meta( $term->term_id, 'rtb_location_removed', true );
|
345 |
-
if ( !$active_only || !$archived ) {
|
346 |
-
$options[$term->term_id] = $term->name;
|
347 |
-
}
|
348 |
-
}
|
349 |
-
|
350 |
-
return $options;
|
351 |
-
}
|
352 |
-
|
353 |
-
/**
|
354 |
-
* Validate location in post data
|
355 |
-
*
|
356 |
-
* @since 1.6
|
357 |
-
*/
|
358 |
-
public function validate_location( $booking ) {
|
359 |
-
|
360 |
-
$booking->location = empty( $_POST['rtb-location'] ) ? '' : absint( $_POST['rtb-location'] );
|
361 |
-
if ( empty( $booking->location ) ) {
|
362 |
-
$booking->validation_errors[] = array(
|
363 |
-
'field' => 'location',
|
364 |
-
'post_variable' => $booking->location,
|
365 |
-
'message' => __( 'Please select a location for your booking.', 'restaurant-reservations' ),
|
366 |
-
);
|
367 |
-
|
368 |
-
} elseif ( !term_exists( $booking->location, $this->location_taxonomy ) ) {
|
369 |
-
$booking->validation_errors[] = array(
|
370 |
-
'field' => 'location',
|
371 |
-
'post_variable' => $booking->location,
|
372 |
-
'message' => __( 'The location you selected is not valid. Please select another location.', 'restaurant-reservations' ),
|
373 |
-
);
|
374 |
-
}
|
375 |
-
}
|
376 |
-
|
377 |
-
/**
|
378 |
-
* Save the booking location when the booking is created or updated.
|
379 |
-
*
|
380 |
-
* @since 1.6
|
381 |
-
*/
|
382 |
-
public function save_booking_location( $booking ) {
|
383 |
-
|
384 |
-
if ( !empty( $booking->location ) ) {
|
385 |
-
wp_set_object_terms( $booking->ID, $booking->location, $this->location_taxonomy );
|
386 |
-
}
|
387 |
-
}
|
388 |
-
|
389 |
-
/**
|
390 |
-
* Load the booking location when teh booking is loaded
|
391 |
-
*
|
392 |
-
* @since 1.6
|
393 |
-
*/
|
394 |
-
public function load_booking_location( $booking, $post ) {
|
395 |
-
|
396 |
-
$terms = wp_get_object_terms( $booking->ID, $this->location_taxonomy, array( 'fields' => 'ids' ) );
|
397 |
-
|
398 |
-
if ( is_a( $terms, 'WP_Error' ) ) {
|
399 |
-
return;
|
400 |
-
}
|
401 |
-
|
402 |
-
$booking->location = current( $terms );
|
403 |
-
}
|
404 |
-
|
405 |
-
/**
|
406 |
-
* Add location column to the list table
|
407 |
-
*
|
408 |
-
* @since 1.6
|
409 |
-
*/
|
410 |
-
public function add_location_column( $columns ) {
|
411 |
-
|
412 |
-
$first = array_splice( $columns, 0, 2 );
|
413 |
-
$first['location'] = __( 'Location', 'restaurant-reservations' );
|
414 |
-
|
415 |
-
return array_merge( $first, $columns );
|
416 |
-
}
|
417 |
-
|
418 |
-
/**
|
419 |
-
* Print the value in the location column for the list table
|
420 |
-
*
|
421 |
-
* @since 1.6
|
422 |
-
*/
|
423 |
-
public function print_location_column( $value, $booking, $column_name ) {
|
424 |
-
|
425 |
-
if ( $column_name !== 'location' ) {
|
426 |
-
return $value;
|
427 |
-
}
|
428 |
-
|
429 |
-
$terms = wp_get_object_terms( $booking->ID, $this->location_taxonomy );
|
430 |
-
|
431 |
-
if ( empty( $terms ) || is_a( $terms, 'WP_Error' ) ) {
|
432 |
-
return '';
|
433 |
-
}
|
434 |
-
|
435 |
-
$location = current( $terms );
|
436 |
-
|
437 |
-
return $location->name;
|
438 |
-
}
|
439 |
-
|
440 |
-
/**
|
441 |
-
* Modify queries to add location taxonomy parameters
|
442 |
-
*
|
443 |
-
* @param array $args Array of arguments passed to rtbQuery
|
444 |
-
* @since 1.6
|
445 |
-
*/
|
446 |
-
public function modify_query( $args, $context = '' ) {
|
447 |
-
|
448 |
-
global $rtb_controller;
|
449 |
-
|
450 |
-
if ( !empty( $args['location'] ) && !empty( $rtb_controller->locations->post_type ) ) {
|
451 |
-
|
452 |
-
if ( !is_array( $args['location'] ) ) {
|
453 |
-
$args['location'] = array( $args['location'] );
|
454 |
-
}
|
455 |
-
|
456 |
-
$args['tax_query'] = array(
|
457 |
-
array(
|
458 |
-
'taxonomy' => $rtb_controller->locations->location_taxonomy,
|
459 |
-
'field' => 'term_id',
|
460 |
-
'terms' => $args['location'],
|
461 |
-
|
462 |
-
)
|
463 |
-
);
|
464 |
-
}
|
465 |
-
|
466 |
-
return $args;
|
467 |
-
}
|
468 |
-
|
469 |
-
/**
|
470 |
-
* Add meta box to the location post editing screen
|
471 |
-
*
|
472 |
-
* @since 1.6
|
473 |
-
*/
|
474 |
-
public function add_meta_boxes() {
|
475 |
-
|
476 |
-
$meta_boxes = array(
|
477 |
-
|
478 |
-
// Metabox to enter schema type
|
479 |
-
array(
|
480 |
-
'id' => 'rtb_location',
|
481 |
-
'title' => __( 'Reservations', 'restaurant-reservations' ),
|
482 |
-
'callback' => array( $this, 'print_location_metabox' ),
|
483 |
-
'post_type' => $this->post_type,
|
484 |
-
'context' => 'side',
|
485 |
-
'priority' => 'default',
|
486 |
-
),
|
487 |
-
);
|
488 |
-
|
489 |
-
// Create filter so addons can modify the metaboxes
|
490 |
-
$meta_boxes = apply_filters( 'rtb_location_metaboxes', $meta_boxes );
|
491 |
-
|
492 |
-
// Create the metaboxes
|
493 |
-
foreach ( $meta_boxes as $meta_box ) {
|
494 |
-
add_meta_box(
|
495 |
-
$meta_box['id'],
|
496 |
-
$meta_box['title'],
|
497 |
-
$meta_box['callback'],
|
498 |
-
$meta_box['post_type'],
|
499 |
-
$meta_box['context'],
|
500 |
-
$meta_box['priority']
|
501 |
-
);
|
502 |
-
}
|
503 |
-
}
|
504 |
-
|
505 |
-
/**
|
506 |
-
* Output a hidden nonce field to secure the saving of term meta
|
507 |
-
*
|
508 |
-
* @since 1.6
|
509 |
-
*/
|
510 |
-
public function add_meta_nonce() {
|
511 |
-
global $post;
|
512 |
-
if ( $post->post_type == $this->post_type ) {
|
513 |
-
wp_nonce_field( 'rtb_location_meta', 'rtb_location_meta_nonce' );
|
514 |
-
}
|
515 |
-
}
|
516 |
-
|
517 |
-
/**
|
518 |
-
* Print metabox on location post editing screen
|
519 |
-
*
|
520 |
-
* @since 1.6
|
521 |
-
*/
|
522 |
-
public function print_location_metabox( $post ) {
|
523 |
-
|
524 |
-
global $rtb_controller;
|
525 |
-
|
526 |
-
$notification_email = '';
|
527 |
-
$reply_to_name = '';
|
528 |
-
$reply_to_address = '';
|
529 |
-
$term_id = get_post_meta( $post->ID, $this->location_taxonomy, true );
|
530 |
-
$admin_email_option = $rtb_controller->settings->get_setting( 'admin-email-option' );
|
531 |
-
if ( $term_id ) {
|
532 |
-
$reply_to_name = get_term_meta( $term_id, 'rtb_reply_to_name', true );
|
533 |
-
$reply_to_address = get_term_meta( $term_id, 'rtb_reply_to_address', true );
|
534 |
-
if ( $admin_email_option ) {
|
535 |
-
$notification_email = get_term_meta( $term_id, 'rtb_admin_email_address', true );
|
536 |
-
}
|
537 |
-
}
|
538 |
-
|
539 |
-
$append_booking_form = get_post_meta( $post->ID, 'rtb_append_booking_form', true );
|
540 |
-
|
541 |
-
?>
|
542 |
-
|
543 |
-
<style type="text/css">.rtb-location-meta-input + .rtb-location-meta-input { margin-top: 2em; }</style>
|
544 |
-
|
545 |
-
<div class="rtb-location-meta-input rtb-location-meta-append-form">
|
546 |
-
<label>
|
547 |
-
<input type="checkbox" name="rtb_append_booking_form" value="1"<?php if ( $append_booking_form ) : ?> checked="checked"<?php endif; ?>>
|
548 |
-
<?php esc_html_e( "Automatically add the booking form to this page.", 'restaurant-reservations' ); ?>
|
549 |
-
</label>
|
550 |
-
</div>
|
551 |
-
|
552 |
-
<div class="rtb-location-meta-input rtb-location-meta-reply-to-name">
|
553 |
-
<label for="rtb_reply_to_name">
|
554 |
-
<?php esc_html_e( 'Reply-To Name', 'restaurant-reservations' ); ?>
|
555 |
-
</label>
|
556 |
-
<input type="text" name="rtb_reply_to_name" id="rtb_reply_to_name" value="<?php esc_attr_e( $reply_to_name ); ?>" placeholder="<?php esc_attr_e( $rtb_controller->settings->get_setting( 'reply-to-name' ) ); ?>">
|
557 |
-
<p class="description">
|
558 |
-
<?php esc_html_e( 'The name which should appear in the Reply-To field of a user notification email.', 'restaurant-reservations' ); ?>
|
559 |
-
</p>
|
560 |
-
</div>
|
561 |
-
|
562 |
-
<div class="rtb-location-meta-input rtb-location-meta-reply-to-address">
|
563 |
-
<label for="rtb_reply_to_address">
|
564 |
-
<?php esc_html_e( 'Reply-To Email Address', 'restaurant-reservations' ); ?>
|
565 |
-
</label>
|
566 |
-
<input type="text" name="rtb_reply_to_address" id="rtb_reply_to_address" value="<?php esc_attr_e( $reply_to_address ); ?>" placeholder="<?php esc_attr_e( $rtb_controller->settings->get_setting( 'reply-to-address' ) ); ?>">
|
567 |
-
<p class="description">
|
568 |
-
<?php esc_html_e( 'The email address which should appear in the Reply-To field of a user notification email.', 'restaurant-reservations' ); ?>
|
569 |
-
</p>
|
570 |
-
</div>
|
571 |
-
|
572 |
-
<?php if ( $admin_email_option ) : ?>
|
573 |
-
<div class="rtb-location-meta-input rtb-location-meta-admin-email">
|
574 |
-
<label for="rtb_admin_email_address">
|
575 |
-
<?php esc_html_e( 'Admin Notification Email Address', 'restaurant-reservations' ); ?>
|
576 |
-
</label>
|
577 |
-
<input type="text" name="rtb_admin_email_address" id="rtb_admin_email_address" value="<?php esc_attr_e( $notification_email ); ?>" placeholder="<?php esc_attr_e( $rtb_controller->settings->get_setting( 'admin-email-address' ) ); ?>">
|
578 |
-
<p class="description">
|
579 |
-
<?php esc_html_e( 'The email address where admin notifications for bookings at this location should be sent.', 'restaurant-reservations' ); ?>
|
580 |
-
</p>
|
581 |
-
</div>
|
582 |
-
<?php endif; ?>
|
583 |
-
|
584 |
-
<?php
|
585 |
-
}
|
586 |
-
|
587 |
-
/**
|
588 |
-
* Append booking form to a location's `post_content`
|
589 |
-
* @since 0.0.1
|
590 |
-
*/
|
591 |
-
public function append_to_content( $content ) {
|
592 |
-
|
593 |
-
if ( !is_main_query() || !in_the_loop() || post_password_required() ) {
|
594 |
-
return $content;
|
595 |
-
}
|
596 |
-
|
597 |
-
global $post;
|
598 |
-
|
599 |
-
$append_booking_form = get_post_meta( $post->ID, 'rtb_append_booking_form', true );
|
600 |
-
|
601 |
-
if ( !$append_booking_form ) {
|
602 |
-
return $content;
|
603 |
-
}
|
604 |
-
|
605 |
-
$term_id = get_post_meta( $post->ID, $this->location_taxonomy, true );
|
606 |
-
|
607 |
-
if ( empty( $term_id ) ) {
|
608 |
-
return $content;
|
609 |
-
}
|
610 |
-
|
611 |
-
return $content . do_shortcode( '[booking-form location=' . absint( $term_id ) .']' );
|
612 |
-
}
|
613 |
-
|
614 |
-
/**
|
615 |
-
* Modify the notification email recipient for each location
|
616 |
-
*
|
617 |
-
* @since 1.6
|
618 |
-
*/
|
619 |
-
public function notification_to_email( $email, $notification ) {
|
620 |
-
|
621 |
-
if ( $notification->target == 'user' || empty( $notification->booking->location ) ) {
|
622 |
-
return $email;
|
623 |
-
}
|
624 |
-
|
625 |
-
$val = get_term_meta( $notification->booking->location, 'rtb_admin_email_address', true );
|
626 |
-
$email = empty( $val ) ? $email : $val;
|
627 |
-
|
628 |
-
return $email;
|
629 |
-
}
|
630 |
-
|
631 |
-
/**
|
632 |
-
* Modify the notification email sender address for each location
|
633 |
-
*
|
634 |
-
* @since 1.6
|
635 |
-
*/
|
636 |
-
public function notification_from_email( $email, $notification ) {
|
637 |
-
|
638 |
-
if ( $notification->target != 'user' || empty( $notification->booking->location ) ) {
|
639 |
-
return $email;
|
640 |
-
}
|
641 |
-
|
642 |
-
$val = get_term_meta( $notification->booking->location, 'rtb_reply_to_address', true );
|
643 |
-
$email = empty( $val ) ? $email : $val;
|
644 |
-
|
645 |
-
return $email;
|
646 |
-
}
|
647 |
-
|
648 |
-
/**
|
649 |
-
* Modify the notification email sender name for each location
|
650 |
-
*
|
651 |
-
* @since 1.6
|
652 |
-
*/
|
653 |
-
public function notification_from_name( $name, $notification ) {
|
654 |
-
|
655 |
-
if ( $notification->target != 'user' || empty( $notification->booking->location ) ) {
|
656 |
-
return $name;
|
657 |
-
}
|
658 |
-
|
659 |
-
$val = get_term_meta( $notification->booking->location, 'rtb_reply_to_name', true );
|
660 |
-
$name = empty( $val ) ? $name : $val;
|
661 |
-
|
662 |
-
return $name;
|
663 |
-
}
|
664 |
-
|
665 |
-
/**
|
666 |
-
* Add a location template tag for notifications
|
667 |
-
*
|
668 |
-
* @since 1.6.1
|
669 |
-
*/
|
670 |
-
public function notification_template_tags( $template_tags, $notification ) {
|
671 |
-
|
672 |
-
$term = empty( $notification->booking->location ) ? null : get_term( $notification->booking->location, $this->location_taxonomy );
|
673 |
-
$location_name = is_null( $term ) || is_wp_error( $term ) ? '' : $term->name;
|
674 |
-
|
675 |
-
$table_number = empty( $notification->booking->table ) ? '' : $notification->booking->table;
|
676 |
-
$table_number = is_array($table_number) ? implode(',', $table_number ) : $table_number;
|
677 |
-
|
678 |
-
return array_merge(
|
679 |
-
array(
|
680 |
-
'{location}' => $location_name,
|
681 |
-
'{table}' => $table_number,
|
682 |
-
),
|
683 |
-
$template_tags
|
684 |
-
);
|
685 |
-
}
|
686 |
-
|
687 |
-
/**
|
688 |
-
* Add a description for the location template tag
|
689 |
-
*
|
690 |
-
* @since 1.6.1
|
691 |
-
*/
|
692 |
-
public function notification_template_tag_descriptions( $descriptions ) {
|
693 |
-
return array_merge(
|
694 |
-
array( '{location}' => __( 'Location for which this booking was made.', 'restaurant-reservations' ) ),
|
695 |
-
$descriptions
|
696 |
-
);
|
697 |
-
}
|
698 |
-
|
699 |
-
/**
|
700 |
-
* Removes Auto-Draft locations that were added due to a bug in v1.7
|
701 |
-
*
|
702 |
-
* Version 1.7 introduced a bug which caused a location term to be
|
703 |
-
* created if the location Add New page was loaded. This term
|
704 |
-
* corresponded to an auto-draft post object and will be removed when
|
705 |
-
* that object is removed. This provides a one-time fix in v1.7.1
|
706 |
-
*
|
707 |
-
* @see https://github.com/NateWr/restaurant-reservations/issues/91
|
708 |
-
* @see https://developer.wordpress.org/reference/functions/wp_delete_auto_drafts/
|
709 |
-
* @since 1.7.1
|
710 |
-
*/
|
711 |
-
public function fix_autodraft_term_error() {
|
712 |
-
|
713 |
-
if ( get_option( 'rtb_autodraft_terms_fixed', false ) ) {
|
714 |
-
return;
|
715 |
-
}
|
716 |
-
|
717 |
-
global $wpdb;
|
718 |
-
|
719 |
-
if ( !$wpdb ) {
|
720 |
-
return;
|
721 |
-
}
|
722 |
-
|
723 |
-
$old_posts = $wpdb->get_col( "SELECT ID FROM $wpdb->posts WHERE post_status = 'auto-draft' AND post_type = '$this->post_type';" );
|
724 |
-
foreach ( (array) $old_posts as $delete ) {
|
725 |
-
// Force delete.
|
726 |
-
wp_delete_post( $delete, true );
|
727 |
-
}
|
728 |
-
|
729 |
-
// Set the `rtb_location_removed` term meta on any terms that are
|
730 |
-
// no longer attached to posts
|
731 |
-
global $wp_version;
|
732 |
-
if ( version_compare( $wp_version, '3.9', '>=' ) ) {
|
733 |
-
$live_terms = $wpdb->get_col( "SELECT meta_value FROM $wpdb->postmeta WHERE meta_key='$this->location_taxonomy';" );
|
734 |
-
$all_terms = get_terms( array(
|
735 |
-
'taxonomy' => $this->location_taxonomy,
|
736 |
-
'hide_empty' => false,
|
737 |
-
'meta_query' => array(
|
738 |
-
array(
|
739 |
-
'compare' => 'NOT EXISTS',
|
740 |
-
'key' => 'rtb_location_removed',
|
741 |
-
)
|
742 |
-
)
|
743 |
-
) );
|
744 |
-
if ( is_array( $all_terms ) ) {
|
745 |
-
foreach( $all_terms as $term ) {
|
746 |
-
if ( !in_array( $term->term_id, $live_terms ) ) {
|
747 |
-
$query = new rtbQuery( array( 'location' => $term->term_id ), 'delete-location-term-check' );
|
748 |
-
$query->prepare_args();
|
749 |
-
$query->get_bookings();
|
750 |
-
|
751 |
-
// Don't delete taxonomy terms if there are bookings assigned to
|
752 |
-
// this location, so the booking associations can remain as
|
753 |
-
// historical data.
|
754 |
-
if ( count( $query->bookings ) ) {
|
755 |
-
add_term_meta( $term->term_id, 'rtb_location_removed', true );
|
756 |
-
} else {
|
757 |
-
wp_delete_term( $term->term_id, $this->location_taxonomy );
|
758 |
-
}
|
759 |
-
}
|
760 |
-
}
|
761 |
-
}
|
762 |
-
}
|
763 |
-
|
764 |
-
update_option( 'rtb_autodraft_terms_fixed', true );
|
765 |
-
}
|
766 |
-
|
767 |
-
/**
|
768 |
-
* If multiple locations exist, adds a select box to the settings page which
|
769 |
-
* allows a user to select whether certain settings are global or location-specific.
|
770 |
-
* Also adds in location-specific settings for a number of different settings, and
|
771 |
-
* makes all settings conditional on the value of the select box.
|
772 |
-
*
|
773 |
-
* @since 2.3.6
|
774 |
-
*/
|
775 |
-
public function maybe_add_location_settings( $sap ) {
|
776 |
-
global $rtb_controller;
|
777 |
-
|
778 |
-
$args = array(
|
779 |
-
'taxonomy' => $this->location_taxonomy,
|
780 |
-
'hide_empty' => false,
|
781 |
-
);
|
782 |
-
|
783 |
-
$terms = get_terms( $args );
|
784 |
-
|
785 |
-
if ( ! $this->do_locations_exist() ) { return $sap; }
|
786 |
-
|
787 |
-
foreach ( $sap->pages['rtb-settings']->sections as $key => $section ) {
|
788 |
-
|
789 |
-
foreach ( $section->settings as $setting_key => $setting ) {
|
790 |
-
|
791 |
-
$sap->pages['rtb-settings']->sections[ $key ]->settings[ $setting_key ]->conditional_on = 'location-select';
|
792 |
-
$sap->pages['rtb-settings']->sections[ $key ]->settings[ $setting_key ]->conditional_on_value = false;
|
793 |
-
|
794 |
-
$sap->pages['rtb-settings']->sections[ $key ]->settings[ $setting_key ]->set_conditional_display();
|
795 |
-
}
|
796 |
-
}
|
797 |
-
|
798 |
-
|
799 |
-
$location_options = array(
|
800 |
-
'' => __( 'Global', 'restaurant-reservations' ),
|
801 |
-
);
|
802 |
-
|
803 |
-
foreach ( $terms as $term ) {
|
804 |
-
|
805 |
-
$location_options[ $term->slug ] = $term->term_id . ' - '.$term->name;
|
806 |
-
}
|
807 |
-
|
808 |
-
// Schedule location-specific options
|
809 |
-
$sap->add_section(
|
810 |
-
'rtb-settings',
|
811 |
-
array(
|
812 |
-
'id' => 'rtb-schedule-location-select',
|
813 |
-
'title' => __( 'Select Schedule Location', 'restaurant-reservations' ),
|
814 |
-
'tab' => 'rtb-schedule-tab',
|
815 |
-
'rank' => 2,
|
816 |
-
)
|
817 |
-
);
|
818 |
-
|
819 |
-
$sap->add_setting(
|
820 |
-
'rtb-settings',
|
821 |
-
'rtb-schedule-location-select',
|
822 |
-
'select',
|
823 |
-
array(
|
824 |
-
'id' => 'location-select',
|
825 |
-
'title' => __( 'Schedule Location', 'restaurant-reservations' ),
|
826 |
-
'description' => __( 'Select which location the schedule will apply to. If a specific location doesn\'t have a schedule set, then it will fall back to the global schedule when booking.', 'restaurant-reservations' ),
|
827 |
-
'blank_option' => false,
|
828 |
-
'options' => $location_options,
|
829 |
-
)
|
830 |
-
);
|
831 |
-
|
832 |
-
// Translateable strings for scheduler components
|
833 |
-
$scheduler_strings = array(
|
834 |
-
'add_rule' => __( 'Add new scheduling rule', 'restaurant-reservations' ),
|
835 |
-
'weekly' => _x( 'Weekly', 'Format of a scheduling rule', 'restaurant-reservations' ),
|
836 |
-
'monthly' => _x( 'Monthly', 'Format of a scheduling rule', 'restaurant-reservations' ),
|
837 |
-
'date' => _x( 'Date', 'Format of a scheduling rule', 'restaurant-reservations' ),
|
838 |
-
'weekdays' => _x( 'Days of the week', 'Label for selecting days of the week in a scheduling rule', 'restaurant-reservations' ),
|
839 |
-
'month_weeks' => _x( 'Weeks of the month', 'Label for selecting weeks of the month in a scheduling rule', 'restaurant-reservations' ),
|
840 |
-
'date_label' => _x( 'Date', 'Label to select a date for a scheduling rule', 'restaurant-reservations' ),
|
841 |
-
'time_label' => _x( 'Time', 'Label to select a time slot for a scheduling rule', 'restaurant-reservations' ),
|
842 |
-
'allday' => _x( 'All day', 'Label to set a scheduling rule to last all day', 'restaurant-reservations' ),
|
843 |
-
'start' => _x( 'Start', 'Label for the starting time of a scheduling rule', 'restaurant-reservations' ),
|
844 |
-
'end' => _x( 'End', 'Label for the ending time of a scheduling rule', 'restaurant-reservations' ),
|
845 |
-
'set_time_prompt' => _x( 'All day long. Want to %sset a time slot%s?', 'Prompt displayed when a scheduling rule is set without any time restrictions', 'restaurant-reservations' ),
|
846 |
-
'toggle' => _x( 'Open and close this rule', 'Toggle a scheduling rule open and closed', 'restaurant-reservations' ),
|
847 |
-
'delete' => _x( 'Delete rule', 'Delete a scheduling rule', 'restaurant-reservations' ),
|
848 |
-
'delete_schedule' => __( 'Delete scheduling rule', 'restaurant-reservations' ),
|
849 |
-
'never' => _x( 'Never', 'Brief default description of a scheduling rule when no weekdays or weeks are included in the rule', 'restaurant-reservations' ),
|
850 |
-
'weekly_always' => _x( 'Every day', 'Brief default description of a scheduling rule when all the weekdays/weeks are included in the rule', 'restaurant-reservations' ),
|
851 |
-
'monthly_weekdays' => _x( '%s on the %s week of the month', 'Brief default description of a scheduling rule when some weekdays are included on only some weeks of the month. %s should be left alone and will be replaced by a comma-separated list of days and weeks in the following format: M, T, W on the first, second week of the month', 'restaurant-reservations' ),
|
852 |
-
'monthly_weeks' => _x( '%s week of the month', 'Brief default description of a scheduling rule when some weeks of the month are included but all or no weekdays are selected. %s should be left alone and will be replaced by a comma-separated list of weeks in the following format: First, second week of the month', 'restaurant-reservations' ),
|
853 |
-
'all_day' => _x( 'All day', 'Brief default description of a scheduling rule when no times are set', 'restaurant-reservations' ),
|
854 |
-
'before' => _x( 'Ends at', 'Brief default description of a scheduling rule when an end time is set but no start time. If the end time is 6pm, it will read: Ends at 6pm', 'restaurant-reservations' ),
|
855 |
-
'after' => _x( 'Starts at', 'Brief default description of a scheduling rule when a start time is set but no end time. If the start time is 6pm, it will read: Starts at 6pm', 'restaurant-reservations' ),
|
856 |
-
'separator' => _x( '—', 'Separator between times of a scheduling rule', 'restaurant-reservations' ),
|
857 |
-
);
|
858 |
-
|
859 |
-
foreach ( $terms as $term ) {
|
860 |
-
|
861 |
-
$sap->add_setting(
|
862 |
-
'rtb-settings',
|
863 |
-
'rtb-schedule',
|
864 |
-
'scheduler',
|
865 |
-
array(
|
866 |
-
'id' => $term->slug . '-schedule-open',
|
867 |
-
'title' => __( 'Schedule', 'restaurant-reservations' ),
|
868 |
-
'description' => __( 'Define the weekly schedule during which you accept bookings.', 'restaurant-reservations' ),
|
869 |
-
'weekdays' => array(
|
870 |
-
'monday' => _x( 'Mo', 'Monday abbreviation', 'restaurant-reservations' ),
|
871 |
-
'tuesday' => _x( 'Tu', 'Tuesday abbreviation', 'restaurant-reservations' ),
|
872 |
-
'wednesday' => _x( 'We', 'Wednesday abbreviation', 'restaurant-reservations' ),
|
873 |
-
'thursday' => _x( 'Th', 'Thursday abbreviation', 'restaurant-reservations' ),
|
874 |
-
'friday' => _x( 'Fr', 'Friday abbreviation', 'restaurant-reservations' ),
|
875 |
-
'saturday' => _x( 'Sa', 'Saturday abbreviation', 'restaurant-reservations' ),
|
876 |
-
'sunday' => _x( 'Su', 'Sunday abbreviation', 'restaurant-reservations' )
|
877 |
-
),
|
878 |
-
'time_format' => $rtb_controller->settings->get_setting( 'time-format' ),
|
879 |
-
'date_format' => $rtb_controller->settings->get_setting( 'date-format' ),
|
880 |
-
'disable_weeks' => true,
|
881 |
-
'disable_date' => true,
|
882 |
-
'strings' => $scheduler_strings,
|
883 |
-
'conditional_on' => 'location-select',
|
884 |
-
'conditional_on_value' => $term->slug,
|
885 |
-
)
|
886 |
-
);
|
887 |
-
|
888 |
-
$sap->add_setting(
|
889 |
-
'rtb-settings',
|
890 |
-
'rtb-schedule',
|
891 |
-
'scheduler',
|
892 |
-
array(
|
893 |
-
'id' => $term->slug . '-schedule-closed',
|
894 |
-
'title' => __( 'Exceptions', 'restaurant-reservations' ),
|
895 |
-
'description' => __( "Define special opening hours for holidays, events or other needs. Leave the time empty if you're closed all day.", 'restaurant-reservations' ),
|
896 |
-
'time_format' => esc_attr( $rtb_controller->settings->get_setting( 'time-format' ) ),
|
897 |
-
'date_format' => esc_attr( $rtb_controller->settings->get_setting( 'date-format' ) ),
|
898 |
-
'disable_weekdays' => true,
|
899 |
-
'disable_weeks' => true,
|
900 |
-
'strings' => $scheduler_strings,
|
901 |
-
'conditional_on' => 'location-select',
|
902 |
-
'conditional_on_value' => $term->slug,
|
903 |
-
)
|
904 |
-
);
|
905 |
-
}
|
906 |
-
|
907 |
-
// Restriction location-specific options
|
908 |
-
$sap->add_section(
|
909 |
-
'rtb-settings',
|
910 |
-
array(
|
911 |
-
'id' => 'rtb-restrictions-location-select',
|
912 |
-
'title' => __( 'Select Seat Restrictions Location', 'restaurant-reservations' ),
|
913 |
-
'tab' => 'rtb-advanced-tab',
|
914 |
-
'rank' => 11,
|
915 |
-
)
|
916 |
-
);
|
917 |
-
|
918 |
-
$sap->add_setting(
|
919 |
-
'rtb-settings',
|
920 |
-
'rtb-restrictions-location-select',
|
921 |
-
'select',
|
922 |
-
array(
|
923 |
-
'id' => 'location-select',
|
924 |
-
'title' => __( 'Seat Restrictions Location', 'restaurant-reservations' ),
|
925 |
-
'description' => __( 'Select which location the restrictions will apply to. If a specific location doesn\'t have restrictions set, then the global total number will be used as a fall-back.', 'restaurant-reservations' ),
|
926 |
-
'blank_option' => false,
|
927 |
-
'options' => $location_options,
|
928 |
-
)
|
929 |
-
);
|
930 |
-
|
931 |
-
$max_reservation_options = array();
|
932 |
-
$max_reservations_upper_limit = apply_filters( 'rtb-max-reservations-upper-limit', 100 );
|
933 |
-
|
934 |
-
for ( $i = 1; $i <= $max_reservations_upper_limit; $i++ ) {
|
935 |
-
|
936 |
-
$max_reservation_options[$i] = $i;
|
937 |
-
}
|
938 |
-
|
939 |
-
$max_people_options = array();
|
940 |
-
$max_people_upper_limit = apply_filters( 'rtb-max-people-upper-limit', 400 );
|
941 |
-
|
942 |
-
for ( $i = 1; $i <= $max_people_upper_limit; $i++ ) {
|
943 |
-
|
944 |
-
$max_people_options[$i] = $i;
|
945 |
-
}
|
946 |
-
|
947 |
-
foreach ( $terms as $term ) {
|
948 |
-
|
949 |
-
$sap->add_setting(
|
950 |
-
'rtb-settings',
|
951 |
-
'rtb-seat-assignments',
|
952 |
-
'select',
|
953 |
-
array(
|
954 |
-
'id' => $term->slug . '-rtb-max-tables-count',
|
955 |
-
'title' => __( 'Max Reservations', 'restaurant-reservations' ),
|
956 |
-
'description' => __( 'How many reservations, if enabled, should be allowed at the same time at this location? Set dining block length to change how long a meal typically lasts.', 'restaurant-reservations' ),
|
957 |
-
'options' => $max_reservation_options,
|
958 |
-
'conditional_on' => 'location-select',
|
959 |
-
'conditional_on_value' => $term->slug,
|
960 |
-
)
|
961 |
-
);
|
962 |
-
|
963 |
-
$sap->add_setting(
|
964 |
-
'rtb-settings',
|
965 |
-
'rtb-seat-assignments',
|
966 |
-
'select',
|
967 |
-
array(
|
968 |
-
'id' => $term->slug . '-rtb-max-people-count',
|
969 |
-
'title' => __( 'Max People', 'restaurant-reservations' ),
|
970 |
-
'description' => __( 'How many people, if enabled, should be allowed to be present at this restaurant location at the same time? Set dining block length to change how long a meal typically lasts. May not work correctly if max reservations is set.', 'restaurant-reservations' ),
|
971 |
-
'options' => $max_people_options,
|
972 |
-
'conditional_on' => 'location-select',
|
973 |
-
'conditional_on_value' => $term->slug,
|
974 |
-
)
|
975 |
-
);
|
976 |
-
}
|
977 |
-
|
978 |
-
return $sap;
|
979 |
-
}
|
980 |
-
|
981 |
-
/**
|
982 |
-
* Blank out the location setting, so that it's always set to 'Global'
|
983 |
-
* on page load, except for immediately after saving a setting.
|
984 |
-
*
|
985 |
-
* @since 2.3.6
|
986 |
-
*/
|
987 |
-
public function remove_location_select_setting() {
|
988 |
-
global $rtb_controller;
|
989 |
-
|
990 |
-
$rtb_controller->settings->set_setting( 'location-select', null );
|
991 |
-
|
992 |
-
$rtb_controller->settings->save_settings();
|
993 |
-
}
|
994 |
-
|
995 |
-
/**
|
996 |
-
* Returns true if locations have been created, false otherwise
|
997 |
-
*
|
998 |
-
* @since 2.3.6
|
999 |
-
*/
|
1000 |
-
public function do_locations_exist() {
|
1001 |
-
|
1002 |
-
$args = array(
|
1003 |
-
'taxonomy' => $this->location_taxonomy,
|
1004 |
-
'hide_empty' => false,
|
1005 |
-
);
|
1006 |
-
|
1007 |
-
$terms = get_terms( $args );
|
1008 |
-
|
1009 |
-
return ( ! empty( $terms ) and ! is_wp_error( $terms ) );
|
1010 |
-
}
|
1011 |
-
|
1012 |
-
function pass_location_data_to_js( $shortcode_arg ) {
|
1013 |
-
global $rtb_controller;
|
1014 |
-
|
1015 |
-
$is_location_specific_shortcode = false;
|
1016 |
-
|
1017 |
-
$locations = [
|
1018 |
-
// false means no slug
|
1019 |
-
'global' => false
|
1020 |
-
];
|
1021 |
-
|
1022 |
-
// Specific location
|
1023 |
-
$location_term = ! empty( $shortcode_arg['location'] ) ? get_term( $shortcode_arg['location'] ) : false;
|
1024 |
-
if( $location_term and ! is_wp_error( $location_term ) ) {
|
1025 |
-
$locations[$location_term->term_id] = $location_term->slug;
|
1026 |
-
|
1027 |
-
$is_location_specific_shortcode = true;
|
1028 |
-
}
|
1029 |
-
// All locations
|
1030 |
-
else {
|
1031 |
-
$terms = get_terms(
|
1032 |
-
array(
|
1033 |
-
'taxonomy' => $this->location_taxonomy,
|
1034 |
-
'hide_empty' => false,
|
1035 |
-
)
|
1036 |
-
);
|
1037 |
-
|
1038 |
-
$options = array();
|
1039 |
-
foreach( $terms as $term ) {
|
1040 |
-
$archived = get_term_meta( $term->term_id, 'rtb_location_removed', true );
|
1041 |
-
if ( ! $archived ) {
|
1042 |
-
$locations[$term->term_id] = $term->slug;
|
1043 |
-
}
|
1044 |
-
}
|
1045 |
-
}
|
1046 |
-
|
1047 |
-
$data = [];
|
1048 |
-
foreach ($locations as $idx => $slug) {
|
1049 |
-
$data[$idx]['disable_dates'] = rtb_get_datepicker_rules( $slug );
|
1050 |
-
$data[$idx]['schedule_open'] = $rtb_controller->settings->get_setting( 'schedule-open', $slug );
|
1051 |
-
$data[$idx]['schedule_closed'] = $rtb_controller->settings->get_setting( 'schedule-closed', $slug );
|
1052 |
-
$data[$idx]['enable_max_reservations'] = is_admin() && current_user_can( 'manage_bookings' ) ? false : $rtb_controller->settings->get_setting( 'rtb-enable-max-tables', $slug );
|
1053 |
-
$data[$idx]['max_people'] = is_admin() && current_user_can( 'manage_bookings' ) ? 100 : $rtb_controller->settings->get_setting( 'rtb-max-people-count', $slug );
|
1054 |
-
|
1055 |
-
// Also override initial instance of rtb_pickadate to apply location data
|
1056 |
-
if( $is_location_specific_shortcode ) {
|
1057 |
-
add_filter( 'rtb_pickadate_args', function( $variable_list ) use ( $data, $idx ) {
|
1058 |
-
|
1059 |
-
return array_merge( $variable_list, $data[$idx] );
|
1060 |
-
});
|
1061 |
-
}
|
1062 |
-
}
|
1063 |
-
|
1064 |
-
wp_localize_script(
|
1065 |
-
'rtb-booking-form',
|
1066 |
-
'rtb_location_data',
|
1067 |
-
apply_filters(
|
1068 |
-
'rtb_location_data',
|
1069 |
-
$data
|
1070 |
-
)
|
1071 |
-
);
|
1072 |
-
|
1073 |
-
return $shortcode_arg;
|
1074 |
-
}
|
1075 |
-
|
1076 |
-
/**
|
1077 |
-
* Add location information in Payment Summary list on checkout
|
1078 |
-
* @param array $summary_data [label -> data] list
|
1079 |
-
* @param rtbBooking $booking current booking
|
1080 |
-
*/
|
1081 |
-
public function add_payment_summary( $summary_data, $booking )
|
1082 |
-
{
|
1083 |
-
global $rtb_controller;
|
1084 |
-
|
1085 |
-
if( property_exists( $booking, 'location' ) && ! empty( $booking->location ) ) {
|
1086 |
-
$loc_term = get_term( $booking->location, $rtb_controller->locations->location_taxonomy, OBJECT );
|
1087 |
-
if ( !$loc_term || is_a( $loc_term, 'WP_Error' ) ) {
|
1088 |
-
unset( $summary_data['location'] );
|
1089 |
-
}
|
1090 |
-
else {
|
1091 |
-
$summary_data['location'] = $loc_term->name;
|
1092 |
-
}
|
1093 |
-
}
|
1094 |
-
|
1095 |
-
return $summary_data;
|
1096 |
-
}
|
1097 |
-
}
|
1098 |
-
}
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Methods for handling multiple locations
|
4 |
+
*
|
5 |
+
* @package RestaurantReservations
|
6 |
+
* @copyright Copyright (c) 2016, Theme of the Crop
|
7 |
+
* @license GPL-2.0+
|
8 |
+
* @since 1.6
|
9 |
+
*/
|
10 |
+
defined( 'ABSPATH' ) || exit;
|
11 |
+
|
12 |
+
if ( ! class_exists( 'rtbMultipleLocations', false ) ) {
|
13 |
+
/**
|
14 |
+
* Class to handle custom post type and post meta fields
|
15 |
+
*
|
16 |
+
* @since 1.6
|
17 |
+
*/
|
18 |
+
class rtbMultipleLocations {
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Post type slug where locations can be found
|
22 |
+
*
|
23 |
+
* @since 1.6
|
24 |
+
*/
|
25 |
+
public $post_type = false;
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Taxonomy to use when assigning bookings to locations
|
29 |
+
*
|
30 |
+
* @since 1.6
|
31 |
+
*/
|
32 |
+
public $location_taxonomy = 'rtb_location';
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Set the loading hook
|
36 |
+
*
|
37 |
+
* @since 1.6
|
38 |
+
*/
|
39 |
+
public function __construct() {
|
40 |
+
add_action( 'plugins_loaded', array( $this, 'load' ), 100 );
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Load locations support
|
45 |
+
*
|
46 |
+
* @since 1.6
|
47 |
+
*/
|
48 |
+
public function load() {
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Allow third-party plugins to enable multiple locations
|
52 |
+
*
|
53 |
+
* Expects a post type slug pointing to the locations or false if
|
54 |
+
* multiple locations are not enabled.
|
55 |
+
*
|
56 |
+
* @since 1.6
|
57 |
+
*/
|
58 |
+
$this->post_type = apply_filters( 'rtb_set_locations_post_type', false );
|
59 |
+
|
60 |
+
if ( !$this->post_type ) {
|
61 |
+
return;
|
62 |
+
}
|
63 |
+
|
64 |
+
$this->hooks();
|
65 |
+
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Set up hooks
|
69 |
+
*
|
70 |
+
* @since 1.6
|
71 |
+
*/
|
72 |
+
public function hooks() {
|
73 |
+
add_action( 'init', array( $this, 'register_taxonomy' ), 1000 ); // after custom post types declared (hopefully!)
|
74 |
+
add_action( 'save_post_' . $this->post_type, array( $this, 'save_location' ), 10, 3 );
|
75 |
+
add_action( 'before_delete_post', array( $this, 'delete_location' ) );
|
76 |
+
add_action( 'rtb_booking_form_fields', array( $this, 'add_location_field' ), 10, 3 );
|
77 |
+
add_action( 'rtb_pre_validate_booking_submission', array( $this, 'validate_location' ) );
|
78 |
+
add_action( 'rtb_insert_booking', array( $this, 'save_booking_location' ) );
|
79 |
+
add_action( 'rtb_update_booking', array( $this, 'save_booking_location' ) );
|
80 |
+
add_action( 'rtb_booking_load_post_data', array( $this, 'load_booking_location' ), 10, 2 );
|
81 |
+
add_filter( 'rtb_query_args', array( $this, 'modify_query' ), 10, 2 );
|
82 |
+
add_filter( 'rtb_bookings_all_table_columns', array( $this, 'add_location_column' ) );
|
83 |
+
add_filter( 'rtb_bookings_table_column', array( $this, 'print_location_column' ), 10, 3 );
|
84 |
+
add_action( 'edit_form_after_title', array( $this, 'add_meta_nonce' ) );
|
85 |
+
add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) );
|
86 |
+
add_filter( 'the_content', array( $this, 'append_to_content' ) );
|
87 |
+
add_filter( 'rtb_notification_email_to_email', array( $this, 'notification_to_email' ), 10, 2 );
|
88 |
+
add_filter( 'rtb_notification_email_from_email', array( $this, 'notification_from_email' ), 10, 2 );
|
89 |
+
add_filter( 'rtb_notification_email_from_name', array( $this, 'notification_from_name' ), 10, 2 );
|
90 |
+
add_filter( 'rtb_notification_template_tags', array( $this, 'notification_template_tags' ), 10, 2 );
|
91 |
+
add_filter( 'rtb_notification_template_tag_descriptions', array( $this, 'notification_template_tag_descriptions' ) );
|
92 |
+
add_action( 'admin_init', array( $this, 'fix_autodraft_term_error' ) );
|
93 |
+
add_filter( 'rtb_settings_page', array( $this, 'maybe_add_location_settings' ) );
|
94 |
+
add_action( 'admin_init', array( $this, 'remove_location_select_setting' ) );
|
95 |
+
add_filter( 'rtb_booking_form_init', array( $this, 'pass_location_data_to_js' ) );
|
96 |
+
add_filter( 'rtb-payment-summary-data', array( $this, 'add_payment_summary' ), 10, 2);
|
97 |
+
}
|
98 |
+
|
99 |
+
/**
|
100 |
+
* Register the location taxonomy
|
101 |
+
*
|
102 |
+
* @since 1.6
|
103 |
+
*/
|
104 |
+
public function register_taxonomy() {
|
105 |
+
|
106 |
+
$args = array(
|
107 |
+
'label' => _x( 'Location', 'Name for grouping bookings', 'restaurant-reservations' ),
|
108 |
+
'hierarchical' => false,
|
109 |
+
'public' => true,
|
110 |
+
'rewrite' => false,
|
111 |
+
);
|
112 |
+
|
113 |
+
/**
|
114 |
+
* Allow third-party plugins to modify the location taxonomy
|
115 |
+
* arguments.
|
116 |
+
*
|
117 |
+
* @since 1.6
|
118 |
+
*/
|
119 |
+
$args = apply_filters( 'rtb_locations_args', $args );
|
120 |
+
|
121 |
+
register_taxonomy( $this->location_taxonomy, RTB_BOOKING_POST_TYPE, $args );
|
122 |
+
}
|
123 |
+
|
124 |
+
/**
|
125 |
+
* Generate taxonomy terms linked to locations and keep them sync'd
|
126 |
+
* with any changes
|
127 |
+
*
|
128 |
+
* @since 1.6
|
129 |
+
*/
|
130 |
+
public function save_location( $post_id, $post, $update ) {
|
131 |
+
|
132 |
+
if (
|
133 |
+
$post->post_status === 'auto-draft' ||
|
134 |
+
( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) ||
|
135 |
+
!current_user_can( 'edit_post', $post_id ) ||
|
136 |
+
!isset( $_POST['rtb_location_meta_nonce'] ) ||
|
137 |
+
!wp_verify_nonce( $_POST['rtb_location_meta_nonce'], 'rtb_location_meta' )
|
138 |
+
) {
|
139 |
+
return $post_id;
|
140 |
+
}
|
141 |
+
|
142 |
+
$term_id = get_post_meta( $post_id, $this->location_taxonomy, true );
|
143 |
+
|
144 |
+
// Create a new term for this location
|
145 |
+
if ( !$term_id ) {
|
146 |
+
|
147 |
+
$term = wp_insert_term(
|
148 |
+
sanitize_text_field( $post->post_title ),
|
149 |
+
$this->location_taxonomy
|
150 |
+
);
|
151 |
+
|
152 |
+
if ( !is_a( $term, 'WP_Error' ) ) {
|
153 |
+
update_post_meta( $post_id, $this->location_taxonomy, $term['term_id'] );
|
154 |
+
$term_id = $term['term_id'];
|
155 |
+
}
|
156 |
+
|
157 |
+
// Update the term for this location
|
158 |
+
} else {
|
159 |
+
wp_update_term(
|
160 |
+
$term_id,
|
161 |
+
$this->location_taxonomy,
|
162 |
+
array(
|
163 |
+
'name' => sanitize_text_field( $post->post_title ),
|
164 |
+
'slug' => sanitize_text_field( $post->post_name ),
|
165 |
+
)
|
166 |
+
);
|
167 |
+
}
|
168 |
+
|
169 |
+
if ( !empty( $_POST['rtb_append_booking_form'] ) ) {
|
170 |
+
update_post_meta( $post_id, 'rtb_append_booking_form', true );
|
171 |
+
} else {
|
172 |
+
delete_post_meta( $post_id, 'rtb_append_booking_form' );
|
173 |
+
}
|
174 |
+
|
175 |
+
if ( $term_id ) {
|
176 |
+
|
177 |
+
if ( !empty( $_POST['rtb_reply_to_name'] ) ) {
|
178 |
+
$reply_to_name = sanitize_text_field( $_POST['rtb_reply_to_name'] );
|
179 |
+
update_term_meta( $term_id, 'rtb_reply_to_name', $reply_to_name );
|
180 |
+
} else {
|
181 |
+
delete_term_meta( $term_id, 'rtb_reply_to_name' );
|
182 |
+
}
|
183 |
+
|
184 |
+
if ( !empty( $_POST['rtb_reply_to_address'] ) ) {
|
185 |
+
$reply_to_address = sanitize_email( $_POST['rtb_reply_to_address'] );
|
186 |
+
update_term_meta( $term_id, 'rtb_reply_to_address', $reply_to_address );
|
187 |
+
} else {
|
188 |
+
delete_term_meta( $term_id, 'rtb_reply_to_address' );
|
189 |
+
}
|
190 |
+
|
191 |
+
if ( !empty( $_POST['rtb_admin_email_address'] ) ) {
|
192 |
+
$email = sanitize_text_field( $_POST['rtb_admin_email_address'] );
|
193 |
+
update_term_meta( $term_id, 'rtb_admin_email_address', $email );
|
194 |
+
} else {
|
195 |
+
delete_term_meta( $term_id, 'rtb_admin_email_address' );
|
196 |
+
}
|
197 |
+
}
|
198 |
+
|
199 |
+
return $post_id;
|
200 |
+
}
|
201 |
+
|
202 |
+
/**
|
203 |
+
* Delete taxonomy terms linked to locations when a location is deleted
|
204 |
+
*
|
205 |
+
* Only does this when no bookings are associated with that term.
|
206 |
+
* Otherwise it may be important to keep the bookings grouped for
|
207 |
+
* historical data.
|
208 |
+
*
|
209 |
+
* @since 1.6
|
210 |
+
*/
|
211 |
+
public function delete_location( $post_id ) {
|
212 |
+
|
213 |
+
if ( !current_user_can( 'delete_posts' ) ) {
|
214 |
+
return $post_id;
|
215 |
+
}
|
216 |
+
|
217 |
+
$term_id = get_post_meta( $post_id, $this->location_taxonomy, true );
|
218 |
+
|
219 |
+
$term = get_term( $term_id, $this->location_taxonomy );
|
220 |
+
|
221 |
+
if ( !$term || is_a( $term, 'WP_Error' ) ) {
|
222 |
+
return;
|
223 |
+
}
|
224 |
+
|
225 |
+
$query = new rtbQuery( array( 'location' => $term_id ), 'delete-location-term-check' );
|
226 |
+
$query->prepare_args();
|
227 |
+
$query->get_bookings();
|
228 |
+
|
229 |
+
// Don't delete taxonomy terms if there are bookings assigned to
|
230 |
+
// this location, so the booking associations can remain as
|
231 |
+
// historical data.
|
232 |
+
if ( count( $query->bookings ) ) {
|
233 |
+
add_term_meta( $term_id, 'rtb_location_removed', true );
|
234 |
+
} else {
|
235 |
+
wp_delete_term( $term_id, $this->location_taxonomy );
|
236 |
+
}
|
237 |
+
|
238 |
+
}
|
239 |
+
|
240 |
+
/**
|
241 |
+
* Get location term id from location post id
|
242 |
+
*
|
243 |
+
* Transforms a location post id into its associated term id. If the
|
244 |
+
* id doesn't match a location post, it will check if the received id
|
245 |
+
* matches a term id and return it if so. Between versions 1.6 and
|
246 |
+
* and 1.6.1, only term ids were accepted as shortcodes, and this
|
247 |
+
* provides a backwards-compatible fallback.
|
248 |
+
*
|
249 |
+
* @param $location_id int The location id (post or term)
|
250 |
+
* @return int The location term id. Default: 0
|
251 |
+
*/
|
252 |
+
public function get_location_term_id( $location_id ) {
|
253 |
+
|
254 |
+
$location_id = absint( $location_id );
|
255 |
+
$term_id = 0;
|
256 |
+
|
257 |
+
if ( get_post_type( $location_id ) === $this->post_type ) {
|
258 |
+
$term_id = get_post_meta( $location_id, $this->location_taxonomy, true );
|
259 |
+
} elseif ( term_exists( $location_id, $this->location_taxonomy ) ) {
|
260 |
+
$term_id = $location_id;
|
261 |
+
}
|
262 |
+
|
263 |
+
return $term_id;
|
264 |
+
}
|
265 |
+
|
266 |
+
|
267 |
+
/**
|
268 |
+
* Add the location selection field to the booking form
|
269 |
+
*
|
270 |
+
* @since 1.6
|
271 |
+
*/
|
272 |
+
public function add_location_field( $fields, $request = null, $args = array() ) {
|
273 |
+
global $rtb_controller;
|
274 |
+
|
275 |
+
// If the location is specified, don't add a field.
|
276 |
+
// A hidden field is added automatically in rtb_print_booking_form()
|
277 |
+
if ( !empty( $args['location'] ) ) {
|
278 |
+
$args['location'] = $this->get_location_term_id( $args['location'] );
|
279 |
+
if ( !empty( $args['location'] ) ) {
|
280 |
+
return $fields;
|
281 |
+
}
|
282 |
+
}
|
283 |
+
|
284 |
+
if ( $request === null ) {
|
285 |
+
global $rtb_controller;
|
286 |
+
$request = $rtb_controller->request;
|
287 |
+
}
|
288 |
+
|
289 |
+
// Select a fieldset in which to place the field
|
290 |
+
$placement = false;
|
291 |
+
if ( isset( $fields['reservation'] ) && isset( $fields['reservation']['fields'] ) ) {
|
292 |
+
$placement = &$fields['reservation']['fields'];
|
293 |
+
} else {
|
294 |
+
$key = key( reset( $fields ) );
|
295 |
+
if ( isset( $fields[$key]['fields'] ) ) {
|
296 |
+
$placement = &$fields[$key]['fields'];
|
297 |
+
}
|
298 |
+
}
|
299 |
+
|
300 |
+
// If we couldn't find any working fieldset, then something odd is
|
301 |
+
// going on. Just pretend we were never here.
|
302 |
+
if ( $placement === false ) {
|
303 |
+
return $fields;
|
304 |
+
}
|
305 |
+
|
306 |
+
$placement = array_merge(
|
307 |
+
array(
|
308 |
+
'location' => array(
|
309 |
+
'title' => esc_html( $rtb_controller->settings->get_setting( 'label-location' ) ),
|
310 |
+
'request_input' => empty( $request->location ) ? '' : $request->location,
|
311 |
+
'callback' => 'rtb_print_form_select_field',
|
312 |
+
'callback_args' => array(
|
313 |
+
'options' => $this->get_location_options(),
|
314 |
+
),
|
315 |
+
'empty_option' => true,
|
316 |
+
'required' => true,
|
317 |
+
)
|
318 |
+
),
|
319 |
+
$placement
|
320 |
+
);
|
321 |
+
|
322 |
+
return $fields;
|
323 |
+
}
|
324 |
+
|
325 |
+
/**
|
326 |
+
* Retrieve a key/value array of location terms and names
|
327 |
+
*
|
328 |
+
* @param bool $active_only Whether or not to retrieve only currently
|
329 |
+
* active locations. Default: true - don't retrieve locations that
|
330 |
+
* have been removed
|
331 |
+
* @since 1.6
|
332 |
+
*/
|
333 |
+
public function get_location_options( $active_only = true ) {
|
334 |
+
|
335 |
+
$terms = get_terms(
|
336 |
+
array(
|
337 |
+
'taxonomy' => $this->location_taxonomy,
|
338 |
+
'hide_empty' => false,
|
339 |
+
)
|
340 |
+
);
|
341 |
+
|
342 |
+
$options = array();
|
343 |
+
foreach( $terms as $term ) {
|
344 |
+
$archived = get_term_meta( $term->term_id, 'rtb_location_removed', true );
|
345 |
+
if ( !$active_only || !$archived ) {
|
346 |
+
$options[$term->term_id] = $term->name;
|
347 |
+
}
|
348 |
+
}
|
349 |
+
|
350 |
+
return $options;
|
351 |
+
}
|
352 |
+
|
353 |
+
/**
|
354 |
+
* Validate location in post data
|
355 |
+
*
|
356 |
+
* @since 1.6
|
357 |
+
*/
|
358 |
+
public function validate_location( $booking ) {
|
359 |
+
|
360 |
+
$booking->location = empty( $_POST['rtb-location'] ) ? '' : absint( $_POST['rtb-location'] );
|
361 |
+
if ( empty( $booking->location ) ) {
|
362 |
+
$booking->validation_errors[] = array(
|
363 |
+
'field' => 'location',
|
364 |
+
'post_variable' => $booking->location,
|
365 |
+
'message' => __( 'Please select a location for your booking.', 'restaurant-reservations' ),
|
366 |
+
);
|
367 |
+
|
368 |
+
} elseif ( !term_exists( $booking->location, $this->location_taxonomy ) ) {
|
369 |
+
$booking->validation_errors[] = array(
|
370 |
+
'field' => 'location',
|
371 |
+
'post_variable' => $booking->location,
|
372 |
+
'message' => __( 'The location you selected is not valid. Please select another location.', 'restaurant-reservations' ),
|
373 |
+
);
|
374 |
+
}
|
375 |
+
}
|
376 |
+
|
377 |
+
/**
|
378 |
+
* Save the booking location when the booking is created or updated.
|
379 |
+
*
|
380 |
+
* @since 1.6
|
381 |
+
*/
|
382 |
+
public function save_booking_location( $booking ) {
|
383 |
+
|
384 |
+
if ( !empty( $booking->location ) ) {
|
385 |
+
wp_set_object_terms( $booking->ID, $booking->location, $this->location_taxonomy );
|
386 |
+
}
|
387 |
+
}
|
388 |
+
|
389 |
+
/**
|
390 |
+
* Load the booking location when teh booking is loaded
|
391 |
+
*
|
392 |
+
* @since 1.6
|
393 |
+
*/
|
394 |
+
public function load_booking_location( $booking, $post ) {
|
395 |
+
|
396 |
+
$terms = wp_get_object_terms( $booking->ID, $this->location_taxonomy, array( 'fields' => 'ids' ) );
|
397 |
+
|
398 |
+
if ( is_a( $terms, 'WP_Error' ) ) {
|
399 |
+
return;
|
400 |
+
}
|
401 |
+
|
402 |
+
$booking->location = current( $terms );
|
403 |
+
}
|
404 |
+
|
405 |
+
/**
|
406 |
+
* Add location column to the list table
|
407 |
+
*
|
408 |
+
* @since 1.6
|
409 |
+
*/
|
410 |
+
public function add_location_column( $columns ) {
|
411 |
+
|
412 |
+
$first = array_splice( $columns, 0, 2 );
|
413 |
+
$first['location'] = __( 'Location', 'restaurant-reservations' );
|
414 |
+
|
415 |
+
return array_merge( $first, $columns );
|
416 |
+
}
|
417 |
+
|
418 |
+
/**
|
419 |
+
* Print the value in the location column for the list table
|
420 |
+
*
|
421 |
+
* @since 1.6
|
422 |
+
*/
|
423 |
+
public function print_location_column( $value, $booking, $column_name ) {
|
424 |
+
|
425 |
+
if ( $column_name !== 'location' ) {
|
426 |
+
return $value;
|
427 |
+
}
|
428 |
+
|
429 |
+
$terms = wp_get_object_terms( $booking->ID, $this->location_taxonomy );
|
430 |
+
|
431 |
+
if ( empty( $terms ) || is_a( $terms, 'WP_Error' ) ) {
|
432 |
+
return '';
|
433 |
+
}
|
434 |
+
|
435 |
+
$location = current( $terms );
|
436 |
+
|
437 |
+
return $location->name;
|
438 |
+
}
|
439 |
+
|
440 |
+
/**
|
441 |
+
* Modify queries to add location taxonomy parameters
|
442 |
+
*
|
443 |
+
* @param array $args Array of arguments passed to rtbQuery
|
444 |
+
* @since 1.6
|
445 |
+
*/
|
446 |
+
public function modify_query( $args, $context = '' ) {
|
447 |
+
|
448 |
+
global $rtb_controller;
|
449 |
+
|
450 |
+
if ( !empty( $args['location'] ) && !empty( $rtb_controller->locations->post_type ) ) {
|
451 |
+
|
452 |
+
if ( !is_array( $args['location'] ) ) {
|
453 |
+
$args['location'] = array( $args['location'] );
|
454 |
+
}
|
455 |
+
|
456 |
+
$args['tax_query'] = array(
|
457 |
+
array(
|
458 |
+
'taxonomy' => $rtb_controller->locations->location_taxonomy,
|
459 |
+
'field' => 'term_id',
|
460 |
+
'terms' => $args['location'],
|
461 |
+
|
462 |
+
)
|
463 |
+
);
|
464 |
+
}
|
465 |
+
|
466 |
+
return $args;
|
467 |
+
}
|
468 |
+
|
469 |
+
/**
|
470 |
+
* Add meta box to the location post editing screen
|
471 |
+
*
|
472 |
+
* @since 1.6
|
473 |
+
*/
|
474 |
+
public function add_meta_boxes() {
|
475 |
+
|
476 |
+
$meta_boxes = array(
|
477 |
+
|
478 |
+
// Metabox to enter schema type
|
479 |
+
array(
|
480 |
+
'id' => 'rtb_location',
|
481 |
+
'title' => __( 'Reservations', 'restaurant-reservations' ),
|
482 |
+
'callback' => array( $this, 'print_location_metabox' ),
|
483 |
+
'post_type' => $this->post_type,
|
484 |
+
'context' => 'side',
|
485 |
+
'priority' => 'default',
|
486 |
+
),
|
487 |
+
);
|
488 |
+
|
489 |
+
// Create filter so addons can modify the metaboxes
|
490 |
+
$meta_boxes = apply_filters( 'rtb_location_metaboxes', $meta_boxes );
|
491 |
+
|
492 |
+
// Create the metaboxes
|
493 |
+
foreach ( $meta_boxes as $meta_box ) {
|
494 |
+
add_meta_box(
|
495 |
+
$meta_box['id'],
|
496 |
+
$meta_box['title'],
|
497 |
+
$meta_box['callback'],
|
498 |
+
$meta_box['post_type'],
|
499 |
+
$meta_box['context'],
|
500 |
+
$meta_box['priority']
|
501 |
+
);
|
502 |
+
}
|
503 |
+
}
|
504 |
+
|
505 |
+
/**
|
506 |
+
* Output a hidden nonce field to secure the saving of term meta
|
507 |
+
*
|
508 |
+
* @since 1.6
|
509 |
+
*/
|
510 |
+
public function add_meta_nonce() {
|
511 |
+
global $post;
|
512 |
+
if ( $post->post_type == $this->post_type ) {
|
513 |
+
wp_nonce_field( 'rtb_location_meta', 'rtb_location_meta_nonce' );
|
514 |
+
}
|
515 |
+
}
|
516 |
+
|
517 |
+
/**
|
518 |
+
* Print metabox on location post editing screen
|
519 |
+
*
|
520 |
+
* @since 1.6
|
521 |
+
*/
|
522 |
+
public function print_location_metabox( $post ) {
|
523 |
+
|
524 |
+
global $rtb_controller;
|
525 |
+
|
526 |
+
$notification_email = '';
|
527 |
+
$reply_to_name = '';
|
528 |
+
$reply_to_address = '';
|
529 |
+
$term_id = get_post_meta( $post->ID, $this->location_taxonomy, true );
|
530 |
+
$admin_email_option = $rtb_controller->settings->get_setting( 'admin-email-option' );
|
531 |
+
if ( $term_id ) {
|
532 |
+
$reply_to_name = get_term_meta( $term_id, 'rtb_reply_to_name', true );
|
533 |
+
$reply_to_address = get_term_meta( $term_id, 'rtb_reply_to_address', true );
|
534 |
+
if ( $admin_email_option ) {
|
535 |
+
$notification_email = get_term_meta( $term_id, 'rtb_admin_email_address', true );
|
536 |
+
}
|
537 |
+
}
|
538 |
+
|
539 |
+
$append_booking_form = get_post_meta( $post->ID, 'rtb_append_booking_form', true );
|
540 |
+
|
541 |
+
?>
|
542 |
+
|
543 |
+
<style type="text/css">.rtb-location-meta-input + .rtb-location-meta-input { margin-top: 2em; }</style>
|
544 |
+
|
545 |
+
<div class="rtb-location-meta-input rtb-location-meta-append-form">
|
546 |
+
<label>
|
547 |
+
<input type="checkbox" name="rtb_append_booking_form" value="1"<?php if ( $append_booking_form ) : ?> checked="checked"<?php endif; ?>>
|
548 |
+
<?php esc_html_e( "Automatically add the booking form to this page.", 'restaurant-reservations' ); ?>
|
549 |
+
</label>
|
550 |
+
</div>
|
551 |
+
|
552 |
+
<div class="rtb-location-meta-input rtb-location-meta-reply-to-name">
|
553 |
+
<label for="rtb_reply_to_name">
|
554 |
+
<?php esc_html_e( 'Reply-To Name', 'restaurant-reservations' ); ?>
|
555 |
+
</label>
|
556 |
+
<input type="text" name="rtb_reply_to_name" id="rtb_reply_to_name" value="<?php esc_attr_e( $reply_to_name ); ?>" placeholder="<?php esc_attr_e( $rtb_controller->settings->get_setting( 'reply-to-name' ) ); ?>">
|
557 |
+
<p class="description">
|
558 |
+
<?php esc_html_e( 'The name which should appear in the Reply-To field of a user notification email.', 'restaurant-reservations' ); ?>
|
559 |
+
</p>
|
560 |
+
</div>
|
561 |
+
|
562 |
+
<div class="rtb-location-meta-input rtb-location-meta-reply-to-address">
|
563 |
+
<label for="rtb_reply_to_address">
|
564 |
+
<?php esc_html_e( 'Reply-To Email Address', 'restaurant-reservations' ); ?>
|
565 |
+
</label>
|
566 |
+
<input type="text" name="rtb_reply_to_address" id="rtb_reply_to_address" value="<?php esc_attr_e( $reply_to_address ); ?>" placeholder="<?php esc_attr_e( $rtb_controller->settings->get_setting( 'reply-to-address' ) ); ?>">
|
567 |
+
<p class="description">
|
568 |
+
<?php esc_html_e( 'The email address which should appear in the Reply-To field of a user notification email.', 'restaurant-reservations' ); ?>
|
569 |
+
</p>
|
570 |
+
</div>
|
571 |
+
|
572 |
+
<?php if ( $admin_email_option ) : ?>
|
573 |
+
<div class="rtb-location-meta-input rtb-location-meta-admin-email">
|
574 |
+
<label for="rtb_admin_email_address">
|
575 |
+
<?php esc_html_e( 'Admin Notification Email Address', 'restaurant-reservations' ); ?>
|
576 |
+
</label>
|
577 |
+
<input type="text" name="rtb_admin_email_address" id="rtb_admin_email_address" value="<?php esc_attr_e( $notification_email ); ?>" placeholder="<?php esc_attr_e( $rtb_controller->settings->get_setting( 'admin-email-address' ) ); ?>">
|
578 |
+
<p class="description">
|
579 |
+
<?php esc_html_e( 'The email address where admin notifications for bookings at this location should be sent.', 'restaurant-reservations' ); ?>
|
580 |
+
</p>
|
581 |
+
</div>
|
582 |
+
<?php endif; ?>
|
583 |
+
|
584 |
+
<?php
|
585 |
+
}
|
586 |
+
|
587 |
+
/**
|
588 |
+
* Append booking form to a location's `post_content`
|
589 |
+
* @since 0.0.1
|
590 |
+
*/
|
591 |
+
public function append_to_content( $content ) {
|
592 |
+
|
593 |
+
if ( !is_main_query() || !in_the_loop() || post_password_required() ) {
|
594 |
+
return $content;
|
595 |
+
}
|
596 |
+
|
597 |
+
global $post;
|
598 |
+
|
599 |
+
$append_booking_form = get_post_meta( $post->ID, 'rtb_append_booking_form', true );
|
600 |
+
|
601 |
+
if ( !$append_booking_form ) {
|
602 |
+
return $content;
|
603 |
+
}
|
604 |
+
|
605 |
+
$term_id = get_post_meta( $post->ID, $this->location_taxonomy, true );
|
606 |
+
|
607 |
+
if ( empty( $term_id ) ) {
|
608 |
+
return $content;
|
609 |
+
}
|
610 |
+
|
611 |
+
return $content . do_shortcode( '[booking-form location=' . absint( $term_id ) .']' );
|
612 |
+
}
|
613 |
+
|
614 |
+
/**
|
615 |
+
* Modify the notification email recipient for each location
|
616 |
+
*
|
617 |
+
* @since 1.6
|
618 |
+
*/
|
619 |
+
public function notification_to_email( $email, $notification ) {
|
620 |
+
|
621 |
+
if ( $notification->target == 'user' || empty( $notification->booking->location ) ) {
|
622 |
+
return $email;
|
623 |
+
}
|
624 |
+
|
625 |
+
$val = get_term_meta( $notification->booking->location, 'rtb_admin_email_address', true );
|
626 |
+
$email = empty( $val ) ? $email : $val;
|
627 |
+
|
628 |
+
return $email;
|
629 |
+
}
|
630 |
+
|
631 |
+
/**
|
632 |
+
* Modify the notification email sender address for each location
|
633 |
+
*
|
634 |
+
* @since 1.6
|
635 |
+
*/
|
636 |
+
public function notification_from_email( $email, $notification ) {
|
637 |
+
|
638 |
+
if ( $notification->target != 'user' || empty( $notification->booking->location ) ) {
|
639 |
+
return $email;
|
640 |
+
}
|
641 |
+
|
642 |
+
$val = get_term_meta( $notification->booking->location, 'rtb_reply_to_address', true );
|
643 |
+
$email = empty( $val ) ? $email : $val;
|
644 |
+
|
645 |
+
return $email;
|
646 |
+
}
|
647 |
+
|
648 |
+
/**
|
649 |
+
* Modify the notification email sender name for each location
|
650 |
+
*
|
651 |
+
* @since 1.6
|
652 |
+
*/
|
653 |
+
public function notification_from_name( $name, $notification ) {
|
654 |
+
|
655 |
+
if ( $notification->target != 'user' || empty( $notification->booking->location ) ) {
|
656 |
+
return $name;
|
657 |
+
}
|
658 |
+
|
659 |
+
$val = get_term_meta( $notification->booking->location, 'rtb_reply_to_name', true );
|
660 |
+
$name = empty( $val ) ? $name : $val;
|
661 |
+
|
662 |
+
return $name;
|
663 |
+
}
|
664 |
+
|
665 |
+
/**
|
666 |
+
* Add a location template tag for notifications
|
667 |
+
*
|
668 |
+
* @since 1.6.1
|
669 |
+
*/
|
670 |
+
public function notification_template_tags( $template_tags, $notification ) {
|
671 |
+
|
672 |
+
$term = empty( $notification->booking->location ) ? null : get_term( $notification->booking->location, $this->location_taxonomy );
|
673 |
+
$location_name = is_null( $term ) || is_wp_error( $term ) ? '' : $term->name;
|
674 |
+
|
675 |
+
$table_number = empty( $notification->booking->table ) ? '' : $notification->booking->table;
|
676 |
+
$table_number = is_array($table_number) ? implode(',', $table_number ) : $table_number;
|
677 |
+
|
678 |
+
return array_merge(
|
679 |
+
array(
|
680 |
+
'{location}' => $location_name,
|
681 |
+
'{table}' => $table_number,
|
682 |
+
),
|
683 |
+
$template_tags
|
684 |
+
);
|
685 |
+
}
|
686 |
+
|
687 |
+
/**
|
688 |
+
* Add a description for the location template tag
|
689 |
+
*
|
690 |
+
* @since 1.6.1
|
691 |
+
*/
|
692 |
+
public function notification_template_tag_descriptions( $descriptions ) {
|
693 |
+
return array_merge(
|
694 |
+
array( '{location}' => __( 'Location for which this booking was made.', 'restaurant-reservations' ) ),
|
695 |
+
$descriptions
|
696 |
+
);
|
697 |
+
}
|
698 |
+
|
699 |
+
/**
|
700 |
+
* Removes Auto-Draft locations that were added due to a bug in v1.7
|
701 |
+
*
|
702 |
+
* Version 1.7 introduced a bug which caused a location term to be
|
703 |
+
* created if the location Add New page was loaded. This term
|
704 |
+
* corresponded to an auto-draft post object and will be removed when
|
705 |
+
* that object is removed. This provides a one-time fix in v1.7.1
|
706 |
+
*
|
707 |
+
* @see https://github.com/NateWr/restaurant-reservations/issues/91
|
708 |
+
* @see https://developer.wordpress.org/reference/functions/wp_delete_auto_drafts/
|
709 |
+
* @since 1.7.1
|
710 |
+
*/
|
711 |
+
public function fix_autodraft_term_error() {
|
712 |
+
|
713 |
+
if ( get_option( 'rtb_autodraft_terms_fixed', false ) ) {
|
714 |
+
return;
|
715 |
+
}
|
716 |
+
|
717 |
+
global $wpdb;
|
718 |
+
|
719 |
+
if ( !$wpdb ) {
|
720 |
+
return;
|
721 |
+
}
|
722 |
+
|
723 |
+
$old_posts = $wpdb->get_col( "SELECT ID FROM $wpdb->posts WHERE post_status = 'auto-draft' AND post_type = '$this->post_type';" );
|
724 |
+
foreach ( (array) $old_posts as $delete ) {
|
725 |
+
// Force delete.
|
726 |
+
wp_delete_post( $delete, true );
|
727 |
+
}
|
728 |
+
|
729 |
+
// Set the `rtb_location_removed` term meta on any terms that are
|
730 |
+
// no longer attached to posts
|
731 |
+
global $wp_version;
|
732 |
+
if ( version_compare( $wp_version, '3.9', '>=' ) ) {
|
733 |
+
$live_terms = $wpdb->get_col( "SELECT meta_value FROM $wpdb->postmeta WHERE meta_key='$this->location_taxonomy';" );
|
734 |
+
$all_terms = get_terms( array(
|
735 |
+
'taxonomy' => $this->location_taxonomy,
|
736 |
+
'hide_empty' => false,
|
737 |
+
'meta_query' => array(
|
738 |
+
array(
|
739 |
+
'compare' => 'NOT EXISTS',
|
740 |
+
'key' => 'rtb_location_removed',
|
741 |
+
)
|
742 |
+
)
|
743 |
+
) );
|
744 |
+
if ( is_array( $all_terms ) ) {
|
745 |
+
foreach( $all_terms as $term ) {
|
746 |
+
if ( !in_array( $term->term_id, $live_terms ) ) {
|
747 |
+
$query = new rtbQuery( array( 'location' => $term->term_id ), 'delete-location-term-check' );
|
748 |
+
$query->prepare_args();
|
749 |
+
$query->get_bookings();
|
750 |
+
|
751 |
+
// Don't delete taxonomy terms if there are bookings assigned to
|
752 |
+
// this location, so the booking associations can remain as
|
753 |
+
// historical data.
|
754 |
+
if ( count( $query->bookings ) ) {
|
755 |
+
add_term_meta( $term->term_id, 'rtb_location_removed', true );
|
756 |
+
} else {
|
757 |
+
wp_delete_term( $term->term_id, $this->location_taxonomy );
|
758 |
+
}
|
759 |
+
}
|
760 |
+
}
|
761 |
+
}
|
762 |
+
}
|
763 |
+
|
764 |
+
update_option( 'rtb_autodraft_terms_fixed', true );
|
765 |
+
}
|
766 |
+
|
767 |
+
/**
|
768 |
+
* If multiple locations exist, adds a select box to the settings page which
|
769 |
+
* allows a user to select whether certain settings are global or location-specific.
|
770 |
+
* Also adds in location-specific settings for a number of different settings, and
|
771 |
+
* makes all settings conditional on the value of the select box.
|
772 |
+
*
|
773 |
+
* @since 2.3.6
|
774 |
+
*/
|
775 |
+
public function maybe_add_location_settings( $sap ) {
|
776 |
+
global $rtb_controller;
|
777 |
+
|
778 |
+
$args = array(
|
779 |
+
'taxonomy' => $this->location_taxonomy,
|
780 |
+
'hide_empty' => false,
|
781 |
+
);
|
782 |
+
|
783 |
+
$terms = get_terms( $args );
|
784 |
+
|
785 |
+
if ( ! $this->do_locations_exist() ) { return $sap; }
|
786 |
+
|
787 |
+
foreach ( $sap->pages['rtb-settings']->sections as $key => $section ) {
|
788 |
+
|
789 |
+
foreach ( $section->settings as $setting_key => $setting ) {
|
790 |
+
|
791 |
+
$sap->pages['rtb-settings']->sections[ $key ]->settings[ $setting_key ]->conditional_on = 'location-select';
|
792 |
+
$sap->pages['rtb-settings']->sections[ $key ]->settings[ $setting_key ]->conditional_on_value = false;
|
793 |
+
|
794 |
+
$sap->pages['rtb-settings']->sections[ $key ]->settings[ $setting_key ]->set_conditional_display();
|
795 |
+
}
|
796 |
+
}
|
797 |
+
|
798 |
+
|
799 |
+
$location_options = array(
|
800 |
+
'' => __( 'Global', 'restaurant-reservations' ),
|
801 |
+
);
|
802 |
+
|
803 |
+
foreach ( $terms as $term ) {
|
804 |
+
|
805 |
+
$location_options[ $term->slug ] = $term->term_id . ' - '.$term->name;
|
806 |
+
}
|
807 |
+
|
808 |
+
// Schedule location-specific options
|
809 |
+
$sap->add_section(
|
810 |
+
'rtb-settings',
|
811 |
+
array(
|
812 |
+
'id' => 'rtb-schedule-location-select',
|
813 |
+
'title' => __( 'Select Schedule Location', 'restaurant-reservations' ),
|
814 |
+
'tab' => 'rtb-schedule-tab',
|
815 |
+
'rank' => 2,
|
816 |
+
)
|
817 |
+
);
|
818 |
+
|
819 |
+
$sap->add_setting(
|
820 |
+
'rtb-settings',
|
821 |
+
'rtb-schedule-location-select',
|
822 |
+
'select',
|
823 |
+
array(
|
824 |
+
'id' => 'location-select',
|
825 |
+
'title' => __( 'Schedule Location', 'restaurant-reservations' ),
|
826 |
+
'description' => __( 'Select which location the schedule will apply to. If a specific location doesn\'t have a schedule set, then it will fall back to the global schedule when booking.', 'restaurant-reservations' ),
|
827 |
+
'blank_option' => false,
|
828 |
+
'options' => $location_options,
|
829 |
+
)
|
830 |
+
);
|
831 |
+
|
832 |
+
// Translateable strings for scheduler components
|
833 |
+
$scheduler_strings = array(
|
834 |
+
'add_rule' => __( 'Add new scheduling rule', 'restaurant-reservations' ),
|
835 |
+
'weekly' => _x( 'Weekly', 'Format of a scheduling rule', 'restaurant-reservations' ),
|
836 |
+
'monthly' => _x( 'Monthly', 'Format of a scheduling rule', 'restaurant-reservations' ),
|
837 |
+
'date' => _x( 'Date', 'Format of a scheduling rule', 'restaurant-reservations' ),
|
838 |
+
'weekdays' => _x( 'Days of the week', 'Label for selecting days of the week in a scheduling rule', 'restaurant-reservations' ),
|
839 |
+
'month_weeks' => _x( 'Weeks of the month', 'Label for selecting weeks of the month in a scheduling rule', 'restaurant-reservations' ),
|
840 |
+
'date_label' => _x( 'Date', 'Label to select a date for a scheduling rule', 'restaurant-reservations' ),
|
841 |
+
'time_label' => _x( 'Time', 'Label to select a time slot for a scheduling rule', 'restaurant-reservations' ),
|
842 |
+
'allday' => _x( 'All day', 'Label to set a scheduling rule to last all day', 'restaurant-reservations' ),
|
843 |
+
'start' => _x( 'Start', 'Label for the starting time of a scheduling rule', 'restaurant-reservations' ),
|
844 |
+
'end' => _x( 'End', 'Label for the ending time of a scheduling rule', 'restaurant-reservations' ),
|
845 |
+
'set_time_prompt' => _x( 'All day long. Want to %sset a time slot%s?', 'Prompt displayed when a scheduling rule is set without any time restrictions', 'restaurant-reservations' ),
|
846 |
+
'toggle' => _x( 'Open and close this rule', 'Toggle a scheduling rule open and closed', 'restaurant-reservations' ),
|
847 |
+
'delete' => _x( 'Delete rule', 'Delete a scheduling rule', 'restaurant-reservations' ),
|
848 |
+
'delete_schedule' => __( 'Delete scheduling rule', 'restaurant-reservations' ),
|
849 |
+
'never' => _x( 'Never', 'Brief default description of a scheduling rule when no weekdays or weeks are included in the rule', 'restaurant-reservations' ),
|
850 |
+
'weekly_always' => _x( 'Every day', 'Brief default description of a scheduling rule when all the weekdays/weeks are included in the rule', 'restaurant-reservations' ),
|
851 |
+
'monthly_weekdays' => _x( '%s on the %s week of the month', 'Brief default description of a scheduling rule when some weekdays are included on only some weeks of the month. %s should be left alone and will be replaced by a comma-separated list of days and weeks in the following format: M, T, W on the first, second week of the month', 'restaurant-reservations' ),
|
852 |
+
'monthly_weeks' => _x( '%s week of the month', 'Brief default description of a scheduling rule when some weeks of the month are included but all or no weekdays are selected. %s should be left alone and will be replaced by a comma-separated list of weeks in the following format: First, second week of the month', 'restaurant-reservations' ),
|
853 |
+
'all_day' => _x( 'All day', 'Brief default description of a scheduling rule when no times are set', 'restaurant-reservations' ),
|
854 |
+
'before' => _x( 'Ends at', 'Brief default description of a scheduling rule when an end time is set but no start time. If the end time is 6pm, it will read: Ends at 6pm', 'restaurant-reservations' ),
|
855 |
+
'after' => _x( 'Starts at', 'Brief default description of a scheduling rule when a start time is set but no end time. If the start time is 6pm, it will read: Starts at 6pm', 'restaurant-reservations' ),
|
856 |
+
'separator' => _x( '—', 'Separator between times of a scheduling rule', 'restaurant-reservations' ),
|
857 |
+
);
|
858 |
+
|
859 |
+
foreach ( $terms as $term ) {
|
860 |
+
|
861 |
+
$sap->add_setting(
|
862 |
+
'rtb-settings',
|
863 |
+
'rtb-schedule',
|
864 |
+
'scheduler',
|
865 |
+
array(
|
866 |
+
'id' => $term->slug . '-schedule-open',
|
867 |
+
'title' => __( 'Schedule', 'restaurant-reservations' ),
|
868 |
+
'description' => __( 'Define the weekly schedule during which you accept bookings.', 'restaurant-reservations' ),
|
869 |
+
'weekdays' => array(
|
870 |
+
'monday' => _x( 'Mo', 'Monday abbreviation', 'restaurant-reservations' ),
|
871 |
+
'tuesday' => _x( 'Tu', 'Tuesday abbreviation', 'restaurant-reservations' ),
|
872 |
+
'wednesday' => _x( 'We', 'Wednesday abbreviation', 'restaurant-reservations' ),
|
873 |
+
'thursday' => _x( 'Th', 'Thursday abbreviation', 'restaurant-reservations' ),
|
874 |
+
'friday' => _x( 'Fr', 'Friday abbreviation', 'restaurant-reservations' ),
|
875 |
+
'saturday' => _x( 'Sa', 'Saturday abbreviation', 'restaurant-reservations' ),
|
876 |
+
'sunday' => _x( 'Su', 'Sunday abbreviation', 'restaurant-reservations' )
|
877 |
+
),
|
878 |
+
'time_format' => $rtb_controller->settings->get_setting( 'time-format' ),
|
879 |
+
'date_format' => $rtb_controller->settings->get_setting( 'date-format' ),
|
880 |
+
'disable_weeks' => true,
|
881 |
+
'disable_date' => true,
|
882 |
+
'strings' => $scheduler_strings,
|
883 |
+
'conditional_on' => 'location-select',
|
884 |
+
'conditional_on_value' => $term->slug,
|
885 |
+
)
|
886 |
+
);
|
887 |
+
|
888 |
+
$sap->add_setting(
|
889 |
+
'rtb-settings',
|
890 |
+
'rtb-schedule',
|
891 |
+
'scheduler',
|
892 |
+
array(
|
893 |
+
'id' => $term->slug . '-schedule-closed',
|
894 |
+
'title' => __( 'Exceptions', 'restaurant-reservations' ),
|
895 |
+
'description' => __( "Define special opening hours for holidays, events or other needs. Leave the time empty if you're closed all day.", 'restaurant-reservations' ),
|
896 |
+
'time_format' => esc_attr( $rtb_controller->settings->get_setting( 'time-format' ) ),
|
897 |
+
'date_format' => esc_attr( $rtb_controller->settings->get_setting( 'date-format' ) ),
|
898 |
+
'disable_weekdays' => true,
|
899 |
+
'disable_weeks' => true,
|
900 |
+
'strings' => $scheduler_strings,
|
901 |
+
'conditional_on' => 'location-select',
|
902 |
+
'conditional_on_value' => $term->slug,
|
903 |
+
)
|
904 |
+
);
|
905 |
+
}
|
906 |
+
|
907 |
+
// Restriction location-specific options
|
908 |
+
$sap->add_section(
|
909 |
+
'rtb-settings',
|
910 |
+
array(
|
911 |
+
'id' => 'rtb-restrictions-location-select',
|
912 |
+
'title' => __( 'Select Seat Restrictions Location', 'restaurant-reservations' ),
|
913 |
+
'tab' => 'rtb-advanced-tab',
|
914 |
+
'rank' => 11,
|
915 |
+
)
|
916 |
+
);
|
917 |
+
|
918 |
+
$sap->add_setting(
|
919 |
+
'rtb-settings',
|
920 |
+
'rtb-restrictions-location-select',
|
921 |
+
'select',
|
922 |
+
array(
|
923 |
+
'id' => 'location-select',
|
924 |
+
'title' => __( 'Seat Restrictions Location', 'restaurant-reservations' ),
|
925 |
+
'description' => __( 'Select which location the restrictions will apply to. If a specific location doesn\'t have restrictions set, then the global total number will be used as a fall-back.', 'restaurant-reservations' ),
|
926 |
+
'blank_option' => false,
|
927 |
+
'options' => $location_options,
|
928 |
+
)
|
929 |
+
);
|
930 |
+
|
931 |
+
$max_reservation_options = array();
|
932 |
+
$max_reservations_upper_limit = apply_filters( 'rtb-max-reservations-upper-limit', 100 );
|
933 |
+
|
934 |
+
for ( $i = 1; $i <= $max_reservations_upper_limit; $i++ ) {
|
935 |
+
|
936 |
+
$max_reservation_options[$i] = $i;
|
937 |
+
}
|
938 |
+
|
939 |
+
$max_people_options = array();
|
940 |
+
$max_people_upper_limit = apply_filters( 'rtb-max-people-upper-limit', 400 );
|
941 |
+
|
942 |
+
for ( $i = 1; $i <= $max_people_upper_limit; $i++ ) {
|
943 |
+
|
944 |
+
$max_people_options[$i] = $i;
|
945 |
+
}
|
946 |
+
|
947 |
+
foreach ( $terms as $term ) {
|
948 |
+
|
949 |
+
$sap->add_setting(
|
950 |
+
'rtb-settings',
|
951 |
+
'rtb-seat-assignments',
|
952 |
+
'select',
|
953 |
+
array(
|
954 |
+
'id' => $term->slug . '-rtb-max-tables-count',
|
955 |
+
'title' => __( 'Max Reservations', 'restaurant-reservations' ),
|
956 |
+
'description' => __( 'How many reservations, if enabled, should be allowed at the same time at this location? Set dining block length to change how long a meal typically lasts.', 'restaurant-reservations' ),
|
957 |
+
'options' => $max_reservation_options,
|
958 |
+
'conditional_on' => 'location-select',
|
959 |
+
'conditional_on_value' => $term->slug,
|
960 |
+
)
|
961 |
+
);
|
962 |
+
|
963 |
+
$sap->add_setting(
|
964 |
+
'rtb-settings',
|
965 |
+
'rtb-seat-assignments',
|
966 |
+
'select',
|
967 |
+
array(
|
968 |
+
'id' => $term->slug . '-rtb-max-people-count',
|
969 |
+
'title' => __( 'Max People', 'restaurant-reservations' ),
|
970 |
+
'description' => __( 'How many people, if enabled, should be allowed to be present at this restaurant location at the same time? Set dining block length to change how long a meal typically lasts. May not work correctly if max reservations is set.', 'restaurant-reservations' ),
|
971 |
+
'options' => $max_people_options,
|
972 |
+
'conditional_on' => 'location-select',
|
973 |
+
'conditional_on_value' => $term->slug,
|
974 |
+
)
|
975 |
+
);
|
976 |
+
}
|
977 |
+
|
978 |
+
return $sap;
|
979 |
+
}
|
980 |
+
|
981 |
+
/**
|
982 |
+
* Blank out the location setting, so that it's always set to 'Global'
|
983 |
+
* on page load, except for immediately after saving a setting.
|
984 |
+
*
|
985 |
+
* @since 2.3.6
|
986 |
+
*/
|
987 |
+
public function remove_location_select_setting() {
|
988 |
+
global $rtb_controller;
|
989 |
+
|
990 |
+
$rtb_controller->settings->set_setting( 'location-select', null );
|
991 |
+
|
992 |
+
$rtb_controller->settings->save_settings();
|
993 |
+
}
|
994 |
+
|
995 |
+
/**
|
996 |
+
* Returns true if locations have been created, false otherwise
|
997 |
+
*
|
998 |
+
* @since 2.3.6
|
999 |
+
*/
|
1000 |
+
public function do_locations_exist() {
|
1001 |
+
|
1002 |
+
$args = array(
|
1003 |
+
'taxonomy' => $this->location_taxonomy,
|
1004 |
+
'hide_empty' => false,
|
1005 |
+
);
|
1006 |
+
|
1007 |
+
$terms = get_terms( $args );
|
1008 |
+
|
1009 |
+
return ( ! empty( $terms ) and ! is_wp_error( $terms ) );
|
1010 |
+
}
|
1011 |
+
|
1012 |
+
function pass_location_data_to_js( $shortcode_arg ) {
|
1013 |
+
global $rtb_controller;
|
1014 |
+
|
1015 |
+
$is_location_specific_shortcode = false;
|
1016 |
+
|
1017 |
+
$locations = [
|
1018 |
+
// false means no slug
|
1019 |
+
'global' => false
|
1020 |
+
];
|
1021 |
+
|
1022 |
+
// Specific location
|
1023 |
+
$location_term = ! empty( $shortcode_arg['location'] ) ? get_term( $shortcode_arg['location'] ) : false;
|
1024 |
+
if( $location_term and ! is_wp_error( $location_term ) ) {
|
1025 |
+
$locations[$location_term->term_id] = $location_term->slug;
|
1026 |
+
|
1027 |
+
$is_location_specific_shortcode = true;
|
1028 |
+
}
|
1029 |
+
// All locations
|
1030 |
+
else {
|
1031 |
+
$terms = get_terms(
|
1032 |
+
array(
|
1033 |
+
'taxonomy' => $this->location_taxonomy,
|
1034 |
+
'hide_empty' => false,
|
1035 |
+
)
|
1036 |
+
);
|
1037 |
+
|
1038 |
+
$options = array();
|
1039 |
+
foreach( $terms as $term ) {
|
1040 |
+
$archived = get_term_meta( $term->term_id, 'rtb_location_removed', true );
|
1041 |
+
if ( ! $archived ) {
|
1042 |
+
$locations[$term->term_id] = $term->slug;
|
1043 |
+
}
|
1044 |
+
}
|
1045 |
+
}
|
1046 |
+
|
1047 |
+
$data = [];
|
1048 |
+
foreach ($locations as $idx => $slug) {
|
1049 |
+
$data[$idx]['disable_dates'] = rtb_get_datepicker_rules( $slug );
|
1050 |
+
$data[$idx]['schedule_open'] = $rtb_controller->settings->get_setting( 'schedule-open', $slug );
|
1051 |
+
$data[$idx]['schedule_closed'] = $rtb_controller->settings->get_setting( 'schedule-closed', $slug );
|
1052 |
+
$data[$idx]['enable_max_reservations'] = is_admin() && current_user_can( 'manage_bookings' ) ? false : $rtb_controller->settings->get_setting( 'rtb-enable-max-tables', $slug );
|
1053 |
+
$data[$idx]['max_people'] = is_admin() && current_user_can( 'manage_bookings' ) ? 100 : $rtb_controller->settings->get_setting( 'rtb-max-people-count', $slug );
|
1054 |
+
|
1055 |
+
// Also override initial instance of rtb_pickadate to apply location data
|
1056 |
+
if( $is_location_specific_shortcode ) {
|
1057 |
+
add_filter( 'rtb_pickadate_args', function( $variable_list ) use ( $data, $idx ) {
|
1058 |
+
|
1059 |
+
return array_merge( $variable_list, $data[$idx] );
|
1060 |
+
});
|
1061 |
+
}
|
1062 |
+
}
|
1063 |
+
|
1064 |
+
wp_localize_script(
|
1065 |
+
'rtb-booking-form',
|
1066 |
+
'rtb_location_data',
|
1067 |
+
apply_filters(
|
1068 |
+
'rtb_location_data',
|
1069 |
+
$data
|
1070 |
+
)
|
1071 |
+
);
|
1072 |
+
|
1073 |
+
return $shortcode_arg;
|
1074 |
+
}
|
1075 |
+
|
1076 |
+
/**
|
1077 |
+
* Add location information in Payment Summary list on checkout
|
1078 |
+
* @param array $summary_data [label -> data] list
|
1079 |
+
* @param rtbBooking $booking current booking
|
1080 |
+
*/
|
1081 |
+
public function add_payment_summary( $summary_data, $booking )
|
1082 |
+
{
|
1083 |
+
global $rtb_controller;
|
1084 |
+
|
1085 |
+
if( property_exists( $booking, 'location' ) && ! empty( $booking->location ) ) {
|
1086 |
+
$loc_term = get_term( $booking->location, $rtb_controller->locations->location_taxonomy, OBJECT );
|
1087 |
+
if ( !$loc_term || is_a( $loc_term, 'WP_Error' ) ) {
|
1088 |
+
unset( $summary_data['location'] );
|
1089 |
+
}
|
1090 |
+
else {
|
1091 |
+
$summary_data['location'] = $loc_term->name;
|
1092 |
+
}
|
1093 |
+
}
|
1094 |
+
|
1095 |
+
return $summary_data;
|
1096 |
+
}
|
1097 |
+
}
|
1098 |
+
}
|
includes/Notification.Email.class.php
CHANGED
@@ -1,308 +1,308 @@
|
|
1 |
-
<?php
|
2 |
-
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
-
|
4 |
-
if ( !class_exists( 'rtbNotificationEmail' ) ) {
|
5 |
-
/**
|
6 |
-
* Class to handle an email notification for Restaurant Reservations
|
7 |
-
*
|
8 |
-
* This class extends rtbNotification and must implement the following methods:
|
9 |
-
* prepare_notification() - set up and validate data
|
10 |
-
* send_notification()
|
11 |
-
*
|
12 |
-
* @since 0.0.1
|
13 |
-
*/
|
14 |
-
class rtbNotificationEmail extends rtbNotification {
|
15 |
-
|
16 |
-
/**
|
17 |
-
* Recipient email
|
18 |
-
* @since 0.0.1
|
19 |
-
*/
|
20 |
-
public $to_email;
|
21 |
-
|
22 |
-
/**
|
23 |
-
* From email
|
24 |
-
* @since 0.0.1
|
25 |
-
*/
|
26 |
-
public $from_email;
|
27 |
-
|
28 |
-
/**
|
29 |
-
* From name
|
30 |
-
* @since 0.0.1
|
31 |
-
*/
|
32 |
-
public $from_name;
|
33 |
-
|
34 |
-
/**
|
35 |
-
* Email subject
|
36 |
-
* @since 0.0.1
|
37 |
-
*/
|
38 |
-
public $subject;
|
39 |
-
|
40 |
-
/**
|
41 |
-
* Email message body
|
42 |
-
* @since 0.0.1
|
43 |
-
*/
|
44 |
-
public $message;
|
45 |
-
|
46 |
-
/**
|
47 |
-
* Email headers
|
48 |
-
* @since 0.0.1
|
49 |
-
*/
|
50 |
-
public $headers;
|
51 |
-
|
52 |
-
/**
|
53 |
-
* Inidividual booking related to this notification, if applicable
|
54 |
-
* @since 0.0.1
|
55 |
-
*/
|
56 |
-
public $booking;
|
57 |
-
|
58 |
-
/**
|
59 |
-
* Prepare and validate notification data
|
60 |
-
*
|
61 |
-
* @return boolean if the data is valid and ready for transport
|
62 |
-
* @since 0.0.1
|
63 |
-
*/
|
64 |
-
public function prepare_notification() {
|
65 |
-
|
66 |
-
$this->set_to_email();
|
67 |
-
$this->set_from_email();
|
68 |
-
$this->set_subject();
|
69 |
-
$this->set_headers();
|
70 |
-
$this->set_message();
|
71 |
-
|
72 |
-
// Return false if we're missing any of the required information
|
73 |
-
if ( empty( $this->to_email) ||
|
74 |
-
empty( $this->from_email) ||
|
75 |
-
empty( $this->from_name) ||
|
76 |
-
empty( $this->subject) ||
|
77 |
-
empty( $this->headers) ||
|
78 |
-
empty( $this->message) ) {
|
79 |
-
return false;
|
80 |
-
}
|
81 |
-
|
82 |
-
return true;
|
83 |
-
}
|
84 |
-
|
85 |
-
/**
|
86 |
-
* Set to email
|
87 |
-
* @since 0.0.1
|
88 |
-
*/
|
89 |
-
public function set_to_email() {
|
90 |
-
global $rtb_controller;
|
91 |
-
|
92 |
-
if ( ! empty( $this->to_email ) ) {
|
93 |
-
|
94 |
-
$to_email = $this->to_email;
|
95 |
-
}
|
96 |
-
elseif ( $this->target == 'user' ) {
|
97 |
-
|
98 |
-
$to_email = empty( $this->booking->email ) ? null : $this->booking->email;
|
99 |
-
}
|
100 |
-
else {
|
101 |
-
|
102 |
-
$to_email = $rtb_controller->settings->get_setting( 'admin-email-address' );
|
103 |
-
}
|
104 |
-
|
105 |
-
$this->to_email = apply_filters( 'rtb_notification_email_to_email', $to_email, $this );
|
106 |
-
|
107 |
-
}
|
108 |
-
|
109 |
-
/**
|
110 |
-
* Set from email
|
111 |
-
* @since 0.0.1
|
112 |
-
*/
|
113 |
-
public function set_from_email() {
|
114 |
-
global $rtb_controller;
|
115 |
-
|
116 |
-
if ( ! empty( $this->from_email ) and ! empty( $this->from_name ) ) {
|
117 |
-
|
118 |
-
$from_email = $this->from_email;
|
119 |
-
$from_name = $this->from_name;
|
120 |
-
}
|
121 |
-
elseif ( $this->target == 'user' ) {
|
122 |
-
|
123 |
-
$from_email = $rtb_controller->settings->get_setting( 'reply-to-address' );
|
124 |
-
$from_name = $rtb_controller->settings->get_setting( 'reply-to-name' );
|
125 |
-
}
|
126 |
-
else {
|
127 |
-
|
128 |
-
$from_email = $this->booking->email;
|
129 |
-
$from_name = $this->booking->name;
|
130 |
-
}
|
131 |
-
|
132 |
-
$this->from_email = apply_filters( 'rtb_notification_email_from_email', $from_email, $this );
|
133 |
-
$this->from_name = apply_filters( 'rtb_notification_email_from_name', $from_name, $this );
|
134 |
-
|
135 |
-
}
|
136 |
-
|
137 |
-
/**
|
138 |
-
* Set email subject
|
139 |
-
* @since 0.0.1
|
140 |
-
*/
|
141 |
-
public function set_subject() {
|
142 |
-
|
143 |
-
global $rtb_controller;
|
144 |
-
|
145 |
-
if( $this->event == 'new_submission' ) {
|
146 |
-
if ( $this->target == 'admin' ) {
|
147 |
-
$subject = $rtb_controller->settings->get_setting( 'subject-booking-admin' );
|
148 |
-
} elseif ( $this->target == 'user' ) {
|
149 |
-
$subject = $rtb_controller->settings->get_setting( 'subject-booking-user' );
|
150 |
-
}
|
151 |
-
|
152 |
-
} elseif ( $this->event == 'rtb_confirmed_booking' ) {
|
153 |
-
$subject = $rtb_controller->settings->get_setting( 'subject-booking-confirmed-admin' );
|
154 |
-
|
155 |
-
}elseif ( $this->event == 'pending_to_confirmed' ) {
|
156 |
-
$subject = $rtb_controller->settings->get_setting( 'subject-confirmed-user' );
|
157 |
-
|
158 |
-
} elseif ( $this->event == 'pending_to_closed' ) {
|
159 |
-
$subject = $rtb_controller->settings->get_setting( 'subject-rejected-user' );
|
160 |
-
|
161 |
-
} elseif ( $this->event == 'booking_cancelled' ) {
|
162 |
-
if ( $this->target == 'admin' ) {
|
163 |
-
$subject = $rtb_controller->settings->get_setting( 'subject-booking-cancelled-admin' );
|
164 |
-
} elseif ( $this->target == 'user' ) {
|
165 |
-
$subject = $rtb_controller->settings->get_setting( 'subject-booking-cancelled-user' );
|
166 |
-
}
|
167 |
-
|
168 |
-
} elseif ( $this->event == 'late_user' ) {
|
169 |
-
$subject = $rtb_controller->settings->get_setting( 'subject-late-user' );
|
170 |
-
|
171 |
-
} elseif ( $this->event == 'reminder' ) {
|
172 |
-
$subject = $rtb_controller->settings->get_setting( 'subject-reminder-user' );
|
173 |
-
|
174 |
-
// Use a subject that's been appended manually if available
|
175 |
-
} else {
|
176 |
-
$subject = empty( $this->subject ) ? '' : $this->subject;
|
177 |
-
}
|
178 |
-
|
179 |
-
$this->subject = $this->process_subject_template( apply_filters( 'rtb_notification_email_subject', $subject, $this ) );
|
180 |
-
|
181 |
-
}
|
182 |
-
|
183 |
-
/**
|
184 |
-
* Set email headers
|
185 |
-
* @since 0.0.1
|
186 |
-
*/
|
187 |
-
public function set_headers( $headers = null ) {
|
188 |
-
|
189 |
-
global $rtb_controller;
|
190 |
-
|
191 |
-
$from_email = apply_filters( 'rtb_notification_email_header_from_email', $rtb_controller->settings->get_setting( 'from-email-address' ) );
|
192 |
-
|
193 |
-
$headers = "From: =?UTF-8?Q?" .
|
194 |
-
quoted_printable_encode(
|
195 |
-
html_entity_decode(
|
196 |
-
$rtb_controller->settings->get_setting( 'reply-to-name' ),
|
197 |
-
ENT_QUOTES,
|
198 |
-
'UTF-8'
|
199 |
-
)
|
200 |
-
) .
|
201 |
-
"?= <" . $from_email . ">\r\n";
|
202 |
-
|
203 |
-
$headers .= "Reply-To: =?UTF-8?Q?" .
|
204 |
-
quoted_printable_encode(
|
205 |
-
html_entity_decode(
|
206 |
-
$this->from_name,
|
207 |
-
ENT_QUOTES,
|
208 |
-
'UTF-8'
|
209 |
-
)
|
210 |
-
) .
|
211 |
-
"?= <" . $this->from_email . ">\r\n";
|
212 |
-
|
213 |
-
$headers .= "Content-Type: text/html; charset=utf-8\r\n";
|
214 |
-
|
215 |
-
$this->headers = apply_filters( 'rtb_notification_email_headers', $headers, $this );
|
216 |
-
|
217 |
-
}
|
218 |
-
|
219 |
-
/**
|
220 |
-
* Set email message body
|
221 |
-
* @since 0.0.1
|
222 |
-
*/
|
223 |
-
public function set_message() {
|
224 |
-
|
225 |
-
if ( $this->event == 'new_submission' ) {
|
226 |
-
if ( $this->target == 'user' ) {
|
227 |
-
$template = $this->get_template( 'template-booking-user' );
|
228 |
-
} elseif ( $this->target == 'admin' ) {
|
229 |
-
$template = $this->get_template( 'template-booking-admin' );
|
230 |
-
}
|
231 |
-
|
232 |
-
} elseif ( $this->event == 'rtb_confirmed_booking' ) {
|
233 |
-
if ( $this->target == 'admin' ) {
|
234 |
-
$template = $this->get_template( 'template-booking-confirmed-admin' );
|
235 |
-
}
|
236 |
-
|
237 |
-
} elseif ( $this->event == 'pending_to_confirmed' ) {
|
238 |
-
if ( $this->target == 'user' ) {
|
239 |
-
$template = $this->get_template( 'template-confirmed-user' );
|
240 |
-
}
|
241 |
-
|
242 |
-
} elseif ( $this->event == 'pending_to_closed' ) {
|
243 |
-
if ( $this->target == 'user' ) {
|
244 |
-
$template = $this->get_template( 'template-rejected-user' );
|
245 |
-
}
|
246 |
-
|
247 |
-
} elseif ( $this->event == 'booking_cancelled' ) {
|
248 |
-
if ( $this->target == 'user' ) {
|
249 |
-
$template = $this->get_template( 'template-booking-cancelled-user' );
|
250 |
-
} elseif ( $this->target == 'admin' ) {
|
251 |
-
$template = $this->get_template( 'template-booking-cancelled-admin' );
|
252 |
-
}
|
253 |
-
|
254 |
-
} elseif ( $this->event == 'late_user' ) {
|
255 |
-
if ( $this->target == 'user' ) {
|
256 |
-
$template = $this->get_template( 'template-late-user' );
|
257 |
-
}
|
258 |
-
|
259 |
-
} elseif ( $this->event == 'reminder' ) {
|
260 |
-
if ( $this->target == 'user' ) {
|
261 |
-
$template = $this->get_template( 'template-reminder-user' );
|
262 |
-
}
|
263 |
-
|
264 |
-
// Use a message that's been appended manually if available
|
265 |
-
} else {
|
266 |
-
$template = empty( $this->message ) ? '' : $this->message;
|
267 |
-
}
|
268 |
-
|
269 |
-
$template = apply_filters( 'rtb_notification_email_template', $template, $this );
|
270 |
-
|
271 |
-
if ( ! empty( $this->manual_message ) ) {
|
272 |
-
$this->message = $this->manual_message;
|
273 |
-
}
|
274 |
-
elseif ( empty( $template ) ) {
|
275 |
-
$this->message = '';
|
276 |
-
} else {
|
277 |
-
$this->message = wpautop( $this->process_template( $template ) );
|
278 |
-
}
|
279 |
-
|
280 |
-
}
|
281 |
-
|
282 |
-
/**
|
283 |
-
* Process template tags for email subjects
|
284 |
-
* @since 0.0.1
|
285 |
-
*/
|
286 |
-
public function process_subject_template( $subject ) {
|
287 |
-
|
288 |
-
$template_tags = array(
|
289 |
-
'{user_name}' => ! empty( $this->booking->name ) ? $this->booking->name : '',
|
290 |
-
'{party}' => ! empty( $this->booking->party ) ? $this->booking->party : '',
|
291 |
-
'{date}' => ! empty( $this->booking->date ) ? $this->booking->format_date( $this->booking->date ) : '',
|
292 |
-
);
|
293 |
-
|
294 |
-
$template_tags = apply_filters( 'rtb_notification_email_subject_template_tags', $template_tags, $this );
|
295 |
-
|
296 |
-
return str_replace( array_keys( $template_tags ), array_values( $template_tags ), $subject );
|
297 |
-
|
298 |
-
}
|
299 |
-
|
300 |
-
/**
|
301 |
-
* Send notification
|
302 |
-
* @since 0.0.1
|
303 |
-
*/
|
304 |
-
public function send_notification() {
|
305 |
-
return wp_mail( $this->to_email, $this->subject, $this->message, $this->headers, apply_filters( 'rtb_notification_email_attachments', array(), $this ) );
|
306 |
-
}
|
307 |
-
}
|
308 |
-
} // endif;
|
1 |
+
<?php
|
2 |
+
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
+
|
4 |
+
if ( !class_exists( 'rtbNotificationEmail' ) ) {
|
5 |
+
/**
|
6 |
+
* Class to handle an email notification for Restaurant Reservations
|
7 |
+
*
|
8 |
+
* This class extends rtbNotification and must implement the following methods:
|
9 |
+
* prepare_notification() - set up and validate data
|
10 |
+
* send_notification()
|
11 |
+
*
|
12 |
+
* @since 0.0.1
|
13 |
+
*/
|
14 |
+
class rtbNotificationEmail extends rtbNotification {
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Recipient email
|
18 |
+
* @since 0.0.1
|
19 |
+
*/
|
20 |
+
public $to_email;
|
21 |
+
|
22 |
+
/**
|
23 |
+
* From email
|
24 |
+
* @since 0.0.1
|
25 |
+
*/
|
26 |
+
public $from_email;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* From name
|
30 |
+
* @since 0.0.1
|
31 |
+
*/
|
32 |
+
public $from_name;
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Email subject
|
36 |
+
* @since 0.0.1
|
37 |
+
*/
|
38 |
+
public $subject;
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Email message body
|
42 |
+
* @since 0.0.1
|
43 |
+
*/
|
44 |
+
public $message;
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Email headers
|
48 |
+
* @since 0.0.1
|
49 |
+
*/
|
50 |
+
public $headers;
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Inidividual booking related to this notification, if applicable
|
54 |
+
* @since 0.0.1
|
55 |
+
*/
|
56 |
+
public $booking;
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Prepare and validate notification data
|
60 |
+
*
|
61 |
+
* @return boolean if the data is valid and ready for transport
|
62 |
+
* @since 0.0.1
|
63 |
+
*/
|
64 |
+
public function prepare_notification() {
|
65 |
+
|
66 |
+
$this->set_to_email();
|
67 |
+
$this->set_from_email();
|
68 |
+
$this->set_subject();
|
69 |
+
$this->set_headers();
|
70 |
+
$this->set_message();
|
71 |
+
|
72 |
+
// Return false if we're missing any of the required information
|
73 |
+
if ( empty( $this->to_email) ||
|
74 |
+
empty( $this->from_email) ||
|
75 |
+
empty( $this->from_name) ||
|
76 |
+
empty( $this->subject) ||
|
77 |
+
empty( $this->headers) ||
|
78 |
+
empty( $this->message) ) {
|
79 |
+
return false;
|
80 |
+
}
|
81 |
+
|
82 |
+
return true;
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Set to email
|
87 |
+
* @since 0.0.1
|
88 |
+
*/
|
89 |
+
public function set_to_email() {
|
90 |
+
global $rtb_controller;
|
91 |
+
|
92 |
+
if ( ! empty( $this->to_email ) ) {
|
93 |
+
|
94 |
+
$to_email = $this->to_email;
|
95 |
+
}
|
96 |
+
elseif ( $this->target == 'user' ) {
|
97 |
+
|
98 |
+
$to_email = empty( $this->booking->email ) ? null : $this->booking->email;
|
99 |
+
}
|
100 |
+
else {
|
101 |
+
|
102 |
+
$to_email = $rtb_controller->settings->get_setting( 'admin-email-address' );
|
103 |
+
}
|
104 |
+
|
105 |
+
$this->to_email = apply_filters( 'rtb_notification_email_to_email', $to_email, $this );
|
106 |
+
|
107 |
+
}
|
108 |
+
|
109 |
+
/**
|
110 |
+
* Set from email
|
111 |
+
* @since 0.0.1
|
112 |
+
*/
|
113 |
+
public function set_from_email() {
|
114 |
+
global $rtb_controller;
|
115 |
+
|
116 |
+
if ( ! empty( $this->from_email ) and ! empty( $this->from_name ) ) {
|
117 |
+
|
118 |
+
$from_email = $this->from_email;
|
119 |
+
$from_name = $this->from_name;
|
120 |
+
}
|
121 |
+
elseif ( $this->target == 'user' ) {
|
122 |
+
|
123 |
+
$from_email = $rtb_controller->settings->get_setting( 'reply-to-address' );
|
124 |
+
$from_name = $rtb_controller->settings->get_setting( 'reply-to-name' );
|
125 |
+
}
|
126 |
+
else {
|
127 |
+
|
128 |
+
$from_email = $this->booking->email;
|
129 |
+
$from_name = $this->booking->name;
|
130 |
+
}
|
131 |
+
|
132 |
+
$this->from_email = apply_filters( 'rtb_notification_email_from_email', $from_email, $this );
|
133 |
+
$this->from_name = apply_filters( 'rtb_notification_email_from_name', $from_name, $this );
|
134 |
+
|
135 |
+
}
|
136 |
+
|
137 |
+
/**
|
138 |
+
* Set email subject
|
139 |
+
* @since 0.0.1
|
140 |
+
*/
|
141 |
+
public function set_subject() {
|
142 |
+
|
143 |
+
global $rtb_controller;
|
144 |
+
|
145 |
+
if( $this->event == 'new_submission' ) {
|
146 |
+
if ( $this->target == 'admin' ) {
|
147 |
+
$subject = $rtb_controller->settings->get_setting( 'subject-booking-admin' );
|
148 |
+
} elseif ( $this->target == 'user' ) {
|
149 |
+
$subject = $rtb_controller->settings->get_setting( 'subject-booking-user' );
|
150 |
+
}
|
151 |
+
|
152 |
+
} elseif ( $this->event == 'rtb_confirmed_booking' ) {
|
153 |
+
$subject = $rtb_controller->settings->get_setting( 'subject-booking-confirmed-admin' );
|
154 |
+
|
155 |
+
}elseif ( $this->event == 'pending_to_confirmed' ) {
|
156 |
+
$subject = $rtb_controller->settings->get_setting( 'subject-confirmed-user' );
|
157 |
+
|
158 |
+
} elseif ( $this->event == 'pending_to_closed' ) {
|
159 |
+
$subject = $rtb_controller->settings->get_setting( 'subject-rejected-user' );
|
160 |
+
|
161 |
+
} elseif ( $this->event == 'booking_cancelled' ) {
|
162 |
+
if ( $this->target == 'admin' ) {
|
163 |
+
$subject = $rtb_controller->settings->get_setting( 'subject-booking-cancelled-admin' );
|
164 |
+
} elseif ( $this->target == 'user' ) {
|
165 |
+
$subject = $rtb_controller->settings->get_setting( 'subject-booking-cancelled-user' );
|
166 |
+
}
|
167 |
+
|
168 |
+
} elseif ( $this->event == 'late_user' ) {
|
169 |
+
$subject = $rtb_controller->settings->get_setting( 'subject-late-user' );
|
170 |
+
|
171 |
+
} elseif ( $this->event == 'reminder' ) {
|
172 |
+
$subject = $rtb_controller->settings->get_setting( 'subject-reminder-user' );
|
173 |
+
|
174 |
+
// Use a subject that's been appended manually if available
|
175 |
+
} else {
|
176 |
+
$subject = empty( $this->subject ) ? '' : $this->subject;
|
177 |
+
}
|
178 |
+
|
179 |
+
$this->subject = $this->process_subject_template( apply_filters( 'rtb_notification_email_subject', $subject, $this ) );
|
180 |
+
|
181 |
+
}
|
182 |
+
|
183 |
+
/**
|
184 |
+
* Set email headers
|
185 |
+
* @since 0.0.1
|
186 |
+
*/
|
187 |
+
public function set_headers( $headers = null ) {
|
188 |
+
|
189 |
+
global $rtb_controller;
|
190 |
+
|
191 |
+
$from_email = apply_filters( 'rtb_notification_email_header_from_email', $rtb_controller->settings->get_setting( 'from-email-address' ) );
|
192 |
+
|
193 |
+
$headers = "From: =?UTF-8?Q?" .
|
194 |
+
quoted_printable_encode(
|
195 |
+
html_entity_decode(
|
196 |
+
$rtb_controller->settings->get_setting( 'reply-to-name' ),
|
197 |
+
ENT_QUOTES,
|
198 |
+
'UTF-8'
|
199 |
+
)
|
200 |
+
) .
|
201 |
+
"?= <" . $from_email . ">\r\n";
|
202 |
+
|
203 |
+
$headers .= "Reply-To: =?UTF-8?Q?" .
|
204 |
+
quoted_printable_encode(
|
205 |
+
html_entity_decode(
|
206 |
+
$this->from_name,
|
207 |
+
ENT_QUOTES,
|
208 |
+
'UTF-8'
|
209 |
+
)
|
210 |
+
) .
|
211 |
+
"?= <" . $this->from_email . ">\r\n";
|
212 |
+
|
213 |
+
$headers .= "Content-Type: text/html; charset=utf-8\r\n";
|
214 |
+
|
215 |
+
$this->headers = apply_filters( 'rtb_notification_email_headers', $headers, $this );
|
216 |
+
|
217 |
+
}
|
218 |
+
|
219 |
+
/**
|
220 |
+
* Set email message body
|
221 |
+
* @since 0.0.1
|
222 |
+
*/
|
223 |
+
public function set_message() {
|
224 |
+
|
225 |
+
if ( $this->event == 'new_submission' ) {
|
226 |
+
if ( $this->target == 'user' ) {
|
227 |
+
$template = $this->get_template( 'template-booking-user' );
|
228 |
+
} elseif ( $this->target == 'admin' ) {
|
229 |
+
$template = $this->get_template( 'template-booking-admin' );
|
230 |
+
}
|
231 |
+
|
232 |
+
} elseif ( $this->event == 'rtb_confirmed_booking' ) {
|
233 |
+
if ( $this->target == 'admin' ) {
|
234 |
+
$template = $this->get_template( 'template-booking-confirmed-admin' );
|
235 |
+
}
|
236 |
+
|
237 |
+
} elseif ( $this->event == 'pending_to_confirmed' ) {
|
238 |
+
if ( $this->target == 'user' ) {
|
239 |
+
$template = $this->get_template( 'template-confirmed-user' );
|
240 |
+
}
|
241 |
+
|
242 |
+
} elseif ( $this->event == 'pending_to_closed' ) {
|
243 |
+
if ( $this->target == 'user' ) {
|
244 |
+
$template = $this->get_template( 'template-rejected-user' );
|
245 |
+
}
|
246 |
+
|
247 |
+
} elseif ( $this->event == 'booking_cancelled' ) {
|
248 |
+
if ( $this->target == 'user' ) {
|
249 |
+
$template = $this->get_template( 'template-booking-cancelled-user' );
|
250 |
+
} elseif ( $this->target == 'admin' ) {
|
251 |
+
$template = $this->get_template( 'template-booking-cancelled-admin' );
|
252 |
+
}
|
253 |
+
|
254 |
+
} elseif ( $this->event == 'late_user' ) {
|
255 |
+
if ( $this->target == 'user' ) {
|
256 |
+
$template = $this->get_template( 'template-late-user' );
|
257 |
+
}
|
258 |
+
|
259 |
+
} elseif ( $this->event == 'reminder' ) {
|
260 |
+
if ( $this->target == 'user' ) {
|
261 |
+
$template = $this->get_template( 'template-reminder-user' );
|
262 |
+
}
|
263 |
+
|
264 |
+
// Use a message that's been appended manually if available
|
265 |
+
} else {
|
266 |
+
$template = empty( $this->message ) ? '' : $this->message;
|
267 |
+
}
|
268 |
+
|
269 |
+
$template = apply_filters( 'rtb_notification_email_template', $template, $this );
|
270 |
+
|
271 |
+
if ( ! empty( $this->manual_message ) ) {
|
272 |
+
$this->message = $this->manual_message;
|
273 |
+
}
|
274 |
+
elseif ( empty( $template ) ) {
|
275 |
+
$this->message = '';
|
276 |
+
} else {
|
277 |
+
$this->message = wpautop( $this->process_template( $template ) );
|
278 |
+
}
|
279 |
+
|
280 |
+
}
|
281 |
+
|
282 |
+
/**
|
283 |
+
* Process template tags for email subjects
|
284 |
+
* @since 0.0.1
|
285 |
+
*/
|
286 |
+
public function process_subject_template( $subject ) {
|
287 |
+
|
288 |
+
$template_tags = array(
|
289 |
+
'{user_name}' => ! empty( $this->booking->name ) ? $this->booking->name : '',
|
290 |
+
'{party}' => ! empty( $this->booking->party ) ? $this->booking->party : '',
|
291 |
+
'{date}' => ! empty( $this->booking->date ) ? $this->booking->format_date( $this->booking->date ) : '',
|
292 |
+
);
|
293 |
+
|
294 |
+
$template_tags = apply_filters( 'rtb_notification_email_subject_template_tags', $template_tags, $this );
|
295 |
+
|
296 |
+
return str_replace( array_keys( $template_tags ), array_values( $template_tags ), $subject );
|
297 |
+
|
298 |
+
}
|
299 |
+
|
300 |
+
/**
|
301 |
+
* Send notification
|
302 |
+
* @since 0.0.1
|
303 |
+
*/
|
304 |
+
public function send_notification() {
|
305 |
+
return wp_mail( $this->to_email, $this->subject, $this->message, $this->headers, apply_filters( 'rtb_notification_email_attachments', array(), $this ) );
|
306 |
+
}
|
307 |
+
}
|
308 |
+
} // endif;
|
includes/Notification.SMS.class.php
CHANGED
@@ -1,151 +1,151 @@
|
|
1 |
-
<?php
|
2 |
-
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
-
|
4 |
-
if ( !class_exists( 'rtbNotificationSMS' ) ) {
|
5 |
-
/**
|
6 |
-
* Class to handle an SMS notification for Restaurant Reservations
|
7 |
-
*
|
8 |
-
* This class extends rtbNotification and must implement the following methods:
|
9 |
-
* prepare_notification() - set up and validate data
|
10 |
-
* send_notification()
|
11 |
-
*
|
12 |
-
* @since 2.1.0
|
13 |
-
*/
|
14 |
-
class rtbNotificationSMS extends rtbNotification {
|
15 |
-
|
16 |
-
/**
|
17 |
-
* Recipient phone number
|
18 |
-
* @since 2.1.0
|
19 |
-
*/
|
20 |
-
public $phone_number;
|
21 |
-
|
22 |
-
/**
|
23 |
-
* Text message body
|
24 |
-
* @since 2.1.0
|
25 |
-
*/
|
26 |
-
public $message;
|
27 |
-
|
28 |
-
/**
|
29 |
-
* The license key received for RTB Ultimate
|
30 |
-
* @since 2.1.0
|
31 |
-
*/
|
32 |
-
public $license_key;
|
33 |
-
|
34 |
-
/**
|
35 |
-
* Email used for purchase, to validate message sending
|
36 |
-
* @since 2.1.0
|
37 |
-
*/
|
38 |
-
public $purchase_email;
|
39 |
-
|
40 |
-
|
41 |
-
/**
|
42 |
-
* Prepare and validate notification data
|
43 |
-
*
|
44 |
-
* @return boolean if the data is valid and ready for transport
|
45 |
-
* @since 2.1.0
|
46 |
-
*/
|
47 |
-
public function prepare_notification() {
|
48 |
-
|
49 |
-
$this->set_phone_number();
|
50 |
-
$this->set_message();
|
51 |
-
$this->set_license_key();
|
52 |
-
$this->set_purchase_email();
|
53 |
-
|
54 |
-
// Return false if we're missing any of the required information
|
55 |
-
if ( empty( $this->phone_number) ||
|
56 |
-
empty( $this->message) ||
|
57 |
-
empty( $this->license_key) ||
|
58 |
-
empty( $this->purchase_email) ) {
|
59 |
-
return false;
|
60 |
-
}
|
61 |
-
|
62 |
-
return true;
|
63 |
-
}
|
64 |
-
|
65 |
-
/**
|
66 |
-
* Set phone number
|
67 |
-
* @since 2.1.0
|
68 |
-
*/
|
69 |
-
public function set_phone_number() {
|
70 |
-
|
71 |
-
$phone_number = $this->booking->phone;
|
72 |
-
|
73 |
-
$this->phone_number = apply_filters( 'rtb_notification_sms_phone_number', $phone_number, $this );
|
74 |
-
|
75 |
-
}
|
76 |
-
|
77 |
-
/**
|
78 |
-
* Set text message body
|
79 |
-
* @since 2.1.0
|
80 |
-
*/
|
81 |
-
public function set_message() {
|
82 |
-
|
83 |
-
if ( $this->event == 'late_user' ) {
|
84 |
-
if ( $this->target == 'user' ) {
|
85 |
-
$template = $this->get_template( 'template-late-user' );
|
86 |
-
}
|
87 |
-
|
88 |
-
} elseif ( $this->event == 'reminder' ) {
|
89 |
-
if ( $this->target == 'user' ) {
|
90 |
-
$template = $this->get_template( 'template-reminder-user' );
|
91 |
-
}
|
92 |
-
|
93 |
-
// Use a message that's been appended manually if available
|
94 |
-
} else {
|
95 |
-
$template = empty( $this->message ) ? '' : $this->message;
|
96 |
-
}
|
97 |
-
|
98 |
-
$this->message = apply_filters( 'rtb_notification_sms_template', $this->process_template( $template ), $this );
|
99 |
-
|
100 |
-
}
|
101 |
-
|
102 |
-
/**
|
103 |
-
* Set license key
|
104 |
-
* @since 2.1.0
|
105 |
-
*/
|
106 |
-
public function set_license_key() {
|
107 |
-
|
108 |
-
if ( ! get_option( 'rtb-ultimate-license-key' ) ) { add_option( 'rtb-ultimate-license-key', 'no_license_key_entered' ); }
|
109 |
-
|
110 |
-
$this->license_key = get_option( 'rtb-ultimate-license-key' );
|
111 |
-
|
112 |
-
}
|
113 |
-
|
114 |
-
/**
|
115 |
-
* Set purchase email
|
116 |
-
* @since 2.1.0
|
117 |
-
*/
|
118 |
-
public function set_purchase_email() {
|
119 |
-
|
120 |
-
global $rtb_controller;
|
121 |
-
|
122 |
-
$this->purchase_email = $rtb_controller->settings->get_setting( 'ultimate-purchase-email' );
|
123 |
-
|
124 |
-
}
|
125 |
-
|
126 |
-
/**
|
127 |
-
* Send notification
|
128 |
-
* @since 2.1.0
|
129 |
-
*/
|
130 |
-
public function send_notification() {
|
131 |
-
global $rtb_controller;
|
132 |
-
|
133 |
-
$url = add_query_arg(
|
134 |
-
array(
|
135 |
-
'license_key' => urlencode( $this->license_key ),
|
136 |
-
'admin_email' => urlencode( $this->purchase_email ),
|
137 |
-
'phone_number' => urlencode( $this->phone_number ),
|
138 |
-
'message' => urlencode( $this->message ),
|
139 |
-
'country_code' => urlencode( $rtb_controller->settings->get_setting( 'rtb-country-code' ) )
|
140 |
-
),
|
141 |
-
'http://www.fivestarplugins.com/sms-handling/sms-client.php'
|
142 |
-
);
|
143 |
-
|
144 |
-
$opts = array('http'=>array('method'=>"GET"));
|
145 |
-
$context = stream_context_create($opts);
|
146 |
-
$return = json_decode( file_get_contents( $url, false, $context ) );
|
147 |
-
|
148 |
-
return isset($return->success) ? $return->success : false;
|
149 |
-
}
|
150 |
-
}
|
151 |
-
} // endif;
|
1 |
+
<?php
|
2 |
+
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
+
|
4 |
+
if ( !class_exists( 'rtbNotificationSMS' ) ) {
|
5 |
+
/**
|
6 |
+
* Class to handle an SMS notification for Restaurant Reservations
|
7 |
+
*
|
8 |
+
* This class extends rtbNotification and must implement the following methods:
|
9 |
+
* prepare_notification() - set up and validate data
|
10 |
+
* send_notification()
|
11 |
+
*
|
12 |
+
* @since 2.1.0
|
13 |
+
*/
|
14 |
+
class rtbNotificationSMS extends rtbNotification {
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Recipient phone number
|
18 |
+
* @since 2.1.0
|
19 |
+
*/
|
20 |
+
public $phone_number;
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Text message body
|
24 |
+
* @since 2.1.0
|
25 |
+
*/
|
26 |
+
public $message;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* The license key received for RTB Ultimate
|
30 |
+
* @since 2.1.0
|
31 |
+
*/
|
32 |
+
public $license_key;
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Email used for purchase, to validate message sending
|
36 |
+
* @since 2.1.0
|
37 |
+
*/
|
38 |
+
public $purchase_email;
|
39 |
+
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Prepare and validate notification data
|
43 |
+
*
|
44 |
+
* @return boolean if the data is valid and ready for transport
|
45 |
+
* @since 2.1.0
|
46 |
+
*/
|
47 |
+
public function prepare_notification() {
|
48 |
+
|
49 |
+
$this->set_phone_number();
|
50 |
+
$this->set_message();
|
51 |
+
$this->set_license_key();
|
52 |
+
$this->set_purchase_email();
|
53 |
+
|
54 |
+
// Return false if we're missing any of the required information
|
55 |
+
if ( empty( $this->phone_number) ||
|
56 |
+
empty( $this->message) ||
|
57 |
+
empty( $this->license_key) ||
|
58 |
+
empty( $this->purchase_email) ) {
|
59 |
+
return false;
|
60 |
+
}
|
61 |
+
|
62 |
+
return true;
|
63 |
+
}
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Set phone number
|
67 |
+
* @since 2.1.0
|
68 |
+
*/
|
69 |
+
public function set_phone_number() {
|
70 |
+
|
71 |
+
$phone_number = $this->booking->phone;
|
72 |
+
|
73 |
+
$this->phone_number = apply_filters( 'rtb_notification_sms_phone_number', $phone_number, $this );
|
74 |
+
|
75 |
+
}
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Set text message body
|
79 |
+
* @since 2.1.0
|
80 |
+
*/
|
81 |
+
public function set_message() {
|
82 |
+
|
83 |
+
if ( $this->event == 'late_user' ) {
|
84 |
+
if ( $this->target == 'user' ) {
|
85 |
+
$template = $this->get_template( 'template-late-user' );
|
86 |
+
}
|
87 |
+
|
88 |
+
} elseif ( $this->event == 'reminder' ) {
|
89 |
+
if ( $this->target == 'user' ) {
|
90 |
+
$template = $this->get_template( 'template-reminder-user' );
|
91 |
+
}
|
92 |
+
|
93 |
+
// Use a message that's been appended manually if available
|
94 |
+
} else {
|
95 |
+
$template = empty( $this->message ) ? '' : $this->message;
|
96 |
+
}
|
97 |
+
|
98 |
+
$this->message = apply_filters( 'rtb_notification_sms_template', $this->process_template( $template ), $this );
|
99 |
+
|
100 |
+
}
|
101 |
+
|
102 |
+
/**
|
103 |
+
* Set license key
|
104 |
+
* @since 2.1.0
|
105 |
+
*/
|
106 |
+
public function set_license_key() {
|
107 |
+
|
108 |
+
if ( ! get_option( 'rtb-ultimate-license-key' ) ) { add_option( 'rtb-ultimate-license-key', 'no_license_key_entered' ); }
|
109 |
+
|
110 |
+
$this->license_key = get_option( 'rtb-ultimate-license-key' );
|
111 |
+
|
112 |
+
}
|
113 |
+
|
114 |
+
/**
|
115 |
+
* Set purchase email
|
116 |
+
* @since 2.1.0
|
117 |
+
*/
|
118 |
+
public function set_purchase_email() {
|
119 |
+
|
120 |
+
global $rtb_controller;
|
121 |
+
|
122 |
+
$this->purchase_email = $rtb_controller->settings->get_setting( 'ultimate-purchase-email' );
|
123 |
+
|
124 |
+
}
|
125 |
+
|
126 |
+
/**
|
127 |
+
* Send notification
|
128 |
+
* @since 2.1.0
|
129 |
+
*/
|
130 |
+
public function send_notification() {
|
131 |
+
global $rtb_controller;
|
132 |
+
|
133 |
+
$url = add_query_arg(
|
134 |
+
array(
|
135 |
+
'license_key' => urlencode( $this->license_key ),
|
136 |
+
'admin_email' => urlencode( $this->purchase_email ),
|
137 |
+
'phone_number' => urlencode( $this->phone_number ),
|
138 |
+
'message' => urlencode( $this->message ),
|
139 |
+
'country_code' => urlencode( $rtb_controller->settings->get_setting( 'rtb-country-code' ) )
|
140 |
+
),
|
141 |
+
'http://www.fivestarplugins.com/sms-handling/sms-client.php'
|
142 |
+
);
|
143 |
+
|
144 |
+
$opts = array('http'=>array('method'=>"GET"));
|
145 |
+
$context = stream_context_create($opts);
|
146 |
+
$return = json_decode( file_get_contents( $url, false, $context ) );
|
147 |
+
|
148 |
+
return isset($return->success) ? $return->success : false;
|
149 |
+
}
|
150 |
+
}
|
151 |
+
} // endif;
|
includes/Notification.class.php
CHANGED
@@ -1,126 +1,126 @@
|
|
1 |
-
<?php
|
2 |
-
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
-
|
4 |
-
if ( !class_exists( 'rtbNotification' ) ) {
|
5 |
-
/**
|
6 |
-
* Base class to handle a notification for Restaurant Reservations
|
7 |
-
*
|
8 |
-
* This class sets up the notification content and sends it when run by
|
9 |
-
* rtbNotifications. This class should be extended for each type of
|
10 |
-
* notification. So, there would be a rtbNotificationEmail class or a
|
11 |
-
* rtbNotificationSMS class.
|
12 |
-
*
|
13 |
-
* @since 0.0.1
|
14 |
-
*/
|
15 |
-
abstract class rtbNotification {
|
16 |
-
|
17 |
-
/**
|
18 |
-
* Event which should trigger this notification
|
19 |
-
* @since 0.0.1
|
20 |
-
*/
|
21 |
-
public $event;
|
22 |
-
|
23 |
-
/**
|
24 |
-
* Target of the notification (who/what will receive it)
|
25 |
-
* @since 0.0.1
|
26 |
-
*/
|
27 |
-
public $target;
|
28 |
-
|
29 |
-
/**
|
30 |
-
* Define the notification essentials
|
31 |
-
* @since 0.0.1
|
32 |
-
*/
|
33 |
-
public function __construct( $event, $target ) {
|
34 |
-
|
35 |
-
$this->event = $event;
|
36 |
-
$this->target = $target;
|
37 |
-
|
38 |
-
}
|
39 |
-
|
40 |
-
/**
|
41 |
-
* Set booking data passed from rtbNotifications
|
42 |
-
*
|
43 |
-
* @var object $booking
|
44 |
-
* @since 0.0.1
|
45 |
-
*/
|
46 |
-
public function set_booking( $booking ) {
|
47 |
-
$this->booking = $booking;
|
48 |
-
}
|
49 |
-
|
50 |
-
/**
|
51 |
-
* Prepare and validate notification data
|
52 |
-
*
|
53 |
-
* @return boolean if the data is valid and ready for transport
|
54 |
-
* @since 0.0.1
|
55 |
-
*/
|
56 |
-
abstract public function prepare_notification();
|
57 |
-
|
58 |
-
/**
|
59 |
-
* Retrieve a notification template
|
60 |
-
* @since 0.0.1
|
61 |
-
*/
|
62 |
-
public function get_template( $type ) {
|
63 |
-
|
64 |
-
global $rtb_controller;
|
65 |
-
|
66 |
-
$template = $rtb_controller->settings->get_setting( $type );
|
67 |
-
|
68 |
-
if ( $template === null ) {
|
69 |
-
return '';
|
70 |
-
} else {
|
71 |
-
return $template;
|
72 |
-
}
|
73 |
-
}
|
74 |
-
|
75 |
-
/**
|
76 |
-
* Process a template and insert booking details
|
77 |
-
* @since 0.0.1
|
78 |
-
*/
|
79 |
-
public function process_template( $message ) {
|
80 |
-
global $rtb_controller;
|
81 |
-
|
82 |
-
if ( empty( $this->booking ) ) { return; }
|
83 |
-
|
84 |
-
$booking_page_id = $rtb_controller->settings->get_setting( 'booking-page' );
|
85 |
-
$booking_page_url = get_permalink( $booking_page_id );
|
86 |
-
|
87 |
-
$cancellation_url = add_query_arg(
|
88 |
-
array(
|
89 |
-
'action' => 'cancel',
|
90 |
-
'booking_id' => $this->booking->ID,
|
91 |
-
'booking_email' => $this->booking->email
|
92 |
-
),
|
93 |
-
$booking_page_url
|
94 |
-
);
|
95 |
-
|
96 |
-
$template_tags = array(
|
97 |
-
'{user_email}' => $this->booking->email,
|
98 |
-
'{user_name}' => $this->booking->name,
|
99 |
-
'{party}' => $this->booking->party,
|
100 |
-
'{table}' => implode(',', $this->booking->table ),
|
101 |
-
'{date}' => $this->booking->format_date( $this->booking->date ),
|
102 |
-
'{phone}' => $this->booking->phone,
|
103 |
-
'{message}' => $this->booking->message,
|
104 |
-
'{bookings_link}' => '<a href="' . admin_url( 'admin.php?page=rtb-bookings&status=pending' ) . '">' . esc_html( $rtb_controller->settings->get_setting( 'label-bookings-link-tag' ) ) . '</a>',
|
105 |
-
'{cancel_link}' => '<a href="' . esc_attr( $cancellation_url ) . '">' . esc_html( $rtb_controller->settings->get_setting( 'label-cancel-link-tag' ) ) . '</a>',
|
106 |
-
'{confirm_link}' => '<a href="' . admin_url( 'admin.php?page=rtb-bookings&rtb-quicklink=confirm&booking=' . esc_attr( $this->booking->ID ) ) . '">' . esc_html( $rtb_controller->settings->get_setting( 'label-confirm-link-tag' ) ) . '</a>',
|
107 |
-
'{close_link}' => '<a href="' . admin_url( 'admin.php?page=rtb-bookings&rtb-quicklink=close&booking=' . esc_attr( $this->booking->ID ) ) . '">' . esc_html( $rtb_controller->settings->get_setting( 'label-close-link-tag' ) ) . '</a>',
|
108 |
-
'{site_name}' => get_bloginfo( 'name' ),
|
109 |
-
'{site_link}' => '<a href="' . home_url( '/' ) . '">' . get_bloginfo( 'name' ) . '</a>',
|
110 |
-
'{current_time}' => date_i18n( get_option( 'date_format' ), current_time( 'timestamp' ) ) . ' ' . date_i18n( get_option( 'time_format' ), current_time( 'timestamp' ) ),
|
111 |
-
);
|
112 |
-
|
113 |
-
$template_tags = apply_filters( 'rtb_notification_template_tags', $template_tags, $this );
|
114 |
-
|
115 |
-
return str_replace( array_keys( $template_tags ), array_values( $template_tags ), $message );
|
116 |
-
|
117 |
-
}
|
118 |
-
|
119 |
-
/**
|
120 |
-
* Send notification
|
121 |
-
* @since 0.0.1
|
122 |
-
*/
|
123 |
-
abstract public function send_notification();
|
124 |
-
|
125 |
-
}
|
126 |
-
} // endif;
|
1 |
+
<?php
|
2 |
+
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
+
|
4 |
+
if ( !class_exists( 'rtbNotification' ) ) {
|
5 |
+
/**
|
6 |
+
* Base class to handle a notification for Restaurant Reservations
|
7 |
+
*
|
8 |
+
* This class sets up the notification content and sends it when run by
|
9 |
+
* rtbNotifications. This class should be extended for each type of
|
10 |
+
* notification. So, there would be a rtbNotificationEmail class or a
|
11 |
+
* rtbNotificationSMS class.
|
12 |
+
*
|
13 |
+
* @since 0.0.1
|
14 |
+
*/
|
15 |
+
abstract class rtbNotification {
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Event which should trigger this notification
|
19 |
+
* @since 0.0.1
|
20 |
+
*/
|
21 |
+
public $event;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Target of the notification (who/what will receive it)
|
25 |
+
* @since 0.0.1
|
26 |
+
*/
|
27 |
+
public $target;
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Define the notification essentials
|
31 |
+
* @since 0.0.1
|
32 |
+
*/
|
33 |
+
public function __construct( $event, $target ) {
|
34 |
+
|
35 |
+
$this->event = $event;
|
36 |
+
$this->target = $target;
|
37 |
+
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Set booking data passed from rtbNotifications
|
42 |
+
*
|
43 |
+
* @var object $booking
|
44 |
+
* @since 0.0.1
|
45 |
+
*/
|
46 |
+
public function set_booking( $booking ) {
|
47 |
+
$this->booking = $booking;
|
48 |
+
}
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Prepare and validate notification data
|
52 |
+
*
|
53 |
+
* @return boolean if the data is valid and ready for transport
|
54 |
+
* @since 0.0.1
|
55 |
+
*/
|
56 |
+
abstract public function prepare_notification();
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Retrieve a notification template
|
60 |
+
* @since 0.0.1
|
61 |
+
*/
|
62 |
+
public function get_template( $type ) {
|
63 |
+
|
64 |
+
global $rtb_controller;
|
65 |
+
|
66 |
+
$template = $rtb_controller->settings->get_setting( $type );
|
67 |
+
|
68 |
+
if ( $template === null ) {
|
69 |
+
return '';
|
70 |
+
} else {
|
71 |
+
return $template;
|
72 |
+
}
|
73 |
+
}
|
74 |
+
|
75 |
+
/**
|
76 |
+
* Process a template and insert booking details
|
77 |
+
* @since 0.0.1
|
78 |
+
*/
|
79 |
+
public function process_template( $message ) {
|
80 |
+
global $rtb_controller;
|
81 |
+
|
82 |
+
if ( empty( $this->booking ) ) { return; }
|
83 |
+
|
84 |
+
$booking_page_id = $rtb_controller->settings->get_setting( 'booking-page' );
|
85 |
+
$booking_page_url = get_permalink( $booking_page_id );
|
86 |
+
|
87 |
+
$cancellation_url = add_query_arg(
|
88 |
+
array(
|
89 |
+
'action' => 'cancel',
|
90 |
+
'booking_id' => $this->booking->ID,
|
91 |
+
'booking_email' => $this->booking->email
|
92 |
+
),
|
93 |
+
$booking_page_url
|
94 |
+
);
|
95 |
+
|
96 |
+
$template_tags = array(
|
97 |
+
'{user_email}' => $this->booking->email,
|
98 |
+
'{user_name}' => $this->booking->name,
|
99 |
+
'{party}' => $this->booking->party,
|
100 |
+
'{table}' => implode(',', $this->booking->table ),
|
101 |
+
'{date}' => $this->booking->format_date( $this->booking->date ),
|
102 |
+
'{phone}' => $this->booking->phone,
|
103 |
+
'{message}' => $this->booking->message,
|
104 |
+
'{bookings_link}' => '<a href="' . admin_url( 'admin.php?page=rtb-bookings&status=pending' ) . '">' . esc_html( $rtb_controller->settings->get_setting( 'label-bookings-link-tag' ) ) . '</a>',
|
105 |
+
'{cancel_link}' => '<a href="' . esc_attr( $cancellation_url ) . '">' . esc_html( $rtb_controller->settings->get_setting( 'label-cancel-link-tag' ) ) . '</a>',
|
106 |
+
'{confirm_link}' => '<a href="' . admin_url( 'admin.php?page=rtb-bookings&rtb-quicklink=confirm&booking=' . esc_attr( $this->booking->ID ) ) . '">' . esc_html( $rtb_controller->settings->get_setting( 'label-confirm-link-tag' ) ) . '</a>',
|
107 |
+
'{close_link}' => '<a href="' . admin_url( 'admin.php?page=rtb-bookings&rtb-quicklink=close&booking=' . esc_attr( $this->booking->ID ) ) . '">' . esc_html( $rtb_controller->settings->get_setting( 'label-close-link-tag' ) ) . '</a>',
|
108 |
+
'{site_name}' => get_bloginfo( 'name' ),
|
109 |
+
'{site_link}' => '<a href="' . home_url( '/' ) . '">' . get_bloginfo( 'name' ) . '</a>',
|
110 |
+
'{current_time}' => date_i18n( get_option( 'date_format' ), current_time( 'timestamp' ) ) . ' ' . date_i18n( get_option( 'time_format' ), current_time( 'timestamp' ) ),
|
111 |
+
);
|
112 |
+
|
113 |
+
$template_tags = apply_filters( 'rtb_notification_template_tags', $template_tags, $this );
|
114 |
+
|
115 |
+
return str_replace( array_keys( $template_tags ), array_values( $template_tags ), $message );
|
116 |
+
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* Send notification
|
121 |
+
* @since 0.0.1
|
122 |
+
*/
|
123 |
+
abstract public function send_notification();
|
124 |
+
|
125 |
+
}
|
126 |
+
} // endif;
|
includes/Notifications.class.php
CHANGED
@@ -1,360 +1,360 @@
|
|
1 |
-
<?php
|
2 |
-
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
-
|
4 |
-
if ( !class_exists( 'rtbNotifications' ) ) {
|
5 |
-
/**
|
6 |
-
* Class to process notifications for Restaurant Reservations
|
7 |
-
*
|
8 |
-
* This class contains the registered notifications and sends them when the
|
9 |
-
* event is triggered.
|
10 |
-
*
|
11 |
-
* @since 0.0.1
|
12 |
-
*/
|
13 |
-
class rtbNotifications {
|
14 |
-
|
15 |
-
/**
|
16 |
-
* Booking object (class rtbBooking)
|
17 |
-
*
|
18 |
-
* @var object
|
19 |
-
* @since 0.0.1
|
20 |
-
*/
|
21 |
-
public $booking;
|
22 |
-
|
23 |
-
/**
|
24 |
-
* Array of rtbNotification objects
|
25 |
-
*
|
26 |
-
* @var array
|
27 |
-
* @since 0.0.1
|
28 |
-
*/
|
29 |
-
public $notifications;
|
30 |
-
|
31 |
-
/**
|
32 |
-
* Register notifications hook early so that other early hooks can
|
33 |
-
* be used by the notification system.
|
34 |
-
* @since 0.0.1
|
35 |
-
*/
|
36 |
-
public function __construct() {
|
37 |
-
|
38 |
-
add_action( 'init', array( $this, 'register_notifications' ) );
|
39 |
-
|
40 |
-
add_action( 'init', array( $this, 'maybe_send_daily_summary' ), 12 );
|
41 |
-
}
|
42 |
-
|
43 |
-
/**
|
44 |
-
* Register notifications
|
45 |
-
* @since 0.0.1
|
46 |
-
*/
|
47 |
-
public function register_notifications() {
|
48 |
-
|
49 |
-
// Hook into all events that require notifications
|
50 |
-
$hooks = array(
|
51 |
-
'rtb_insert_booking' => array( $this, 'new_submission' ), // Booking submitted
|
52 |
-
'rtb_booking_paid' => array( $this, 'new_submission' ), // Booking deposit paid
|
53 |
-
'rtb_confirmed_booking' => array( $this, 'new_confirmed_submission' ), // Booking confirmed
|
54 |
-
'pending_to_confirmed' => array( $this, 'pending_to_confirmed' ), // Booking confirmed
|
55 |
-
'pending_to_closed' => array( $this, 'pending_to_closed' ), // Booking can not be made
|
56 |
-
'pending_to_cancelled' => array( $this, 'booking_cancelled' ), // Booking cancelled
|
57 |
-
'confirmed_to_cancelled' => array( $this, 'booking_cancelled' ), // Booking cancelled
|
58 |
-
);
|
59 |
-
|
60 |
-
$hooks = apply_filters( 'rtb_notification_transition_callbacks', $hooks );
|
61 |
-
|
62 |
-
foreach ( $hooks as $hook => $callback ) {
|
63 |
-
add_action( $hook, $callback );
|
64 |
-
}
|
65 |
-
|
66 |
-
// Register notifications
|
67 |
-
require_once( RTB_PLUGIN_DIR . '/includes/Notification.class.php' );
|
68 |
-
require_once( RTB_PLUGIN_DIR . '/includes/Notification.Email.class.php' );
|
69 |
-
|
70 |
-
$this->notifications = array(
|
71 |
-
new rtbNotificationEmail( 'new_submission', 'user' ),
|
72 |
-
new rtbNotificationEmail( 'pending_to_confirmed', 'user' ),
|
73 |
-
new rtbNotificationEmail( 'pending_to_closed', 'user' ),
|
74 |
-
new rtbNotificationEmail( 'booking_cancelled', 'user' ),
|
75 |
-
);
|
76 |
-
|
77 |
-
global $rtb_controller;
|
78 |
-
if ( $rtb_controller->settings->get_setting( 'admin-email-option' ) ) {
|
79 |
-
$this->notifications[] = new rtbNotificationEmail( 'new_submission', 'admin' );
|
80 |
-
}
|
81 |
-
|
82 |
-
if ( $rtb_controller->settings->get_setting( 'admin-confirmed-email-option' ) ) {
|
83 |
-
$this->notifications[] = new rtbNotificationEmail( 'rtb_confirmed_booking', 'admin' );
|
84 |
-
}
|
85 |
-
|
86 |
-
if ( $rtb_controller->settings->get_setting( 'admin-cancelled-email-option' ) ) {
|
87 |
-
$this->notifications[] = new rtbNotificationEmail( 'booking_cancelled', 'admin' );
|
88 |
-
}
|
89 |
-
|
90 |
-
$this->notifications = apply_filters( 'rtb_notifications', $this->notifications );
|
91 |
-
}
|
92 |
-
|
93 |
-
/**
|
94 |
-
* Send a summary of bookings today, if enabled
|
95 |
-
* @since 2.3.6
|
96 |
-
*/
|
97 |
-
public function maybe_send_daily_summary() {
|
98 |
-
global $rtb_controller;
|
99 |
-
|
100 |
-
if ( empty( $rtb_controller->settings->get_setting( 'daily-summary-address' ) ) ) { return; }
|
101 |
-
|
102 |
-
$last_send_datetime = new DateTime( get_option( 'rtb-daily-summary-send-date', date( 'Y-m-d', strtotime( '-1 day' ) ) ), wp_timezone() );
|
103 |
-
|
104 |
-
$last_send_datetime->add( new DateInterval( 'P1D' ) );
|
105 |
-
|
106 |
-
$send_time_hours = substr( $rtb_controller->settings->get_setting( 'daily-summary-address-send-time' ), 0, strpos( $rtb_controller->settings->get_setting( 'daily-summary-address-send-time' ), ':' ) );
|
107 |
-
$send_time_minutes = substr( $rtb_controller->settings->get_setting( 'daily-summary-address-send-time' ), strpos( $rtb_controller->settings->get_setting( 'daily-summary-address-send-time' ), ':' ) + 1 );
|
108 |
-
|
109 |
-
$next_send_time = $last_send_datetime->format('U') + $send_time_hours * 60*60 + $send_time_minutes * 60;
|
110 |
-
|
111 |
-
if ( $next_send_time > time() ) { return; }
|
112 |
-
|
113 |
-
require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
|
114 |
-
|
115 |
-
$display_table = $rtb_controller->permissions->check_permission( 'premium_table_restrictions' ) && $rtb_controller->settings->get_setting( 'enable-tables' );
|
116 |
-
|
117 |
-
$args = array(
|
118 |
-
'post_type' => 'rtb-booking',
|
119 |
-
'posts_per_page' => -1,
|
120 |
-
'date_query' => array(
|
121 |
-
'year' => date( 'Y' ),
|
122 |
-
'month' => date( 'm' ),
|
123 |
-
'day' => date( 'd' )
|
124 |
-
),
|
125 |
-
'post_status' => array_keys( $rtb_controller->cpts->booking_statuses ),
|
126 |
-
'orderby' => 'date',
|
127 |
-
'order' => 'ASC'
|
128 |
-
);
|
129 |
-
|
130 |
-
$bookings = get_posts( $args );
|
131 |
-
|
132 |
-
ob_start();
|
133 |
-
|
134 |
-
?>
|
135 |
-
|
136 |
-
<?php if ( empty( $bookings ) ) { ?>
|
137 |
-
|
138 |
-
<p><?php _e( 'There are currently no bookings today.', 'restaurant-reservations' ); ?></p>
|
139 |
-
|
140 |
-
<?php } else { ?>
|
141 |
-
|
142 |
-
<p><?php _e( 'Please find a summary of today\'s reservations in the table below.', 'restaurant-reservations' ); ?></p>
|
143 |
-
|
144 |
-
<table class='rtb-view-bookings-table'>
|
145 |
-
<thead>
|
146 |
-
<tr>
|
147 |
-
<th><?php _e('Time', 'restaurant-reservations'); ?></th>
|
148 |
-
<th><?php _e('Party', 'restaurant-reservations'); ?></th>
|
149 |
-
<th><?php _e('Name', 'restaurant-reservations'); ?></th>
|
150 |
-
<th><?php _e('Email', 'restaurant-reservations'); ?></th>
|
151 |
-
<th><?php _e('Phone', 'restaurant-reservations'); ?></th>
|
152 |
-
<?php if ( $display_table ) {?> <th><?php _e('Table', 'restaurant-reservations'); ?></th><?php } ?>
|
153 |
-
<th><?php _e('Status', 'restaurant-reservations'); ?></th>
|
154 |
-
<th><?php _e('Details', 'restaurant-reservations'); ?></th>
|
155 |
-
</tr>
|
156 |
-
</thead>
|
157 |
-
<tbody>
|
158 |
-
<?php foreach ( $bookings as $booking ) { ?>
|
159 |
-
<?php $booking_object = new rtbBooking(); ?>
|
160 |
-
<?php $booking_object->load_post( $booking ); ?>
|
161 |
-
<tr>
|
162 |
-
<td><?php echo ( new DateTime( $booking_object->date ) )->format( 'H:i:s' ); ?></td>
|
163 |
-
<td><?php echo $booking_object->party; ?></td>
|
164 |
-
<td><?php echo $booking_object->name; ?></td>
|
165 |
-
<td><?php echo $booking_object->email; ?></td>
|
166 |
-
<td><?php echo $booking_object->phone; ?></td>
|
167 |
-
<?php if ( $display_table ) { $table = implode(', ', $booking_object->table ); echo "<td>{$table}</td>"; } ?>
|
168 |
-
<td><?php echo $rtb_controller->cpts->booking_statuses[$booking_object->post_status]['label'] ?></td>
|
169 |
-
<td><?php echo apply_filters( 'rtb_bookings_table_column_details', $booking_object->message, $booking_object ); ?></td>
|
170 |
-
</tr>
|
171 |
-
<?php } ?>
|
172 |
-
</tbody>
|
173 |
-
</table>
|
174 |
-
|
175 |
-
<?php } ?>
|
176 |
-
|
177 |
-
<?php
|
178 |
-
|
179 |
-
$email_content = ob_get_clean();
|
180 |
-
|
181 |
-
$notification = new rtbNotificationEmail( 'daily_summary', 'admin' );
|
182 |
-
|
183 |
-
$notification->to_email = $rtb_controller->settings->get_setting( 'daily-summary-address' );
|
184 |
-
$notification->from_email = $rtb_controller->settings->get_setting( 'reply-to-address' );
|
185 |
-
$notification->from_name = $rtb_controller->settings->get_setting( 'reply-to-name' );
|
186 |
-
$notification->subject = __( 'Daily Email Summary', 'restaurant-reservations' );
|
187 |
-
$notification->manual_message = $email_content;
|
188 |
-
|
189 |
-
if ( $notification->prepare_notification() ) {
|
190 |
-
|
191 |
-
if ( $notification->send_notification() ) {
|
192 |
-
|
193 |
-
$now = new DateTime( 'now', wp_timezone() );
|
194 |
-
|
195 |
-
update_option( 'rtb-daily-summary-send-date', $now->format( 'Y-m-d' ) );
|
196 |
-
}
|
197 |
-
}
|
198 |
-
}
|
199 |
-
|
200 |
-
/**
|
201 |
-
* Set booking data
|
202 |
-
* @since 0.0.1
|
203 |
-
*/
|
204 |
-
public function set_booking( $booking_post ) {
|
205 |
-
require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
|
206 |
-
$this->booking = new rtbBooking();
|
207 |
-
$this->booking->load_wp_post( $booking_post );
|
208 |
-
}
|
209 |
-
|
210 |
-
/**
|
211 |
-
* New booking submissions
|
212 |
-
*
|
213 |
-
* @var object $booking
|
214 |
-
* @since 0.0.1
|
215 |
-
*/
|
216 |
-
public function new_submission( $booking ) {
|
217 |
-
|
218 |
-
// Bail early if $booking is not a rtbBooking object
|
219 |
-
if ( get_class( $booking ) != 'rtbBooking' ) {
|
220 |
-
return;
|
221 |
-
}
|
222 |
-
|
223 |
-
// trigger an event so that admin notifications for a new confirmed booking can be sent
|
224 |
-
if ( $booking->post_status == 'confirmed' ) {
|
225 |
-
do_action( 'rtb_confirmed_booking', get_post( $booking->ID ) );
|
226 |
-
}
|
227 |
-
|
228 |
-
// If the post status is not pending, trigger a post status
|
229 |
-
// transition as though it's gone from pending_to_{status}
|
230 |
-
if ( $booking->post_status != 'pending' and $booking->post_status != 'draft' ) {
|
231 |
-
do_action( 'pending_to_' . $booking->post_status, get_post( $booking->ID ) );
|
232 |
-
|
233 |
-
// Otherwise proceed with the new_submission event
|
234 |
-
} else {
|
235 |
-
$this->booking = $booking;
|
236 |
-
$this->event( 'new_submission' );
|
237 |
-
}
|
238 |
-
}
|
239 |
-
|
240 |
-
/**
|
241 |
-
* New confirmed booking
|
242 |
-
* @since 2.1.0
|
243 |
-
*/
|
244 |
-
public function new_confirmed_submission( $booking_post ) {
|
245 |
-
|
246 |
-
if ( $booking_post->post_type != RTB_BOOKING_POST_TYPE ) {
|
247 |
-
return;
|
248 |
-
}
|
249 |
-
|
250 |
-
$this->set_booking( $booking_post );
|
251 |
-
|
252 |
-
$this->event( 'rtb_confirmed_booking' );
|
253 |
-
|
254 |
-
}
|
255 |
-
|
256 |
-
/**
|
257 |
-
* Booking confirmed
|
258 |
-
* @since 0.0.1
|
259 |
-
*/
|
260 |
-
public function pending_to_confirmed( $booking_post ) {
|
261 |
-
|
262 |
-
if ( $booking_post->post_type != RTB_BOOKING_POST_TYPE ) {
|
263 |
-
return;
|
264 |
-
}
|
265 |
-
|
266 |
-
$this->clear_to_email( 'pending_to_confirmed' );
|
267 |
-
|
268 |
-
$this->set_booking( $booking_post );
|
269 |
-
|
270 |
-
$this->event( 'pending_to_confirmed' );
|
271 |
-
|
272 |
-
}
|
273 |
-
|
274 |
-
/**
|
275 |
-
* Booking can not be made
|
276 |
-
* @since 0.0.1
|
277 |
-
*/
|
278 |
-
public function pending_to_closed( $booking_post ) {
|
279 |
-
|
280 |
-
if ( $booking_post->post_type != RTB_BOOKING_POST_TYPE ) {
|
281 |
-
return;
|
282 |
-
}
|
283 |
-
|
284 |
-
$this->set_booking( $booking_post );
|
285 |
-
|
286 |
-
$this->event( 'pending_to_closed' );
|
287 |
-
|
288 |
-
}
|
289 |
-
|
290 |
-
/**
|
291 |
-
* Booking has been cancelled by the guest
|
292 |
-
*/
|
293 |
-
public function booking_cancelled( $booking_post ) {
|
294 |
-
|
295 |
-
if ( $booking_post->post_type != RTB_BOOKING_POST_TYPE ) {
|
296 |
-
return;
|
297 |
-
}
|
298 |
-
|
299 |
-
$this->set_booking( $booking_post );
|
300 |
-
|
301 |
-
$this->event( 'booking_cancelled' );
|
302 |
-
|
303 |
-
}
|
304 |
-
|
305 |
-
/**
|
306 |
-
* Booking was confirmed and is now completed. Send out an optional
|
307 |
-
* follow-up email.
|
308 |
-
*
|
309 |
-
* @since 0.0.1
|
310 |
-
*/
|
311 |
-
public function confirmed_to_closed( $booking_post ) {
|
312 |
-
|
313 |
-
if ( $booking_post->post_type != RTB_BOOKING_POST_TYPE ) {
|
314 |
-
return;
|
315 |
-
}
|
316 |
-
|
317 |
-
$this->set_booking( $booking_post );
|
318 |
-
|
319 |
-
$this->event( 'confirmed_to_closed' );
|
320 |
-
|
321 |
-
}
|
322 |
-
|
323 |
-
/**
|
324 |
-
* Clear the 'to_email' property of the selected event notification
|
325 |
-
*
|
326 |
-
* @since 2.5.2
|
327 |
-
*/
|
328 |
-
public function clear_to_email( $event ) {
|
329 |
-
|
330 |
-
foreach ( $this->notifications as $notification ) {
|
331 |
-
|
332 |
-
if ( $event == $notification->event ) {
|
333 |
-
|
334 |
-
$notification->to_email = '';
|
335 |
-
}
|
336 |
-
}
|
337 |
-
}
|
338 |
-
|
339 |
-
/**
|
340 |
-
* Process notifications for an event
|
341 |
-
* @since 0.0.1
|
342 |
-
*/
|
343 |
-
public function event( $event ) {
|
344 |
-
|
345 |
-
foreach( $this->notifications as $notification ) {
|
346 |
-
|
347 |
-
if ( $event == $notification->event ) {
|
348 |
-
$notification->set_booking( $this->booking );
|
349 |
-
if ( $notification->prepare_notification() ) {
|
350 |
-
do_action( 'rtb_send_notification_before', $notification );
|
351 |
-
$notification->send_notification();
|
352 |
-
do_action( 'rtb_send_notification_after', $notification );
|
353 |
-
}
|
354 |
-
}
|
355 |
-
}
|
356 |
-
|
357 |
-
}
|
358 |
-
|
359 |
-
}
|
360 |
-
} // endif;
|
1 |
+
<?php
|
2 |
+
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
+
|
4 |
+
if ( !class_exists( 'rtbNotifications' ) ) {
|
5 |
+
/**
|
6 |
+
* Class to process notifications for Restaurant Reservations
|
7 |
+
*
|
8 |
+
* This class contains the registered notifications and sends them when the
|
9 |
+
* event is triggered.
|
10 |
+
*
|
11 |
+
* @since 0.0.1
|
12 |
+
*/
|
13 |
+
class rtbNotifications {
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Booking object (class rtbBooking)
|
17 |
+
*
|
18 |
+
* @var object
|
19 |
+
* @since 0.0.1
|
20 |
+
*/
|
21 |
+
public $booking;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Array of rtbNotification objects
|
25 |
+
*
|
26 |
+
* @var array
|
27 |
+
* @since 0.0.1
|
28 |
+
*/
|
29 |
+
public $notifications;
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Register notifications hook early so that other early hooks can
|
33 |
+
* be used by the notification system.
|
34 |
+
* @since 0.0.1
|
35 |
+
*/
|
36 |
+
public function __construct() {
|
37 |
+
|
38 |
+
add_action( 'init', array( $this, 'register_notifications' ) );
|
39 |
+
|
40 |
+
add_action( 'init', array( $this, 'maybe_send_daily_summary' ), 12 );
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Register notifications
|
45 |
+
* @since 0.0.1
|
46 |
+
*/
|
47 |
+
public function register_notifications() {
|
48 |
+
|
49 |
+
// Hook into all events that require notifications
|
50 |
+
$hooks = array(
|
51 |
+
'rtb_insert_booking' => array( $this, 'new_submission' ), // Booking submitted
|
52 |
+
'rtb_booking_paid' => array( $this, 'new_submission' ), // Booking deposit paid
|
53 |
+
'rtb_confirmed_booking' => array( $this, 'new_confirmed_submission' ), // Booking confirmed
|
54 |
+
'pending_to_confirmed' => array( $this, 'pending_to_confirmed' ), // Booking confirmed
|
55 |
+
'pending_to_closed' => array( $this, 'pending_to_closed' ), // Booking can not be made
|
56 |
+
'pending_to_cancelled' => array( $this, 'booking_cancelled' ), // Booking cancelled
|
57 |
+
'confirmed_to_cancelled' => array( $this, 'booking_cancelled' ), // Booking cancelled
|
58 |
+
);
|
59 |
+
|
60 |
+
$hooks = apply_filters( 'rtb_notification_transition_callbacks', $hooks );
|
61 |
+
|
62 |
+
foreach ( $hooks as $hook => $callback ) {
|
63 |
+
add_action( $hook, $callback );
|
64 |
+
}
|
65 |
+
|
66 |
+
// Register notifications
|
67 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Notification.class.php' );
|
68 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Notification.Email.class.php' );
|
69 |
+
|
70 |
+
$this->notifications = array(
|
71 |
+
new rtbNotificationEmail( 'new_submission', 'user' ),
|
72 |
+
new rtbNotificationEmail( 'pending_to_confirmed', 'user' ),
|
73 |
+
new rtbNotificationEmail( 'pending_to_closed', 'user' ),
|
74 |
+
new rtbNotificationEmail( 'booking_cancelled', 'user' ),
|
75 |
+
);
|
76 |
+
|
77 |
+
global $rtb_controller;
|
78 |
+
if ( $rtb_controller->settings->get_setting( 'admin-email-option' ) ) {
|
79 |
+
$this->notifications[] = new rtbNotificationEmail( 'new_submission', 'admin' );
|
80 |
+
}
|
81 |
+
|
82 |
+
if ( $rtb_controller->settings->get_setting( 'admin-confirmed-email-option' ) ) {
|
83 |
+
$this->notifications[] = new rtbNotificationEmail( 'rtb_confirmed_booking', 'admin' );
|
84 |
+
}
|
85 |
+
|
86 |
+
if ( $rtb_controller->settings->get_setting( 'admin-cancelled-email-option' ) ) {
|
87 |
+
$this->notifications[] = new rtbNotificationEmail( 'booking_cancelled', 'admin' );
|
88 |
+
}
|
89 |
+
|
90 |
+
$this->notifications = apply_filters( 'rtb_notifications', $this->notifications );
|
91 |
+
}
|
92 |
+
|
93 |
+
/**
|
94 |
+
* Send a summary of bookings today, if enabled
|
95 |
+
* @since 2.3.6
|
96 |
+
*/
|
97 |
+
public function maybe_send_daily_summary() {
|
98 |
+
global $rtb_controller;
|
99 |
+
|
100 |
+
if ( empty( $rtb_controller->settings->get_setting( 'daily-summary-address' ) ) ) { return; }
|
101 |
+
|
102 |
+
$last_send_datetime = new DateTime( get_option( 'rtb-daily-summary-send-date', date( 'Y-m-d', strtotime( '-1 day' ) ) ), wp_timezone() );
|
103 |
+
|
104 |
+
$last_send_datetime->add( new DateInterval( 'P1D' ) );
|
105 |
+
|
106 |
+
$send_time_hours = substr( $rtb_controller->settings->get_setting( 'daily-summary-address-send-time' ), 0, strpos( $rtb_controller->settings->get_setting( 'daily-summary-address-send-time' ), ':' ) );
|
107 |
+
$send_time_minutes = substr( $rtb_controller->settings->get_setting( 'daily-summary-address-send-time' ), strpos( $rtb_controller->settings->get_setting( 'daily-summary-address-send-time' ), ':' ) + 1 );
|
108 |
+
|
109 |
+
$next_send_time = $last_send_datetime->format('U') + $send_time_hours * 60*60 + $send_time_minutes * 60;
|
110 |
+
|
111 |
+
if ( $next_send_time > time() ) { return; }
|
112 |
+
|
113 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
|
114 |
+
|
115 |
+
$display_table = $rtb_controller->permissions->check_permission( 'premium_table_restrictions' ) && $rtb_controller->settings->get_setting( 'enable-tables' );
|
116 |
+
|
117 |
+
$args = array(
|
118 |
+
'post_type' => 'rtb-booking',
|
119 |
+
'posts_per_page' => -1,
|
120 |
+
'date_query' => array(
|
121 |
+
'year' => date( 'Y' ),
|
122 |
+
'month' => date( 'm' ),
|
123 |
+
'day' => date( 'd' )
|
124 |
+
),
|
125 |
+
'post_status' => array_keys( $rtb_controller->cpts->booking_statuses ),
|
126 |
+
'orderby' => 'date',
|
127 |
+
'order' => 'ASC'
|
128 |
+
);
|
129 |
+
|
130 |
+
$bookings = get_posts( $args );
|
131 |
+
|
132 |
+
ob_start();
|
133 |
+
|
134 |
+
?>
|
135 |
+
|
136 |
+
<?php if ( empty( $bookings ) ) { ?>
|
137 |
+
|
138 |
+
<p><?php _e( 'There are currently no bookings today.', 'restaurant-reservations' ); ?></p>
|
139 |
+
|
140 |
+
<?php } else { ?>
|
141 |
+
|
142 |
+
<p><?php _e( 'Please find a summary of today\'s reservations in the table below.', 'restaurant-reservations' ); ?></p>
|
143 |
+
|
144 |
+
<table class='rtb-view-bookings-table'>
|
145 |
+
<thead>
|
146 |
+
<tr>
|
147 |
+
<th><?php _e('Time', 'restaurant-reservations'); ?></th>
|
148 |
+
<th><?php _e('Party', 'restaurant-reservations'); ?></th>
|
149 |
+
<th><?php _e('Name', 'restaurant-reservations'); ?></th>
|
150 |
+
<th><?php _e('Email', 'restaurant-reservations'); ?></th>
|
151 |
+
<th><?php _e('Phone', 'restaurant-reservations'); ?></th>
|
152 |
+
<?php if ( $display_table ) {?> <th><?php _e('Table', 'restaurant-reservations'); ?></th><?php } ?>
|
153 |
+
<th><?php _e('Status', 'restaurant-reservations'); ?></th>
|
154 |
+
<th><?php _e('Details', 'restaurant-reservations'); ?></th>
|
155 |
+
</tr>
|
156 |
+
</thead>
|
157 |
+
<tbody>
|
158 |
+
<?php foreach ( $bookings as $booking ) { ?>
|
159 |
+
<?php $booking_object = new rtbBooking(); ?>
|
160 |
+
<?php $booking_object->load_post( $booking ); ?>
|
161 |
+
<tr>
|
162 |
+
<td><?php echo ( new DateTime( $booking_object->date ) )->format( 'H:i:s' ); ?></td>
|
163 |
+
<td><?php echo $booking_object->party; ?></td>
|
164 |
+
<td><?php echo $booking_object->name; ?></td>
|
165 |
+
<td><?php echo $booking_object->email; ?></td>
|
166 |
+
<td><?php echo $booking_object->phone; ?></td>
|
167 |
+
<?php if ( $display_table ) { $table = implode(', ', $booking_object->table ); echo "<td>{$table}</td>"; } ?>
|
168 |
+
<td><?php echo $rtb_controller->cpts->booking_statuses[$booking_object->post_status]['label'] ?></td>
|
169 |
+
<td><?php echo apply_filters( 'rtb_bookings_table_column_details', $booking_object->message, $booking_object ); ?></td>
|
170 |
+
</tr>
|
171 |
+
<?php } ?>
|
172 |
+
</tbody>
|
173 |
+
</table>
|
174 |
+
|
175 |
+
<?php } ?>
|
176 |
+
|
177 |
+
<?php
|
178 |
+
|
179 |
+
$email_content = ob_get_clean();
|
180 |
+
|
181 |
+
$notification = new rtbNotificationEmail( 'daily_summary', 'admin' );
|
182 |
+
|
183 |
+
$notification->to_email = $rtb_controller->settings->get_setting( 'daily-summary-address' );
|
184 |
+
$notification->from_email = $rtb_controller->settings->get_setting( 'reply-to-address' );
|
185 |
+
$notification->from_name = $rtb_controller->settings->get_setting( 'reply-to-name' );
|
186 |
+
$notification->subject = __( 'Daily Email Summary', 'restaurant-reservations' );
|
187 |
+
$notification->manual_message = $email_content;
|
188 |
+
|
189 |
+
if ( $notification->prepare_notification() ) {
|
190 |
+
|
191 |
+
if ( $notification->send_notification() ) {
|
192 |
+
|
193 |
+
$now = new DateTime( 'now', wp_timezone() );
|
194 |
+
|
195 |
+
update_option( 'rtb-daily-summary-send-date', $now->format( 'Y-m-d' ) );
|
196 |
+
}
|
197 |
+
}
|
198 |
+
}
|
199 |
+
|
200 |
+
/**
|
201 |
+
* Set booking data
|
202 |
+
* @since 0.0.1
|
203 |
+
*/
|
204 |
+
public function set_booking( $booking_post ) {
|
205 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
|
206 |
+
$this->booking = new rtbBooking();
|
207 |
+
$this->booking->load_wp_post( $booking_post );
|
208 |
+
}
|
209 |
+
|
210 |
+
/**
|
211 |
+
* New booking submissions
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|