Version Description
(2014-05-08) = * Replace dashicons caret with CSS-only caret in booking form
Download this release
Release Info
Developer | NateWr |
Plugin | Restaurant Reservations |
Version | 1.0.1 |
Comparing to | |
See all releases |
Version 1.0.1
- GPL.txt +280 -0
- assets/css/admin.css +91 -0
- assets/css/booking-form.css +73 -0
- assets/js/admin.js +22 -0
- assets/js/booking-form.js +201 -0
- docs/index.html +169 -0
- docs/style.css +31 -0
- includes/Booking.class.php +421 -0
- includes/CustomPostTypes.class.php +231 -0
- includes/Notification.Email.class.php +187 -0
- includes/Notification.class.php +106 -0
- includes/Notifications.class.php +187 -0
- includes/Settings.class.php +540 -0
- includes/WP_List_Table.BookingsTable.class.php +650 -0
- includes/WP_Widget.BookingFormWidget.class.php +87 -0
- includes/template-functions.php +283 -0
- lib/simple-admin-pages/README.md +379 -0
- lib/simple-admin-pages/classes/AdminPage.Submenu.class.php +38 -0
- lib/simple-admin-pages/classes/AdminPage.Themes.class.php +15 -0
- lib/simple-admin-pages/classes/AdminPage.class.php +215 -0
- lib/simple-admin-pages/classes/AdminPageSection.class.php +132 -0
- lib/simple-admin-pages/classes/AdminPageSetting.Editor.class.php +44 -0
- lib/simple-admin-pages/classes/AdminPageSetting.HTML.class.php +33 -0
- lib/simple-admin-pages/classes/AdminPageSetting.OpeningHours.class.php +167 -0
- lib/simple-admin-pages/classes/AdminPageSetting.Scheduler.class.php +596 -0
- lib/simple-admin-pages/classes/AdminPageSetting.Select.class.php +60 -0
- lib/simple-admin-pages/classes/AdminPageSetting.SelectPost.class.php +61 -0
- lib/simple-admin-pages/classes/AdminPageSetting.SelectTaxonomy.class.php +66 -0
- lib/simple-admin-pages/classes/AdminPageSetting.Text.class.php +35 -0
- lib/simple-admin-pages/classes/AdminPageSetting.Textarea.class.php +56 -0
- lib/simple-admin-pages/classes/AdminPageSetting.Toggle.class.php +43 -0
- lib/simple-admin-pages/classes/AdminPageSetting.class.php +225 -0
- lib/simple-admin-pages/classes/Library.class.php +406 -0
- lib/simple-admin-pages/css/admin.css +225 -0
- lib/simple-admin-pages/js/admin.js +340 -0
- lib/simple-admin-pages/lib/pickadate/legacy.js +10 -0
- lib/simple-admin-pages/lib/pickadate/picker.date.js +5 -0
- lib/simple-admin-pages/lib/pickadate/picker.js +7 -0
- lib/simple-admin-pages/lib/pickadate/picker.time.js +5 -0
- lib/simple-admin-pages/lib/pickadate/themes/default.css +4 -0
- lib/simple-admin-pages/lib/pickadate/themes/default.date.css +1 -0
- lib/simple-admin-pages/lib/pickadate/themes/default.time.css +1 -0
- lib/simple-admin-pages/lib/pickadate/themes/rtl.css +3 -0
- lib/simple-admin-pages/lib/pickadate/translations/ar.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/bg_BG.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/bs_BA.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/ca_ES.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/cs_CZ.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/da_DK.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/de_DE.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/el_GR.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/es_ES.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/et_EE.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/eu_ES.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/fi_FI.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/fr_FR.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/gl_ES.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/he_IL.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/hr_HR.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/hu_HU.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/id_ID.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/is_IS.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/it_IT.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/ja_JP.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/ko_KR.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/nl_NL.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/no_NO.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/pl_PL.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/pt_BR.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/pt_PT.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/ro_RO.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/ru_RU.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/sk_SK.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/sl_SI.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/sv_SE.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/th_TH.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/tr_TR.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/uk_UA.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/zh_CN.js +1 -0
- lib/simple-admin-pages/lib/pickadate/translations/zh_TW.js +1 -0
- lib/simple-admin-pages/simple-admin-pages.php +57 -0
- readme.txt +61 -0
- restaurant-reservations.php +334 -0
- screenshot-1.PNG +0 -0
- screenshot-2.PNG +0 -0
- screenshot-3.PNG +0 -0
- screenshot-4.PNG +0 -0
- screenshot-5.PNG +0 -0
- screenshot-6.PNG +0 -0
- screenshot-7.PNG +0 -0
- screenshot-8.PNG +0 -0
- screenshot-9.PNG +0 -0
GPL.txt
ADDED
@@ -0,0 +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
|
assets/css/admin.css
ADDED
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/* CSS Stylesheet for the admin interface for Restaurant Reservations */
|
2 |
+
|
3 |
+
.clearfix:before,
|
4 |
+
.clearfix:after { /* thanks bootstrap */
|
5 |
+
content: " ";
|
6 |
+
display: table;
|
7 |
+
}
|
8 |
+
|
9 |
+
.clearfix:after {
|
10 |
+
clear: both;
|
11 |
+
}
|
12 |
+
|
13 |
+
/* Bookings Table */
|
14 |
+
#rtb-bookings-table .subsubsub {
|
15 |
+
float: right;
|
16 |
+
}
|
17 |
+
|
18 |
+
#rtb-bookings-table .subsubsub .trash a {
|
19 |
+
color: #a00;
|
20 |
+
}
|
21 |
+
|
22 |
+
#rtb-bookings-table .subsubsub .trash a:hover {
|
23 |
+
color: red;
|
24 |
+
}
|
25 |
+
|
26 |
+
#rtb-filters {
|
27 |
+
float: left;
|
28 |
+
}
|
29 |
+
|
30 |
+
#rtb-filters .subsubsub {
|
31 |
+
margin-bottom: 0.5em;
|
32 |
+
float: none;
|
33 |
+
}
|
34 |
+
|
35 |
+
#rtb-filters .date-filters {
|
36 |
+
clear: both;
|
37 |
+
padding: 1em;
|
38 |
+
margin: 1em 0 2em;
|
39 |
+
background: #fff;
|
40 |
+
-webkit-box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
|
41 |
+
box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
|
42 |
+
}
|
43 |
+
|
44 |
+
#rtb-filters .date-filters .datepicker {
|
45 |
+
max-width: 10em;
|
46 |
+
}
|
47 |
+
|
48 |
+
#rtb-filters .current {
|
49 |
+
font-weight: 600;
|
50 |
+
color: #000;
|
51 |
+
}
|
52 |
+
|
53 |
+
#rtb-bookings-table .column-message .rtb-message-data,
|
54 |
+
#rtb-bookings-table .message-row {
|
55 |
+
display: none;
|
56 |
+
}
|
57 |
+
|
58 |
+
#rtb-bookings-table tr.closed {
|
59 |
+
opacity: 0.7;
|
60 |
+
filter: opacity(alpha=70);
|
61 |
+
}
|
62 |
+
#rtb-bookings-table tr.closed:hover {
|
63 |
+
opacity: 1;
|
64 |
+
filter: opacity(alpha=100);
|
65 |
+
}
|
66 |
+
|
67 |
+
#rtb-bookings-table tr.pending .check-column,
|
68 |
+
#rtb-bookings-table tr.pending.message-row td {
|
69 |
+
border-left: 4px solid #dd3d36;
|
70 |
+
}
|
71 |
+
|
72 |
+
@media screen and (max-width: 782px) {
|
73 |
+
|
74 |
+
#rtb-bookings-table .fixed .column-date {
|
75 |
+
display: table-cell;
|
76 |
+
}
|
77 |
+
}
|
78 |
+
|
79 |
+
/* Settings Pages (most of this is handled by the Simple Admin Pages library) */
|
80 |
+
.rtb-template-tags-box {
|
81 |
+
margin-top: 0.5em;
|
82 |
+
padding: 1em;
|
83 |
+
border-left: 4px solid #2ea2cc;
|
84 |
+
background: #fff;
|
85 |
+
-webkit-box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
|
86 |
+
box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
|
87 |
+
}
|
88 |
+
.rtb-template-tags-box strong {
|
89 |
+
min-width: 8em;
|
90 |
+
display: inline-block;
|
91 |
+
}
|
assets/css/booking-form.css
ADDED
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/* Frontend CSS Stylesheet for Restaurant Reservations */
|
2 |
+
|
3 |
+
.rtb-booking-form fieldset {
|
4 |
+
padding-bottom: 1em;
|
5 |
+
margin-bottom: 1em;
|
6 |
+
}
|
7 |
+
.rtb-booking-form legend {
|
8 |
+
padding: 0 0.5em;
|
9 |
+
}
|
10 |
+
.rtb-booking-form fieldset>div {
|
11 |
+
margin-top: 1em;
|
12 |
+
}
|
13 |
+
.rtb-booking-form label {
|
14 |
+
display: block;
|
15 |
+
}
|
16 |
+
.rtb-booking-form input {
|
17 |
+
width: 100%;
|
18 |
+
max-width: 15em;
|
19 |
+
}
|
20 |
+
.rtb-booking-form textarea {
|
21 |
+
width: 100%;
|
22 |
+
max-width: 30em;
|
23 |
+
}
|
24 |
+
.rtb-booking-form .add-message {
|
25 |
+
margin-top: 1em;
|
26 |
+
}
|
27 |
+
.rtb-booking-form .message {
|
28 |
+
position: absolute;
|
29 |
+
top: -9999px;
|
30 |
+
left: -9999px;
|
31 |
+
}
|
32 |
+
.rtb-booking-form .message-open {
|
33 |
+
position: relative;
|
34 |
+
top: auto;
|
35 |
+
left: auto;
|
36 |
+
}
|
37 |
+
.rtb-booking-form .message textarea {
|
38 |
+
min-height: 6em;
|
39 |
+
}
|
40 |
+
.rtb-booking-form .rtb-error {
|
41 |
+
font-size: 0.85em;
|
42 |
+
padding: 0.25em;
|
43 |
+
background: #f50;
|
44 |
+
color: #fff;
|
45 |
+
}
|
46 |
+
.rtb-booking-form .rtb-error:before {
|
47 |
+
content: ' ';
|
48 |
+
border-top: 4px solid;
|
49 |
+
border-right: 4px solid transparent;
|
50 |
+
border-left: 4px solid transparent;
|
51 |
+
width: 0;
|
52 |
+
height: 0;
|
53 |
+
display: inline-block;
|
54 |
+
margin: 0.25em 0.5em;
|
55 |
+
}
|
56 |
+
|
57 |
+
/* Compatibility styles for pickadate on common themes */
|
58 |
+
#rtb-date_root .picker__button--clear,
|
59 |
+
#rtb-date_root .picker__button--today {
|
60 |
+
/* don't adopt the theme's button text color */
|
61 |
+
color: #000;
|
62 |
+
}
|
63 |
+
#rtb-date_root .picker__nav--next,
|
64 |
+
#rtb-date_root .picker__nav--prev {
|
65 |
+
/* next/prev calendar arrows button areas sometimes don't cover the arrows */
|
66 |
+
min-height: 2em;
|
67 |
+
}
|
68 |
+
#rtb-time_root .picker__list,
|
69 |
+
#rtb-time_root .picker__list li {
|
70 |
+
/* override some theme's list styles */
|
71 |
+
list-style: none;
|
72 |
+
margin: 0 0 0 1px;
|
73 |
+
}
|
assets/js/admin.js
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/* Javascript for Restaurant Reservations admin */
|
2 |
+
jQuery(document).ready(function ($) {
|
3 |
+
|
4 |
+
// Show or hide a booking message in the bookings table
|
5 |
+
$( '#rtb-bookings-table .column-message a' ).click( function () {
|
6 |
+
var message_id = $(this).data( 'id' );
|
7 |
+
if ( $(this).parent().parent().siblings( '#' + message_id ).length ) {
|
8 |
+
$( '#' + $(this).data( 'id' ) ).fadeOut( function() {
|
9 |
+
$(this).remove();
|
10 |
+
});
|
11 |
+
$(this).children( '.dashicons' ).removeClass( 'dashicons-welcome-comments' ).addClass( 'dashicons-testimonial' );
|
12 |
+
} else {
|
13 |
+
var row = $(this).closest( 'tr' );
|
14 |
+
row.after( '<tr class="' + row.attr( 'class' ) + ' message-row" id="' + message_id + '"><td colspan="' + row.children( 'th, td' ).length + '">' + $(this).siblings( '.rtb-message-data' ).html() + '</td></tr>' );
|
15 |
+
$( '#' + message_id ).fadeIn();
|
16 |
+
$(this).children( '.dashicons' ).removeClass( 'dashicons-testimonial' ).addClass( 'dashicons-welcome-comments' );
|
17 |
+
}
|
18 |
+
|
19 |
+
return false;
|
20 |
+
});
|
21 |
+
|
22 |
+
});
|
assets/js/booking-form.js
ADDED
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/* Javascript for Restaurant Reservations booking form */
|
2 |
+
jQuery(document).ready(function ($) {
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Scroll to the first error message on the booking form
|
6 |
+
*/
|
7 |
+
if ( $( '.rtb-booking-form .rtb-error' ).length ) {
|
8 |
+
$('html, body').animate({
|
9 |
+
scrollTop: $( '.rtb-booking-form .rtb-error' ).first().offset().top + -40
|
10 |
+
}, 500);
|
11 |
+
}
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Show the message field on the booking form
|
15 |
+
*/
|
16 |
+
$( '.rtb-booking-form .add-message a' ).click( function() {
|
17 |
+
$(this).hide();
|
18 |
+
$(this).parent().siblings( '.message' ).addClass( 'message-open' );
|
19 |
+
|
20 |
+
return false;
|
21 |
+
});
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Enable datepickers on load
|
25 |
+
*/
|
26 |
+
if ( typeof rtb_pickadate !== 'undefined' ) {
|
27 |
+
|
28 |
+
// Declare datepicker
|
29 |
+
var $date_input = $( '#rtb-date' ).pickadate({
|
30 |
+
format: rtb_pickadate.date_format,
|
31 |
+
min: true,
|
32 |
+
container: 'body',
|
33 |
+
});
|
34 |
+
|
35 |
+
// Declare timepicker
|
36 |
+
var $time_input = $( '#rtb-time' ).pickatime({
|
37 |
+
format: rtb_pickadate.time_format,
|
38 |
+
});
|
39 |
+
|
40 |
+
var datepicker = $date_input.pickadate( 'picker' );
|
41 |
+
var timepicker = $time_input.pickatime( 'picker' );
|
42 |
+
|
43 |
+
if ( typeof datepicker == 'undefined' ) {
|
44 |
+
return;
|
45 |
+
}
|
46 |
+
|
47 |
+
// Pass conditional configuration parameters
|
48 |
+
if ( rtb_pickadate.disable_dates.length ) {
|
49 |
+
datepicker.set( 'disable', rtb_pickadate.disable_dates );
|
50 |
+
}
|
51 |
+
|
52 |
+
// Update timepicker on pageload and whenever the datepicker is closed
|
53 |
+
rtb_update_timepicker_range();
|
54 |
+
datepicker.on( {
|
55 |
+
close: function() {
|
56 |
+
rtb_update_timepicker_range();
|
57 |
+
}
|
58 |
+
});
|
59 |
+
}
|
60 |
+
|
61 |
+
// Update the timepicker's range based on the currently selected date
|
62 |
+
function rtb_update_timepicker_range() {
|
63 |
+
|
64 |
+
// Reset enabled/disabled rules on this timepicker
|
65 |
+
timepicker.set( 'enable', false );
|
66 |
+
timepicker.set( 'disable', false );
|
67 |
+
|
68 |
+
var selected_date = new Date( datepicker.get() );
|
69 |
+
var selected_date_year = selected_date.getFullYear();
|
70 |
+
var selected_date_month = selected_date.getMonth();
|
71 |
+
var selected_date_date = selected_date.getDate();
|
72 |
+
|
73 |
+
// Declaring the first element true inverts the timepicker settings. All
|
74 |
+
// times subsequently declared are valid. Any time that doesn't fall
|
75 |
+
// within those declarations is invalid.
|
76 |
+
// See: http://amsul.ca/pickadate.js/time.htm#disable-times-all
|
77 |
+
var valid_times = [ { from: [0, 0], to: [24, 0] } ];
|
78 |
+
|
79 |
+
// Check if this date is an exception to the rules
|
80 |
+
if ( typeof rtb_pickadate.schedule_closed != 'undefined' ) {
|
81 |
+
|
82 |
+
var excp_date = [];
|
83 |
+
var excp_start_date = [];
|
84 |
+
var excp_start_time = [];
|
85 |
+
var excp_end_date = [];
|
86 |
+
var excp_end_time = [];
|
87 |
+
for ( var closed_key in rtb_pickadate.schedule_closed ) {
|
88 |
+
|
89 |
+
excp_date = new Date( rtb_pickadate.schedule_closed[closed_key].date );
|
90 |
+
if ( excp_date.getFullYear() == selected_date_year &&
|
91 |
+
excp_date.getMonth() == selected_date_month &&
|
92 |
+
excp_date.getDate() == selected_date_date
|
93 |
+
) {
|
94 |
+
|
95 |
+
// Closed all day
|
96 |
+
if ( typeof rtb_pickadate.schedule_closed[closed_key].time == 'undefined' ) {
|
97 |
+
timepicker.set( 'disable', [ true ] );
|
98 |
+
|
99 |
+
return;
|
100 |
+
}
|
101 |
+
|
102 |
+
if ( typeof rtb_pickadate.schedule_closed[closed_key].time.start !== 'undefined' ) {
|
103 |
+
excp_start_date = new Date( '1 January 2000 ' + rtb_pickadate.schedule_closed[closed_key].time.start );
|
104 |
+
excp_start_time = [ excp_start_date.getHours(), excp_start_date.getMinutes() ];
|
105 |
+
} else {
|
106 |
+
excp_start_time = [ 0, 0 ]; // Start of the day
|
107 |
+
}
|
108 |
+
|
109 |
+
if ( typeof rtb_pickadate.schedule_closed[closed_key].time.end !== 'undefined' ) {
|
110 |
+
excp_end_date = new Date( '1 January 2000 ' + rtb_pickadate.schedule_closed[closed_key].time.end );
|
111 |
+
excp_end_time = [ excp_end_date.getHours(), excp_end_date.getMinutes() ];
|
112 |
+
} else {
|
113 |
+
excp_end_time = [ 24, 0 ]; // End of the day
|
114 |
+
}
|
115 |
+
|
116 |
+
valid_times.push( { from: excp_start_time, to: excp_end_time, inverted: true } );
|
117 |
+
}
|
118 |
+
}
|
119 |
+
|
120 |
+
excp_date = excp_start_date = excp_start_time = excp_end_date = excp_end_time = null;
|
121 |
+
|
122 |
+
// Exit early if this date is an exception
|
123 |
+
if ( valid_times.length > 1 ) {
|
124 |
+
timepicker.set( 'disable', valid_times );
|
125 |
+
|
126 |
+
return;
|
127 |
+
}
|
128 |
+
}
|
129 |
+
|
130 |
+
// Get any rules which apply to this weekday
|
131 |
+
if ( typeof rtb_pickadate.schedule_open != 'undefined' ) {
|
132 |
+
|
133 |
+
var selected_date_weekday = selected_date.getDay();
|
134 |
+
|
135 |
+
var weekdays = {
|
136 |
+
sunday: 0,
|
137 |
+
monday: 1,
|
138 |
+
tuesday: 2,
|
139 |
+
wednesday: 3,
|
140 |
+
thursday: 4,
|
141 |
+
friday: 5,
|
142 |
+
saturday: 6,
|
143 |
+
};
|
144 |
+
|
145 |
+
var rule_start_date = [];
|
146 |
+
var rule_start_time = [];
|
147 |
+
var rule_end_date = [];
|
148 |
+
var rule_end_time = [];
|
149 |
+
for ( var open_key in rtb_pickadate.schedule_open ) {
|
150 |
+
|
151 |
+
if ( typeof rtb_pickadate.schedule_open[open_key].weekdays !== 'undefined' ) {
|
152 |
+
for ( var weekdays_key in rtb_pickadate.schedule_open[open_key].weekdays ) {
|
153 |
+
if ( weekdays[weekdays_key] == selected_date_weekday ) {
|
154 |
+
|
155 |
+
// Closed all day
|
156 |
+
if ( typeof rtb_pickadate.schedule_open[open_key].time == 'undefined' ) {
|
157 |
+
timepicker.set( 'disable', [ true ] );
|
158 |
+
|
159 |
+
return;
|
160 |
+
}
|
161 |
+
|
162 |
+
if ( typeof rtb_pickadate.schedule_open[open_key].time.start !== 'undefined' ) {
|
163 |
+
rule_start_date = new Date( '1 January 2000 ' + rtb_pickadate.schedule_open[open_key].time.start );
|
164 |
+
rule_start_time = [ rule_start_date.getHours(), rule_start_date.getMinutes() ];
|
165 |
+
} else {
|
166 |
+
rule_start_time = [ 0, 0 ]; // Start of the day
|
167 |
+
}
|
168 |
+
|
169 |
+
if ( typeof rtb_pickadate.schedule_open[open_key].time.end !== 'undefined' ) {
|
170 |
+
rule_end_date = new Date( '1 January 2000 ' + rtb_pickadate.schedule_open[open_key].time.end );
|
171 |
+
rule_end_time = [ rule_end_date.getHours(), rule_end_date.getMinutes() ];
|
172 |
+
} else {
|
173 |
+
rule_end_time = [ 24, 0 ]; // End of the day
|
174 |
+
}
|
175 |
+
|
176 |
+
valid_times.push( { from: rule_start_time, to: rule_end_time, inverted: true } );
|
177 |
+
|
178 |
+
}
|
179 |
+
}
|
180 |
+
}
|
181 |
+
}
|
182 |
+
|
183 |
+
rule_start_date = rule_start_time = rule_end_date = rule_end_time = null;
|
184 |
+
|
185 |
+
// Pass any valid times located
|
186 |
+
if ( valid_times.length > 1 ) {
|
187 |
+
timepicker.set( 'disable', valid_times );
|
188 |
+
|
189 |
+
return;
|
190 |
+
}
|
191 |
+
|
192 |
+
}
|
193 |
+
|
194 |
+
// Set it to always open if no rules have been defined
|
195 |
+
timepicker.set( 'enable', true );
|
196 |
+
timepicker.set( 'disable', false );
|
197 |
+
|
198 |
+
return;
|
199 |
+
}
|
200 |
+
|
201 |
+
});
|
docs/index.html
ADDED
@@ -0,0 +1,169 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="en-US">
|
3 |
+
<head>
|
4 |
+
|
5 |
+
<meta charset="UTF-8">
|
6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7 |
+
<title>Restaurant Reservations | How to install and use this plugin</title>
|
8 |
+
<link rel="profile" href="http://gmpg.org/xfn/11">
|
9 |
+
<meta name="robots" content="noindex,nofollow">
|
10 |
+
<link rel="stylesheet" href="style.css" type="text/css" media="all" />
|
11 |
+
|
12 |
+
</head>
|
13 |
+
|
14 |
+
<body>
|
15 |
+
|
16 |
+
<div id="toc">
|
17 |
+
<ul>
|
18 |
+
<li>
|
19 |
+
<a href="#install">Installation</a>
|
20 |
+
</li>
|
21 |
+
<li>
|
22 |
+
<a href="#setup">Quick Setup</a>
|
23 |
+
</li>
|
24 |
+
<li>
|
25 |
+
<a href="#manage_bookings">Manage bookings</a>
|
26 |
+
</li>
|
27 |
+
<li>
|
28 |
+
<a href="#schedule">Set the booking schedule</a>
|
29 |
+
</li>
|
30 |
+
<li>
|
31 |
+
<a href="#settings">The settings page</a>
|
32 |
+
</li>
|
33 |
+
<li>
|
34 |
+
<a href="#extend">Extend this plugin</a>
|
35 |
+
</li>
|
36 |
+
</ul>
|
37 |
+
</div>
|
38 |
+
|
39 |
+
<div id="content">
|
40 |
+
|
41 |
+
<h1>Guide to the Restaurant Reservations plugin for WordPress</h1>
|
42 |
+
<p>This guide will help you install and configure the plugin for your site.</p>
|
43 |
+
<p>Further support for this plugin can be found at <a href="http://themeofthecrop.com/?utm_source=Plugin&utm_medium=Plugin%20Help%20Documentation&utm_campaign=Restaurant%20Reservations">themeofthecrop.com</a>.</p>
|
44 |
+
|
45 |
+
<a name="install"></a>
|
46 |
+
<h2>Installation</h2>
|
47 |
+
<p>The following steps describe how to upload the Restaurant Reservations plugin to an
|
48 |
+
existing WordPress installation. If you have not yet installed WordPress, consult the
|
49 |
+
<a href="http://codex.wordpress.org/Installing_WordPress">WordPress
|
50 |
+
documentation</a> for more information.</p>
|
51 |
+
<ol>
|
52 |
+
<li>
|
53 |
+
Unpack the restaurant-reservations.zip file. It should create a folder
|
54 |
+
named <code>/restaurant-reservations/</code>.
|
55 |
+
</li>
|
56 |
+
<li>
|
57 |
+
Upload the <code>/restaurant-reservations/</code> folder to your
|
58 |
+
WordPress plugin directory at <code>/wp-content/plugins/</code>.
|
59 |
+
</li>
|
60 |
+
<li>
|
61 |
+
Go to your WordPress admin dashboard at yoursite.com/wp-admin and
|
62 |
+
click the <strong>Plugins</strong> item in the menu on the left.
|
63 |
+
</li>
|
64 |
+
<li>
|
65 |
+
Find <strong>Restaurant Reservations</strong> in the list of Plugins
|
66 |
+
and click the <strong>Activate</strong> link below the plugin name.
|
67 |
+
</li>
|
68 |
+
</ol>
|
69 |
+
<p>The plugin is now active. Next you'll need to get it setup.</p>
|
70 |
+
|
71 |
+
<a name="setup"></a>
|
72 |
+
<h2>Quick Setup</h2>
|
73 |
+
<p>To get this plugin working as quickly as possible, all you need to do is
|
74 |
+
display the booking form. There are three ways to do this:</p>
|
75 |
+
<ol>
|
76 |
+
<li>
|
77 |
+
Automatically add the booking form to any page on your website. Go
|
78 |
+
to the <strong>Booking > Settings</strong> page in your admin
|
79 |
+
dashboard. Select one of your pages in the dropdown list for
|
80 |
+
<strong>Booking Page</strong>. The booking form will automatically
|
81 |
+
be added to that page.
|
82 |
+
</li>
|
83 |
+
<li>
|
84 |
+
Add the Booking Form widget. Go to <strong>Appearance > Widgets</strong>
|
85 |
+
and find the Booking Form widget. Add that to any sidebar to display
|
86 |
+
your booking form.
|
87 |
+
</li>
|
88 |
+
<li>
|
89 |
+
Use the <code>[booking-form]</code> shortcode to add your booking
|
90 |
+
form to any page or post on your website. Simply paste
|
91 |
+
<code>[booking-form]</code> into the content of any page or post.
|
92 |
+
</li>
|
93 |
+
</ol>
|
94 |
+
<p>Once you've done that, your users can submit their bookings. The next
|
95 |
+
section will describe how to view, confirm and reject them in your
|
96 |
+
admin panel.</p>
|
97 |
+
|
98 |
+
<a name="manage_bookings"></a>
|
99 |
+
<h2>Manage Bookings</h2>
|
100 |
+
<p>To view, confirm and reject bookings, go to the <strong>Bookings</strong>
|
101 |
+
page in your admin panel. This will show you all of your bookings with
|
102 |
+
the current status on the right.</p>
|
103 |
+
<p>On this page you can filter bookings by date, filter bookings by status
|
104 |
+
and change a booking's status. <em>Tip: you can combine filters, so that you
|
105 |
+
are viewing just Pending bookings for Today</em>.</p>
|
106 |
+
<p>When a new booking is made,
|
107 |
+
it is set to Pending. You can then either set the status to Confirmed
|
108 |
+
or Closed. The user will then receive an email letting them know that
|
109 |
+
their booking was confirmed or rejected.</p>
|
110 |
+
<p>To change the status of any booking, click the checkbox to the left of
|
111 |
+
the booking. From the <strong>Bulk Actions</strong> dropdown list,
|
112 |
+
select the appropriate action (ie - Set to Confirmed) and click
|
113 |
+
<strong>Apply</strong>.</p>
|
114 |
+
<p><em>Remember, a user will receive an email notification, so only change
|
115 |
+
the status when you're ready.</em> At the moment, a notification is sent
|
116 |
+
to the user when they make a booking request and when that request is
|
117 |
+
changed from pending to confirmed or pending to closed.</p>
|
118 |
+
|
119 |
+
<a name="schedule"></a>
|
120 |
+
<h2>Set the booking schedule</h2>
|
121 |
+
<p>You probably only want to accept bookings when you're open. You may only
|
122 |
+
accept bookings during certain opening hours. You can define scheduling
|
123 |
+
rules to limit when people can submit booking requests.</p>
|
124 |
+
<p>To do this, go to the <strong>Bookings > Settings</strong> page in the
|
125 |
+
admin dashboard. On this page, click the <strong>Booking Schedule</strong>
|
126 |
+
tab.</p>
|
127 |
+
<p>Under <strong>Schedule</strong>, click the <strong>Add new scheduling
|
128 |
+
rule</strong> button. Set the weekdays and opening hours.</p>
|
129 |
+
<p>These rules are <em>complimentary</em>. That means if you have two rules
|
130 |
+
which occur at different times on the same day, bookings will be
|
131 |
+
accepted during both times. This allows you to set morning and evening
|
132 |
+
hours, for example.</p>
|
133 |
+
<p>There are three other settings on this page.</p>
|
134 |
+
<p><strong>Exceptions</strong> allows you to specify custom scheduling rules
|
135 |
+
on certain dates. This will help you indicate if you're closed on
|
136 |
+
a holiday or have special opening hours one day.</p>
|
137 |
+
<p><strong>Early Bookings</strong> and <strong>Late Bookings</strong> allow
|
138 |
+
you to set limits on how early or late people can make bookings. If you
|
139 |
+
don't want to recieve a booking for 4:45pm at 4:40pm, set the
|
140 |
+
<strong>Late Bookings</strong> setting to something more appropriate.</p>
|
141 |
+
|
142 |
+
<a name="settings"></a>
|
143 |
+
<h2>The settings page</h2>
|
144 |
+
<p>In addition to the <a href="#schedule">Booking Schedule</a>, there are
|
145 |
+
two other settings tabs. These settings allow you to customize
|
146 |
+
messages when people submit a booking, notification emails sent out
|
147 |
+
when bookings are confirmed or rejected, and more.</p>
|
148 |
+
<p>If you want to receive an admin notification email when a new booking
|
149 |
+
request is made, make sure to check the box next to <strong>Admin
|
150 |
+
Notification</strong> under the Notificaitons tab.</p>
|
151 |
+
|
152 |
+
<a name="extend"></a>
|
153 |
+
<h2>Extend this plugin</h2>
|
154 |
+
<p>This plugin has been designed from the ground up to be extensible and
|
155 |
+
customizable. There are dozens of hooks to allow you to customize the
|
156 |
+
functionality and output. These include hooks to completely replace the
|
157 |
+
booking form's HTML output, add new settings, add new booking form
|
158 |
+
information, and add your own notifications.</p>
|
159 |
+
<p>If you have any questions, please
|
160 |
+
<a href="http://themeofthecrop.com/about/support/?utm_source=Plugin&utm_medium=Plugin%20Help%20Documentation&utm_campaign=Restaurant%20Reservations">contact me</a>.</p>
|
161 |
+
<p>This plugin is available on
|
162 |
+
<a href="https://github.com/NateWr/restaurant-reservations">GitHub</a>
|
163 |
+
if you would like to contribute pull requests.</p>
|
164 |
+
|
165 |
+
</div>
|
166 |
+
|
167 |
+
</body>
|
168 |
+
|
169 |
+
</html>
|
docs/style.css
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* Documentation Markup
|
3 |
+
**********************/
|
4 |
+
body {
|
5 |
+
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
6 |
+
background-color: #eee;
|
7 |
+
margin: 0;
|
8 |
+
}
|
9 |
+
li {
|
10 |
+
padding-bottom: 5px;
|
11 |
+
}
|
12 |
+
#toc {
|
13 |
+
position: absolute;
|
14 |
+
width: 250px;
|
15 |
+
}
|
16 |
+
#content {
|
17 |
+
padding: 20px;
|
18 |
+
margin-left: 300px;
|
19 |
+
background-color: #fff;
|
20 |
+
box-shadow: 0 0 5px #000;
|
21 |
+
}
|
22 |
+
#toc>ul>li {
|
23 |
+
padding-bottom: 10px;
|
24 |
+
padding-top: 10px;
|
25 |
+
}
|
26 |
+
code {
|
27 |
+
font-family: Courier, monospace;
|
28 |
+
padding: 2px 5px;
|
29 |
+
background-color: #eee;
|
30 |
+
border-radius: 2px;
|
31 |
+
}
|
includes/Booking.class.php
ADDED
@@ -0,0 +1,421 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
public function __construct() {}
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Load the booking information from a WP_Post object or an ID
|
30 |
+
*
|
31 |
+
* @uses load_wp_post()
|
32 |
+
* @since 0.0.1
|
33 |
+
*/
|
34 |
+
public function load_post( $post ) {
|
35 |
+
|
36 |
+
if ( is_int( $post ) || is_string( $post ) ) {
|
37 |
+
$post = get_post( $post );
|
38 |
+
}
|
39 |
+
|
40 |
+
if ( get_class( $post ) == 'WP_Post' && $post->post_type == RTB_BOOKING_POST_TYPE ) {
|
41 |
+
$this->load_wp_post( $post );
|
42 |
+
return true;
|
43 |
+
} else {
|
44 |
+
return false;
|
45 |
+
}
|
46 |
+
|
47 |
+
}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Load data from WP post object and retrieve metadata
|
51 |
+
*
|
52 |
+
* @uses load_post_metadata()
|
53 |
+
* @since 0.0.1
|
54 |
+
*/
|
55 |
+
public function load_wp_post( $post ) {
|
56 |
+
|
57 |
+
// Store post for access to other data if needed by extensions
|
58 |
+
$this->post = $post;
|
59 |
+
|
60 |
+
$this->ID = $post->ID;
|
61 |
+
$this->name = $post->post_title;
|
62 |
+
$this->date = $this->format_date( $post->post_date );
|
63 |
+
$this->message = apply_filters( 'the_content', $post->post_content );
|
64 |
+
$this->post_status = $post->post_status;
|
65 |
+
|
66 |
+
$this->load_post_metadata();
|
67 |
+
|
68 |
+
do_action( 'rtb_booking_load_post_data', $this, $post );
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Store metadata for post
|
73 |
+
* @since 0.0.1
|
74 |
+
*/
|
75 |
+
public function load_post_metadata() {
|
76 |
+
|
77 |
+
$meta_defaults = array(
|
78 |
+
'party' => '',
|
79 |
+
'email' => '',
|
80 |
+
'phone' => '',
|
81 |
+
'date_submission' => '',
|
82 |
+
);
|
83 |
+
|
84 |
+
$meta_defaults = apply_filters( 'rtb_booking_metadata_defaults', $meta_defaults );
|
85 |
+
|
86 |
+
if ( is_array( $meta = get_post_meta( $this->ID, 'rtb', true ) ) ) {
|
87 |
+
$meta = array_merge( $meta_defaults, get_post_meta( $this->ID, 'rtb', true ) );
|
88 |
+
} else {
|
89 |
+
$meta = $meta_defaults;
|
90 |
+
}
|
91 |
+
|
92 |
+
$this->party = $meta['party'];
|
93 |
+
$this->email = $meta['email'];
|
94 |
+
$this->phone = $meta['phone'];
|
95 |
+
$this->date_submission = $meta['date_submission'];
|
96 |
+
}
|
97 |
+
|
98 |
+
/**
|
99 |
+
* Format date
|
100 |
+
* @since 0.0.1
|
101 |
+
*/
|
102 |
+
public function format_date( $date ) {
|
103 |
+
$date = mysql2date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $date);
|
104 |
+
return apply_filters( 'get_the_date', $date );
|
105 |
+
}
|
106 |
+
|
107 |
+
/**
|
108 |
+
* Insert a new booking submission into the database
|
109 |
+
*
|
110 |
+
* Validates the data, adds it to the database and executes notifications
|
111 |
+
* @since 0.0.1
|
112 |
+
*/
|
113 |
+
public function insert_booking() {
|
114 |
+
|
115 |
+
// Check if this request has already been processed. If multiple forms
|
116 |
+
// exist on the same page, this prevents a single submission from
|
117 |
+
// being added twice.
|
118 |
+
if ( $this->request_processed === true ) {
|
119 |
+
return true;
|
120 |
+
}
|
121 |
+
|
122 |
+
$this->request_processed = true;
|
123 |
+
|
124 |
+
$this->validate_submission();
|
125 |
+
if ( $this->is_valid_submission() === false ) {
|
126 |
+
return false;
|
127 |
+
}
|
128 |
+
|
129 |
+
if ( $this->insert_post_data() === false ) {
|
130 |
+
return false;
|
131 |
+
} else {
|
132 |
+
$this->request_inserted = true;
|
133 |
+
}
|
134 |
+
|
135 |
+
do_action( 'rtb_insert_booking', $this );
|
136 |
+
|
137 |
+
return true;
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Validate submission data. Expects to find data in $_POST.
|
142 |
+
* @since 0.0.1
|
143 |
+
*/
|
144 |
+
public function validate_submission() {
|
145 |
+
|
146 |
+
$this->validation_errors = array();
|
147 |
+
|
148 |
+
// Date
|
149 |
+
$date = empty( $_POST['rtb-date'] ) ? false : $_POST['rtb-date'];
|
150 |
+
if ( $date === false ) {
|
151 |
+
$this->validation_errors[] = array(
|
152 |
+
'field' => 'date',
|
153 |
+
'error_msg' => 'Booking request missing date',
|
154 |
+
'message' => __( 'Please enter the date you would like to book.', RTB_TEXTDOMAIN ),
|
155 |
+
);
|
156 |
+
|
157 |
+
} else {
|
158 |
+
try {
|
159 |
+
$date = new DateTime( $_POST['rtb-date'] );
|
160 |
+
} catch ( Exception $e ) {
|
161 |
+
$this->validation_errors[] = array(
|
162 |
+
'field' => 'date',
|
163 |
+
'error_msg' => $e->getMessage(),
|
164 |
+
'message' => __( 'The date you entered is not valid. Please select from one of the dates in the calendar.', RTB_TEXTDOMAIN ),
|
165 |
+
);
|
166 |
+
}
|
167 |
+
}
|
168 |
+
|
169 |
+
// Time
|
170 |
+
$time = empty( $_POST['rtb-time'] ) ? false : $_POST['rtb-time'];
|
171 |
+
if ( $time === false ) {
|
172 |
+
$this->validation_errors[] = array(
|
173 |
+
'field' => 'time',
|
174 |
+
'error_msg' => 'Booking request missing time',
|
175 |
+
'message' => __( 'Please enter the time you would like to book.', RTB_TEXTDOMAIN ),
|
176 |
+
);
|
177 |
+
|
178 |
+
} else {
|
179 |
+
try {
|
180 |
+
$time = new DateTime( $_POST['rtb-time'] );
|
181 |
+
} catch ( Exception $e ) {
|
182 |
+
$this->validation_errors[] = array(
|
183 |
+
'field' => 'time',
|
184 |
+
'error_msg' => $e->getMessage(),
|
185 |
+
'message' => __( 'The time you entered is not valid. Please select from one of the times provided.', RTB_TEXTDOMAIN ),
|
186 |
+
);
|
187 |
+
}
|
188 |
+
}
|
189 |
+
|
190 |
+
// Check against valid open dates/times
|
191 |
+
if ( is_object( $time ) && is_object( $date ) ) {
|
192 |
+
|
193 |
+
global $rtb_controller;
|
194 |
+
|
195 |
+
$request = new DateTime( $date->format( 'Y-m-d' ) . ' ' . $time->format( 'H:i:s' ) );
|
196 |
+
|
197 |
+
$early_bookings = $rtb_controller->settings->get_setting( 'early-bookings' );
|
198 |
+
if ( !empty( $early_bookings ) ) {
|
199 |
+
$early_bookings_seconds = $early_bookings * 24 * 60 * 60; // Advanced bookings allowance in seconds
|
200 |
+
if ( $request->format( 'U' ) > ( current_time( 'timestamp' ) + $early_bookings_seconds ) ) {
|
201 |
+
$this->validation_errors[] = array(
|
202 |
+
'field' => 'time',
|
203 |
+
'error_msg' => 'Booking request too far in the future',
|
204 |
+
'message' => sprintf( __( 'Sorry, bookings can not be made more than %s days in advance.', RTB_TEXTDOMAIN ), $early_bookings ),
|
205 |
+
);
|
206 |
+
}
|
207 |
+
}
|
208 |
+
|
209 |
+
$late_bookings = $rtb_controller->settings->get_setting( 'late-bookings' );
|
210 |
+
if ( empty( $late_bookings ) ) {
|
211 |
+
if ( $request->format( 'U' ) < current_time( 'timestamp' ) ) {
|
212 |
+
$this->validation_errors[] = array(
|
213 |
+
'field' => 'time',
|
214 |
+
'error_msg' => 'Booking request in the past',
|
215 |
+
'message' => __( 'Sorry, bookings can not be made in the past.', RTB_TEXTDOMAIN ),
|
216 |
+
);
|
217 |
+
}
|
218 |
+
|
219 |
+
} else {
|
220 |
+
$late_bookings_seconds = $late_bookings * 60; // Late bookings allowance in seconds
|
221 |
+
if ( $request->format( 'U' ) < ( current_time( 'timestamp' ) + $late_bookings_seconds ) ) {
|
222 |
+
$this->validation_errors[] = array(
|
223 |
+
'field' => 'time',
|
224 |
+
'error_msg' => 'Booking request made too close to the reserved time',
|
225 |
+
'message' => sprintf( __( 'Sorry, bookings must be made more than %s minutes in advance.', RTB_TEXTDOMAIN ), $late_bookings ),
|
226 |
+
);
|
227 |
+
}
|
228 |
+
}
|
229 |
+
|
230 |
+
// Check against scheduling exception rules
|
231 |
+
$exceptions = $rtb_controller->settings->get_setting( 'schedule-closed' );
|
232 |
+
if ( empty( $this->validation_errors ) && !empty( $exceptions ) ) {
|
233 |
+
$exception_is_active = false;
|
234 |
+
$datetime_is_valid = false;
|
235 |
+
foreach( $exceptions as $exception ) {
|
236 |
+
$excp_date = new DateTime( $exception['date'] );
|
237 |
+
if ( $excp_date->format( 'Y-m-d' ) == $request->format( 'Y-m-d' ) ) {
|
238 |
+
$exception_is_active = true;
|
239 |
+
|
240 |
+
// Closed all day
|
241 |
+
if ( empty( $exception['time'] ) ) {
|
242 |
+
continue;
|
243 |
+
}
|
244 |
+
|
245 |
+
$excp_start_time = empty( $exception['time']['start'] ) ? $request : new DateTime( $exception['date'] . ' ' . $exception['time']['start'] );
|
246 |
+
$excp_end_time = empty( $exception['time']['end'] ) ? $request : new DateTime( $exception['date'] . ' ' . $exception['time']['end'] );
|
247 |
+
|
248 |
+
if ( $request->format( 'U' ) >= $excp_start_time->format( 'U' ) && $request->format( 'U' ) <= $excp_end_time->format( 'U' ) ) {
|
249 |
+
$datetime_is_valid = true;
|
250 |
+
break;
|
251 |
+
}
|
252 |
+
}
|
253 |
+
}
|
254 |
+
|
255 |
+
if ( $exception_is_active && !$datetime_is_valid ) {
|
256 |
+
$this->validation_errors[] = array(
|
257 |
+
'field' => 'date',
|
258 |
+
'error_msg' => 'Booking request made on invalid date or time in an exception rule',
|
259 |
+
'message' => __( 'Sorry, no bookings are being accepted then.', RTB_TEXTDOMAIN ),
|
260 |
+
);
|
261 |
+
}
|
262 |
+
}
|
263 |
+
|
264 |
+
// Check against weekly scheduling rules
|
265 |
+
$rules = $rtb_controller->settings->get_setting( 'schedule-open' );
|
266 |
+
if ( empty( $exception_is_active ) && empty( $this->validation_errors ) && !empty( $rules ) ) {
|
267 |
+
$request_weekday = strtolower( $request->format( 'l' ) );
|
268 |
+
$time_is_valid = null;
|
269 |
+
$day_is_valid = null;
|
270 |
+
foreach( $rules as $rule ) {
|
271 |
+
|
272 |
+
if ( !empty( $rule['weekdays'][ $request_weekday ] ) ) {
|
273 |
+
$day_is_valid = true;
|
274 |
+
|
275 |
+
if ( empty( $rule['time'] ) ) {
|
276 |
+
$time_is_valid = true; // Days with no time values are open all day
|
277 |
+
break;
|
278 |
+
}
|
279 |
+
|
280 |
+
$too_early = true;
|
281 |
+
$too_late = true;
|
282 |
+
|
283 |
+
// Too early
|
284 |
+
if ( !empty( $rule['time']['start'] ) ) {
|
285 |
+
$rule_start_time = new DateTime( $request->format( 'Y-m-d' ) . ' ' . $rule['time']['start'] );
|
286 |
+
if ( $rule_start_time->format( 'U' ) <= $request->format( 'U' ) ) {
|
287 |
+
$too_early = false;
|
288 |
+
}
|
289 |
+
}
|
290 |
+
|
291 |
+
// Too late
|
292 |
+
if ( !empty( $rule['time']['end'] ) ) {
|
293 |
+
$rule_end_time = new DateTime( $request->format( 'Y-m-d' ) . ' ' . $rule['time']['end'] );
|
294 |
+
if ( $rule_end_time->format( 'U' ) >= $request->format( 'U' ) ) {
|
295 |
+
$too_late = false;
|
296 |
+
}
|
297 |
+
}
|
298 |
+
|
299 |
+
// Valid time found
|
300 |
+
if ( $too_early === false && $too_late === false) {
|
301 |
+
$time_is_valid = true;
|
302 |
+
break;
|
303 |
+
}
|
304 |
+
}
|
305 |
+
}
|
306 |
+
|
307 |
+
if ( !$day_is_valid ) {
|
308 |
+
$this->validation_errors[] = array(
|
309 |
+
'field' => 'date',
|
310 |
+
'error_msg' => 'Booking request made on an invalid date',
|
311 |
+
'message' => __( 'Sorry, no bookings are being accepted on that date.', RTB_TEXTDOMAIN ),
|
312 |
+
);
|
313 |
+
} elseif ( !$time_is_valid ) {
|
314 |
+
$this->validation_errors[] = array(
|
315 |
+
'field' => 'time',
|
316 |
+
'error_msg' => 'Booking request made at an invalid time',
|
317 |
+
'message' => __( 'Sorry, no bookings are being accepted at that time.', RTB_TEXTDOMAIN ),
|
318 |
+
);
|
319 |
+
}
|
320 |
+
}
|
321 |
+
|
322 |
+
// Accept the date if it has passed validation
|
323 |
+
if ( empty( $this->validation_errors ) ) {
|
324 |
+
$this->date = $request->format( 'Y-m-d H:i:s' );
|
325 |
+
}
|
326 |
+
}
|
327 |
+
|
328 |
+
// Save requested date/time values in case they need to be
|
329 |
+
// printed in the form again
|
330 |
+
$this->request_date = empty( $_POST['rtb-date'] ) ? '' : $_POST['rtb-date'];
|
331 |
+
$this->request_time = empty( $_POST['rtb-time'] ) ? '' : $_POST['rtb-time'];
|
332 |
+
|
333 |
+
// Name
|
334 |
+
$this->name = empty( $_POST['rtb-name'] ) ? '' : wp_strip_all_tags( sanitize_text_field( $_POST['rtb-name'] ), true ); // @todo should I limit length?
|
335 |
+
if ( empty( $this->name ) ) {
|
336 |
+
$this->validation_errors[] = array(
|
337 |
+
'field' => 'name',
|
338 |
+
'post_variable' => $this->name,
|
339 |
+
'message' => __( 'Please enter a name for this booking.', RTB_TEXTDOMAIN ),
|
340 |
+
);
|
341 |
+
}
|
342 |
+
|
343 |
+
// Party
|
344 |
+
$this->party = empty( $_POST['rtb-party'] ) ? '' : sanitize_text_field( $_POST['rtb-party'] );
|
345 |
+
if ( empty( $this->party ) ) {
|
346 |
+
$this->validation_errors[] = array(
|
347 |
+
'field' => 'party',
|
348 |
+
'post_variable' => $this->party,
|
349 |
+
'message' => __( 'Please let us know how many people will be in your party.', RTB_TEXTDOMAIN ),
|
350 |
+
);
|
351 |
+
}
|
352 |
+
|
353 |
+
// Email/Phone
|
354 |
+
$this->email = empty( $_POST['rtb-email'] ) ? '' : sanitize_text_field( $_POST['rtb-email'] ); // @todo email validation? send notification back to form on bad email address.
|
355 |
+
if ( empty( $this->email ) ) {
|
356 |
+
$this->validation_errors[] = array(
|
357 |
+
'field' => 'email',
|
358 |
+
'post_variable' => $this->email,
|
359 |
+
'message' => __( 'Please enter an email address so we can confirm your booking.', RTB_TEXTDOMAIN ),
|
360 |
+
);
|
361 |
+
}
|
362 |
+
|
363 |
+
// Phone/Message
|
364 |
+
$this->phone = empty( $_POST['rtb-phone'] ) ? '' : sanitize_text_field( $_POST['rtb-phone'] );
|
365 |
+
$this->message = empty( $_POST['rtb-message'] ) ? '' : sanitize_text_field( $_POST['rtb-message'] );
|
366 |
+
|
367 |
+
do_action( 'rtb_validate_booking_submission', $this );
|
368 |
+
|
369 |
+
}
|
370 |
+
|
371 |
+
/**
|
372 |
+
* Check if submission is valid
|
373 |
+
* @since 0.0.1
|
374 |
+
*/
|
375 |
+
public function is_valid_submission() {
|
376 |
+
if ( !count( $this->validation_errors ) ) {
|
377 |
+
return true;
|
378 |
+
}
|
379 |
+
return false;
|
380 |
+
}
|
381 |
+
|
382 |
+
/**
|
383 |
+
* Insert post data for a new booking.
|
384 |
+
* @since 0.0.1
|
385 |
+
*/
|
386 |
+
public function insert_post_data() {
|
387 |
+
|
388 |
+
$args = array(
|
389 |
+
'post_type' => RTB_BOOKING_POST_TYPE,
|
390 |
+
'post_title' => $this->name,
|
391 |
+
'post_content' => $this->message,
|
392 |
+
'post_date' => $this->date,
|
393 |
+
'post_status' => 'pending',
|
394 |
+
);
|
395 |
+
|
396 |
+
$args = apply_filters( 'rtb_insert_booking_data', $args, $this );
|
397 |
+
|
398 |
+
$id = wp_insert_post( $args );
|
399 |
+
|
400 |
+
if ( is_wp_error( $id ) || $id === false ) {
|
401 |
+
$this->insert_post_error = $id;
|
402 |
+
return false;
|
403 |
+
} else {
|
404 |
+
$this->ID = $id;
|
405 |
+
}
|
406 |
+
|
407 |
+
$meta = array(
|
408 |
+
'party' => $this->party,
|
409 |
+
'email' => $this->email,
|
410 |
+
'phone' => $this->phone,
|
411 |
+
'date_submission' => current_time( 'timestamp' ),
|
412 |
+
);
|
413 |
+
|
414 |
+
$meta = apply_filters( 'rtb_insert_booking_metadata', $meta, $this );
|
415 |
+
|
416 |
+
return update_post_meta( $this->ID, 'rtb', $meta );
|
417 |
+
|
418 |
+
}
|
419 |
+
|
420 |
+
}
|
421 |
+
} // endif;
|
includes/CustomPostTypes.class.php
ADDED
@@ -0,0 +1,231 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 stasuses
|
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 |
+
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Initialize custom post types
|
31 |
+
* @since 0.1
|
32 |
+
*/
|
33 |
+
public function load_cpts() {
|
34 |
+
|
35 |
+
// Define the booking custom post type
|
36 |
+
$args = array(
|
37 |
+
'labels' => array(
|
38 |
+
'name' => __( 'Bookings', RTB_TEXTDOMAIN ),
|
39 |
+
'singular_name' => __( 'Booking', RTB_TEXTDOMAIN ),
|
40 |
+
'menu_name' => __( 'Bookings', RTB_TEXTDOMAIN ),
|
41 |
+
'name_admin_bar' => __( 'Bookings', RTB_TEXTDOMAIN ),
|
42 |
+
'add_new' => __( 'Add New', RTB_TEXTDOMAIN ),
|
43 |
+
'add_new_item' => __( 'Add New Booking', RTB_TEXTDOMAIN ),
|
44 |
+
'edit_item' => __( 'Edit Booking', RTB_TEXTDOMAIN ),
|
45 |
+
'new_item' => __( 'New Booking', RTB_TEXTDOMAIN ),
|
46 |
+
'view_item' => __( 'View Booking', RTB_TEXTDOMAIN ),
|
47 |
+
'search_items' => __( 'Search Bookings', RTB_TEXTDOMAIN ),
|
48 |
+
'not_found' => __( 'No bookings found', RTB_TEXTDOMAIN ),
|
49 |
+
'not_found_in_trash' => __( 'No bookings found in trash', RTB_TEXTDOMAIN ),
|
50 |
+
'all_items' => __( 'All Bookings', RTB_TEXTDOMAIN ),
|
51 |
+
),
|
52 |
+
'menu_icon' => 'dashicons-calendar',
|
53 |
+
'public' => false,
|
54 |
+
'supports' => array(
|
55 |
+
'title',
|
56 |
+
'revisions'
|
57 |
+
)
|
58 |
+
);
|
59 |
+
|
60 |
+
// Create filter so addons can modify the arguments
|
61 |
+
$args = apply_filters( 'rtb_booking_args', $args );
|
62 |
+
|
63 |
+
// Add an action so addons can hook in before the post type is registered
|
64 |
+
do_action( 'rtb_booking_pre_register' );
|
65 |
+
|
66 |
+
// Register the post type
|
67 |
+
register_post_type( RTB_BOOKING_POST_TYPE, $args );
|
68 |
+
|
69 |
+
// Add an action so addons can hook in after the post type is registered
|
70 |
+
do_action( 'rtb_booking_post_register' );
|
71 |
+
}
|
72 |
+
|
73 |
+
/**
|
74 |
+
* Set an array of valid booking statuses and register any custom statuses
|
75 |
+
* @since 0.0.1
|
76 |
+
*/
|
77 |
+
public function set_booking_statuses() {
|
78 |
+
|
79 |
+
$this->booking_statuses['pending'] = array(
|
80 |
+
'label' => _x( 'Pending', 'Booking status when it is pending review', RTB_TEXTDOMAIN ),
|
81 |
+
'default' => true, // Whether or not this status is part of WP Core
|
82 |
+
'user_selectable' => true, // Whether or not a user can set a booking to this status
|
83 |
+
);
|
84 |
+
|
85 |
+
$this->booking_statuses['confirmed'] = array (
|
86 |
+
'label' => _x( 'Confirmed', 'Booking status for a confirmed booking', RTB_TEXTDOMAIN ),
|
87 |
+
'default' => false, // Whether or not this status is part of WP Core
|
88 |
+
'user_selectable' => true, // Whether or not a user can set a booking to this status
|
89 |
+
'public' => false,
|
90 |
+
'exclude_from_search' => true,
|
91 |
+
'show_in_admin_all_list' => true,
|
92 |
+
'show_in_admin_status_list' => true,
|
93 |
+
'label_count' => _n_noop( 'Confirmed <span class="count">(%s)</span>', 'Confirmed <span class="count">(%s)</span>', RTB_TEXTDOMAIN ),
|
94 |
+
);
|
95 |
+
|
96 |
+
$this->booking_statuses['closed'] = array(
|
97 |
+
'label' => _x( 'Closed', 'Booking status for a closed booking', RTB_TEXTDOMAIN ),
|
98 |
+
'default' => false, // Whether or not this status is part of WP Core
|
99 |
+
'user_selectable' => true, // Whether or not a user can set a booking to this status
|
100 |
+
'public' => false,
|
101 |
+
'exclude_from_search' => true,
|
102 |
+
'show_in_admin_all_list' => true,
|
103 |
+
'show_in_admin_status_list' => true,
|
104 |
+
'label_count' => _n_noop( 'Closed <span class="count">(%s)</span>', 'Closed <span class="count">(%s)</span>', RTB_TEXTDOMAIN )
|
105 |
+
);
|
106 |
+
|
107 |
+
// Let addons hook in to add/edit/remove post statuses
|
108 |
+
$this->booking_statuses = apply_filters( 'rtb_post_statuses_args', $this->booking_statuses );
|
109 |
+
|
110 |
+
// Register the custom post statuses
|
111 |
+
foreach ( $this->booking_statuses as $status => $args ) {
|
112 |
+
if ( $args['default'] === false ) {
|
113 |
+
register_post_status( $status, $args );
|
114 |
+
}
|
115 |
+
}
|
116 |
+
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* Print an HTML element to select a booking status
|
121 |
+
* @since 0.0.1
|
122 |
+
* @note This is no longer used in the bookings table, but it could be
|
123 |
+
* useful in the future, so leave it in for now (0.0.1) until the plugin is
|
124 |
+
* more fleshed out.
|
125 |
+
*/
|
126 |
+
public function print_booking_status_select( $current = false ) {
|
127 |
+
|
128 |
+
if ( $current === false ) {
|
129 |
+
$current = 'none';
|
130 |
+
}
|
131 |
+
|
132 |
+
// Output stored select field if available
|
133 |
+
if ( !empty( $this->status_select_html[$current] ) ) {
|
134 |
+
return $this->status_select_html[$current];
|
135 |
+
}
|
136 |
+
|
137 |
+
ob_start();
|
138 |
+
?>
|
139 |
+
|
140 |
+
<select name="rtb-select-status">
|
141 |
+
<?php foreach ( $this->booking_statuses as $status => $args ) : ?>
|
142 |
+
<?php if ( $args['user_selectable'] === true ) : ?>
|
143 |
+
<option value="<?php echo esc_attr( $status ); ?>"<?php echo $status == $current ? ' selected="selected"' : ''; ?>><?php echo esc_attr( $args['label'] ); ?></option>
|
144 |
+
<?php endif; ?>
|
145 |
+
<?php endforeach; ?>
|
146 |
+
</select>
|
147 |
+
|
148 |
+
<?php
|
149 |
+
$output = ob_get_clean();
|
150 |
+
|
151 |
+
// Store output so we don't need to loop for every row
|
152 |
+
$this->status_select_html[$current] = $output;
|
153 |
+
|
154 |
+
return $output;
|
155 |
+
|
156 |
+
}
|
157 |
+
|
158 |
+
/**
|
159 |
+
* Delete a booking request (or send to trash)
|
160 |
+
*
|
161 |
+
* @since 0.0.1
|
162 |
+
*/
|
163 |
+
public function delete_booking( $id ) {
|
164 |
+
|
165 |
+
if ( !current_user_can( 'manage_bookings' ) ) {
|
166 |
+
return;
|
167 |
+
}
|
168 |
+
|
169 |
+
// If we're already looking at trashed posts, delete it for good.
|
170 |
+
// Otherwise, just send it to trash.
|
171 |
+
if ( !empty( $_GET['status'] ) && $_GET['status'] == 'trash' ) {
|
172 |
+
$screen = get_current_screen();
|
173 |
+
if ( $screen->base == 'toplevel_page_rtb-bookings' ) {
|
174 |
+
$result = wp_delete_post( $id, true );
|
175 |
+
}
|
176 |
+
} else {
|
177 |
+
$result = wp_trash_post( $id );
|
178 |
+
}
|
179 |
+
|
180 |
+
if ( $result === false ) {
|
181 |
+
return false;
|
182 |
+
} else {
|
183 |
+
return true;
|
184 |
+
}
|
185 |
+
}
|
186 |
+
|
187 |
+
/**
|
188 |
+
* Update a booking status.
|
189 |
+
* @since 0.0.1
|
190 |
+
*/
|
191 |
+
function update_booking_status( $id, $status ) {
|
192 |
+
|
193 |
+
if ( !current_user_can( 'manage_bookings' ) ) {
|
194 |
+
return;
|
195 |
+
}
|
196 |
+
|
197 |
+
if ( !$this->is_valid_booking_status( $status ) ) {
|
198 |
+
return false;
|
199 |
+
}
|
200 |
+
|
201 |
+
$booking = get_post( $id );
|
202 |
+
|
203 |
+
if ( is_wp_error( $booking ) || !is_object( $booking ) ) {
|
204 |
+
return false;
|
205 |
+
}
|
206 |
+
|
207 |
+
if ( $booking->post_status === $status ) {
|
208 |
+
return null;
|
209 |
+
}
|
210 |
+
|
211 |
+
$result = wp_update_post(
|
212 |
+
array(
|
213 |
+
'ID' => $id,
|
214 |
+
'post_status' => $status,
|
215 |
+
'edit_date' => current_time( 'mysql' ),
|
216 |
+
)
|
217 |
+
);
|
218 |
+
|
219 |
+
return $result ? true : false;
|
220 |
+
}
|
221 |
+
|
222 |
+
/**
|
223 |
+
* Check if status is valid for bookings
|
224 |
+
* @since 0.0.1
|
225 |
+
*/
|
226 |
+
public function is_valid_booking_status( $status ) {
|
227 |
+
return isset( $this->booking_statuses[$status] ) ? true : false;
|
228 |
+
}
|
229 |
+
|
230 |
+
}
|
231 |
+
} // endif;
|
includes/Notification.Email.class.php
ADDED
@@ -0,0 +1,187 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
* Prepare and validate notification data
|
54 |
+
*
|
55 |
+
* @return boolean if the data is valid and ready for transport
|
56 |
+
* @since 0.0.1
|
57 |
+
*/
|
58 |
+
public function prepare_notification() {
|
59 |
+
|
60 |
+
$this->set_to_email();
|
61 |
+
$this->set_from_email();
|
62 |
+
$this->set_subject();
|
63 |
+
$this->set_headers();
|
64 |
+
$this->set_message();
|
65 |
+
|
66 |
+
// Return false if we're missing any of the required information
|
67 |
+
if ( empty( $this->to_email) ||
|
68 |
+
empty( $this->from_email) ||
|
69 |
+
empty( $this->from_name) ||
|
70 |
+
empty( $this->subject) ||
|
71 |
+
empty( $this->headers) ||
|
72 |
+
empty( $this->message) ) {
|
73 |
+
return false;
|
74 |
+
}
|
75 |
+
|
76 |
+
return true;
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Set to email
|
81 |
+
* @since 0.0.1
|
82 |
+
*/
|
83 |
+
public function set_to_email() {
|
84 |
+
|
85 |
+
if ( $this->target == 'user' ) {
|
86 |
+
$this->to_email = empty( $this->booking->email ) ? null : $this->booking->email;
|
87 |
+
|
88 |
+
} else {
|
89 |
+
global $rtb_controller;
|
90 |
+
$this->to_email = $rtb_controller->settings->get_setting( 'admin-email-address' );
|
91 |
+
}
|
92 |
+
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Set from email
|
97 |
+
* @since 0.0.1
|
98 |
+
*/
|
99 |
+
public function set_from_email() {
|
100 |
+
|
101 |
+
global $rtb_controller;
|
102 |
+
|
103 |
+
$this->from_email = $rtb_controller->settings->get_setting( 'reply-to-address' );
|
104 |
+
$this->from_name = $rtb_controller->settings->get_setting( 'reply-to-name' );
|
105 |
+
|
106 |
+
}
|
107 |
+
|
108 |
+
/**
|
109 |
+
* Set email subject
|
110 |
+
* @since 0.0.1
|
111 |
+
*/
|
112 |
+
public function set_subject() {
|
113 |
+
|
114 |
+
global $rtb_controller;
|
115 |
+
|
116 |
+
if( $this->event == 'new_submission' ) {
|
117 |
+
if ( $this->target == 'admin' ) {
|
118 |
+
$this->subject = $rtb_controller->settings->get_setting( 'subject-booking-admin' );
|
119 |
+
} elseif ( $this->target == 'user' ) {
|
120 |
+
$this->subject = $rtb_controller->settings->get_setting( 'subject-booking-user' );
|
121 |
+
}
|
122 |
+
|
123 |
+
} elseif ( $this->event == 'pending_to_confirmed' ) {
|
124 |
+
$this->subject = $rtb_controller->settings->get_setting( 'subject-confirmed-user' );
|
125 |
+
|
126 |
+
} elseif ( $this->event == 'pending_to_closed' ) {
|
127 |
+
$this->subject = $rtb_controller->settings->get_setting( 'subject-rejected-user' );
|
128 |
+
}
|
129 |
+
|
130 |
+
}
|
131 |
+
|
132 |
+
/**
|
133 |
+
* Set email headers
|
134 |
+
* @since 0.0.1
|
135 |
+
*/
|
136 |
+
public function set_headers( $headers = null ) {
|
137 |
+
|
138 |
+
$headers = "From: " . stripslashes_deep( html_entity_decode( $this->from_name, ENT_COMPAT, 'UTF-8' ) ) . " <" . $this->from_email . ">\r\n";
|
139 |
+
$headers .= "Reply-To: ". $this->from_email . "\r\n";
|
140 |
+
$headers .= "Content-Type: text/html; charset=utf-8\r\n";
|
141 |
+
$this->headers = apply_filters( 'rtb_notification_email_headers', $headers, $this );
|
142 |
+
|
143 |
+
}
|
144 |
+
|
145 |
+
/**
|
146 |
+
* Set email message body
|
147 |
+
* @since 0.0.1
|
148 |
+
*/
|
149 |
+
public function set_message() {
|
150 |
+
|
151 |
+
$settings = get_option( 'rtb-settings' );
|
152 |
+
|
153 |
+
if ( $this->event == 'new_submission' ) {
|
154 |
+
if ( $this->target == 'user' ) {
|
155 |
+
$template = 'template-booking-user';
|
156 |
+
} elseif ( $this->target == 'admin' ) {
|
157 |
+
$template = 'template-booking-admin';
|
158 |
+
}
|
159 |
+
|
160 |
+
} elseif ( $this->event == 'pending_to_confirmed' ) {
|
161 |
+
if ( $this->target == 'user' ) {
|
162 |
+
$template = 'template-confirmed-user';
|
163 |
+
}
|
164 |
+
|
165 |
+
} elseif ( $this->event == 'pending_to_closed' ) {
|
166 |
+
if ( $this->target == 'user' ) {
|
167 |
+
$template = 'template-rejected-user';
|
168 |
+
}
|
169 |
+
}
|
170 |
+
|
171 |
+
if ( !isset( $template ) ) {
|
172 |
+
$this->message = '';
|
173 |
+
} else {
|
174 |
+
$this->message = wpautop( $this->process_template( $this->get_template( $template ) ) );
|
175 |
+
}
|
176 |
+
|
177 |
+
}
|
178 |
+
|
179 |
+
/**
|
180 |
+
* Send notification
|
181 |
+
* @since 0.0.1
|
182 |
+
*/
|
183 |
+
public function send_notification() {
|
184 |
+
wp_mail( $this->to_email, $this->subject, $this->message, $this->headers );
|
185 |
+
}
|
186 |
+
}
|
187 |
+
} // endif;
|
includes/Notification.class.php
ADDED
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
|
81 |
+
$template_tags = array(
|
82 |
+
'{user_name}' => $this->booking->name,
|
83 |
+
'{party}' => $this->booking->party,
|
84 |
+
'{date}' => $this->booking->date,
|
85 |
+
'{bookings_link}' => '<a href="' . admin_url( 'admin.php?page=rtb-bookings&status=pending' ) . '">' . __( 'View pending bookings', RTB_TEXTDOMAIN ) . '</a>',
|
86 |
+
'{confirm_link}' => '<a href="' . admin_url( 'admin.php?page=rtb-bookings&rtb-quicklink=confirm&booking=' . esc_attr( $this->booking->ID ) ) . '">' . __( 'Confirm this booking', RTB_TEXTDOMAIN ) . '</a>',
|
87 |
+
'{close_link}' => '<a href="' . admin_url( 'admin.php?page=rtb-bookings&rtb-quicklink=close&booking=' . esc_attr( $this->booking->ID ) ) . '">' . __( 'Reject this booking', RTB_TEXTDOMAIN ) . '</a>',
|
88 |
+
'{site_name}' => get_bloginfo( 'name' ),
|
89 |
+
'{site_link}' => '<a href="' . home_url( '/' ) . '">' . get_bloginfo( 'name' ) . '</a>',
|
90 |
+
'{current_time}' => date( get_option( 'date_format' ), current_time( 'timestamp' ) ) . ' ' . date( get_option( 'time_format' ), current_time( 'timestamp' ) ),
|
91 |
+
);
|
92 |
+
|
93 |
+
$template_tags = apply_filters( 'rtb_notification_template_tags', $template_tags, $this );
|
94 |
+
|
95 |
+
return str_replace( array_keys( $template_tags ), array_values( $template_tags ), $message );
|
96 |
+
|
97 |
+
}
|
98 |
+
|
99 |
+
/**
|
100 |
+
* Send notification
|
101 |
+
* @since 0.0.1
|
102 |
+
*/
|
103 |
+
abstract public function send_notification();
|
104 |
+
|
105 |
+
}
|
106 |
+
} // endif;
|
includes/Notifications.class.php
ADDED
@@ -0,0 +1,187 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
* Hooks to execute notifications on
|
33 |
+
*
|
34 |
+
* Post Status Transitions and other hooks.
|
35 |
+
* See: http://codex.wordpress.org/Post_Status_Transitions
|
36 |
+
*
|
37 |
+
* @var array
|
38 |
+
* @since 0.0.1
|
39 |
+
*/
|
40 |
+
public $hooks;
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Set up notifications
|
44 |
+
* @since 0.0.1
|
45 |
+
*/
|
46 |
+
public function __construct() {
|
47 |
+
|
48 |
+
// Hook into all status changes that require notifications
|
49 |
+
$hooks = array(
|
50 |
+
'rtb_insert_booking' => array( $this, 'new_submission' ), // Booking submitted
|
51 |
+
'pending_to_confirmed' => array( $this, 'pending_to_confirmed' ), // Booking confirmed
|
52 |
+
'pending_to_closed' => array( $this, 'pending_to_closed' ), // Booking can not be made
|
53 |
+
);
|
54 |
+
|
55 |
+
$hooks = apply_filters( 'rtb_notification_transition_callbacks', $hooks );
|
56 |
+
|
57 |
+
foreach ( $hooks as $hook => $callback ) {
|
58 |
+
add_action( $hook, $callback );
|
59 |
+
}
|
60 |
+
|
61 |
+
// Register notifications
|
62 |
+
add_action( 'init', array( $this, 'register_notifications' ) );
|
63 |
+
}
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Register notifications
|
67 |
+
* @since 0.0.1
|
68 |
+
*/
|
69 |
+
public function register_notifications() {
|
70 |
+
|
71 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Notification.class.php' );
|
72 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Notification.Email.class.php' );
|
73 |
+
|
74 |
+
$this->notifications = array(
|
75 |
+
new rtbNotificationEmail( 'new_submission', 'user' ),
|
76 |
+
new rtbNotificationEmail( 'pending_to_confirmed', 'user' ),
|
77 |
+
new rtbNotificationEmail( 'pending_to_closed', 'user' ),
|
78 |
+
);
|
79 |
+
|
80 |
+
global $rtb_controller;
|
81 |
+
$admin_email_option = $rtb_controller->settings->get_setting( 'admin-email-option' );
|
82 |
+
if ( $admin_email_option ) {
|
83 |
+
$this->notifications[] = new rtbNotificationEmail( 'new_submission', 'admin' );
|
84 |
+
}
|
85 |
+
|
86 |
+
$this->notifications = apply_filters( 'rtb_notifications', $this->notifications );
|
87 |
+
}
|
88 |
+
|
89 |
+
/**
|
90 |
+
* Set booking data
|
91 |
+
* @since 0.0.1
|
92 |
+
*/
|
93 |
+
public function set_booking( $booking_post ) {
|
94 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
|
95 |
+
$this->booking = new rtbBooking();
|
96 |
+
$this->booking->load_wp_post( $booking_post );
|
97 |
+
}
|
98 |
+
|
99 |
+
/**
|
100 |
+
* New booking submissions
|
101 |
+
*
|
102 |
+
* @var object $booking
|
103 |
+
* @since 0.0.1
|
104 |
+
*/
|
105 |
+
public function new_submission( $booking ) {
|
106 |
+
|
107 |
+
// Bail early if $booking is not a rtbBooking object
|
108 |
+
if ( get_class( $booking ) != 'rtbBooking' ) {
|
109 |
+
return;
|
110 |
+
}
|
111 |
+
|
112 |
+
$this->booking = $booking;
|
113 |
+
|
114 |
+
$this->event( 'new_submission' );
|
115 |
+
|
116 |
+
}
|
117 |
+
|
118 |
+
/**
|
119 |
+
* Booking confirmed
|
120 |
+
* @since 0.0.1
|
121 |
+
*/
|
122 |
+
public function pending_to_confirmed( $booking_post ) {
|
123 |
+
|
124 |
+
if ( $booking_post->post_type != RTB_BOOKING_POST_TYPE ) {
|
125 |
+
return;
|
126 |
+
}
|
127 |
+
|
128 |
+
$this->set_booking( $booking_post );
|
129 |
+
|
130 |
+
$this->event( 'pending_to_confirmed' );
|
131 |
+
|
132 |
+
}
|
133 |
+
|
134 |
+
/**
|
135 |
+
* Booking can not be made
|
136 |
+
* @since 0.0.1
|
137 |
+
*/
|
138 |
+
public function pending_to_closed( $booking_post ) {
|
139 |
+
|
140 |
+
if ( $booking_post->post_type != RTB_BOOKING_POST_TYPE ) {
|
141 |
+
return;
|
142 |
+
}
|
143 |
+
|
144 |
+
$this->set_booking( $booking_post );
|
145 |
+
|
146 |
+
$this->event( 'pending_to_closed' );
|
147 |
+
|
148 |
+
}
|
149 |
+
|
150 |
+
/**
|
151 |
+
* Booking was confirmed and is now completed. Send out an optional
|
152 |
+
* follow-up email.
|
153 |
+
*
|
154 |
+
* @since 0.0.1
|
155 |
+
*/
|
156 |
+
public function confirmed_to_closed( $booking_post ) {
|
157 |
+
|
158 |
+
if ( $booking_post->post_type != RTB_BOOKING_POST_TYPE ) {
|
159 |
+
return;
|
160 |
+
}
|
161 |
+
|
162 |
+
$this->set_booking( $booking_post );
|
163 |
+
|
164 |
+
$this->event( 'confirmed_to_closed' );
|
165 |
+
|
166 |
+
}
|
167 |
+
|
168 |
+
/**
|
169 |
+
* Process notifications for an event
|
170 |
+
* @since 0.0.1
|
171 |
+
*/
|
172 |
+
public function event( $event ) {
|
173 |
+
|
174 |
+
foreach( $this->notifications as $notification ) {
|
175 |
+
|
176 |
+
if ( $event == $notification->event ) {
|
177 |
+
$notification->set_booking( $this->booking );
|
178 |
+
if ( $notification->prepare_notification() ) {
|
179 |
+
$notification->send_notification();
|
180 |
+
}
|
181 |
+
}
|
182 |
+
}
|
183 |
+
|
184 |
+
}
|
185 |
+
|
186 |
+
}
|
187 |
+
} // endif;
|
includes/Settings.class.php
ADDED
@@ -0,0 +1,540 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
if ( !defined( 'ABSPATH' ) ) exit;
|
3 |
+
|
4 |
+
if ( !class_exists( 'rtbBooking' ) ) {
|
5 |
+
/**
|
6 |
+
* Class to handle configurable settings for Restaurant Reservations
|
7 |
+
*
|
8 |
+
* @since 0.0.1
|
9 |
+
*/
|
10 |
+
class rtbSettings {
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Default values for settings
|
14 |
+
* @since 0.0.1
|
15 |
+
*/
|
16 |
+
public $defaults = array();
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Stored values for settings
|
20 |
+
* @since 0.0.1
|
21 |
+
*/
|
22 |
+
public $settings = array();
|
23 |
+
|
24 |
+
public function __construct() {
|
25 |
+
|
26 |
+
add_action( 'init', array( $this, 'set_defaults' ) );
|
27 |
+
|
28 |
+
add_action( 'init', array( $this, 'load_settings_panel' ) );
|
29 |
+
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Load the plugin's default settings
|
34 |
+
* @since 0.0.1
|
35 |
+
*/
|
36 |
+
public function set_defaults() {
|
37 |
+
|
38 |
+
$this->defaults = array(
|
39 |
+
|
40 |
+
'success-message' => _x( 'Thanks, your booking request is waiting to be confirmed. Updates will be sent to the email address you provided.', RTB_TEXTDOMAIN ),
|
41 |
+
'date-format' => _x( 'mmmm d, yyyy', 'Default date format for display. Must match formatting rules at http://amsul.ca/pickadate.js/date.htm#formatting-rules', RTB_TEXTDOMAIN ),
|
42 |
+
'time-format' => _x( 'h:i A', 'Default time format for display. Must match formatting rules at http://amsul.ca/pickadate.js/time.htm#formats', RTB_TEXTDOMAIN ),
|
43 |
+
|
44 |
+
// Email address where admin notifications should be sent
|
45 |
+
'admin-email-address' => get_option( 'admin_email' ),
|
46 |
+
|
47 |
+
// Name and email address which should appear in the Reply-To section of notification emails
|
48 |
+
'reply-to-name' => get_bloginfo( 'name' ),
|
49 |
+
'reply-to-address' => get_option( 'admin_email' ),
|
50 |
+
|
51 |
+
// Email template sent to an admin when a new booking request is made
|
52 |
+
'subject-booking-admin' => _x( 'New Booking Request', 'Default email subject for admin notifications of new bookings', RTB_TEXTDOMAIN ),
|
53 |
+
'template-booking-admin' => _x( 'A new booking request has been made at {site_name}:
|
54 |
+
|
55 |
+
{user_name}
|
56 |
+
{party} people
|
57 |
+
{date}
|
58 |
+
|
59 |
+
{bookings_link}
|
60 |
+
{confirm_link}
|
61 |
+
{close_link}
|
62 |
+
|
63 |
+
|
64 |
+
|
65 |
+
<em>This message was sent by {site_link} on {current_time}.</em>',
|
66 |
+
'Default email sent to the admin when a new booking request is made. The tags in {brackets} will be replaced by the appropriate content and should be left in place. HTML is allowed, but be aware that many email clients do not handle HTML very well.',
|
67 |
+
RTB_TEXTDOMAIN
|
68 |
+
),
|
69 |
+
|
70 |
+
// Email template sent to a user when a new booking request is made
|
71 |
+
'subject-booking-user' => sprintf( _x( 'Your booking at %s is pending', 'Default email subject sent to user when they request a booking. %s will be replaced by the website name', RTB_TEXTDOMAIN ), get_bloginfo( 'name' ) ),
|
72 |
+
'template-booking-user' => _x( 'Thanks {user_name},
|
73 |
+
|
74 |
+
Your booking request is <strong>waiting to be confirmed</strong>.
|
75 |
+
|
76 |
+
Give us a few moments to make sure that we\'ve got space for you. You will receive another email from us soon. If this request was made outside of our normal working hours, we may not be able to confirm it until we\'re open again.
|
77 |
+
|
78 |
+
<strong>Your request details:</strong>
|
79 |
+
{user_name}
|
80 |
+
{party} people
|
81 |
+
{date}
|
82 |
+
|
83 |
+
|
84 |
+
|
85 |
+
<em>This message was sent by {site_link} on {current_time}.</em>',
|
86 |
+
'Default email sent to users when they make a new booking request. The tags in {brackets} will be replaced by the appropriate content and should be left in place. HTML is allowed, but be aware that many email clients do not handle HTML very well.',
|
87 |
+
RTB_TEXTDOMAIN
|
88 |
+
),
|
89 |
+
|
90 |
+
// Email template sent to a user when a booking request is confirmed
|
91 |
+
'subject-confirmed-user' => sprintf( _x( 'Your booking at %s is confirmed', 'Default email subject sent to user when their booking is confirmed. %s will be replaced by the website name', RTB_TEXTDOMAIN ), get_bloginfo( 'name' ) ),
|
92 |
+
'template-confirmed-user' => _x( 'Hi {user_name},
|
93 |
+
|
94 |
+
Your booking request has been <strong>confirmed</strong>. We look forward to seeing you soon.
|
95 |
+
|
96 |
+
<strong>Your booking:</strong>
|
97 |
+
{user_name}
|
98 |
+
{party} people
|
99 |
+
{date}
|
100 |
+
|
101 |
+
|
102 |
+
|
103 |
+
<em>This message was sent by {site_link} on {current_time}.</em>',
|
104 |
+
'Default email sent to users when they make a new booking request. The tags in {brackets} will be replaced by the appropriate content and should be left in place. HTML is allowed, but be aware that many email clients do not handle HTML very well.',
|
105 |
+
RTB_TEXTDOMAIN
|
106 |
+
),
|
107 |
+
|
108 |
+
// Email template sent to a user when a booking request is rejected
|
109 |
+
'subject-rejected-user' => sprintf( _x( 'Your booking at %s was not accepted', 'Default email subject sent to user when their booking is rejected. %s will be replaced by the website name', RTB_TEXTDOMAIN ), get_bloginfo( 'name' ) ),
|
110 |
+
'template-rejected-user' => _x( 'Hi {user_name},
|
111 |
+
|
112 |
+
Sorry, we could not accomodate your booking request. We\'re full or not open at the time you requested:
|
113 |
+
|
114 |
+
{user_name}
|
115 |
+
{party} people
|
116 |
+
{date}
|
117 |
+
|
118 |
+
|
119 |
+
|
120 |
+
<em>This message was sent by {site_link} on {current_time}.</em>',
|
121 |
+
'Default email sent to users when they make a new booking request. The tags in {brackets} will be replaced by the appropriate content and should be left in place. HTML is allowed, but be aware that many email clients do not handle HTML very well.',
|
122 |
+
RTB_TEXTDOMAIN
|
123 |
+
),
|
124 |
+
);
|
125 |
+
|
126 |
+
$this->defaults = apply_filters( 'rtb_defaults', $this->defaults );
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* Get a setting's value or fallback to a default if one exists
|
131 |
+
* @since 0.0.1
|
132 |
+
*/
|
133 |
+
public function get_setting( $setting ) {
|
134 |
+
|
135 |
+
if ( empty( $this->settings ) ) {
|
136 |
+
$this->settings = get_option( 'rtb-settings' );
|
137 |
+
}
|
138 |
+
|
139 |
+
if ( !empty( $this->settings[ $setting ] ) ) {
|
140 |
+
return $this->settings[ $setting ];
|
141 |
+
}
|
142 |
+
|
143 |
+
if ( !empty( $this->defaults[ $setting ] ) ) {
|
144 |
+
return $this->defaults[ $setting ];
|
145 |
+
}
|
146 |
+
|
147 |
+
return null;
|
148 |
+
}
|
149 |
+
|
150 |
+
/**
|
151 |
+
* Load the admin settings page
|
152 |
+
* @since 0.0.1
|
153 |
+
* @sa https://github.com/NateWr/simple-admin-pages
|
154 |
+
*/
|
155 |
+
public function load_settings_panel() {
|
156 |
+
|
157 |
+
require_once( RTB_PLUGIN_DIR . '/lib/simple-admin-pages/simple-admin-pages.php' );
|
158 |
+
$sap = sap_initialize_library(
|
159 |
+
$args = array(
|
160 |
+
'version' => '2.0.a.1',
|
161 |
+
'lib_url' => RTB_PLUGIN_URL . '/lib/simple-admin-pages/',
|
162 |
+
)
|
163 |
+
);
|
164 |
+
|
165 |
+
$sap->add_page(
|
166 |
+
'submenu',
|
167 |
+
array(
|
168 |
+
'id' => 'rtb-settings',
|
169 |
+
'title' => __( 'Settings', RTB_TEXTDOMAIN ),
|
170 |
+
'menu_title' => __( 'Settings', RTB_TEXTDOMAIN ),
|
171 |
+
'parent_menu' => 'rtb-bookings',
|
172 |
+
'description' => '',
|
173 |
+
'capability' => 'manage_options',
|
174 |
+
'default_tab' => 'general',
|
175 |
+
)
|
176 |
+
);
|
177 |
+
|
178 |
+
$sap->add_section(
|
179 |
+
'rtb-settings',
|
180 |
+
array(
|
181 |
+
'id' => 'general',
|
182 |
+
'title' => __( 'General', RTB_TEXTDOMAIN ),
|
183 |
+
'is_tab' => true,
|
184 |
+
)
|
185 |
+
);
|
186 |
+
|
187 |
+
$sap->add_setting(
|
188 |
+
'rtb-settings',
|
189 |
+
'general',
|
190 |
+
'post',
|
191 |
+
array(
|
192 |
+
'id' => 'booking-page',
|
193 |
+
'title' => __( 'Booking Page', RTB_TEXTDOMAIN ),
|
194 |
+
'description' => __( 'Select a page on your site to automatically display the booking form and confirmation message.', RTB_TEXTDOMAIN ),
|
195 |
+
'blank_option' => true,
|
196 |
+
'args' => array(
|
197 |
+
'post_type' => 'page',
|
198 |
+
'posts_per_page' => -1,
|
199 |
+
'post_status' => 'publish',
|
200 |
+
),
|
201 |
+
)
|
202 |
+
);
|
203 |
+
|
204 |
+
$sap->add_setting(
|
205 |
+
'rtb-settings',
|
206 |
+
'general',
|
207 |
+
'textarea',
|
208 |
+
array(
|
209 |
+
'id' => 'success-message',
|
210 |
+
'title' => __( 'Success Message', RTB_TEXTDOMAIN ),
|
211 |
+
'description' => __( 'Enter the message to display when a booking request is made.', RTB_TEXTDOMAIN ),
|
212 |
+
'placeholder' => $this->defaults['success-message'],
|
213 |
+
)
|
214 |
+
);
|
215 |
+
|
216 |
+
$sap->add_setting(
|
217 |
+
'rtb-settings',
|
218 |
+
'general',
|
219 |
+
'text',
|
220 |
+
array(
|
221 |
+
'id' => 'date-format',
|
222 |
+
'title' => __( 'Date Format', RTB_TEXTDOMAIN ),
|
223 |
+
'description' => __( 'Define how the date should appear after it has been selected. <a href="http://amsul.ca/pickadate.js/date.htm#formatting-rules">Formatting rules</a>', RTB_TEXTDOMAIN ),
|
224 |
+
'placeholder' => $this->defaults['date-format'],
|
225 |
+
)
|
226 |
+
);
|
227 |
+
|
228 |
+
$sap->add_setting(
|
229 |
+
'rtb-settings',
|
230 |
+
'general',
|
231 |
+
'text',
|
232 |
+
array(
|
233 |
+
'id' => 'time-format',
|
234 |
+
'title' => __( 'Time Format', RTB_TEXTDOMAIN ),
|
235 |
+
'description' => __( 'Define how the time should appear after it has been selected. <a href="http://amsul.ca/pickadate.js/time.htm#formatting-rules">Formatting rules</a>', RTB_TEXTDOMAIN ),
|
236 |
+
'placeholder' => $this->defaults['time-format'],
|
237 |
+
)
|
238 |
+
);
|
239 |
+
|
240 |
+
$sap->add_section(
|
241 |
+
'rtb-settings',
|
242 |
+
array(
|
243 |
+
'id' => 'schedule',
|
244 |
+
'title' => __( 'Booking Schedule', RTB_TEXTDOMAIN ),
|
245 |
+
'is_tab' => true,
|
246 |
+
)
|
247 |
+
);
|
248 |
+
|
249 |
+
$sap->add_setting(
|
250 |
+
'rtb-settings',
|
251 |
+
'schedule',
|
252 |
+
'scheduler',
|
253 |
+
array(
|
254 |
+
'id' => 'schedule-open',
|
255 |
+
'title' => __( 'Schedule', RTB_TEXTDOMAIN ),
|
256 |
+
'description' => __( 'Define the weekly schedule during which you accept bookings.', RTB_TEXTDOMAIN ),
|
257 |
+
'weekdays' => array(
|
258 |
+
'monday' => _x( 'Mo', 'Monday abbreviation', RTB_TEXTDOMAIN ),
|
259 |
+
'tuesday' => _x( 'Tu', 'Tuesday abbreviation', RTB_TEXTDOMAIN ),
|
260 |
+
'wednesday' => _x( 'We', 'Wednesday abbreviation', RTB_TEXTDOMAIN ),
|
261 |
+
'thursday' => _x( 'Th', 'Thursday abbreviation', RTB_TEXTDOMAIN ),
|
262 |
+
'friday' => _x( 'Fr', 'Friday abbreviation', RTB_TEXTDOMAIN ),
|
263 |
+
'saturday' => _x( 'Sa', 'Saturday abbreviation', RTB_TEXTDOMAIN ),
|
264 |
+
'sunday' => _x( 'Su', 'Sunday abbreviation', RTB_TEXTDOMAIN )
|
265 |
+
),
|
266 |
+
'time_format' => $this->get_setting( 'time-format' ),
|
267 |
+
'date_format' => $this->get_setting( 'date-format' ),
|
268 |
+
'disable_weeks' => true,
|
269 |
+
'disable_date' => true,
|
270 |
+
)
|
271 |
+
);
|
272 |
+
|
273 |
+
$sap->add_setting(
|
274 |
+
'rtb-settings',
|
275 |
+
'schedule',
|
276 |
+
'scheduler',
|
277 |
+
array(
|
278 |
+
'id' => 'schedule-closed',
|
279 |
+
'title' => __( 'Exceptions', RTB_TEXTDOMAIN ),
|
280 |
+
'description' => __( "Define special opening hours for holidays, events or other needs. Leave the time empty if you're closed all day.", RTB_TEXTDOMAIN ),
|
281 |
+
'time_format' => $this->get_setting( 'time-format' ),
|
282 |
+
'date_format' => $this->get_setting( 'date-format' ),
|
283 |
+
'disable_weekdays' => true,
|
284 |
+
'disable_weeks' => true,
|
285 |
+
'instance_schedule_summaries' => array(
|
286 |
+
'all_day' => _x( 'Closed all day', 'Brief description of a scheduling exception when no times are set', SAP_TEXTDOMAIN ),
|
287 |
+
),
|
288 |
+
)
|
289 |
+
);
|
290 |
+
|
291 |
+
$sap->add_setting(
|
292 |
+
'rtb-settings',
|
293 |
+
'schedule',
|
294 |
+
'select',
|
295 |
+
array(
|
296 |
+
'id' => 'early-bookings',
|
297 |
+
'title' => __( 'Early Bookings', RTB_TEXTDOMAIN ),
|
298 |
+
'description' => __( 'Select how early customers can make their booking.', RTB_TEXTDOMAIN ),
|
299 |
+
'blank_option' => false,
|
300 |
+
'options' => array(
|
301 |
+
'' => __( 'Any time', RTB_TEXTDOMAIN ),
|
302 |
+
'1' => __( 'Up to 1 day in advance', RTB_TEXTDOMAIN ),
|
303 |
+
'7' => __( 'Up to 1 week in advance', RTB_TEXTDOMAIN ),
|
304 |
+
'14' => __( 'Up to 2 weeks in advance', RTB_TEXTDOMAIN ),
|
305 |
+
'30' => __( 'Up to 30 days in advance', RTB_TEXTDOMAIN ),
|
306 |
+
'90' => __( 'Up to 90 days in advance', RTB_TEXTDOMAIN ),
|
307 |
+
)
|
308 |
+
)
|
309 |
+
);
|
310 |
+
|
311 |
+
$sap->add_setting(
|
312 |
+
'rtb-settings',
|
313 |
+
'schedule',
|
314 |
+
'select',
|
315 |
+
array(
|
316 |
+
'id' => 'late-bookings',
|
317 |
+
'title' => __( 'Late Bookings', RTB_TEXTDOMAIN ),
|
318 |
+
'description' => __( 'Select how late customers can make their booking.', RTB_TEXTDOMAIN ),
|
319 |
+
'blank_option' => false,
|
320 |
+
'options' => array(
|
321 |
+
'' => __( 'Up to the last minute', RTB_TEXTDOMAIN ),
|
322 |
+
'15' => __( 'Up to 15 minutes in advance', RTB_TEXTDOMAIN ),
|
323 |
+
'30' => __( 'Up to 30 minutes in advance', RTB_TEXTDOMAIN ),
|
324 |
+
'45' => __( 'Up to 45 minutes in advance', RTB_TEXTDOMAIN ),
|
325 |
+
'60' => __( 'Up to 1 hour in advance', RTB_TEXTDOMAIN ),
|
326 |
+
)
|
327 |
+
)
|
328 |
+
);
|
329 |
+
|
330 |
+
$sap->add_section(
|
331 |
+
'rtb-settings',
|
332 |
+
array(
|
333 |
+
'id' => 'notifications',
|
334 |
+
'title' => __( 'Notifications', RTB_TEXTDOMAIN ),
|
335 |
+
'is_tab' => true,
|
336 |
+
)
|
337 |
+
);
|
338 |
+
|
339 |
+
$sap->add_setting(
|
340 |
+
'rtb-settings',
|
341 |
+
'notifications',
|
342 |
+
'text',
|
343 |
+
array(
|
344 |
+
'id' => 'reply-to-name',
|
345 |
+
'title' => __( 'Reply-To Name', RTB_TEXTDOMAIN ),
|
346 |
+
'description' => __( 'The name which should appear in the Reply-To field of a notification email', RTB_TEXTDOMAIN ),
|
347 |
+
'placeholder' => $this->defaults['reply-to-name'],
|
348 |
+
)
|
349 |
+
);
|
350 |
+
|
351 |
+
$sap->add_setting(
|
352 |
+
'rtb-settings',
|
353 |
+
'notifications',
|
354 |
+
'text',
|
355 |
+
array(
|
356 |
+
'id' => 'reply-to-address',
|
357 |
+
'title' => __( 'Reply-To Email Address', RTB_TEXTDOMAIN ),
|
358 |
+
'description' => __( 'The email address which should appear in the Reply-To field of a notification email.', RTB_TEXTDOMAIN ),
|
359 |
+
'placeholder' => $this->defaults['reply-to-address'],
|
360 |
+
)
|
361 |
+
);
|
362 |
+
|
363 |
+
$sap->add_setting(
|
364 |
+
'rtb-settings',
|
365 |
+
'notifications',
|
366 |
+
'toggle',
|
367 |
+
array(
|
368 |
+
'id' => 'admin-email-option',
|
369 |
+
'title' => __( 'Admin Notification', RTB_TEXTDOMAIN ),
|
370 |
+
'label' => __( 'Send an email notification to an administrator when a new booking is requested.', RTB_TEXTDOMAIN )
|
371 |
+
)
|
372 |
+
);
|
373 |
+
|
374 |
+
$sap->add_setting(
|
375 |
+
'rtb-settings',
|
376 |
+
'notifications',
|
377 |
+
'text',
|
378 |
+
array(
|
379 |
+
'id' => 'admin-email-address',
|
380 |
+
'title' => __( 'Admin Email Address', RTB_TEXTDOMAIN ),
|
381 |
+
'description' => __( 'The email address where admin notifications should be sent.', RTB_TEXTDOMAIN ),
|
382 |
+
'placeholder' => $this->defaults['admin-email-address'],
|
383 |
+
)
|
384 |
+
);
|
385 |
+
|
386 |
+
$sap->add_section(
|
387 |
+
'rtb-settings',
|
388 |
+
array(
|
389 |
+
'id' => 'notifications-templates',
|
390 |
+
'title' => __( 'Email Templates', RTB_TEXTDOMAIN ),
|
391 |
+
'tab' => 'notifications',
|
392 |
+
'description' => 'Adjust the messages that are emailed to users and admins during the booking process.',
|
393 |
+
)
|
394 |
+
);
|
395 |
+
|
396 |
+
// @todo this should be generated automatically from an array of tags/descriptions somewhere, so that addons
|
397 |
+
// can easily add/edit without conflicting with each other.
|
398 |
+
$sap->add_setting(
|
399 |
+
'rtb-settings',
|
400 |
+
'notifications-templates',
|
401 |
+
'html',
|
402 |
+
array(
|
403 |
+
'id' => 'template-tags-description',
|
404 |
+
'title' => __( 'Template Tags', RTB_TEXTDOMAIN ),
|
405 |
+
'html' => '
|
406 |
+
<p class="description">' . __( 'Use the following tags to automatically add booking information to the emails.', RTB_TEXTDOMAIN ) . '</p>
|
407 |
+
<div class="rtb-template-tags-box">
|
408 |
+
<strong>{user_name}</strong> ' . __( 'Name of the user who made the booking', RTB_TEXTDOMAIN ) . '
|
409 |
+
</div>
|
410 |
+
<div class="rtb-template-tags-box">
|
411 |
+
<strong>{party}</strong> ' . __( 'Number of people booked', RTB_TEXTDOMAIN ) . '
|
412 |
+
</div>
|
413 |
+
<div class="rtb-template-tags-box">
|
414 |
+
<strong>{date}</strong> ' . __( 'Date and time of the booking', RTB_TEXTDOMAIN ) . '
|
415 |
+
</div>
|
416 |
+
<div class="rtb-template-tags-box">
|
417 |
+
<strong>{bookings_link}</strong> ' . __( 'A link to the admin panel showing pending bookings', RTB_TEXTDOMAIN ) . '
|
418 |
+
</div>
|
419 |
+
<div class="rtb-template-tags-box">
|
420 |
+
<strong>{confirm_link}</strong> ' . __( 'A link to confirm this booking. Only include this in admin notifications', RTB_TEXTDOMAIN ) . '
|
421 |
+
</div>
|
422 |
+
<div class="rtb-template-tags-box">
|
423 |
+
<strong>{close_link}</strong> ' . __( 'A link to reject this booking. Only include this in admin notifications', RTB_TEXTDOMAIN ) . '
|
424 |
+
</div>
|
425 |
+
<div class="rtb-template-tags-box">
|
426 |
+
<strong>{site_name}</strong> ' . __( 'The name of this website', RTB_TEXTDOMAIN ) . '
|
427 |
+
</div>
|
428 |
+
<div class="rtb-template-tags-box">
|
429 |
+
<strong>{site_link}</strong> ' . __( 'A link to this website', RTB_TEXTDOMAIN ) . '
|
430 |
+
</div>
|
431 |
+
<div class="rtb-template-tags-box">
|
432 |
+
<strong>{current_time}</strong> ' . __( 'Current date and time', RTB_TEXTDOMAIN ) . '
|
433 |
+
</div>',
|
434 |
+
)
|
435 |
+
);
|
436 |
+
|
437 |
+
$sap->add_setting(
|
438 |
+
'rtb-settings',
|
439 |
+
'notifications-templates',
|
440 |
+
'text',
|
441 |
+
array(
|
442 |
+
'id' => 'subject-booking-admin',
|
443 |
+
'title' => __( 'Admin Notification Subject', RTB_TEXTDOMAIN ),
|
444 |
+
'description' => __( 'The email subject for admin notifications.', RTB_TEXTDOMAIN ),
|
445 |
+
'placeholder' => $this->defaults['subject-booking-admin'],
|
446 |
+
)
|
447 |
+
);
|
448 |
+
|
449 |
+
$sap->add_setting(
|
450 |
+
'rtb-settings',
|
451 |
+
'notifications-templates',
|
452 |
+
'editor',
|
453 |
+
array(
|
454 |
+
'id' => 'template-booking-admin',
|
455 |
+
'title' => __( 'Admin Notification Email', RTB_TEXTDOMAIN ),
|
456 |
+
'description' => __( 'Enter the email an admin should receive when an initial booking request is made.', RTB_TEXTDOMAIN ),
|
457 |
+
'default' => $this->defaults['template-booking-admin'],
|
458 |
+
)
|
459 |
+
);
|
460 |
+
|
461 |
+
$sap->add_setting(
|
462 |
+
'rtb-settings',
|
463 |
+
'notifications-templates',
|
464 |
+
'text',
|
465 |
+
array(
|
466 |
+
'id' => 'subject-booking-user',
|
467 |
+
'title' => __( 'New Request Email Subject', RTB_TEXTDOMAIN ),
|
468 |
+
'description' => __( 'The email subject a user should receive when they make an initial booking request.', RTB_TEXTDOMAIN ),
|
469 |
+
'placeholder' => $this->defaults['subject-booking-user'],
|
470 |
+
)
|
471 |
+
);
|
472 |
+
|
473 |
+
$sap->add_setting(
|
474 |
+
'rtb-settings',
|
475 |
+
'notifications-templates',
|
476 |
+
'editor',
|
477 |
+
array(
|
478 |
+
'id' => 'template-booking-user',
|
479 |
+
'title' => __( 'New Request Email', RTB_TEXTDOMAIN ),
|
480 |
+
'description' => __( 'Enter the email a user should receive when they make an initial booking request.', RTB_TEXTDOMAIN ),
|
481 |
+
'default' => $this->defaults['template-booking-user'],
|
482 |
+
)
|
483 |
+
);
|
484 |
+
|
485 |
+
$sap->add_setting(
|
486 |
+
'rtb-settings',
|
487 |
+
'notifications-templates',
|
488 |
+
'text',
|
489 |
+
array(
|
490 |
+
'id' => 'subject-confirmed-user',
|
491 |
+
'title' => __( 'Confirmed Email Subject', RTB_TEXTDOMAIN ),
|
492 |
+
'description' => __( 'The email subject a user should receive when their booking has been confirmed.', RTB_TEXTDOMAIN ),
|
493 |
+
'placeholder' => $this->defaults['subject-confirmed-user'],
|
494 |
+
)
|
495 |
+
);
|
496 |
+
|
497 |
+
$sap->add_setting(
|
498 |
+
'rtb-settings',
|
499 |
+
'notifications-templates',
|
500 |
+
'editor',
|
501 |
+
array(
|
502 |
+
'id' => 'template-confirmed-user',
|
503 |
+
'title' => __( 'Confirmed Email', RTB_TEXTDOMAIN ),
|
504 |
+
'description' => __( 'Enter the email a user should receive when their booking has been confirmed.', RTB_TEXTDOMAIN ),
|
505 |
+
'default' => $this->defaults['template-confirmed-user'],
|
506 |
+
)
|
507 |
+
);
|
508 |
+
|
509 |
+
$sap->add_setting(
|
510 |
+
'rtb-settings',
|
511 |
+
'notifications-templates',
|
512 |
+
'text',
|
513 |
+
array(
|
514 |
+
'id' => 'subject-rejected-user',
|
515 |
+
'title' => __( 'Rejected Email Subject', RTB_TEXTDOMAIN ),
|
516 |
+
'description' => __( 'The email subject a user should receive when their booking has been rejected.', RTB_TEXTDOMAIN ),
|
517 |
+
'placeholder' => $this->defaults['subject-rejected-user'],
|
518 |
+
)
|
519 |
+
);
|
520 |
+
|
521 |
+
$sap->add_setting(
|
522 |
+
'rtb-settings',
|
523 |
+
'notifications-templates',
|
524 |
+
'editor',
|
525 |
+
array(
|
526 |
+
'id' => 'template-rejected-user',
|
527 |
+
'title' => __( 'Rejected Email', RTB_TEXTDOMAIN ),
|
528 |
+
'description' => __( 'Enter the email a user should receive when their booking has been rejected.', RTB_TEXTDOMAIN ),
|
529 |
+
'default' => $this->defaults['template-rejected-user'],
|
530 |
+
)
|
531 |
+
);
|
532 |
+
|
533 |
+
$sap = apply_filters( 'rtb_settings_page', $sap );
|
534 |
+
|
535 |
+
$sap->add_admin_menus();
|
536 |
+
|
537 |
+
}
|
538 |
+
|
539 |
+
}
|
540 |
+
} // endif;
|
includes/WP_List_Table.BookingsTable.class.php
ADDED
@@ -0,0 +1,650 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
if ( ! defined( 'ABSPATH' ) ) exit;
|
3 |
+
|
4 |
+
if ( ! class_exists( 'WP_List_Table' ) ) {
|
5 |
+
require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
|
6 |
+
}
|
7 |
+
|
8 |
+
if ( !class_exists( 'rtbBookingsTable' ) ) {
|
9 |
+
/**
|
10 |
+
* Bookings Table Class
|
11 |
+
*
|
12 |
+
* Extends WP_List_Table to display the list of bookings in a format similar to
|
13 |
+
* the default WordPress post tables.
|
14 |
+
*
|
15 |
+
* @h/t Easy Digital Downloads by Pippin: https://easydigitaldownloads.com/
|
16 |
+
* @since 0.0.1
|
17 |
+
*/
|
18 |
+
class rtbBookingsTable extends WP_List_Table {
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Number of results to show per page
|
22 |
+
*
|
23 |
+
* @var string
|
24 |
+
* @since 0.0.1
|
25 |
+
*/
|
26 |
+
public $per_page = 30;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* URL of this page
|
30 |
+
*
|
31 |
+
* @var string
|
32 |
+
* @since 0.0.1
|
33 |
+
*/
|
34 |
+
public $base_url;
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Array of booking counts by total and status
|
38 |
+
*
|
39 |
+
* @var array
|
40 |
+
* @since 0.0.1
|
41 |
+
*/
|
42 |
+
public $booking_counts;
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Array of bookings
|
46 |
+
*
|
47 |
+
* @var array
|
48 |
+
* @since 0.0.1
|
49 |
+
*/
|
50 |
+
public $bookings;
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Current date filters
|
54 |
+
*
|
55 |
+
* @var string
|
56 |
+
* @since 0.0.1
|
57 |
+
*/
|
58 |
+
public $filter_start_date = null;
|
59 |
+
public $filter_end_date = null;
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Current query string
|
63 |
+
*
|
64 |
+
* @var string
|
65 |
+
* @since 0.0.1
|
66 |
+
*/
|
67 |
+
public $query_string;
|
68 |
+
|
69 |
+
public function __construct() {
|
70 |
+
|
71 |
+
global $status, $page;
|
72 |
+
|
73 |
+
// Set parent defaults
|
74 |
+
parent::__construct( array(
|
75 |
+
'singular' => __( 'Booking', RTB_TEXTDOMAIN ),
|
76 |
+
'plural' => __( 'Bookings', RTB_TEXTDOMAIN ),
|
77 |
+
'ajax' => false
|
78 |
+
) );
|
79 |
+
|
80 |
+
// Set the date filter
|
81 |
+
$this->set_date_filter();
|
82 |
+
|
83 |
+
// Strip unwanted query vars from the query string or ensure the correct
|
84 |
+
// vars are used
|
85 |
+
$this->query_string_maintenance();
|
86 |
+
|
87 |
+
// Run any bulk action requests
|
88 |
+
$this->process_bulk_action();
|
89 |
+
|
90 |
+
// Run any quicklink requests
|
91 |
+
$this->process_quicklink_action();
|
92 |
+
|
93 |
+
// Retrieve a count of the number of bookings by status
|
94 |
+
$this->get_booking_counts();
|
95 |
+
|
96 |
+
// Retrieve bookings data for the table
|
97 |
+
$this->bookings_data();
|
98 |
+
|
99 |
+
$this->base_url = admin_url( 'admin.php?page=' . RTB_BOOKING_POST_TYPE );
|
100 |
+
}
|
101 |
+
|
102 |
+
/**
|
103 |
+
* Set the correct date filter
|
104 |
+
*
|
105 |
+
* $_POST values should always overwrite $_GET values
|
106 |
+
*
|
107 |
+
* @since 0.0.1
|
108 |
+
*/
|
109 |
+
public function set_date_filter( $start_date = null, $end_date = null) {
|
110 |
+
|
111 |
+
if ( !empty( $_GET['action'] ) && $_GET['action'] == 'clear_date_filters' ) {
|
112 |
+
$this->filter_start_date = null;
|
113 |
+
$this->filter_end_date = null;
|
114 |
+
}
|
115 |
+
|
116 |
+
$this->filter_start_date = $start_date;
|
117 |
+
$this->filter_end_date = $end_date;
|
118 |
+
|
119 |
+
if ( $start_date === null ) {
|
120 |
+
$this->filter_start_date = !empty( $_GET['start-date'] ) ? sanitize_text_field( $_GET['start-date'] ) : null;
|
121 |
+
$this->filter_start_date = !empty( $_POST['start-date'] ) ? sanitize_text_field( $_POST['start-date'] ) : $this->filter_start_date;
|
122 |
+
}
|
123 |
+
|
124 |
+
if ( $end_date === null ) {
|
125 |
+
$this->filter_end_date = !empty( $_GET['end-date'] ) ? sanitize_text_field( $_GET['end-date'] ) : null;
|
126 |
+
$this->filter_end_date = !empty( $_POST['end-date'] ) ? sanitize_text_field( $_POST['end-date'] ) : $this->filter_end_date;
|
127 |
+
}
|
128 |
+
}
|
129 |
+
|
130 |
+
/**
|
131 |
+
* Strip unwanted query vars from the query string or ensure the correct
|
132 |
+
* vars are passed around and those we don't want to preserve are discarded.
|
133 |
+
*
|
134 |
+
* @since 0.0.1
|
135 |
+
*/
|
136 |
+
public function query_string_maintenance() {
|
137 |
+
|
138 |
+
$this->query_string = remove_query_arg( array( 'action', 'start-date', 'end-date' ) );
|
139 |
+
|
140 |
+
if ( $this->filter_start_date !== null ) {
|
141 |
+
$this->query_string = add_query_arg( array( 'start-date' => $this->filter_start_date ), $this->query_string );
|
142 |
+
}
|
143 |
+
|
144 |
+
if ( $this->filter_end_date !== null ) {
|
145 |
+
$this->query_string = add_query_arg( array( 'end-date' => $this->filter_end_date ), $this->query_string );
|
146 |
+
}
|
147 |
+
|
148 |
+
}
|
149 |
+
|
150 |
+
/**
|
151 |
+
* Show the time views, date filters and the search box
|
152 |
+
* @since 0.0.1
|
153 |
+
*/
|
154 |
+
public function advanced_filters() {
|
155 |
+
|
156 |
+
// Show the schedule views (today, upcoming, all)
|
157 |
+
if ( !empty( $_GET['schedule'] ) ) {
|
158 |
+
$schedule = sanitize_text_field( $_GET['schedule'] );
|
159 |
+
} else {
|
160 |
+
$schedule = '';
|
161 |
+
}
|
162 |
+
|
163 |
+
// Use a custom schedule if a date range has been entered
|
164 |
+
if ( $this->filter_start_date !== null || $this->filter_end_date !== null ) {
|
165 |
+
$schedule = 'custom';
|
166 |
+
}
|
167 |
+
|
168 |
+
// Strip out existing date filters from the schedule view urls
|
169 |
+
$schedule_query_string = remove_query_arg( array( 'schedule', 'start-date', 'end-date' ), $this->query_string );
|
170 |
+
|
171 |
+
$views = array(
|
172 |
+
'all' => sprintf( '<a href="%s"%s>%s</a>', $schedule_query_string, $schedule == '' ? ' class="current"' : '', __( 'All', RTB_TEXTDOMAIN ) ),
|
173 |
+
'today' => sprintf( '<a href="%s"%s>%s</a>', add_query_arg( array( 'schedule' => 'today', 'paged' => FALSE ), $schedule_query_string ), $schedule === 'today' ? ' class="current"' : '', __( 'Today', RTB_TEXTDOMAIN ) ),
|
174 |
+
'upcoming' => sprintf( '<a href="%s"%s>%s</a>', add_query_arg( array( 'schedule' => 'upcoming', 'paged' => FALSE ), remove_query_arg( array( 'order' ), $schedule_query_string ) ), $schedule === 'upcoming' ? ' class="current"' : '', __( 'Upcoming', RTB_TEXTDOMAIN ) ),
|
175 |
+
);
|
176 |
+
|
177 |
+
if ( $schedule == 'custom' ) {
|
178 |
+
$start_date = !empty( $this->filter_start_date ) ? $this->filter_start_date : '*';
|
179 |
+
$end_date = !empty( $this->filter_end_date ) ? $this->filter_end_date : '*';
|
180 |
+
$views['custom'] = '<span class="current">' . $start_date . _x( '—', 'Separator between two dates in a date range', RTB_TEXTDOMAIN ) . $end_date . '</span>';
|
181 |
+
}
|
182 |
+
|
183 |
+
$views = apply_filters( 'rtn_bookings_table_views_schedule', $views );
|
184 |
+
?>
|
185 |
+
|
186 |
+
<div id="rtb-filters" class="clearfix">
|
187 |
+
<ul class="subsubsub rtb-views-schedule">
|
188 |
+
<li><?php echo join( ' | </li><li>', $views ); ?></li>
|
189 |
+
</ul>
|
190 |
+
|
191 |
+
<div class="date-filters">
|
192 |
+
<label for="start-date" class="screen-reader-text"><?php _e( 'Start Date:', RTB_TEXTDOMAIN ); ?></label>
|
193 |
+
<input type="text" id="start-date" name="start-date" class="datepicker" value="<?php echo esc_attr( $this->filter_start_date ); ?>" placeholder="<?php _e( 'Start Date', RTB_TEXTDOMAIN ); ?>" />
|
194 |
+
<label for="end-date" class="screen-reader-text"><?php _e( 'End Date:', RTB_TEXTDOMAIN ); ?></label>
|
195 |
+
<input type="text" id="end-date" name="end-date" class="datepicker" value="<?php echo esc_attr( $this->filter_end_date ); ?>" placeholder="<?php _e( 'End Date', RTB_TEXTDOMAIN ); ?>" />
|
196 |
+
<input type="submit" class="button-secondary" value="<?php _e( 'Apply', RTB_TEXTDOMAIN ); ?>"/>
|
197 |
+
<?php if( !empty( $start_date ) || !empty( $end_date ) ) : ?>
|
198 |
+
<a href="<?php echo add_query_arg( array( 'action' => 'clear_date_filters' ) ); ?>" class="button-secondary"><?php _e( 'Clear Filter', RTB_TEXTDOMAIN ); ?></a>
|
199 |
+
<?php endif; ?>
|
200 |
+
</div>
|
201 |
+
|
202 |
+
<?php if( !empty( $_GET['status'] ) ) : ?>
|
203 |
+
<input type="hidden" name="status" value="<?php echo esc_attr( sanitize_text_field( $_GET['status'] ) ); ?>"/>
|
204 |
+
<?php endif; ?>
|
205 |
+
|
206 |
+
<?php
|
207 |
+
// @todo Add support for the search box that uses more than just
|
208 |
+
// the 's' argument in WP_Query. I need to search at least the
|
209 |
+
// email post meta as well or this search box could be
|
210 |
+
// misleading for people who expect to search across all
|
211 |
+
// visible data
|
212 |
+
// $this->search_box( __( 'Search', RTB_TEXTDOMAIN ), 'rtb-bookings' );
|
213 |
+
?>
|
214 |
+
|
215 |
+
<?php
|
216 |
+
// @todo use a datepicker. need to bring in styles for jquery ui or use pickadate
|
217 |
+
// wp_enqueue_script('jquery-ui-datepicker');
|
218 |
+
?>
|
219 |
+
|
220 |
+
</div>
|
221 |
+
|
222 |
+
<?php
|
223 |
+
}
|
224 |
+
|
225 |
+
/**
|
226 |
+
* Retrieve the view types
|
227 |
+
* @since 0.0.1
|
228 |
+
* @todo it would be nice if the default view showed all upcoming pending + confirmed bookings, but not closed ones.
|
229 |
+
*/
|
230 |
+
public function get_views() {
|
231 |
+
|
232 |
+
$current = isset( $_GET['status'] ) ? $_GET['status'] : '';
|
233 |
+
|
234 |
+
$views = array(
|
235 |
+
'all' => sprintf( '<a href="%s"%s>%s</a>', remove_query_arg( array( 'status', 'paged' ), $this->query_string ), $current === 'all' || $current == '' ? ' class="current"' : '', __( 'All', RTB_TEXTDOMAIN ) . ' <span class="count">(' . $this->booking_counts['total'] . ')</span>' ),
|
236 |
+
'pending' => sprintf( '<a href="%s"%s>%s</a>', add_query_arg( array( 'status' => 'pending', 'paged' => FALSE ), $this->query_string ), $current === 'pending' ? ' class="current"' : '', __( 'Pending', RTB_TEXTDOMAIN ) . ' <span class="count">(' . $this->booking_counts['pending'] . ')</span>' ),
|
237 |
+
'confirmed' => sprintf( '<a href="%s"%s>%s</a>', add_query_arg( array( 'status' => 'confirmed', 'paged' => FALSE ), $this->query_string ), $current === 'confirmed' ? ' class="current"' : '', __( 'Confirmed', RTB_TEXTDOMAIN ) . ' <span class="count">(' . $this->booking_counts['confirmed'] . ')</span>' ),
|
238 |
+
'closed' => sprintf( '<a href="%s"%s>%s</a>', add_query_arg( array( 'status' => 'closed', 'paged' => FALSE ), $this->query_string ), $current === 'closed' ? ' class="current"' : '', __( 'Closed', RTB_TEXTDOMAIN ) . ' <span class="count">(' . $this->booking_counts['closed'] . ')</span>' ),
|
239 |
+
'trash' => sprintf( '<a href="%s"%s>%s</a>', add_query_arg( array( 'status' => 'trash', 'paged' => FALSE ), $this->query_string ), $current === 'trash' ? ' class="current"' : '', __( 'Trash', RTB_TEXTDOMAIN ) . ' <span class="count">(' . $this->booking_counts['trash'] . ')</span>' ),
|
240 |
+
);
|
241 |
+
|
242 |
+
return apply_filters( 'rtb_bookings_table_views_status', $views );
|
243 |
+
}
|
244 |
+
|
245 |
+
/**
|
246 |
+
* Generates content for a single row of the table
|
247 |
+
* @since 0.0.1
|
248 |
+
*/
|
249 |
+
function single_row( $item ) {
|
250 |
+
static $row_class = '';
|
251 |
+
$row_class = ( $row_class == '' ? 'alternate' : '' );
|
252 |
+
|
253 |
+
echo '<tr class="' . esc_attr( $item->post_status );
|
254 |
+
echo $row_class == '' ? '' : ' ' . $row_class;
|
255 |
+
echo '">';
|
256 |
+
$this->single_row_columns( $item );
|
257 |
+
echo '</tr>';
|
258 |
+
}
|
259 |
+
|
260 |
+
/**
|
261 |
+
* Retrieve the table columns
|
262 |
+
* @since 0.0.1
|
263 |
+
*/
|
264 |
+
public function get_columns() {
|
265 |
+
$columns = array(
|
266 |
+
'cb' => '<input type="checkbox" />', //Render a checkbox instead of text
|
267 |
+
'date' => __( 'Date', RTB_TEXTDOMAIN ),
|
268 |
+
'party' => __( 'Party', RTB_TEXTDOMAIN ),
|
269 |
+
'name' => __( 'Name', RTB_TEXTDOMAIN ),
|
270 |
+
'email' => __( 'Email', RTB_TEXTDOMAIN ),
|
271 |
+
'phone' => __( 'Phone', RTB_TEXTDOMAIN ),
|
272 |
+
'message' => __( 'Message', RTB_TEXTDOMAIN ),
|
273 |
+
'status' => __( 'Status', RTB_TEXTDOMAIN )
|
274 |
+
);
|
275 |
+
|
276 |
+
return apply_filters( 'rtb_bookings_table_columns', $columns );
|
277 |
+
}
|
278 |
+
|
279 |
+
/**
|
280 |
+
* Retrieve the table's sortable columns
|
281 |
+
* @since 0.0.1
|
282 |
+
*/
|
283 |
+
public function get_sortable_columns() {
|
284 |
+
$columns = array(
|
285 |
+
'date' => array( 'date', true ),
|
286 |
+
'name' => array( 'title', true ),
|
287 |
+
);
|
288 |
+
return apply_filters( 'rtb_bookings_table_sortable_columns', $columns );
|
289 |
+
}
|
290 |
+
|
291 |
+
/**
|
292 |
+
* This function renders most of the columns in the list table.
|
293 |
+
* @since 0.0.1
|
294 |
+
*/
|
295 |
+
public function column_default( $booking, $column_name ) {
|
296 |
+
switch ( $column_name ) {
|
297 |
+
case 'date' :
|
298 |
+
$value = $booking->date;
|
299 |
+
break;
|
300 |
+
case 'party' :
|
301 |
+
$value = $booking->party;
|
302 |
+
break;
|
303 |
+
case 'name' :
|
304 |
+
$value = $booking->name;
|
305 |
+
break;
|
306 |
+
case 'email' :
|
307 |
+
$value = $booking->email;
|
308 |
+
break;
|
309 |
+
case 'phone' :
|
310 |
+
$value = $booking->phone;
|
311 |
+
break;
|
312 |
+
case 'message' :
|
313 |
+
$value = '';
|
314 |
+
if ( trim( $booking->message ) ) {
|
315 |
+
$value = '<a href="#" data-id="message-' . esc_attr( $booking->ID ) . '"><span class="dashicons dashicons-testimonial"></span></a>';
|
316 |
+
$value .= '<div class="rtb-message-data">' . $booking->message . '</div>';
|
317 |
+
}
|
318 |
+
break;
|
319 |
+
case 'status' :
|
320 |
+
global $rtb_controller;
|
321 |
+
if ( !empty( $rtb_controller->cpts->booking_statuses[$booking->post_status] ) ) {
|
322 |
+
$value = $rtb_controller->cpts->booking_statuses[$booking->post_status]['label'];
|
323 |
+
} elseif ( $booking->post_status == 'trash' ) {
|
324 |
+
$value = _x( 'Trash', 'Status label for bookings put in the trash', RTB_TEXTDOMAIN );
|
325 |
+
} else {
|
326 |
+
$value = $booking->post_status;
|
327 |
+
}
|
328 |
+
break;
|
329 |
+
default:
|
330 |
+
$value = isset( $booking->$column_name ) ? $booking->$column_name : '';
|
331 |
+
break;
|
332 |
+
|
333 |
+
}
|
334 |
+
|
335 |
+
return apply_filters( 'rtb_bookings_table_column', $value, $booking, $column_name );
|
336 |
+
}
|
337 |
+
|
338 |
+
/**
|
339 |
+
* Render the checkbox column
|
340 |
+
* @since 0.0.1
|
341 |
+
*/
|
342 |
+
public function column_cb( $booking ) {
|
343 |
+
return sprintf(
|
344 |
+
'<input type="checkbox" name="%1$s[]" value="%2$s" />',
|
345 |
+
'bookings',
|
346 |
+
$booking->ID
|
347 |
+
);
|
348 |
+
}
|
349 |
+
|
350 |
+
/**
|
351 |
+
* Retrieve the bulk actions
|
352 |
+
* @since 0.0.1
|
353 |
+
*/
|
354 |
+
public function get_bulk_actions() {
|
355 |
+
$actions = array(
|
356 |
+
'delete' => __( 'Delete', RTB_TEXTDOMAIN ),
|
357 |
+
'set-status-confirmed' => __( 'Set To Confirmed', RTB_TEXTDOMAIN ),
|
358 |
+
'set-status-pending' => __( 'Set To Pending Review', RTB_TEXTDOMAIN ),
|
359 |
+
'set-status-closed' => __( 'Set To Closed', RTB_TEXTDOMAIN )
|
360 |
+
);
|
361 |
+
|
362 |
+
return apply_filters( 'rtb_bookings_table_bulk_actions', $actions );
|
363 |
+
}
|
364 |
+
|
365 |
+
/**
|
366 |
+
* Process the bulk actions
|
367 |
+
* @since 0.0.1
|
368 |
+
*/
|
369 |
+
public function process_bulk_action() {
|
370 |
+
$ids = isset( $_POST['bookings'] ) ? $_POST['bookings'] : false;
|
371 |
+
$action = isset( $_POST['action'] ) ? $_POST['action'] : false;
|
372 |
+
|
373 |
+
if( empty( $action ) || $action == '-1' ) {
|
374 |
+
return;
|
375 |
+
}
|
376 |
+
|
377 |
+
if ( !current_user_can( 'manage_bookings' ) ) {
|
378 |
+
return;
|
379 |
+
}
|
380 |
+
|
381 |
+
if ( ! is_array( $ids ) ) {
|
382 |
+
$ids = array( $ids );
|
383 |
+
}
|
384 |
+
|
385 |
+
global $rtb_controller;
|
386 |
+
$results = array();
|
387 |
+
foreach ( $ids as $id ) {
|
388 |
+
if ( 'delete' === $action ) {
|
389 |
+
$results[$id] = $rtb_controller->cpts->delete_booking( $id );
|
390 |
+
}
|
391 |
+
|
392 |
+
if ( 'set-status-confirmed' === $action ) {
|
393 |
+
$results[$id] = $rtb_controller->cpts->update_booking_status( $id, 'confirmed' );
|
394 |
+
}
|
395 |
+
|
396 |
+
if ( 'set-status-pending' === $action ) {
|
397 |
+
$results[$id] = $rtb_controller->cpts->update_booking_status( $id, 'pending' );
|
398 |
+
}
|
399 |
+
|
400 |
+
if ( 'set-status-closed' === $action ) {
|
401 |
+
$results[$id] = $rtb_controller->cpts->update_booking_status( $id, 'closed' );
|
402 |
+
}
|
403 |
+
|
404 |
+
$results = apply_filters( 'rtb_bookings_table_bulk_action', $results, $id, $action );
|
405 |
+
}
|
406 |
+
|
407 |
+
if( count( $results ) ) {
|
408 |
+
$this->results = $results;
|
409 |
+
$this->last_action = $action;
|
410 |
+
add_action( 'rtb_bookings_table_top', array( $this, 'admin_notice_bulk_actions' ) );
|
411 |
+
}
|
412 |
+
|
413 |
+
}
|
414 |
+
|
415 |
+
/**
|
416 |
+
* Process quicklink actions sent out in notification emails
|
417 |
+
* @since 0.0.1
|
418 |
+
*/
|
419 |
+
public function process_quicklink_action() {
|
420 |
+
|
421 |
+
if ( empty( $_REQUEST['rtb-quicklink'] ) ) {
|
422 |
+
return;
|
423 |
+
}
|
424 |
+
|
425 |
+
if ( !current_user_can( 'manage_bookings' ) ) {
|
426 |
+
return;
|
427 |
+
}
|
428 |
+
|
429 |
+
global $rtb_controller;
|
430 |
+
|
431 |
+
$results = array();
|
432 |
+
|
433 |
+
$id = !empty( $_REQUEST['booking'] ) ? $_REQUEST['booking'] : false;
|
434 |
+
|
435 |
+
if ( $_REQUEST['rtb-quicklink'] == 'confirm' ) {
|
436 |
+
$results[$id] = $rtb_controller->cpts->update_booking_status( $id, 'confirmed' );
|
437 |
+
$this->last_action = 'set-status-confirmed';
|
438 |
+
} elseif ( $_REQUEST['rtb-quicklink'] == 'close' ) {
|
439 |
+
$results[$id] = $rtb_controller->cpts->update_booking_status( $id, 'closed' );
|
440 |
+
$this->last_action = 'set-status-closed';
|
441 |
+
}
|
442 |
+
|
443 |
+
if( count( $results ) ) {
|
444 |
+
$this->results = $results;
|
445 |
+
add_action( 'rtb_bookings_table_top', array( $this, 'admin_notice_bulk_actions' ) );
|
446 |
+
}
|
447 |
+
}
|
448 |
+
|
449 |
+
/**
|
450 |
+
* Display an admin notice when a bulk action is completed
|
451 |
+
* @since 0.0.1
|
452 |
+
*/
|
453 |
+
public function admin_notice_bulk_actions() {
|
454 |
+
|
455 |
+
$success = 0;
|
456 |
+
$failure = 0;
|
457 |
+
foreach( $this->results as $id => $result ) {
|
458 |
+
if ( $result === true || $result === null ) {
|
459 |
+
$success++;
|
460 |
+
} else {
|
461 |
+
$failure++;
|
462 |
+
}
|
463 |
+
}
|
464 |
+
|
465 |
+
if ( $success > 0 ) :
|
466 |
+
?>
|
467 |
+
|
468 |
+
<div id="rtb-admin-notice-bulk-<?php esc_attr( $this->last_action ); ?>" class="updated">
|
469 |
+
|
470 |
+
<?php if ( $this->last_action == 'delete' ) : ?>
|
471 |
+
<p><?php echo sprintf( _n( '%d booking deleted successfully.', '%d bookings deleted successfully.', $success, RTB_TEXTDOMAIN ), $success ); ?></p>
|
472 |
+
|
473 |
+
<?php elseif ( $this->last_action == 'set-status-confirmed' ) : ?>
|
474 |
+
<p><?php echo sprintf( _n( '%d booking confirmed.', '%d bookings confirmed.', $success, RTB_TEXTDOMAIN ), $success ); ?></p>
|
475 |
+
|
476 |
+
<?php elseif ( $this->last_action == 'set-status-pending' ) : ?>
|
477 |
+
<p><?php echo sprintf( _n( '%d booking set to pending.', '%d bookings set to pending.', $success, RTB_TEXTDOMAIN ), $success ); ?></p>
|
478 |
+
|
479 |
+
<?php elseif ( $this->last_action == 'set-status-closed' ) : ?>
|
480 |
+
<p><?php echo sprintf( _n( '%d booking closed.', '%d bookings closed.', $success, RTB_TEXTDOMAIN ), $success ); ?></p>
|
481 |
+
|
482 |
+
<?php endif; ?>
|
483 |
+
</div>
|
484 |
+
|
485 |
+
<?php
|
486 |
+
endif;
|
487 |
+
|
488 |
+
if ( $failure > 0 ) :
|
489 |
+
?>
|
490 |
+
|
491 |
+
<div id="rtb-admin-notice-bulk-<?php esc_attr( $this->last_action ); ?>" class="error">
|
492 |
+
<p><?php echo sprintf( _n( '%d booking had errors and could not be processed.', '%d bookings had errors and could not be processed.', $failure, RTB_TEXTDOMAIN ), $failure ); ?></p>
|
493 |
+
</div>
|
494 |
+
|
495 |
+
<?php
|
496 |
+
endif;
|
497 |
+
}
|
498 |
+
|
499 |
+
/**
|
500 |
+
* Retrieve the counts of bookings
|
501 |
+
* @since 0.0.1
|
502 |
+
*/
|
503 |
+
public function get_booking_counts() {
|
504 |
+
|
505 |
+
global $wpdb;
|
506 |
+
|
507 |
+
$where = "WHERE p.post_type = '" . RTB_BOOKING_POST_TYPE . "'";
|
508 |
+
|
509 |
+
if ( $this->filter_start_date !== null || $this->filter_end_date !== null ) {
|
510 |
+
|
511 |
+
if ( $this->filter_start_date !== null ) {
|
512 |
+
$start_date = new DateTime( $this->filter_start_date );
|
513 |
+
$where .= " AND p.post_date >= '" . $start_date->format( 'Y-m-d H:i:s' ) . "'";
|
514 |
+
}
|
515 |
+
|
516 |
+
if ( $this->filter_end_date !== null ) {
|
517 |
+
$end_date = new DateTime( $this->filter_end_date );
|
518 |
+
$where .= " AND p.post_date <= '" . $end_date->format( 'Y-m-d H:i:s' ) . "'";
|
519 |
+
}
|
520 |
+
} elseif ( !empty( $_GET['schedule'] ) ) {
|
521 |
+
|
522 |
+
if ( $_GET['schedule'] == 'today' ) {
|
523 |
+
$where .= " AND p.post_date >= '" . date( 'Y-m-d' ) . "' AND p.post_date <= '" . date( 'Y-m-d', current_time( 'timestamp' ) + 86400 ) . "'";
|
524 |
+
|
525 |
+
} elseif ( $_GET['schedule'] == 'upcoming' ) {
|
526 |
+
$where .= " AND p.post_date >= '" . date( 'Y-m-d H:i:s', current_time( 'timestamp' ) - 3600 ) . "'";
|
527 |
+
}
|
528 |
+
}
|
529 |
+
|
530 |
+
|
531 |
+
$query = "SELECT p.post_status,count( * ) AS num_posts
|
532 |
+
FROM $wpdb->posts p
|
533 |
+
$where
|
534 |
+
GROUP BY p.post_status
|
535 |
+
";
|
536 |
+
|
537 |
+
$count = $wpdb->get_results( $query, ARRAY_A );
|
538 |
+
|
539 |
+
$this->booking_counts = array();
|
540 |
+
foreach ( get_post_stati() as $state ) {
|
541 |
+
$this->booking_counts[$state] = 0;
|
542 |
+
}
|
543 |
+
|
544 |
+
$this->booking_counts['total'] = 0;
|
545 |
+
foreach ( (array) $count as $row ) {
|
546 |
+
$this->booking_counts[$row['post_status']] = $row['num_posts'];
|
547 |
+
$this->booking_counts['total'] += $row['num_posts'];
|
548 |
+
}
|
549 |
+
|
550 |
+
}
|
551 |
+
|
552 |
+
/**
|
553 |
+
* Retrieve all the data for all the bookings
|
554 |
+
* @since 0.0.1
|
555 |
+
*/
|
556 |
+
public function bookings_data() {
|
557 |
+
|
558 |
+
$args = array(
|
559 |
+
'post_type' => RTB_BOOKING_POST_TYPE,
|
560 |
+
'posts_per_page' => $this->per_page,
|
561 |
+
'paged' => isset( $_GET['paged'] ) ? $_GET['paged'] : 1,
|
562 |
+
'post_status' => isset( $_GET['status'] ) ? $_GET['status'] : array( 'confirmed', 'pending', 'closed' ),
|
563 |
+
);
|
564 |
+
|
565 |
+
if ( isset( $_GET['orderby'] ) ) {
|
566 |
+
$args['orderby'] = $_GET['orderby'];
|
567 |
+
}
|
568 |
+
|
569 |
+
$args['order'] = isset( $_GET['order'] ) ? $_GET['order'] : 'ASC';
|
570 |
+
|
571 |
+
if ( $this->filter_start_date !== null || $this->filter_end_date !== null ) {
|
572 |
+
|
573 |
+
$date_query = array();
|
574 |
+
|
575 |
+
if ( $this->filter_start_date !== null ) {
|
576 |
+
$date_query['after'] = $this->filter_start_date;
|
577 |
+
}
|
578 |
+
|
579 |
+
if ( $this->filter_end_date !== null ) {
|
580 |
+
$date_query['before'] = $this->filter_end_date;
|
581 |
+
}
|
582 |
+
|
583 |
+
if ( count( $date_query ) ) {
|
584 |
+
$args['date_query'] = $date_query;
|
585 |
+
}
|
586 |
+
|
587 |
+
} elseif ( isset( $_GET['schedule'] ) ) {
|
588 |
+
|
589 |
+
if ( $_GET['schedule'] == 'today' ) {
|
590 |
+
$today = getdate();
|
591 |
+
$args['year'] = $today['year'];
|
592 |
+
$args['monthnum'] = $today['mon'];
|
593 |
+
$args['day'] = $today['mday'];
|
594 |
+
}
|
595 |
+
|
596 |
+
if ( $_GET['schedule'] == 'upcoming' ) {
|
597 |
+
$args['date_query'] = array(
|
598 |
+
array(
|
599 |
+
'after' => '-1 hour', // show bookings that have just passed
|
600 |
+
)
|
601 |
+
);
|
602 |
+
if ( empty( $_GET['order'] ) ) {
|
603 |
+
$args['order'] = 'ASC';
|
604 |
+
}
|
605 |
+
}
|
606 |
+
}
|
607 |
+
|
608 |
+
$args = apply_filters( 'rtb_bookings_table_query_args', $args );
|
609 |
+
|
610 |
+
// Make query
|
611 |
+
$query = new WP_Query( $args );
|
612 |
+
|
613 |
+
if ( $query->have_posts() ) {
|
614 |
+
while ( $query->have_posts() ) {
|
615 |
+
$query->the_post();
|
616 |
+
|
617 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
|
618 |
+
$booking = new rtbBooking();
|
619 |
+
if ( $booking->load_post( $query->post ) ) {
|
620 |
+
$this->bookings[] = $booking;
|
621 |
+
}
|
622 |
+
}
|
623 |
+
}
|
624 |
+
}
|
625 |
+
|
626 |
+
/**
|
627 |
+
* Setup the final data for the table
|
628 |
+
* @since 0.0.1
|
629 |
+
*/
|
630 |
+
public function prepare_items() {
|
631 |
+
|
632 |
+
$columns = $this->get_columns();
|
633 |
+
$hidden = array(); // No hidden columns
|
634 |
+
$sortable = $this->get_sortable_columns();
|
635 |
+
|
636 |
+
$this->_column_headers = array( $columns, $hidden, $sortable );
|
637 |
+
|
638 |
+
$this->items = $this->bookings;
|
639 |
+
|
640 |
+
$total_items = empty( $_GET['status'] ) ? $this->booking_counts['total'] : $this->booking_counts[$_GET['status']];
|
641 |
+
|
642 |
+
$this->set_pagination_args( array(
|
643 |
+
'total_items' => $total_items,
|
644 |
+
'per_page' => $this->per_page,
|
645 |
+
'total_pages' => ceil( $total_items / $this->per_page )
|
646 |
+
)
|
647 |
+
);
|
648 |
+
}
|
649 |
+
}
|
650 |
+
} // endif;
|
includes/WP_Widget.BookingFormWidget.class.php
ADDED
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
if ( ! defined( 'ABSPATH' ) ) exit;
|
3 |
+
|
4 |
+
if ( ! class_exists( 'WP_Widget' ) ) {
|
5 |
+
require_once ABSPATH . 'wp-admin/includes/widgets.php';
|
6 |
+
}
|
7 |
+
|
8 |
+
if ( !class_exists( 'rtbBookingFormWidget' ) ) {
|
9 |
+
/**
|
10 |
+
* Booking form widget
|
11 |
+
*
|
12 |
+
* Extends WP_Widget to display a booking form in a widget.
|
13 |
+
* @since 0.0.1
|
14 |
+
*/
|
15 |
+
class rtbBookingFormWidget extends WP_Widget {
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Register widget with WordPress.
|
19 |
+
* @since 0.0.1
|
20 |
+
*/
|
21 |
+
function __construct() {
|
22 |
+
|
23 |
+
parent::__construct(
|
24 |
+
'rtb_booking_form_widget',
|
25 |
+
__('Booking Form', RTB_TEXTDOMAIN),
|
26 |
+
array( 'description' => __( 'Display a form to accept bookings.', RTB_TEXTDOMAIN ), )
|
27 |
+
);
|
28 |
+
|
29 |
+
}
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Print the widget content
|
33 |
+
* @since 0.0.1
|
34 |
+
*/
|
35 |
+
public function widget( $args, $instance ) {
|
36 |
+
|
37 |
+
global $rtb_controller;
|
38 |
+
|
39 |
+
// Don't show the widget if the form has already been displayed. The
|
40 |
+
// date and time pickers don't yet support multiple forms on a page.
|
41 |
+
if ( $rtb_controller->form_rendered === true ) {
|
42 |
+
return;
|
43 |
+
}
|
44 |
+
|
45 |
+
// Print the widget's HTML markup
|
46 |
+
echo $args['before_widget'];
|
47 |
+
if( isset( $instance['title'] ) ) {
|
48 |
+
$title = apply_filters( 'widget_title', $instance['title'] );
|
49 |
+
echo $args['before_title'] . $title . $args['after_title'];
|
50 |
+
}
|
51 |
+
echo rtb_print_booking_form();
|
52 |
+
echo $args['after_widget'];
|
53 |
+
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Print the form to configure this widget in the admin panel
|
58 |
+
* @since 1.0
|
59 |
+
*/
|
60 |
+
public function form( $instance ) {
|
61 |
+
?>
|
62 |
+
|
63 |
+
<p>
|
64 |
+
<label for="<?php echo $this->get_field_id( 'title' ); ?>"> <?php _e( 'Title' ); ?></label>
|
65 |
+
<input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text"<?php if ( isset( $instance['title'] ) ) : ?> value="<?php echo esc_attr( $instance['title'] ); ?>"<?php endif; ?>>
|
66 |
+
</p>
|
67 |
+
|
68 |
+
<?php
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Sanitize and save the widget form values.
|
73 |
+
* @since 1.0
|
74 |
+
*/
|
75 |
+
public function update( $new_instance, $old_instance ) {
|
76 |
+
|
77 |
+
$instance = array();
|
78 |
+
if ( !empty( $new_instance['title'] ) ) {
|
79 |
+
$instance['title'] = strip_tags( $new_instance['title'] );
|
80 |
+
}
|
81 |
+
|
82 |
+
return $instance;
|
83 |
+
|
84 |
+
}
|
85 |
+
|
86 |
+
}
|
87 |
+
} // endif
|
includes/template-functions.php
ADDED
@@ -0,0 +1,283 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Template functions for rendering booking forms, etc.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Create a shortcode to render the booking form
|
8 |
+
* @since 0.0.1
|
9 |
+
*/
|
10 |
+
if ( !function_exists( 'rtb_booking_form_shortcode' ) ) {
|
11 |
+
function rtb_booking_form_shortcode() {
|
12 |
+
return rtb_print_booking_form();
|
13 |
+
}
|
14 |
+
add_shortcode( 'booking-form', 'rtb_booking_form_shortcode' );
|
15 |
+
} // endif;
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Print the booking form's HTML code, including error handling and confirmation
|
19 |
+
* notices.
|
20 |
+
* @since 0.0.1
|
21 |
+
*/
|
22 |
+
if ( !function_exists( 'rtb_print_booking_form' ) ) {
|
23 |
+
function rtb_print_booking_form() {
|
24 |
+
|
25 |
+
global $rtb_controller;
|
26 |
+
|
27 |
+
// Only allow the form to be displayed once on a page
|
28 |
+
if ( $rtb_controller->form_rendered === true ) {
|
29 |
+
return;
|
30 |
+
} else {
|
31 |
+
$rtb_controller->form_rendered = true;
|
32 |
+
}
|
33 |
+
|
34 |
+
// Enqueue assets for the form
|
35 |
+
rtb_enqueue_assets();
|
36 |
+
|
37 |
+
// Allow themes and plugins to override the booking form's HTML output.
|
38 |
+
$output = apply_filters( 'rtb_booking_form_html_pre', '' );
|
39 |
+
if ( !empty( $output ) ) {
|
40 |
+
return $output;
|
41 |
+
}
|
42 |
+
|
43 |
+
// Process a booking request
|
44 |
+
if ( !empty( $_POST['action'] ) && $_POST['action'] == 'booking_request' ) {
|
45 |
+
|
46 |
+
if ( empty( $rtb_controller->request ) ) {
|
47 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
|
48 |
+
$rtb_controller->request = new rtbBooking();
|
49 |
+
}
|
50 |
+
|
51 |
+
$rtb_controller->request->insert_booking();
|
52 |
+
}
|
53 |
+
|
54 |
+
// Set up a dummy request object if no request has been made. This just
|
55 |
+
// simplifies the display of values in the form below
|
56 |
+
if ( empty( $rtb_controller->request ) ) {
|
57 |
+
$request = new stdClass();
|
58 |
+
$request->request_processed = false;
|
59 |
+
$request->request_inserted = false;
|
60 |
+
} else {
|
61 |
+
$request = $rtb_controller->request;
|
62 |
+
}
|
63 |
+
|
64 |
+
// Define the form's action parameter
|
65 |
+
$booking_page = $rtb_controller->settings->get_setting( 'booking-page' );
|
66 |
+
if ( !empty( $booking_page ) ) {
|
67 |
+
$booking_page = get_permalink( $booking_page );
|
68 |
+
}
|
69 |
+
|
70 |
+
ob_start();
|
71 |
+
|
72 |
+
?>
|
73 |
+
|
74 |
+
<div class="rtb-booking-form">
|
75 |
+
<?php if ( $request->request_inserted === true ) : ?>
|
76 |
+
<div class="rtb-message">
|
77 |
+
<p><?php echo $rtb_controller->settings->get_setting( 'success-message' ); ?></p>
|
78 |
+
</div>
|
79 |
+
<?php else : ?>
|
80 |
+
<form method="POST" action="<?php echo esc_attr( $booking_page ); ?>">
|
81 |
+
<input type="hidden" name="action" value="booking_request">
|
82 |
+
<fieldset class="reservation">
|
83 |
+
<legend>
|
84 |
+
<?php _e( 'Book a table', RTB_TEXTDOMAIN ); ?>
|
85 |
+
</legend>
|
86 |
+
<div class="date">
|
87 |
+
<?php echo rtb_print_form_error( 'date' ); ?>
|
88 |
+
<label for="rtb-date">
|
89 |
+
<?php _e( 'Date', RTB_TEXTDOMAIN ); ?>
|
90 |
+
</label>
|
91 |
+
<input type="text" name="rtb-date" id="rtb-date" value="<?php echo empty( $request->request_date ) ? '' : esc_attr( $request->request_date ); ?>">
|
92 |
+
</div>
|
93 |
+
<div class="time">
|
94 |
+
<?php echo rtb_print_form_error( 'time' ); ?>
|
95 |
+
<label for="rtb-time">
|
96 |
+
<?php _e( 'Time', RTB_TEXTDOMAIN ); ?>
|
97 |
+
</label>
|
98 |
+
<input type="text" name="rtb-time" id="rtb-time" value="<?php echo empty( $request->request_time ) ? '' : esc_attr( $request->request_time ); ?>">
|
99 |
+
</div>
|
100 |
+
<div class="party">
|
101 |
+
<?php echo rtb_print_form_error( 'party' ); ?>
|
102 |
+
<label for="rtb-party">
|
103 |
+
<?php _e( 'Party', RTB_TEXTDOMAIN ); ?>
|
104 |
+
</label>
|
105 |
+
<input type="text" name="rtb-party" id="rtb-party" value="<?php echo empty( $request->party ) ? '' : esc_attr( $request->party ); ?>">
|
106 |
+
</div>
|
107 |
+
</fieldset>
|
108 |
+
<fieldset class="contact">
|
109 |
+
<legend>
|
110 |
+
<?php _e( 'Contact Details', RTB_TEXTDOMAIN ); ?>
|
111 |
+
</legend>
|
112 |
+
<div class="name">
|
113 |
+
<?php echo rtb_print_form_error( 'name' ); ?>
|
114 |
+
<label for="rtb-name">
|
115 |
+
<?php _e( 'Name', RTB_TEXTDOMAIN ); ?>
|
116 |
+
</label>
|
117 |
+
<input type="text" name="rtb-name" id="rtb-name" placeholder="Your name" value="<?php echo empty( $request->name ) ? '' : esc_attr( $request->name ); ?>">
|
118 |
+
</div>
|
119 |
+
<div class="email">
|
120 |
+
<?php echo rtb_print_form_error( 'email' ); ?>
|
121 |
+
<label for="rtb-email">
|
122 |
+
<?php _e( 'Email', RTB_TEXTDOMAIN ); ?>
|
123 |
+
</label>
|
124 |
+
<input type="text" name="rtb-email" id="rtb-email" placeholder="your@email.com" value="<?php echo empty( $request->email ) ? '' : esc_attr( $request->email ); ?>">
|
125 |
+
</div>
|
126 |
+
<div class="phone">
|
127 |
+
<?php echo rtb_print_form_error( 'phone' ); ?>
|
128 |
+
<label for="rtb-phone">
|
129 |
+
<?php _e( 'Phone', RTB_TEXTDOMAIN ); ?>
|
130 |
+
</label>
|
131 |
+
<input type="text" name="rtb-phone" id="rtb-phone" placeholder="Your phone number" value="<?php echo empty( $request->phone ) ? '' : esc_attr( $request->phone ); ?>">
|
132 |
+
</div>
|
133 |
+
<div class="add-message">
|
134 |
+
<a href="#">
|
135 |
+
<?php _e( 'Add a Message', RTB_TEXTDOMAIN ); ?>
|
136 |
+
</a>
|
137 |
+
</div>
|
138 |
+
<div class="message">
|
139 |
+
<?php echo rtb_print_form_error( 'message' ); ?>
|
140 |
+
<label for="rtb-message">
|
141 |
+
<?php _e( 'Message', RTB_TEXTDOMAIN ); ?>
|
142 |
+
</label>
|
143 |
+
<textarea name="rtb-message" id="rtb-message"><?php echo empty( $request->message ) ? '' : esc_attr( $request->message ); ?></textarea>
|
144 |
+
</div>
|
145 |
+
</fieldset>
|
146 |
+
<button type="submit"><?php _e( 'Request Booking', RTB_TEXTDOMAIN ); ?></button>
|
147 |
+
</form>
|
148 |
+
<?php endif; ?>
|
149 |
+
</div>
|
150 |
+
|
151 |
+
<?php
|
152 |
+
|
153 |
+
$output = ob_get_clean();
|
154 |
+
|
155 |
+
$output = apply_filters( 'rtb_booking_form_html_post', $output );
|
156 |
+
|
157 |
+
return $output;
|
158 |
+
}
|
159 |
+
} // endif;
|
160 |
+
|
161 |
+
/**
|
162 |
+
* Enqueue the front-end CSS and Javascript for the booking form
|
163 |
+
* @since 0.0.1
|
164 |
+
*/
|
165 |
+
if ( !function_exists( 'rtb_enqueue_assets' ) ) {
|
166 |
+
function rtb_enqueue_assets() {
|
167 |
+
|
168 |
+
wp_enqueue_style( 'rtb-booking-form' );
|
169 |
+
|
170 |
+
wp_enqueue_style( 'pickadate-default', RTB_PLUGIN_URL . '/lib/simple-admin-pages/lib/pickadate/themes/default.css' );
|
171 |
+
wp_enqueue_style( 'pickadate-date', RTB_PLUGIN_URL . '/lib/simple-admin-pages/lib/pickadate/themes/default.date.css' );
|
172 |
+
wp_enqueue_style( 'pickadate-time', RTB_PLUGIN_URL . '/lib/simple-admin-pages/lib/pickadate/themes/default.time.css' );
|
173 |
+
wp_enqueue_script( 'pickadate', RTB_PLUGIN_URL . '/lib/simple-admin-pages/lib/pickadate/picker.js', array( 'jquery' ), '', true );
|
174 |
+
wp_enqueue_script( 'pickadate-date', RTB_PLUGIN_URL . '/lib/simple-admin-pages/lib/pickadate/picker.date.js', array( 'jquery' ), '', true );
|
175 |
+
wp_enqueue_script( 'pickadate-time', RTB_PLUGIN_URL . '/lib/simple-admin-pages/lib/pickadate/picker.time.js', array( 'jquery' ), '', true );
|
176 |
+
wp_enqueue_script( 'pickadate-legacy', RTB_PLUGIN_URL . '/lib/simple-admin-pages/lib/pickadate/legacy.js', array( 'jquery' ), '', true );
|
177 |
+
// @todo is there some way I can enqueue this for RTL languages
|
178 |
+
// wp_enqueue_style( 'pickadate-rtl', RTB_PLUGIN_URL . '/lib/simple-admin-pages/lib/pickadate/themes/rtl.css' );
|
179 |
+
|
180 |
+
wp_enqueue_script( 'rtb-booking-form' );
|
181 |
+
|
182 |
+
// Pass date and time format settings to the pickadate controls
|
183 |
+
global $rtb_controller;
|
184 |
+
wp_localize_script(
|
185 |
+
'rtb-booking-form',
|
186 |
+
'rtb_pickadate',
|
187 |
+
array(
|
188 |
+
'date_format' => $rtb_controller->settings->get_setting( 'date-format' ),
|
189 |
+
'time_format' => $rtb_controller->settings->get_setting( 'time-format' ),
|
190 |
+
'disable_dates' => rtb_get_datepicker_rules(),
|
191 |
+
'schedule_open' => $rtb_controller->settings->get_setting( 'schedule-open' ),
|
192 |
+
'schedule_closed' => $rtb_controller->settings->get_setting( 'schedule-closed' ),
|
193 |
+
'early_bookings' => $rtb_controller->settings->get_setting( 'early-bookings' ),
|
194 |
+
'late_bookings' => $rtb_controller->settings->get_setting( 'late-bookings' ),
|
195 |
+
)
|
196 |
+
);
|
197 |
+
|
198 |
+
}
|
199 |
+
} // endif;
|
200 |
+
|
201 |
+
/**
|
202 |
+
* Get rules for datepicker date ranges
|
203 |
+
* See: http://amsul.ca/pickadate.js/date.htm#disable-dates
|
204 |
+
* @since 0.0.1
|
205 |
+
*/
|
206 |
+
if ( !function_exists( 'rtb_get_datepicker_rules' ) ) {
|
207 |
+
function rtb_get_datepicker_rules() {
|
208 |
+
|
209 |
+
global $rtb_controller;
|
210 |
+
|
211 |
+
$disable_rules = array();
|
212 |
+
|
213 |
+
$disabled_weekdays = array(
|
214 |
+
'sunday' => 1,
|
215 |
+
'monday' => 2,
|
216 |
+
'tuesday' => 3,
|
217 |
+
'wednesday' => 4,
|
218 |
+
'thursday' => 5,
|
219 |
+
'friday' => 6,
|
220 |
+
'saturday' => 7,
|
221 |
+
);
|
222 |
+
|
223 |
+
// Determine which weekdays should be disabled
|
224 |
+
$enabled_dates = array();
|
225 |
+
$schedule_open = $rtb_controller->settings->get_setting( 'schedule-open' );
|
226 |
+
if ( is_array( $schedule_open ) ) {
|
227 |
+
foreach ( $schedule_open as $rule ) {
|
228 |
+
if ( !empty( $rule['weekdays'] ) ) {
|
229 |
+
foreach ( $rule['weekdays'] as $weekday => $value ) {
|
230 |
+
unset( $disabled_weekdays[ $weekday ] );
|
231 |
+
}
|
232 |
+
}
|
233 |
+
}
|
234 |
+
|
235 |
+
if ( count( $disabled_weekdays ) < 7 ) {
|
236 |
+
foreach ( $disabled_weekdays as $weekday ) {
|
237 |
+
$disable_rules[] = $weekday;
|
238 |
+
}
|
239 |
+
}
|
240 |
+
}
|
241 |
+
|
242 |
+
// Handle exception dates
|
243 |
+
$schedule_closed = $rtb_controller->settings->get_setting( 'schedule-closed' );
|
244 |
+
if ( is_array( $schedule_closed ) ) {
|
245 |
+
foreach ( $schedule_closed as $rule ) {
|
246 |
+
|
247 |
+
// Disable exception dates that are closed all day
|
248 |
+
if ( !empty( $rule['date'] ) && empty( $rule['time'] ) ) {
|
249 |
+
$date = new DateTime( $rule['date'] );
|
250 |
+
$disable_rules[] = array( $date->format( 'Y' ), ( $date->format( 'n' ) - 1 ), $date->format( 'j' ) );
|
251 |
+
|
252 |
+
// Enable exception dates that have opening times
|
253 |
+
} elseif ( !empty( $rule['date'] ) ) {
|
254 |
+
$date = new DateTime( $rule['date'] );
|
255 |
+
$disable_rules[] = array( $date->format( 'Y' ), ( $date->format( 'n' ) - 1 ), $date->format( 'j' ), 'inverted' );
|
256 |
+
}
|
257 |
+
|
258 |
+
}
|
259 |
+
}
|
260 |
+
|
261 |
+
return $disable_rules;
|
262 |
+
|
263 |
+
}
|
264 |
+
} // endif;
|
265 |
+
|
266 |
+
/**
|
267 |
+
* Print a form validation error
|
268 |
+
* @since 0.0.1
|
269 |
+
*/
|
270 |
+
if ( !function_exists( 'rtb_print_form_error' ) ) {
|
271 |
+
function rtb_print_form_error( $field ) {
|
272 |
+
|
273 |
+
global $rtb_controller;
|
274 |
+
|
275 |
+
if ( !empty( $rtb_controller->request ) && !empty( $rtb_controller->request->validation_errors ) ) {
|
276 |
+
foreach ( $rtb_controller->request->validation_errors as $error ) {
|
277 |
+
if ( $error['field'] == $field ) {
|
278 |
+
echo '<div class="rtb-error">' . $error['message'] . '</div>';
|
279 |
+
}
|
280 |
+
}
|
281 |
+
}
|
282 |
+
}
|
283 |
+
} // endif;
|
lib/simple-admin-pages/README.md
ADDED
@@ -0,0 +1,379 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Simple Admin Pages for WordPress
|
2 |
+
================================
|
3 |
+
|
4 |
+
Simple Admin Pages is a very small utility library to easily add new admin
|
5 |
+
pages to the WordPress admin interface. It simply collects WordPress' useful
|
6 |
+
Settings API into reuseable classes and implements a set of simple controls.
|
7 |
+
|
8 |
+
*Please note that Simple Admin Pages is still undergoing testing and development
|
9 |
+
in the wild. I don't recommend you use it in your own plugins and themes just
|
10 |
+
yet.*
|
11 |
+
|
12 |
+
## Settings Pages Supported
|
13 |
+
|
14 |
+
- Settings sub-page
|
15 |
+
- Themes sub-page
|
16 |
+
- Submenu pages for custom menu items
|
17 |
+
|
18 |
+
## General Controls Supported
|
19 |
+
|
20 |
+
- Text field
|
21 |
+
- Textarea field
|
22 |
+
- Checkbox (single option to enable/disable setting)
|
23 |
+
- Select dropdown with custom options
|
24 |
+
- Select dropdown of any post type
|
25 |
+
- Select dropdown of any taxonomy type
|
26 |
+
- HTML Content (for instructions, links or other inert text)
|
27 |
+
|
28 |
+
## Controls Supported for Special Use Cases
|
29 |
+
|
30 |
+
- Business Opening Hours
|
31 |
+
|
32 |
+
## Usage
|
33 |
+
|
34 |
+
Here's a simple example of how you can use this library to create an admin page.
|
35 |
+
|
36 |
+
```
|
37 |
+
// Instantiate the Simple Admin Library
|
38 |
+
require_once( 'path/to/simple-admin-pages/simple-admin-pages.php' );
|
39 |
+
$sap = sap_initialize_library(
|
40 |
+
array(
|
41 |
+
'version' => '2.0.a.1', // Version of the library
|
42 |
+
'lib_url' => PLUGIN_URL . '/lib/simple-admin-pages/', // URL path to sap library
|
43 |
+
)
|
44 |
+
);
|
45 |
+
|
46 |
+
// Create a page for the options under the Settings (options) menu
|
47 |
+
$sap->add_page(
|
48 |
+
'options', // Admin menu which this page should be added to
|
49 |
+
array( // Array of key/value pairs matching the AdminPage class constructor variables
|
50 |
+
'id' => 'basic-settings',
|
51 |
+
'title' => __( 'Page Title', SAP_TEXTDOMAIN ),
|
52 |
+
'menu_title' => __( 'menu Title', SAP_TEXTDOMAIN ),
|
53 |
+
'description' => '',
|
54 |
+
'capability' => 'manage_options' // User permissions access level
|
55 |
+
)
|
56 |
+
);
|
57 |
+
|
58 |
+
// Create a basic details section
|
59 |
+
$sap->add_section(
|
60 |
+
'basic-settings', // Page to add this section to
|
61 |
+
array( // Array of key/value pairs matching the AdminPageSection class constructor variables
|
62 |
+
'id' => 'basic-details',
|
63 |
+
'title' => __( 'Basic Details', SAP_TEXTDOMAIN ),
|
64 |
+
'description' => __( 'This section includes some basic details for you to configure.', SAP_TEXTDOMAIN )
|
65 |
+
)
|
66 |
+
);
|
67 |
+
|
68 |
+
// Create the options fields
|
69 |
+
$sap->add_setting(
|
70 |
+
'basic-settings', // Page to add this setting to
|
71 |
+
'basic-details', // Section to add this setting to
|
72 |
+
'select', // Type of setting (see sapLibrary::get_setting_classname()
|
73 |
+
array(
|
74 |
+
'id' => 'select-field',
|
75 |
+
'title' => __( 'Select Field', SAP_TEXTDOMAIN ),
|
76 |
+
'description' => __( 'A demonstration of the select field type.', SAP_TEXTDOMAIN ),
|
77 |
+
'options' => array(
|
78 |
+
'one' => __( 'Option 1', SAP_TEXTDOMAIN ),
|
79 |
+
'two' => __( 'Option 2', SAP_TEXTDOMAIN ),
|
80 |
+
'three' => __( 'Option 3', SAP_TEXTDOMAIN )
|
81 |
+
)
|
82 |
+
)
|
83 |
+
);
|
84 |
+
|
85 |
+
// Allow third-party addons to hook into your settings page
|
86 |
+
$sap = apply_filters( 'sap_page_setup', $sap );
|
87 |
+
|
88 |
+
// Register all admin pages and settings with WordPress
|
89 |
+
$sap->add_admin_menus();
|
90 |
+
```
|
91 |
+
|
92 |
+
Check out the documentation section below for more examples and explanation.
|
93 |
+
|
94 |
+
## License
|
95 |
+
|
96 |
+
Simple Admin Pages is released under the GNU GPL 2 or later.
|
97 |
+
|
98 |
+
## Requirements
|
99 |
+
|
100 |
+
Simple Admin Pages has been tested with WordPress versions 3.5 and above.
|
101 |
+
|
102 |
+
## Roadmap
|
103 |
+
|
104 |
+
- Better documentation
|
105 |
+
- Support custom top-level admin pages
|
106 |
+
- More custom data types
|
107 |
+
|
108 |
+
## Documentation
|
109 |
+
|
110 |
+
### sap_initialize_library()
|
111 |
+
Instantiate the library by loading the simple-admin-pages.php file and calling sap_initialize_library. You'll do everything with the $sap object that is returned.
|
112 |
+
|
113 |
+
**args**
|
114 |
+
An array of properties to pass to the library.
|
115 |
+
|
116 |
+
*version*
|
117 |
+
(required)
|
118 |
+
|
119 |
+
This used to ensure that plugins can play well together even if they use different versions of the library. The version will convert . to _ and append that to the class names which are loaded. If version 1.0 is passed, the library will attempt to load a class named sapAdminPage_1_0.
|
120 |
+
|
121 |
+
*lib_url*
|
122 |
+
(required)
|
123 |
+
|
124 |
+
The lib_url is used to print stylesheets or scripts attached to the library.
|
125 |
+
|
126 |
+
```
|
127 |
+
require_once( 'path/to/simple-admin-pages/simple-admin-pages.php' );
|
128 |
+
$sap = sap_initialize_library(
|
129 |
+
$args = array(
|
130 |
+
'version' => '2.0.a.2', // Version of the library
|
131 |
+
'lib_url' => PLUGIN_URL . '/lib/simple-admin-pages/', // URL path to sap library
|
132 |
+
)
|
133 |
+
);
|
134 |
+
```
|
135 |
+
|
136 |
+
### sapLibrary::add_page()
|
137 |
+
Create a new page with the library by calling the add_page() method. You can attach the page to the options (Settings) or themes (Appearance) menus or any custom menu.
|
138 |
+
|
139 |
+
**type**
|
140 |
+
(required)
|
141 |
+
|
142 |
+
What type of admin menu page to create. Accepts:
|
143 |
+
|
144 |
+
- "options" - A subpage of the Settings menu
|
145 |
+
- "themes" - A subpage of the Appearance menu
|
146 |
+
- "submenu" - A subpage of any top-level menu item
|
147 |
+
|
148 |
+
**args**
|
149 |
+
|
150 |
+
An array of properties to pass to the page.
|
151 |
+
|
152 |
+
*id*
|
153 |
+
(required)
|
154 |
+
|
155 |
+
All settings attached to this page will be stored with the page ID and can be retrieved with get_options( $page_id ).
|
156 |
+
|
157 |
+
*title*
|
158 |
+
(required)
|
159 |
+
|
160 |
+
This will be displayed at the top of the page.
|
161 |
+
|
162 |
+
*menu_title*
|
163 |
+
(required)
|
164 |
+
|
165 |
+
The title to display in the menu.
|
166 |
+
|
167 |
+
*description*
|
168 |
+
(optional)
|
169 |
+
|
170 |
+
Actually, I think this one isn't used at the moment.
|
171 |
+
|
172 |
+
*capability*
|
173 |
+
(required)
|
174 |
+
|
175 |
+
The user permissions access level (capability in WP terms) required to access and edit this page.
|
176 |
+
|
177 |
+
*default_tab*
|
178 |
+
(optional)
|
179 |
+
|
180 |
+
If your page will have multiple tabs, you need to specify a default tab to display when the page is initially loaded. This must match the ID used in the add_section() method. *Leave this parameter out if you don't need any tabs.*
|
181 |
+
|
182 |
+
```
|
183 |
+
$sap->add_page(
|
184 |
+
$type,
|
185 |
+
$args = array(
|
186 |
+
'id' => 'my-settings',
|
187 |
+
'title' => __( 'Page Title', SAP_TEXTDOMAIN ),
|
188 |
+
'menu_title' => __( 'menu Title', SAP_TEXTDOMAIN ),
|
189 |
+
'description' => '',
|
190 |
+
'capability' => 'manage_options'
|
191 |
+
'default_tab' => 'tab-one',
|
192 |
+
)
|
193 |
+
);
|
194 |
+
```
|
195 |
+
|
196 |
+
### sapLibrary::add_section()
|
197 |
+
Create a new section to attach it to an existing page.
|
198 |
+
|
199 |
+
Sections can act as Tabs or as internal sections within the normal settings flow of a Tab or Page. In other words, you can define a section as a tab, attach a section to another section which is acting as a tab, or ignore tabs altogether to display all of your sections at once. The example below adds a tab and then adds a sub-section to that tab.
|
200 |
+
|
201 |
+
**page_id**
|
202 |
+
|
203 |
+
Page this section should be attached to. Must match the id passed in add_page().
|
204 |
+
|
205 |
+
**args**
|
206 |
+
|
207 |
+
An array of properties to pass to the section.
|
208 |
+
|
209 |
+
*id*
|
210 |
+
|
211 |
+
(required)
|
212 |
+
Unique slug to which settings will be attached.
|
213 |
+
|
214 |
+
*title*
|
215 |
+
(required)
|
216 |
+
|
217 |
+
This will be displayed at the top of the settings section.
|
218 |
+
|
219 |
+
*description*
|
220 |
+
(optional)
|
221 |
+
|
222 |
+
An optional description to display below the title.
|
223 |
+
|
224 |
+
*is_tab*
|
225 |
+
(optional)
|
226 |
+
|
227 |
+
Set this to true if this section should act like a tab.
|
228 |
+
|
229 |
+
*tab*
|
230 |
+
(optional)
|
231 |
+
|
232 |
+
Use this to attach a section to an existing tab.
|
233 |
+
|
234 |
+
#### Add a section to a page with no tabs:
|
235 |
+
```
|
236 |
+
$sap->add_section(
|
237 |
+
$page_id,
|
238 |
+
$args = array(
|
239 |
+
'id' => 'basic-details-section',
|
240 |
+
'title' => __( 'Basic Details', SAP_TEXTDOMAIN ),
|
241 |
+
'description' => __( 'This section includes some basic details for you to configure.', SAP_TEXTDOMAIN )
|
242 |
+
)
|
243 |
+
);
|
244 |
+
```
|
245 |
+
|
246 |
+
#### Add a tab and a sub-section to a page with tabs:
|
247 |
+
```
|
248 |
+
/**
|
249 |
+
* Create a section that acts as a tab with the is_tab parameter.
|
250 |
+
*/
|
251 |
+
$sap->add_section(
|
252 |
+
$page_id,
|
253 |
+
$args = array(
|
254 |
+
'id' => 'tab-one',
|
255 |
+
'title' => __( 'Tab One', SAP_TEXTDOMAIN ),
|
256 |
+
'description' => __( 'This tab includes some settings for you to configure.', SAP_TEXTDOMAIN ),
|
257 |
+
'is_tab' => true,
|
258 |
+
)
|
259 |
+
);
|
260 |
+
|
261 |
+
/**
|
262 |
+
* Create a sub-section of the tab we just created with the tab parameter.
|
263 |
+
*/
|
264 |
+
$sap->add_section(
|
265 |
+
$page_id,
|
266 |
+
$args = array(
|
267 |
+
'id' => 'section-one-under-tab-one',
|
268 |
+
'title' => __( 'Section One', SAP_TEXTDOMAIN ),
|
269 |
+
'description' => __( 'This section includes some settings for you to configure.', SAP_TEXTDOMAIN ),
|
270 |
+
'tab' => 'tab-one',
|
271 |
+
)
|
272 |
+
);
|
273 |
+
```
|
274 |
+
|
275 |
+
### sapLibrary::add_setting()
|
276 |
+
Create a new setting and attach to an existing section.
|
277 |
+
|
278 |
+
There are several types of settings, each with their own input arguments. I'll try to document it more in the future. For now, check out the AdminPageSetting.*.class.php files in /classes/.
|
279 |
+
|
280 |
+
**page_id**
|
281 |
+
(required)
|
282 |
+
|
283 |
+
Page this setting should be attached to. Must match the id passed in add_page().
|
284 |
+
|
285 |
+
**section_id**
|
286 |
+
(required)
|
287 |
+
|
288 |
+
Section this setting should be attached to. Must match the id passed in add_setting().
|
289 |
+
|
290 |
+
**type**
|
291 |
+
(required)
|
292 |
+
|
293 |
+
Type of setting to add. There are currently several types supported and you can extend the library with your own. I'll try to document this further. For now, you can see all the types supported by default at sapLibrary::get_setting_classname().
|
294 |
+
|
295 |
+
**args**
|
296 |
+
|
297 |
+
An array of properties to pass to the setting.
|
298 |
+
|
299 |
+
*id*
|
300 |
+
(required)
|
301 |
+
|
302 |
+
Unique slug under which the setting will be saved. You would then retrieve the setting with:
|
303 |
+
|
304 |
+
```
|
305 |
+
$options = get_option( $page_id );
|
306 |
+
$options[$setting_id];
|
307 |
+
```
|
308 |
+
|
309 |
+
*title*
|
310 |
+
(required)
|
311 |
+
|
312 |
+
Title of the setting. Typically acts as the field label.
|
313 |
+
|
314 |
+
*description*
|
315 |
+
(optional)
|
316 |
+
|
317 |
+
An optional description to display with the setting. Useful for instructions.
|
318 |
+
|
319 |
+
*...*
|
320 |
+
|
321 |
+
Several setting types have additional parameters. I'll try to document them further.
|
322 |
+
|
323 |
+
```
|
324 |
+
$sap->add_setting(
|
325 |
+
$page_id,
|
326 |
+
$section_id,
|
327 |
+
$type,
|
328 |
+
array(
|
329 |
+
'id' => 'my-first-setting',
|
330 |
+
'title' => __( 'My First Setting', SAP_TEXTDOMAIN ),
|
331 |
+
'description' => __( 'A demonstration of my first setting', SAP_TEXTDOMAIN );
|
332 |
+
...
|
333 |
+
)
|
334 |
+
);
|
335 |
+
```
|
336 |
+
|
337 |
+
### sapLibrary::add_admin_menus()
|
338 |
+
Once everything is configured, run this method to register the pages with WordPress.
|
339 |
+
|
340 |
+
```
|
341 |
+
// Before you run add_admin_menus, filter the whole library so that
|
342 |
+
// third-party addons can hook into your settings page to add new settings
|
343 |
+
// or adjust existing ones.
|
344 |
+
$sap = apply_filters( 'sap_page_setup', $sap );
|
345 |
+
|
346 |
+
$sap->add_admin_menus();
|
347 |
+
```
|
348 |
+
|
349 |
+
### Backwards Compatibility
|
350 |
+
Version 2.0 introduced changes which break backwards compatibility due to the way that the library now stores data in the database. If you are upgrading the version of this library used in your plugin or theme, you must call ```$sap->port_data(2);``` **after** you have declared all of your settings but **before** you call ```$sap->add_admin_menus();```.
|
351 |
+
|
352 |
+
*Note: to ensure all of the old options are found and ported, you shouldn't change any of the structure or ids of your settings. Just drop this method into your existing flow.*
|
353 |
+
|
354 |
+
This changes the way that your settings are stored in the database. Previously, each setting was stored as its own option. Now all the settings on a page are stored in one row.
|
355 |
+
|
356 |
+
You will need to update your plugin to retrieve the settings from their new location. If you previously accessed a setting this way:
|
357 |
+
|
358 |
+
```
|
359 |
+
$my_setting = get_option( $my_setting_id );
|
360 |
+
```
|
361 |
+
|
362 |
+
You should now access the setting this way:
|
363 |
+
|
364 |
+
```
|
365 |
+
$all_page_settings = get_option( $settings_page_id );
|
366 |
+
$all_page_settings[ $my_setting_id ];
|
367 |
+
```
|
368 |
+
|
369 |
+
## Changelog
|
370 |
+
|
371 |
+
- 2.0.a.1 - 2014-04-03
|
372 |
+
- Save all data on a page as one row in wp_options
|
373 |
+
|
374 |
+
- 1.1 - never released
|
375 |
+
- Support themes pages
|
376 |
+
- Support submenu pages for custom menu items
|
377 |
+
|
378 |
+
- 1.0 - 2013-11-20
|
379 |
+
- Initial release
|
lib/simple-admin-pages/classes/AdminPage.Submenu.class.php
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Register, display and save an settings page as a submenu item in the
|
5 |
+
* WordPress admin menu.
|
6 |
+
*
|
7 |
+
* @since 1.1
|
8 |
+
* @package Simple Admin Pages
|
9 |
+
*/
|
10 |
+
|
11 |
+
class sapAdminPageSubmenu_2_0_a_1 extends sapAdminPage_2_0_a_1 {
|
12 |
+
|
13 |
+
public $setup_function = 'add_submenu_page'; // WP function to register the page
|
14 |
+
|
15 |
+
public $parent_menu = null; // Which menu to attach this submenu page to
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Add the page to the appropriate menu slot.
|
19 |
+
* @since 1.0
|
20 |
+
*/
|
21 |
+
public function add_admin_menu() {
|
22 |
+
|
23 |
+
// Don't register if no parent menu is specified
|
24 |
+
if ( !$this->parent_menu ) {
|
25 |
+
return;
|
26 |
+
}
|
27 |
+
|
28 |
+
call_user_func(
|
29 |
+
$this->setup_function,
|
30 |
+
$this->parent_menu,
|
31 |
+
$this->title,
|
32 |
+
$this->menu_title,
|
33 |
+
$this->capability,
|
34 |
+
$this->id,
|
35 |
+
array( $this, 'display_admin_menu' )
|
36 |
+
);
|
37 |
+
}
|
38 |
+
}
|
lib/simple-admin-pages/classes/AdminPage.Themes.class.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Register, display and save a settings page in the WordPress admin Appearance
|
5 |
+
* menu.
|
6 |
+
*
|
7 |
+
* @since 1.0
|
8 |
+
* @package Simple Admin Pages
|
9 |
+
*/
|
10 |
+
|
11 |
+
class sapAdminPageThemes_2_0_a_1 extends sapAdminPage_2_0_a_1 {
|
12 |
+
|
13 |
+
public $setup_function = 'add_theme_page'; // WP function to register the page
|
14 |
+
|
15 |
+
}
|
lib/simple-admin-pages/classes/AdminPage.class.php
ADDED
@@ -0,0 +1,215 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Register, display and save a settings page in the WordPress admin menu.
|
5 |
+
*
|
6 |
+
* @since 1.0
|
7 |
+
* @package Simple Admin Pages
|
8 |
+
*/
|
9 |
+
|
10 |
+
class sapAdminPage_2_0_a_1 {
|
11 |
+
|
12 |
+
public $title;
|
13 |
+
public $menu_title;
|
14 |
+
public $description; // optional description for this page
|
15 |
+
public $capability; // user permissions needed to edit this panel
|
16 |
+
public $id; // id of this page
|
17 |
+
public $sections = array(); // array of sections to display on this page
|
18 |
+
public $show_button = true; // whether or not to show the Save Changes button
|
19 |
+
|
20 |
+
public $setup_function = 'add_options_page'; // WP function to register the page
|
21 |
+
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Initialize the page
|
25 |
+
* @since 1.0
|
26 |
+
*/
|
27 |
+
public function __construct( $args ) {
|
28 |
+
|
29 |
+
// Parse the values passed
|
30 |
+
$this->parse_args( $args );
|
31 |
+
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Parse the arguments passed in the construction and assign them to
|
36 |
+
* internal variables.
|
37 |
+
* @since 1.1
|
38 |
+
*/
|
39 |
+
private function parse_args( $args ) {
|
40 |
+
foreach ( $args as $key => $val ) {
|
41 |
+
switch ( $key ) {
|
42 |
+
|
43 |
+
case 'id' :
|
44 |
+
$this->{$key} = esc_attr( $val );
|
45 |
+
|
46 |
+
default :
|
47 |
+
$this->{$key} = $val;
|
48 |
+
|
49 |
+
}
|
50 |
+
}
|
51 |
+
}
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Add the page to the appropriate menu slot.
|
55 |
+
* @note The default will be to post to the options page, but other classes
|
56 |
+
* should override this function.
|
57 |
+
* @since 1.0
|
58 |
+
*/
|
59 |
+
public function add_admin_menu() {
|
60 |
+
call_user_func( $this->setup_function, $this->title, $this->menu_title, $this->capability, $this->id, array( $this, 'display_admin_menu' ) );
|
61 |
+
}
|
62 |
+
|
63 |
+
/**
|
64 |
+
* Add a section to the page
|
65 |
+
* @since 1.0
|
66 |
+
*/
|
67 |
+
public function add_section( $section ) {
|
68 |
+
|
69 |
+
if ( !$section ) {
|
70 |
+
return;
|
71 |
+
}
|
72 |
+
|
73 |
+
$this->sections[ $section->id ] = $section;
|
74 |
+
|
75 |
+
}
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Register the settings and sanitization callbacks for each setting
|
79 |
+
* @since 1.0
|
80 |
+
*/
|
81 |
+
public function register_admin_menu() {
|
82 |
+
|
83 |
+
foreach ( $this->sections as $section ) {
|
84 |
+
$section->add_settings_section();
|
85 |
+
|
86 |
+
foreach ( $section->settings as $setting ) {
|
87 |
+
$setting->add_settings_field( $section->id );
|
88 |
+
}
|
89 |
+
}
|
90 |
+
|
91 |
+
register_setting( $this->id, $this->id, array( $this, 'sanitize_callback' ) );
|
92 |
+
}
|
93 |
+
|
94 |
+
/**
|
95 |
+
* Loop through the settings and sanitize the data
|
96 |
+
* @since 2.0
|
97 |
+
*/
|
98 |
+
public function sanitize_callback( $value ) {
|
99 |
+
|
100 |
+
if ( empty( $_POST['_wp_http_referer'] ) ) {
|
101 |
+
return $value;
|
102 |
+
}
|
103 |
+
|
104 |
+
// Get the current page/tab so we only update those settings
|
105 |
+
parse_str( $_POST['_wp_http_referer'], $referrer );
|
106 |
+
$current_page = $this->get_current_page( $referrer );
|
107 |
+
|
108 |
+
// Use a new empty value so only values for settings that were added are
|
109 |
+
// passed to the db.
|
110 |
+
$new_value = array();
|
111 |
+
|
112 |
+
foreach ( $this->sections as $section ) {
|
113 |
+
foreach ( $section->settings as $setting ) {
|
114 |
+
if ( $setting->tab == $current_page ) {
|
115 |
+
$setting_value = isset( $value[$setting->id] ) ? $value[$setting->id] : '';
|
116 |
+
$new_value[$setting->id] = $setting->sanitize_callback_wrapper( $setting_value );
|
117 |
+
}
|
118 |
+
}
|
119 |
+
}
|
120 |
+
|
121 |
+
// Pull in the existing values so we never overwrite values that were
|
122 |
+
// on a different tab
|
123 |
+
$old_value = get_option( $this->id );
|
124 |
+
|
125 |
+
if ( is_array( $old_value ) ) {
|
126 |
+
return array_merge( $old_value, $new_value );
|
127 |
+
} else {
|
128 |
+
return $new_value;
|
129 |
+
}
|
130 |
+
|
131 |
+
}
|
132 |
+
|
133 |
+
/**
|
134 |
+
* Get the current page/tab being viewed
|
135 |
+
* @since 2.0
|
136 |
+
*/
|
137 |
+
public function get_current_page( $request ) {
|
138 |
+
|
139 |
+
if ( !empty( $request['tab'] ) ) {
|
140 |
+
return $request['tab'];
|
141 |
+
} elseif ( !empty( $this->default_tab ) ) {
|
142 |
+
return $this->default_tab;
|
143 |
+
} else {
|
144 |
+
return $this->id;
|
145 |
+
}
|
146 |
+
|
147 |
+
}
|
148 |
+
|
149 |
+
/**
|
150 |
+
* Output the settings passed to this page
|
151 |
+
* @since 1.0
|
152 |
+
*/
|
153 |
+
public function display_admin_menu() {
|
154 |
+
|
155 |
+
if ( !$this->title && !count( $this->settings ) ) {
|
156 |
+
return;
|
157 |
+
}
|
158 |
+
|
159 |
+
$current_page = $this->get_current_page( $_GET );
|
160 |
+
|
161 |
+
?>
|
162 |
+
|
163 |
+
<div class="wrap">
|
164 |
+
|
165 |
+
<?php $this->display_page_title(); ?>
|
166 |
+
|
167 |
+
<?php if ( isset( $this->default_tab ) ) : ?>
|
168 |
+
<h2 class="nav-tab-wrapper">
|
169 |
+
<?php
|
170 |
+
foreach( $this->sections as $section ) {
|
171 |
+
|
172 |
+
if ( isset( $section->is_tab ) && $section->is_tab === true ) {
|
173 |
+
|
174 |
+
$tab_url = add_query_arg(
|
175 |
+
array(
|
176 |
+
'settings-updated' => false,
|
177 |
+
'tab' => $section->id
|
178 |
+
)
|
179 |
+
);
|
180 |
+
|
181 |
+
$active = $current_page == $section->id ? ' nav-tab-active' : '';
|
182 |
+
echo '<a href="' . esc_url( $tab_url ) . '" title="' . esc_attr( $section->title ) . '" class="nav-tab' . $active . '">';
|
183 |
+
echo esc_html( $section->title );
|
184 |
+
echo '</a>';
|
185 |
+
}
|
186 |
+
}
|
187 |
+
?>
|
188 |
+
</h2>
|
189 |
+
<?php endif; ?>
|
190 |
+
|
191 |
+
<form method="post" action="options.php">
|
192 |
+
<?php settings_fields( $this->id ); ?>
|
193 |
+
<?php do_settings_sections( $current_page ); ?>
|
194 |
+
<?php if ( $this->show_button ) { submit_button(); } ?>
|
195 |
+
</form>
|
196 |
+
</div>
|
197 |
+
|
198 |
+
<?php
|
199 |
+
}
|
200 |
+
|
201 |
+
/**
|
202 |
+
* Output the title of the page
|
203 |
+
* @since 1.0
|
204 |
+
*/
|
205 |
+
public function display_page_title() {
|
206 |
+
|
207 |
+
if ( empty( $this->title ) ) {
|
208 |
+
return;
|
209 |
+
}
|
210 |
+
?>
|
211 |
+
<h2><?php echo $this->title; ?></h2>
|
212 |
+
<?php
|
213 |
+
}
|
214 |
+
|
215 |
+
}
|
lib/simple-admin-pages/classes/AdminPageSection.class.php
ADDED
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Register, display and save a section on a custom admin menu
|
5 |
+
*
|
6 |
+
* @since 1.0
|
7 |
+
* @package Simple Admin Pages
|
8 |
+
*/
|
9 |
+
|
10 |
+
class sapAdminPageSection_2_0_a_1 {
|
11 |
+
|
12 |
+
// Page defaults
|
13 |
+
public $id; // unique id for this section
|
14 |
+
public $title; // optional title to display above this section
|
15 |
+
public $description; // optional description of the section
|
16 |
+
public $settings = array(); // Array of settings to display in this option set
|
17 |
+
|
18 |
+
// Array to store errors
|
19 |
+
public $errors = array();
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Initialize the section
|
23 |
+
* @since 1.0
|
24 |
+
*/
|
25 |
+
public function __construct( $args ) {
|
26 |
+
|
27 |
+
// Parse the values passed
|
28 |
+
$this->parse_args( $args );
|
29 |
+
|
30 |
+
// Set an error if there is no id for this section
|
31 |
+
if ( !isset( $this->id ) ) {
|
32 |
+
$this->set_error(
|
33 |
+
array(
|
34 |
+
'type' => 'missing_data',
|
35 |
+
'data' => 'id'
|
36 |
+
)
|
37 |
+
);
|
38 |
+
}
|
39 |
+
|
40 |
+
}
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Parse the arguments passed in the construction and assign them to
|
44 |
+
* internal variables.
|
45 |
+
* @since 1.0
|
46 |
+
*/
|
47 |
+
private function parse_args( $args ) {
|
48 |
+
foreach ( $args as $key => $val ) {
|
49 |
+
switch ( $key ) {
|
50 |
+
|
51 |
+
case 'id' :
|
52 |
+
$this->{$key} = esc_attr( $val );
|
53 |
+
|
54 |
+
default :
|
55 |
+
$this->{$key} = $val;
|
56 |
+
|
57 |
+
}
|
58 |
+
}
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Add a setting to this section
|
63 |
+
* @since 1.0
|
64 |
+
*/
|
65 |
+
public function add_setting( $setting ) {
|
66 |
+
if ( !$setting ) {
|
67 |
+
return;
|
68 |
+
}
|
69 |
+
|
70 |
+
$this->settings[ $setting->id ] = $setting;
|
71 |
+
}
|
72 |
+
|
73 |
+
/**
|
74 |
+
* Display the description for this section
|
75 |
+
* @since 1.0
|
76 |
+
*/
|
77 |
+
public function display_section() {
|
78 |
+
|
79 |
+
if ( !count( $this->settings ) ) {
|
80 |
+
return;
|
81 |
+
}
|
82 |
+
|
83 |
+
if ( !empty( $this->description ) ) :
|
84 |
+
?>
|
85 |
+
|
86 |
+
<p class="description"><?php echo $this->description; ?></p>
|
87 |
+
|
88 |
+
<?php
|
89 |
+
endif;
|
90 |
+
}
|
91 |
+
|
92 |
+
/**
|
93 |
+
* Add the settings section to the page in WordPress
|
94 |
+
* @since 1.0
|
95 |
+
*/
|
96 |
+
public function add_settings_section() {
|
97 |
+
add_settings_section( $this->id, $this->title, array( $this, 'display_section' ), $this->get_page_slug() );
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Determine the page slug to use when calling add_settings_section.
|
102 |
+
*
|
103 |
+
* Tabs should use their own ID and settings that are attached to tabs
|
104 |
+
* should use that tab's ID.
|
105 |
+
* @since 2.0
|
106 |
+
*/
|
107 |
+
public function get_page_slug() {
|
108 |
+
if ( isset( $this->is_tab ) && $this->is_tab === true ) {
|
109 |
+
return $this->id;
|
110 |
+
} elseif ( isset( $this->tab ) ) {
|
111 |
+
return $this->tab;
|
112 |
+
} else {
|
113 |
+
return $this->page;
|
114 |
+
}
|
115 |
+
}
|
116 |
+
|
117 |
+
/**
|
118 |
+
* Set an error
|
119 |
+
* @since 1.0
|
120 |
+
*/
|
121 |
+
public function set_error( $error ) {
|
122 |
+
$this->errors[] = array_merge(
|
123 |
+
$error,
|
124 |
+
array(
|
125 |
+
'class' => get_class( $this ),
|
126 |
+
'id' => $this->id,
|
127 |
+
'backtrace' => debug_backtrace()
|
128 |
+
)
|
129 |
+
);
|
130 |
+
}
|
131 |
+
|
132 |
+
}
|
lib/simple-admin-pages/classes/AdminPageSetting.Editor.class.php
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Register, display and save a TinyMC Editor field setting in the admin menu
|
5 |
+
*
|
6 |
+
* @since 1.0
|
7 |
+
* @package Simple Admin Pages
|
8 |
+
*/
|
9 |
+
|
10 |
+
class sapAdminPageSettingEditor_2_0_a_1 extends sapAdminPageSetting_2_0_a_1 {
|
11 |
+
|
12 |
+
public $sanitize_callback = 'wp_kses_post';
|
13 |
+
|
14 |
+
/**
|
15 |
+
* List of arguments accepted by wp_editor
|
16 |
+
* @since 2.0
|
17 |
+
*/
|
18 |
+
public $args = array();
|
19 |
+
|
20 |
+
/**
|
21 |
+
* wp_editor() will handle the escaping
|
22 |
+
* @since 2.0
|
23 |
+
*/
|
24 |
+
public function esc_value( $val ) {
|
25 |
+
return $val;
|
26 |
+
}
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Display this setting
|
30 |
+
* @since 2.0
|
31 |
+
*/
|
32 |
+
public function display_setting() {
|
33 |
+
|
34 |
+
$this->args['textarea_name'] = $this->get_input_name();
|
35 |
+
|
36 |
+
$value = empty( $this->value ) && !empty( $this->default ) ? $this->default : $this->value;
|
37 |
+
|
38 |
+
wp_editor( $value, preg_replace( '/[^\da-z]/i', '', $this->id), $this->args );
|
39 |
+
|
40 |
+
$this->display_description();
|
41 |
+
|
42 |
+
}
|
43 |
+
|
44 |
+
}
|
lib/simple-admin-pages/classes/AdminPageSetting.HTML.class.php
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Register and save an arbitrary HTML chunk in the admin menu
|
5 |
+
*
|
6 |
+
* This allows you to easily add in a dummy "setting" with any arbitrary HTML
|
7 |
+
* code. It's good for displaying a link to documentation, upgrades or anything
|
8 |
+
* else you can think of.
|
9 |
+
*
|
10 |
+
* Data in this field will not be saved or passed. It's purely for presenting
|
11 |
+
* information.
|
12 |
+
*
|
13 |
+
* @since 1.0
|
14 |
+
* @package Simple Admin Pages
|
15 |
+
*/
|
16 |
+
|
17 |
+
class sapAdminPageSettingHTML_2_0_a_1 extends sapAdminPageSetting_2_0_a_1 {
|
18 |
+
|
19 |
+
public $sanitize_callback = 'sanitize_text_field';
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Display this setting
|
23 |
+
* @since 1.0
|
24 |
+
*/
|
25 |
+
public function display_setting() {
|
26 |
+
|
27 |
+
echo $this->html;
|
28 |
+
|
29 |
+
$this->display_description();
|
30 |
+
|
31 |
+
}
|
32 |
+
|
33 |
+
}
|
lib/simple-admin-pages/classes/AdminPageSetting.OpeningHours.class.php
ADDED
@@ -0,0 +1,167 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Register, display and save a series of fields to specify the opening hours
|
5 |
+
* of a business/company.
|
6 |
+
*
|
7 |
+
* This setting accepts the following arguments in its constructor function.
|
8 |
+
*
|
9 |
+
* $args = array(
|
10 |
+
* 'id' => 'setting_id', // Unique id
|
11 |
+
* 'title' => 'My Setting', // Title or label for the setting
|
12 |
+
* 'description' => 'Description', // Help text description
|
13 |
+
* 'weekday_names' => array( // Optional array of custom
|
14 |
+
* 'monday' => 'Monday', // weekday names. These can be
|
15 |
+
* 'tuesday' => 'Tuesday', // passed in any order to
|
16 |
+
* 'wednesday' => 'Wednesday', // set a new start of the week.
|
17 |
+
* 'thursday' => 'Thursday',
|
18 |
+
* 'friday' => 'Friday',
|
19 |
+
* 'saturday' => 'Saturday',
|
20 |
+
* 'sunday' => 'Sunday'
|
21 |
+
* );
|
22 |
+
* );
|
23 |
+
*
|
24 |
+
* @since 1.0
|
25 |
+
* @package Simple Admin Pages
|
26 |
+
*/
|
27 |
+
|
28 |
+
class sapAdminPageSettingOpeningHours_2_0_a_1 extends sapAdminPageSetting_2_0_a_1 {
|
29 |
+
|
30 |
+
public $sanitize_callback = 'sanitize_text_field';
|
31 |
+
|
32 |
+
// Array of days of the week
|
33 |
+
public $weekdays = array(
|
34 |
+
'monday' => 'Monday',
|
35 |
+
'tuesday' => 'Tuesday',
|
36 |
+
'wednesday' => 'Wednesday',
|
37 |
+
'thursday' => 'Thursday',
|
38 |
+
'friday' => 'Friday',
|
39 |
+
'saturday' => 'Saturday',
|
40 |
+
'sunday' => 'Sunday'
|
41 |
+
);
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Parse the arguments passed in the construction and assign them to
|
45 |
+
* internal variables.
|
46 |
+
* @since 1.0
|
47 |
+
*/
|
48 |
+
private function parse_args( $args ) {
|
49 |
+
foreach ( $args as $key => $val ) {
|
50 |
+
switch ( $key ) {
|
51 |
+
|
52 |
+
case 'id' :
|
53 |
+
$this->{$key} = esc_attr( $val );
|
54 |
+
|
55 |
+
case 'weekdays' :
|
56 |
+
|
57 |
+
$this->weekdays = $val;
|
58 |
+
|
59 |
+
default :
|
60 |
+
$this->{$key} = $val;
|
61 |
+
|
62 |
+
}
|
63 |
+
}
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Escape the value to display it in text fields and other input fields
|
68 |
+
*
|
69 |
+
* @since 1.0
|
70 |
+
*/
|
71 |
+
public function esc_value( $val ) {
|
72 |
+
|
73 |
+
$value = array();
|
74 |
+
|
75 |
+
// Loop over the values and sanitize them
|
76 |
+
for ( $i = 0; $i < 7; $i++ ) {
|
77 |
+
$value[$i]['day'] = isset( $val[$i] ) && isset( $val[$i]['day'] ) ? esc_attr( $val[$i]['day'] ) : '';
|
78 |
+
$value[$i]['hours'] = isset( $val[$i] ) && isset( $val[$i]['hours'] ) ? esc_attr( $val[$i]['hours'] ) : '';
|
79 |
+
}
|
80 |
+
|
81 |
+
return $value;
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* Get a day's display name
|
86 |
+
* @since 1.0
|
87 |
+
*/
|
88 |
+
private function get_day_name( $day ) {
|
89 |
+
foreach ( $this->weekdays as $id => $name ) {
|
90 |
+
if ( $day == $id ) {
|
91 |
+
return $name;
|
92 |
+
}
|
93 |
+
}
|
94 |
+
|
95 |
+
return '';
|
96 |
+
}
|
97 |
+
|
98 |
+
/**
|
99 |
+
* Display this setting
|
100 |
+
* @since 1.0
|
101 |
+
* @todo integrate time picker
|
102 |
+
*/
|
103 |
+
public function display_setting() {
|
104 |
+
|
105 |
+
$this->display_description();
|
106 |
+
|
107 |
+
for ($i = 0; $i < 7; $i++) {
|
108 |
+
|
109 |
+
?>
|
110 |
+
|
111 |
+
<table class="sap-opening-hours">
|
112 |
+
<tr>
|
113 |
+
<td>
|
114 |
+
<input type="hidden" id="sap-opening-hours-day-<?php echo $i; ?>-name" name="<?php echo $this->get_input_name(); ?>[<?php echo $i; ?>][day_name]" value="<?php echo esc_attr( $this->get_day_name( $this->value[$i]['day'] ) ); ?>">
|
115 |
+
<select name="<?php echo $this->get_input_name(); ?>[<?php echo $i; ?>][day]" id="<?php echo $this->id . '-' . $i; ?>-day" class="sap-opening-hours-day" data-target="#sap-opening-hours-day-<?php echo $i; ?>-name">
|
116 |
+
<option value=""></option>
|
117 |
+
|
118 |
+
<?php foreach ( $this->weekdays as $id => $name ) : ?>
|
119 |
+
|
120 |
+
<option value="<?php echo $id; ?>" data-name="<?php echo esc_attr( $name ); ?>"<?php if ( $this->value[$i]['day'] == $id ) : ?> selected<?php endif; ?>>
|
121 |
+
<?php echo $name; ?>
|
122 |
+
</option>
|
123 |
+
|
124 |
+
<?php endforeach; ?>
|
125 |
+
|
126 |
+
</select>
|
127 |
+
</td>
|
128 |
+
<td>
|
129 |
+
<input name="<?php echo $this->get_input_name(); ?>[<?php echo $i; ?>][hours]" type="text" id="<?php echo $this->id . '-' . $i; ?>-hours" value="<?php echo $this->value[$i]['hours']; ?>" class="regular-text sap-opening-hours-hours" />
|
130 |
+
</td>
|
131 |
+
</tr>
|
132 |
+
</table>
|
133 |
+
|
134 |
+
<?php
|
135 |
+
|
136 |
+
}
|
137 |
+
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Sanitize the array of text inputs for this setting
|
142 |
+
* @since 1.0
|
143 |
+
*/
|
144 |
+
public function sanitize_callback_wrapper( $values ) {
|
145 |
+
|
146 |
+
// If no sanitization callback exists, don't register the setting.
|
147 |
+
if ( !isset( $this->sanitize_callback ) || !trim( $this->sanitize_callback ) ) {
|
148 |
+
return;
|
149 |
+
}
|
150 |
+
|
151 |
+
// If this isn't an array, just sanitize it as a string
|
152 |
+
if (!is_array( $values ) ) {
|
153 |
+
return call_user_func( $this->sanitize_callback, $values );
|
154 |
+
}
|
155 |
+
|
156 |
+
// Loop over the values and sanitize them
|
157 |
+
for ( $i = 0; $i < 7; $i++ ) {
|
158 |
+
if ( isset( $values[ $i ] ) && is_array( $values[ $i ] ) ) {
|
159 |
+
$values[$i]['day'] = call_user_func( $this->sanitize_callback, $values[$i]['day'] );
|
160 |
+
$values[$i]['hours'] = call_user_func( $this->sanitize_callback, $values[$i]['hours'] );
|
161 |
+
}
|
162 |
+
}
|
163 |
+
|
164 |
+
return $values;
|
165 |
+
}
|
166 |
+
|
167 |
+
}
|
lib/simple-admin-pages/classes/AdminPageSetting.Scheduler.class.php
ADDED
@@ -0,0 +1,596 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Register, display and save a schedule of dates and times.
|
5 |
+
*
|
6 |
+
* This is designed for use for opening hours, a booking schedule or anything
|
7 |
+
* that requires recurring dates and times.
|
8 |
+
*
|
9 |
+
* @since 2.0
|
10 |
+
* @package Simple Admin Pages
|
11 |
+
*/
|
12 |
+
|
13 |
+
class sapAdminPageSettingScheduler_2_0_a_1 extends sapAdminPageSetting_2_0_a_1 {
|
14 |
+
|
15 |
+
public $sanitize_callback = 'sanitize_text_field';
|
16 |
+
|
17 |
+
public $weekdays = array(
|
18 |
+
'monday' => 'Mo',
|
19 |
+
'tuesday' => 'Tu',
|
20 |
+
'wednesday' => 'We',
|
21 |
+
'thursday' => 'Th',
|
22 |
+
'friday' => 'Fr',
|
23 |
+
'saturday' => 'Sa',
|
24 |
+
'sunday' => 'Su',
|
25 |
+
);
|
26 |
+
|
27 |
+
public $weeks = array(
|
28 |
+
'first' => '1st',
|
29 |
+
'second' => '2nd',
|
30 |
+
'third' => '3rd',
|
31 |
+
'fourth' => '4th',
|
32 |
+
'last' => 'last',
|
33 |
+
);
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Number of minutes between time selection intervals
|
37 |
+
*/
|
38 |
+
public $time_interval = 15;
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Display format for time selection
|
42 |
+
* See http://amsul.ca/pickadate.js/ for formatting options
|
43 |
+
*/
|
44 |
+
public $time_format = 'h:i A';
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Display format for date selection
|
48 |
+
* See http://amsul.ca/pickadate.js/ for formatting options
|
49 |
+
*/
|
50 |
+
public $date_format = 'd mmmm, yyyy';
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Boolean to disable the weekday selection option
|
54 |
+
*/
|
55 |
+
public $disable_weekdays = false;
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Boolean to disable the weeks selection option
|
59 |
+
*/
|
60 |
+
public $disable_weeks = false;
|
61 |
+
|
62 |
+
/**
|
63 |
+
* Boolean to disable the date selection option
|
64 |
+
*/
|
65 |
+
public $disable_date = false;
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Boolean to disable the time selection option
|
69 |
+
*/
|
70 |
+
public $disable_time = false;
|
71 |
+
|
72 |
+
/**
|
73 |
+
* Boolean to disable the end time selection option
|
74 |
+
*/
|
75 |
+
public $disable_end_time = false;
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Array containing strings for schedule summaries. See the default
|
79 |
+
* summaries at set_schedule_summaries(). Use this to overwrite the defaults
|
80 |
+
* with anything more useful to this instance.
|
81 |
+
*/
|
82 |
+
public $instance_schedule_summaries;
|
83 |
+
|
84 |
+
/**
|
85 |
+
* Escape the value to display it in text fields and other input fields
|
86 |
+
* @since 2.0
|
87 |
+
*/
|
88 |
+
public function esc_value( $val ) {
|
89 |
+
|
90 |
+
$value = array();
|
91 |
+
|
92 |
+
if ( empty( $val ) ) {
|
93 |
+
return $value;
|
94 |
+
}
|
95 |
+
|
96 |
+
foreach ( $val as $i => $rule ) {
|
97 |
+
|
98 |
+
if ( !empty( $rule['weekdays'] ) ) {
|
99 |
+
$value[$i]['weekdays'] = array();
|
100 |
+
foreach ( $rule['weekdays'] as $day => $flag ) {
|
101 |
+
if ( $flag !== '1' ) {
|
102 |
+
continue;
|
103 |
+
}
|
104 |
+
|
105 |
+
$value[$i]['weekdays'][$day] = $flag;
|
106 |
+
}
|
107 |
+
}
|
108 |
+
|
109 |
+
if ( !empty( $rule['weeks'] ) ) {
|
110 |
+
$value[$i]['weeks'] = array();
|
111 |
+
foreach ( $rule['weeks'] as $week => $flag ) {
|
112 |
+
if ( $flag !== '1' ) {
|
113 |
+
continue;
|
114 |
+
}
|
115 |
+
|
116 |
+
$value[$i]['weeks'][$week] = $flag;
|
117 |
+
}
|
118 |
+
}
|
119 |
+
|
120 |
+
if ( !empty( $rule['date'] ) ) {
|
121 |
+
$value[$i]['date'] = esc_attr( $rule['date'] );
|
122 |
+
}
|
123 |
+
|
124 |
+
if ( !empty( $rule['time']['start'] ) ) {
|
125 |
+
$value[$i]['time']['start'] = esc_attr( $rule['time']['start'] );
|
126 |
+
}
|
127 |
+
if ( !empty( $rule['time']['end'] ) ) {
|
128 |
+
$value[$i]['time']['end'] = esc_attr( $rule['time']['end'] );
|
129 |
+
}
|
130 |
+
}
|
131 |
+
|
132 |
+
return $value;
|
133 |
+
}
|
134 |
+
|
135 |
+
/**
|
136 |
+
* Compile and pass configurable variables to the javascript file, so they
|
137 |
+
* can be used when we initialize the pickadate components
|
138 |
+
* @since 2.0
|
139 |
+
*/
|
140 |
+
public function pass_to_scripts() {
|
141 |
+
|
142 |
+
// Create a global variable containing settings for all schedulers
|
143 |
+
// that are being rendered on the page. This allows us to pass different
|
144 |
+
// settings for different schedulers on the same page.
|
145 |
+
global $sap_scheduler_settings;
|
146 |
+
|
147 |
+
if ( !isset( $sap_scheduler_settings ) ) {
|
148 |
+
$sap_scheduler_settings = array();
|
149 |
+
}
|
150 |
+
|
151 |
+
$sap_scheduler_settings[ $this->id ] = array(
|
152 |
+
'time_interval' => $this->time_interval,
|
153 |
+
'time_format' => $this->time_format,
|
154 |
+
'date_format' => $this->date_format,
|
155 |
+
'template' => $this->get_template(),
|
156 |
+
'weekdays' => $this->weekdays,
|
157 |
+
'weeks' => $this->weeks,
|
158 |
+
'disable_weekdays' => $this->disable_weekdays,
|
159 |
+
'disable_weeks' => $this->disable_weeks,
|
160 |
+
'disable_date' => $this->disable_date,
|
161 |
+
'disable_time' => $this->disable_time,
|
162 |
+
'summaries' => $this->schedule_summaries,
|
163 |
+
);
|
164 |
+
|
165 |
+
// This gets called multiple times, but only the last call is actually
|
166 |
+
// pushed to the script.
|
167 |
+
wp_localize_script(
|
168 |
+
'sap-admin-script',
|
169 |
+
'sap_scheduler',
|
170 |
+
array(
|
171 |
+
'settings' => $sap_scheduler_settings
|
172 |
+
)
|
173 |
+
);
|
174 |
+
|
175 |
+
}
|
176 |
+
|
177 |
+
/**
|
178 |
+
* Display this setting
|
179 |
+
* @since 2.0
|
180 |
+
*/
|
181 |
+
public function display_setting() {
|
182 |
+
|
183 |
+
$this->display_description();
|
184 |
+
|
185 |
+
// Define summary text to use when a rule is displayed in brief
|
186 |
+
$this->set_schedule_summaries();
|
187 |
+
|
188 |
+
// Pass data to the script files to handle js interactions
|
189 |
+
$this->pass_to_scripts();
|
190 |
+
|
191 |
+
?>
|
192 |
+
|
193 |
+
<div class="sap-scheduler" id="<?php echo $this->id; ?>">
|
194 |
+
<?php
|
195 |
+
foreach ( $this->value as $id => $rule ) {
|
196 |
+
echo $this->get_template( $id, $rule, true );
|
197 |
+
}
|
198 |
+
?>
|
199 |
+
</div>
|
200 |
+
<div class="sap-add-scheduler">
|
201 |
+
<a href="#" class="button">
|
202 |
+
<?php _e( 'Add new scheduling rule', SAP_TEXTDOMAIN ); ?>
|
203 |
+
</a>
|
204 |
+
</div>
|
205 |
+
|
206 |
+
<?php
|
207 |
+
}
|
208 |
+
|
209 |
+
/**
|
210 |
+
* Retrieve the template for a scheduling rule
|
211 |
+
* @since 2.0
|
212 |
+
*/
|
213 |
+
public function get_template( $id = 0, $values = array(), $list = false ) {
|
214 |
+
|
215 |
+
$date_format = $this->get_date_format( $values );
|
216 |
+
$time_format = $this->get_time_format( $values );
|
217 |
+
|
218 |
+
ob_start();
|
219 |
+
?>
|
220 |
+
|
221 |
+
<div class="sap-scheduler-rule clearfix<?php echo $list ? ' list' : ''; ?>">
|
222 |
+
<div class="sap-scheduler-date <?php echo $date_format; echo $this->disable_time === true ? ' full-width' : ''; ?>">
|
223 |
+
<ul class="sap-selector">
|
224 |
+
|
225 |
+
<?php if ( !$this->has_multiple_date_formats() ) : ?>
|
226 |
+
<li>
|
227 |
+
<div class="dashicons dashicons-calendar"></div>
|
228 |
+
<?php if ( $date_format == 'weekly' ) : ?>
|
229 |
+
<?php _ex( 'Weekly', 'Format of a scheduling rule', SAP_TEXTDOMAIN ); ?>
|
230 |
+
<?php elseif ( $date_format == 'monthly' ) : ?>
|
231 |
+
<?php _ex( 'Monthly', 'Format of a scheduling rule', SAP_TEXTDOMAIN ); ?>
|
232 |
+
<?php elseif ( $date_format == 'date' ) : ?>
|
233 |
+
<?php _ex( 'Date', 'Format of a scheduling rule', SAP_TEXTDOMAIN ); ?>
|
234 |
+
<?php endif; ?>
|
235 |
+
</li>
|
236 |
+
<?php else : ?>
|
237 |
+
|
238 |
+
<?php if ( $this->disable_weekdays === false ) : ?>
|
239 |
+
<li>
|
240 |
+
<div class="dashicons dashicons-calendar"></div>
|
241 |
+
<a href="#" data-format="weekly"<?php echo $date_format == 'weekly' ? ' class="selected"' : ''; ?>>
|
242 |
+
<?php _ex( 'Weekly', 'Format of a scheduling rule', SAP_TEXTDOMAIN ); ?>
|
243 |
+
</a>
|
244 |
+
</li>
|
245 |
+
<?php endif; ?>
|
246 |
+
|
247 |
+
<?php if ( $this->disable_weeks === false ) : ?>
|
248 |
+
<li>
|
249 |
+
<a href="#" data-format="monthly"<?php echo $date_format == 'monthly' ? ' class="selected"' : ''; ?>>
|
250 |
+
<?php _ex( 'Monthly', 'Format of a scheduling rule', SAP_TEXTDOMAIN ); ?>
|
251 |
+
</a>
|
252 |
+
</li>
|
253 |
+
<?php endif; ?>
|
254 |
+
|
255 |
+
<?php if ( $this->disable_date === false ) : ?>
|
256 |
+
<li>
|
257 |
+
<a href="#" data-format="date"<?php echo $date_format == 'date' ? ' class="selected"' : ''; ?>>
|
258 |
+
<?php _ex( 'Date', 'Format of a scheduling rule', SAP_TEXTDOMAIN ); ?>
|
259 |
+
</a>
|
260 |
+
</li>
|
261 |
+
<?php endif; ?>
|
262 |
+
|
263 |
+
<?php endif; ?>
|
264 |
+
</ul>
|
265 |
+
|
266 |
+
<?php if ( $this->disable_weekdays === false ) : ?>
|
267 |
+
<ul class="sap-scheduler-weekdays">
|
268 |
+
<li class="label">
|
269 |
+
<?php _ex( 'Days of the week', 'Label for selecting days of the week in a scheduling rule', SAP_TEXTDOMAIN ); ?>
|
270 |
+
</li>
|
271 |
+
<?php
|
272 |
+
foreach ( $this->weekdays as $slug => $label ) :
|
273 |
+
$input_name = $this->get_input_name() . '[' . $id . '][weekdays][' . esc_attr( $slug ) . ']';
|
274 |
+
?>
|
275 |
+
<li>
|
276 |
+
<input type="checkbox" name="<?php echo $input_name; ?>" id="<?php echo $input_name; ?>" value="1"<?php echo empty( $values['weekdays'][$slug] ) ? '' : ' checked="checked"'; ?> data-day="<?php echo esc_attr( $slug ); ?>"><label for="<?php echo $input_name; ?>"><?php echo ucfirst( $label ); ?></label>
|
277 |
+
</li>
|
278 |
+
<?php endforeach; ?>
|
279 |
+
</ul>
|
280 |
+
<?php endif; ?>
|
281 |
+
|
282 |
+
<?php if ( $this->disable_weeks === false ) : ?>
|
283 |
+
<ul class="sap-scheduler-weeks">
|
284 |
+
<li class="label">
|
285 |
+
<?php _ex( 'Weeks of the month', 'Label for selecting weeks of the month in a scheduling rule', SAP_TEXTDOMAIN ); ?>
|
286 |
+
</li>
|
287 |
+
<?php
|
288 |
+
foreach ( $this->weeks as $slug => $label ) :
|
289 |
+
$input_name = $this->get_input_name() . '[' . $id . '][weeks][' . esc_attr( $slug ) . ']';
|
290 |
+
?>
|
291 |
+
<li>
|
292 |
+
<input type="checkbox" name="<?php echo $input_name; ?>" id="<?php echo $input_name; ?>" value="1"<?php echo empty( $values['weeks'][$slug] ) ? '' : ' checked="checked"'; ?> data-week="<?php echo esc_attr( $slug ); ?>"><label for="<?php echo $input_name; ?>"><?php echo ucfirst( $label ); ?></label>
|
293 |
+
</li>
|
294 |
+
<?php endforeach; ?>
|
295 |
+
</ul>
|
296 |
+
<?php endif; ?>
|
297 |
+
|
298 |
+
<?php if ( $this->disable_date === false ) : ?>
|
299 |
+
<div class="sap-scheduler-date-input">
|
300 |
+
<label for="<?php echo $this->get_input_name(); ?>[<?php echo $id; ?>][date]">
|
301 |
+
<?php _e( 'Date', SAP_TEXTDOMAIN ); ?>
|
302 |
+
</label>
|
303 |
+
<input type="text" name="<?php echo $this->get_input_name(); ?>[<?php echo $id; ?>][date]" id="<?php echo $this->get_input_name(); ?>[<?php echo $id; ?>][date]" value="<?php echo empty( $values['date'] ) ? '' : $values['date']; ?>">
|
304 |
+
</div>
|
305 |
+
<?php endif; ?>
|
306 |
+
|
307 |
+
</div>
|
308 |
+
|
309 |
+
<?php if ( $this->disable_time === false ) : ?>
|
310 |
+
<div class="sap-scheduler-time <?php echo $time_format; ?>">
|
311 |
+
|
312 |
+
<ul class="sap-selector">
|
313 |
+
<li>
|
314 |
+
<div class="dashicons dashicons-clock"></div>
|
315 |
+
<a href="#" data-format="time-slot"<?php echo $time_format == 'time-slot' ? ' class="selected"' : ''; ?>>
|
316 |
+
<?php _ex( 'Time', 'Label to select time slot for a scheduling rule', SAP_TEXTDOMAIN ); ?>
|
317 |
+
</a>
|
318 |
+
</li>
|
319 |
+
<li>
|
320 |
+
<a href="#" data-format="all-day"<?php echo $time_format == 'all-day' ? ' class="selected"' : ''; ?>>
|
321 |
+
<?php _ex( 'All day', 'Label to set a scheduling rule to last all day', SAP_TEXTDOMAIN ); ?>
|
322 |
+
</a>
|
323 |
+
</li>
|
324 |
+
</ul>
|
325 |
+
|
326 |
+
<div class="sap-scheduler-time-input clearfix">
|
327 |
+
|
328 |
+
<div class="start">
|
329 |
+
<label for="<?php echo $this->get_input_name(); ?>[<?php echo $id; ?>][time][start]">
|
330 |
+
<?php _ex( 'Start', 'Label for the starting time of a scheduling rule', SAP_TEXTDOMAIN ); ?>
|
331 |
+
</label>
|
332 |
+
<input type="text" name="<?php echo $this->get_input_name() . '[' . $id . '][time][start]'; ?>" id="<?php echo $this->get_input_name() . '[' . $id . '][time][start]'; ?>" value="<?php echo empty( $values['time']['start'] ) ? '' : $values['time']['start']; ?>">
|
333 |
+
</div>
|
334 |
+
|
335 |
+
<?php if ( $this->disable_end_time === false ) : ?>
|
336 |
+
<div class="end">
|
337 |
+
<label for="<?php echo $this->get_input_name(); ?>[<?php echo $id; ?>][time][end]">
|
338 |
+
<?php _ex( 'End', 'Label for the ending time of a scheduling rule', SAP_TEXTDOMAIN ); ?>
|
339 |
+
</label>
|
340 |
+
<input type="text" name="<?php echo $this->get_input_name() . '[' . $id . '][time][end]'; ?>" id="<?php echo $this->get_input_name() . '[' . $id . '][time][end]'; ?>" value="<?php echo empty( $values['time']['end'] ) ? '' : $values['time']['end']; ?>">
|
341 |
+
</div>
|
342 |
+
<?php endif; ?>
|
343 |
+
|
344 |
+
</div>
|
345 |
+
|
346 |
+
<div class="sap-scheduler-all-day">
|
347 |
+
<?php _ex( 'All day long. Want to <a href="#" data-format="time-slot">set a time slot</a>?', 'Prompt displayed when a scheduling rule is set without any time restrictions.', SAP_TEXTDOMAIN ); ?>
|
348 |
+
</div>
|
349 |
+
|
350 |
+
</div>
|
351 |
+
<?php endif; ?>
|
352 |
+
|
353 |
+
<div class="sap-scheduler-brief">
|
354 |
+
<div class="date">
|
355 |
+
<div class="dashicons dashicons-calendar"></div>
|
356 |
+
<span class="value"><?php echo $this->get_date_summary( $values ); ?></span>
|
357 |
+
</div>
|
358 |
+
<?php if ( $this->disable_time === false ) : ?>
|
359 |
+
<div class="time">
|
360 |
+
<div class="dashicons dashicons-clock"></div>
|
361 |
+
<span class="value"><?php echo $this->get_time_summary( $values ); ?></span>
|
362 |
+
</div>
|
363 |
+
<?php endif; ?>
|
364 |
+
</div>
|
365 |
+
<div class="sap-scheduler-control">
|
366 |
+
<a href="#" class="toggle" title="<?php _e( 'Open and close this rule', SAP_TEXTDOMAIN ); ?>">
|
367 |
+
<div class="dashicons dashicons-<?php echo $list ? 'edit' : 'arrow-up-alt2'; ?>"></div>
|
368 |
+
<span class="screen-reader-text">
|
369 |
+
<?php _e( 'Open and close this rule', SAP_TEXTDOMAIN ); ?>
|
370 |
+
</span>
|
371 |
+
</a>
|
372 |
+
<a href="#" class="delete" title="<?php _e( 'Delete rule', SAP_TEXTDOMAIN ); ?>">
|
373 |
+
<div class="dashicons dashicons-dismiss"></div>
|
374 |
+
<span class="screen-reader-text">
|
375 |
+
<?php _e( 'Delete scheduling rule', SAP_TEXTDOMAIN ); ?>
|
376 |
+
</span>
|
377 |
+
</a>
|
378 |
+
</div>
|
379 |
+
</div>
|
380 |
+
|
381 |
+
<?php
|
382 |
+
$output = ob_get_clean();
|
383 |
+
|
384 |
+
return $output;
|
385 |
+
}
|
386 |
+
|
387 |
+
/**
|
388 |
+
* Determine the date format of a rule (weeky/monthly/date)
|
389 |
+
* @since 2.0
|
390 |
+
*/
|
391 |
+
public function get_date_format( $values ) {
|
392 |
+
|
393 |
+
if ( !empty( $values['date'] ) ) {
|
394 |
+
return 'date';
|
395 |
+
} elseif ( !empty( $values['weeks'] ) ) {
|
396 |
+
return 'monthly';
|
397 |
+
} elseif ( !empty( $values['weekdays'] ) ) {
|
398 |
+
return 'weekly';
|
399 |
+
}
|
400 |
+
|
401 |
+
if ( $this->disable_weekdays === false ) {
|
402 |
+
return 'weekly';
|
403 |
+
}
|
404 |
+
if ( $this->disable_weeks === false ) {
|
405 |
+
return 'monthly';
|
406 |
+
}
|
407 |
+
if ( $this->disable_date === false ) {
|
408 |
+
return 'date';
|
409 |
+
}
|
410 |
+
}
|
411 |
+
|
412 |
+
/**
|
413 |
+
* Determine the time format of a rule (time-slot/all-day)
|
414 |
+
* @since 2.0
|
415 |
+
*/
|
416 |
+
public function get_time_format( $values ) {
|
417 |
+
if ( empty( $values['time']['start'] ) && empty( $values['time']['end'] ) ) {
|
418 |
+
return 'all-day';
|
419 |
+
}
|
420 |
+
|
421 |
+
return 'time-slot';
|
422 |
+
}
|
423 |
+
|
424 |
+
/**
|
425 |
+
* Determine if multiple date formats are enabled
|
426 |
+
* @since 2.0
|
427 |
+
*/
|
428 |
+
public function has_multiple_date_formats() {
|
429 |
+
$i = 0;
|
430 |
+
if ( $this->disable_weekdays === false ) {
|
431 |
+
$i++;
|
432 |
+
}
|
433 |
+
if ( $this->disable_weeks === false ) {
|
434 |
+
$i++;
|
435 |
+
}
|
436 |
+
if ( $this->disable_date === false ) {
|
437 |
+
$i++;
|
438 |
+
}
|
439 |
+
|
440 |
+
if ( $i > 1 ) {
|
441 |
+
return true;
|
442 |
+
} else {
|
443 |
+
return false;
|
444 |
+
}
|
445 |
+
}
|
446 |
+
|
447 |
+
/**
|
448 |
+
* Set some default summary strings that can be used when the scheduler
|
449 |
+
* rule is shown in brief
|
450 |
+
* @since 2.0
|
451 |
+
*/
|
452 |
+
public function set_schedule_summaries() {
|
453 |
+
|
454 |
+
if ( !empty( $this->schedule_summaries ) ) {
|
455 |
+
return;
|
456 |
+
}
|
457 |
+
|
458 |
+
$this->schedule_summaries = array(
|
459 |
+
'never' => _x( 'Never', 'Brief default description of a scheduling rule when no weekdays or weeks are included in the rule.', SAP_TEXTDOMAIN ),
|
460 |
+
'weekly_always' => _x( 'Every day', 'Brief default description of a scheduling rule when all the weekdays/weeks are included in the rule.', SAP_TEXTDOMAIN ),
|
461 |
+
'monthly_weekdays' => _x( '{days} on the {weeks} week of the month', 'Brief default description of a scheduling rule when some weekdays are included on only some weeks of the month. The {days} and {weeks} bits should be left alone and will be replaced by a comma-separated list of days (the first one) and weeks (the second one) in the following format: M, T, W on the first, second week of the month', SAP_TEXTDOMAIN ),
|
462 |
+
'monthly_weeks' => _x( '{weeks} week of the month', 'Brief description of a scheduling rule when some weeks of the month are included but all or no weekdays are selected. {weeks} should be left alone and will be replaced by a comma-separated list of weeks (the second one) in the following format: First, second week of the month', SAP_TEXTDOMAIN ),
|
463 |
+
'all_day' => _x( 'All day', 'Brief default description of a scheduling rule when no times are set', SAP_TEXTDOMAIN ),
|
464 |
+
'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.', SAP_TEXTDOMAIN ),
|
465 |
+
'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.', SAP_TEXTDOMAIN ),
|
466 |
+
'separator' => _x( '—', 'Default separator between times of a scheduling rule.', SAP_TEXTDOMAIN ),
|
467 |
+
);
|
468 |
+
|
469 |
+
if ( !empty( $this->instance_schedule_summaries ) ) {
|
470 |
+
$this->schedule_summaries = array_merge( $this->schedule_summaries, $this->instance_schedule_summaries );
|
471 |
+
}
|
472 |
+
}
|
473 |
+
|
474 |
+
/**
|
475 |
+
* Print the date phrase, a brief description of the date settings
|
476 |
+
* @since 2.0
|
477 |
+
*/
|
478 |
+
public function get_date_summary( $values = array() ) {
|
479 |
+
|
480 |
+
if ( !empty( $values['date'] ) ) {
|
481 |
+
return $values['date'];
|
482 |
+
}
|
483 |
+
|
484 |
+
if ( empty( $values['weekdays'] ) && $this->disable_weekdays === false ) {
|
485 |
+
return $this->schedule_summaries['never'];
|
486 |
+
}
|
487 |
+
|
488 |
+
if ( empty( $values['weekdays'] ) ) {
|
489 |
+
$weekdays = '';
|
490 |
+
} elseif ( count( $values['weekdays'] ) == 7 ) {
|
491 |
+
$weekdays = $this->schedule_summaries['weekly_always'];
|
492 |
+
} else {
|
493 |
+
$arr = array();
|
494 |
+
foreach ( $values['weekdays'] as $weekday => $state ) {
|
495 |
+
$arr[] = $this->weekdays[$weekday];
|
496 |
+
}
|
497 |
+
$weekdays = join( ', ', $arr );
|
498 |
+
}
|
499 |
+
|
500 |
+
if ( ( empty( $values['weeks'] ) || count( $values['weeks'] ) == 5 ) && $this->disable_weekdays === false ) {
|
501 |
+
return $weekdays;
|
502 |
+
}
|
503 |
+
|
504 |
+
if ( empty( $values['weeks'] ) ) {
|
505 |
+
return $this->schedule_summaries['never'];
|
506 |
+
}
|
507 |
+
|
508 |
+
$arr = array();
|
509 |
+
foreach ( $values['weeks'] as $weeks => $state ) {
|
510 |
+
$arr[] = $this->weeks[$weeks];
|
511 |
+
}
|
512 |
+
$weeks = join( ', ', $arr );
|
513 |
+
|
514 |
+
if ( !empty( $weekdays ) ) {
|
515 |
+
return str_replace( array( '{days}', '{weeks}' ), array( $weekdays, $weeks ), $this->schedule_summaries['monthly_weekdays'] );
|
516 |
+
} else {
|
517 |
+
return str_replace( '{weeks}', ucfirst( $weeks ), $this->schedule_summaries['monthly_weeks'] );
|
518 |
+
}
|
519 |
+
|
520 |
+
}
|
521 |
+
|
522 |
+
/**
|
523 |
+
* Print the time phrase, a brief description of the time settings
|
524 |
+
* @since 2.0
|
525 |
+
*/
|
526 |
+
public function get_time_summary( $values = array() ) {
|
527 |
+
|
528 |
+
if ( empty( $values['time']['start'] ) && empty( $values['time']['end'] ) ) {
|
529 |
+
return $this->schedule_summaries['all_day'];
|
530 |
+
}
|
531 |
+
|
532 |
+
if ( empty( $values['time']['start'] ) ) {
|
533 |
+
return $this->schedule_summaries['before'] . ' ' . $values['time']['end'];
|
534 |
+
}
|
535 |
+
|
536 |
+
if ( empty( $values['time']['end'] ) ) {
|
537 |
+
return $this->schedule_summaries['after'] . ' ' . $values['time']['start'];
|
538 |
+
}
|
539 |
+
|
540 |
+
return $values['time']['start'] . $this->schedule_summaries['separator'] . $values['time']['end'];
|
541 |
+
|
542 |
+
}
|
543 |
+
|
544 |
+
/**
|
545 |
+
* Sanitize the array of text inputs for this setting
|
546 |
+
* @since 2.0
|
547 |
+
*/
|
548 |
+
public function sanitize_callback_wrapper( $values ) {
|
549 |
+
|
550 |
+
$output = array();
|
551 |
+
|
552 |
+
foreach ( $values as $i => $rule ) {
|
553 |
+
|
554 |
+
if ( !empty( $rule['weekdays'] ) ) {
|
555 |
+
$output[$i]['weekdays'] = array();
|
556 |
+
foreach ( $rule['weekdays'] as $day => $flag ) {
|
557 |
+
if ( $flag !== '1' ||
|
558 |
+
( $day !== 'monday' && $day !== 'tuesday' && $day !== 'wednesday' && $day !== 'thursday' && $day !== 'friday' && $day !== 'saturday' && $day !== 'sunday' ) ) {
|
559 |
+
continue;
|
560 |
+
}
|
561 |
+
|
562 |
+
$output[$i]['weekdays'][$day] = $flag;
|
563 |
+
}
|
564 |
+
}
|
565 |
+
|
566 |
+
if ( !empty( $rule['weeks'] ) ) {
|
567 |
+
$output[$i]['weeks'] = array();
|
568 |
+
foreach ( $rule['weeks'] as $week => $flag ) {
|
569 |
+
if ( $flag !== '1' ||
|
570 |
+
( $week !== 'first' && $week !== 'second' && $week !== 'third' && $week !== 'fourth' && $week !== 'last' ) ) {
|
571 |
+
continue;
|
572 |
+
}
|
573 |
+
|
574 |
+
$output[$i]['weeks'][$week] = $flag;
|
575 |
+
}
|
576 |
+
}
|
577 |
+
|
578 |
+
if ( !empty( $rule['date'] ) ) {
|
579 |
+
$date = new DateTime( $rule['date'] );
|
580 |
+
if ( checkdate( $date->format( 'n' ), $date->format( 'j' ), $date->format( 'Y' ) ) ) {
|
581 |
+
$output[$i]['date'] = call_user_func( $this->sanitize_callback, $rule['date'] );
|
582 |
+
}
|
583 |
+
}
|
584 |
+
|
585 |
+
if ( !empty( $rule['time']['start'] ) ) {
|
586 |
+
$output[$i]['time']['start'] = call_user_func( $this->sanitize_callback, $rule['time']['start'] );
|
587 |
+
}
|
588 |
+
if ( !empty( $rule['time']['end'] ) ) {
|
589 |
+
$output[$i]['time']['end'] = call_user_func( $this->sanitize_callback, $rule['time']['end'] );
|
590 |
+
}
|
591 |
+
}
|
592 |
+
|
593 |
+
return $output;
|
594 |
+
}
|
595 |
+
|
596 |
+
}
|
lib/simple-admin-pages/classes/AdminPageSetting.Select.class.php
ADDED
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Register, display and save a selection option with a drop-down menu.
|
5 |
+
*
|
6 |
+
* This setting accepts the following arguments in its constructor function.
|
7 |
+
*
|
8 |
+
* $args = array(
|
9 |
+
* 'id' => 'setting_id', // Unique id
|
10 |
+
* 'title' => 'My Setting', // Title or label for the setting
|
11 |
+
* 'description' => 'Description', // Help text description
|
12 |
+
* 'blank_option' => true, // Whether or not to show a blank option
|
13 |
+
* 'options' => array( // An array of key/value pairs which
|
14 |
+
* 'option1' => 'Option 1', // define the options.
|
15 |
+
* 'option2' => 'Option 2',
|
16 |
+
* ...
|
17 |
+
* );
|
18 |
+
* );
|
19 |
+
*
|
20 |
+
* @since 1.0
|
21 |
+
* @package Simple Admin Pages
|
22 |
+
*/
|
23 |
+
|
24 |
+
class sapAdminPageSettingSelect_2_0_a_1 extends sapAdminPageSetting_2_0_a_1 {
|
25 |
+
|
26 |
+
public $sanitize_callback = 'sanitize_text_field';
|
27 |
+
|
28 |
+
// Whether or not to display a blank option
|
29 |
+
public $blank_option = true;
|
30 |
+
|
31 |
+
// An array of options for this select field, accepted as a key/value pair.
|
32 |
+
public $options = array();
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Display this setting
|
36 |
+
* @since 1.0
|
37 |
+
*/
|
38 |
+
public function display_setting() {
|
39 |
+
|
40 |
+
?>
|
41 |
+
|
42 |
+
<select name="<?php echo $this->get_input_name(); ?>" id="<?php echo $this->id; ?>">
|
43 |
+
|
44 |
+
<?php if ( $this->blank_option === true ) : ?>
|
45 |
+
<option></option>
|
46 |
+
<?php endif; ?>
|
47 |
+
|
48 |
+
<?php foreach ( $this->options as $id => $title ) : ?>
|
49 |
+
<option value="<?php echo esc_attr( $id ); ?>"<?php if( $this->value == $id ) : ?> selected="selected"<?php endif; ?>><?php echo esc_html( $title ); ?></option>
|
50 |
+
<?php endforeach; ?>
|
51 |
+
|
52 |
+
</select>
|
53 |
+
|
54 |
+
<?php
|
55 |
+
|
56 |
+
$this->display_description();
|
57 |
+
|
58 |
+
}
|
59 |
+
|
60 |
+
}
|
lib/simple-admin-pages/classes/AdminPageSetting.SelectPost.class.php
ADDED
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Register, display and save a selection with a drop-down list of any post type
|
5 |
+
*
|
6 |
+
* This setting accepts the following arguments in its constructor function.
|
7 |
+
*
|
8 |
+
* $args = array(
|
9 |
+
* 'id' => 'setting_id', // Unique id
|
10 |
+
* 'title' => 'My Setting', // Title or label for the setting
|
11 |
+
* 'description' => 'Description', // Help text description
|
12 |
+
* 'blank_option' => true, // Whether or not to show a blank option
|
13 |
+
* 'args' => array(); // Arguments to pass to WordPress's get_post() function
|
14 |
+
* );
|
15 |
+
*
|
16 |
+
* @since 1.0
|
17 |
+
* @package Simple Admin Pages
|
18 |
+
*/
|
19 |
+
|
20 |
+
class sapAdminPageSettingSelectPost_2_0_a_1 extends sapAdminPageSetting_2_0_a_1 {
|
21 |
+
|
22 |
+
public $sanitize_callback = 'intval';
|
23 |
+
|
24 |
+
// Whether or not to display a blank option
|
25 |
+
public $blank_option = true;
|
26 |
+
|
27 |
+
/**
|
28 |
+
* An array of arguments accepted by get_posts().
|
29 |
+
* See: http://codex.wordpress.org/Template_Tags/get_posts
|
30 |
+
*/
|
31 |
+
public $args = array();
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Display this setting
|
35 |
+
* @since 1.0
|
36 |
+
*/
|
37 |
+
public function display_setting() {
|
38 |
+
|
39 |
+
$posts = get_posts( $this->args );
|
40 |
+
|
41 |
+
?>
|
42 |
+
|
43 |
+
<select name="<?php echo $this->get_input_name(); ?>" id="<?php echo $this->get_input_name(); ?>">
|
44 |
+
|
45 |
+
<?php if ( $this->blank_option === true ) : ?>
|
46 |
+
<option></option>
|
47 |
+
<?php endif; ?>
|
48 |
+
|
49 |
+
<?php foreach ( $posts as $post ) : ?>
|
50 |
+
<option value="<?php echo esc_attr( $post->ID ); ?>"<?php if( $this->value == $post->ID ) : ?> selected="selected"<?php endif; ?>><?php echo esc_html( $post->post_title ); ?></option>
|
51 |
+
<?php endforeach; ?>
|
52 |
+
|
53 |
+
</select>
|
54 |
+
|
55 |
+
<?php
|
56 |
+
|
57 |
+
$this->display_description();
|
58 |
+
|
59 |
+
}
|
60 |
+
|
61 |
+
}
|
lib/simple-admin-pages/classes/AdminPageSetting.SelectTaxonomy.class.php
ADDED
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Register, display and save a selection with a drop-down list of any taxonomy
|
5 |
+
*
|
6 |
+
* This setting accepts the following arguments in its constructor function.
|
7 |
+
*
|
8 |
+
* $args = array(
|
9 |
+
* 'id' => 'setting_id', // Unique id
|
10 |
+
* 'title' => 'My Setting', // Title or label for the setting
|
11 |
+
* 'description' => 'Description', // Help text description
|
12 |
+
* 'taxonomies' => array(); // Array of taxonomies to fetch (required)
|
13 |
+
* 'blank_option' => true, // Whether or not to show a blank option
|
14 |
+
* 'args' => array(); // Arguments to pass to WordPress's get_terms() function
|
15 |
+
* );
|
16 |
+
* type
|
17 |
+
*
|
18 |
+
* @since 1.0
|
19 |
+
* @package Simple Admin Pages
|
20 |
+
*/
|
21 |
+
|
22 |
+
class sapAdminPageSettingSelectTaxonomy_2_0_a_1 extends sapAdminPageSetting_2_0_a_1 {
|
23 |
+
|
24 |
+
public $sanitize_callback = 'intval';
|
25 |
+
|
26 |
+
// Whether or not to display a blank option
|
27 |
+
public $blank_option = true;
|
28 |
+
|
29 |
+
// Arrays of taxonomies to fetch (required)
|
30 |
+
public $taxonomies;
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Array of options accepted by get_terms()
|
34 |
+
* See: http://codex.wordpress.org/Function_Reference/get_terms
|
35 |
+
*/
|
36 |
+
public $args = array();
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Display this setting
|
40 |
+
* @since 1.0
|
41 |
+
*/
|
42 |
+
public function display_setting() {
|
43 |
+
|
44 |
+
$terms = get_terms( $this->taxonomies, $this->args );
|
45 |
+
|
46 |
+
?>
|
47 |
+
|
48 |
+
<select name="<?php echo $this->get_input_name(); ?>" id="<?php echo $this->get_input_name(); ?>">
|
49 |
+
|
50 |
+
<?php if ( $this->blank_option === true ) : ?>
|
51 |
+
<option></option>
|
52 |
+
<?php endif; ?>
|
53 |
+
|
54 |
+
<?php foreach ( $terms as $term ) : ?>
|
55 |
+
<option value="<?php echo esc_attr( $term->term_id ); ?>"<?php if( $this->value == $term->term_id ) : ?> selected="selected"<?php endif; ?>><?php echo esc_html( $term->name ); ?></option>
|
56 |
+
<?php endforeach; ?>
|
57 |
+
|
58 |
+
</select>
|
59 |
+
|
60 |
+
<?php
|
61 |
+
|
62 |
+
$this->display_description();
|
63 |
+
|
64 |
+
}
|
65 |
+
|
66 |
+
}
|
lib/simple-admin-pages/classes/AdminPageSetting.Text.class.php
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Register, display and save a text field setting in the admin menu
|
5 |
+
*
|
6 |
+
* @since 1.0
|
7 |
+
* @package Simple Admin Pages
|
8 |
+
*/
|
9 |
+
|
10 |
+
class sapAdminPageSettingText_2_0_a_1 extends sapAdminPageSetting_2_0_a_1 {
|
11 |
+
|
12 |
+
public $sanitize_callback = 'sanitize_text_field';
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Placeholder string for the input field
|
16 |
+
* @since 2.0
|
17 |
+
*/
|
18 |
+
public $placeholder = '';
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Display this setting
|
22 |
+
* @since 1.0
|
23 |
+
*/
|
24 |
+
public function display_setting() {
|
25 |
+
?>
|
26 |
+
|
27 |
+
<input name="<?php echo $this->get_input_name(); ?>" type="text" id="<?php echo $this->get_input_name(); ?>" value="<?php echo $this->value; ?>"<?php echo !empty( $this->placeholder ) ? ' placeholder="' . esc_attr( $this->placeholder ) . '"' : ''; ?> class="regular-text" />
|
28 |
+
|
29 |
+
<?php
|
30 |
+
|
31 |
+
$this->display_description();
|
32 |
+
|
33 |
+
}
|
34 |
+
|
35 |
+
}
|
lib/simple-admin-pages/classes/AdminPageSetting.Textarea.class.php
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Register, display and save a textarea field setting in the admin menu
|
5 |
+
*
|
6 |
+
* @since 1.0
|
7 |
+
* @package Simple Admin Pages
|
8 |
+
*
|
9 |
+
* @todo textareas should have an option to swap new lines for <br>s
|
10 |
+
*/
|
11 |
+
|
12 |
+
class sapAdminPageSettingTextarea_2_0_a_1 extends sapAdminPageSetting_2_0_a_1 {
|
13 |
+
|
14 |
+
/*
|
15 |
+
* Size of this textarea
|
16 |
+
*
|
17 |
+
* This is put directly into a css class [size]-text,
|
18 |
+
* and setting this to 'large' will link into WordPress's existing textarea
|
19 |
+
* style for full-width textareas.
|
20 |
+
*/
|
21 |
+
public $size = 'small';
|
22 |
+
|
23 |
+
public $sanitize_callback = 'sanitize_text_field';
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Escape the value to display it safely HTML textarea fields
|
27 |
+
* @since 1.0
|
28 |
+
*/
|
29 |
+
public function esc_value( $val ) {
|
30 |
+
return esc_textarea( $val );
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Set the size of this textarea field
|
35 |
+
* @since 1.0
|
36 |
+
*/
|
37 |
+
public function set_size( $size ) {
|
38 |
+
$this->size = esc_attr( $size );
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Display this setting
|
43 |
+
* @since 1.0
|
44 |
+
*/
|
45 |
+
public function display_setting() {
|
46 |
+
?>
|
47 |
+
|
48 |
+
<textarea name="<?php echo $this->get_input_name(); ?>" id="<?php echo $this->get_input_name(); ?>" class="<?php echo $this->size; ?>-text"<?php echo !empty( $this->placeholder ) ? ' placeholder="' . esc_attr( $this->placeholder ) . '"' : ''; ?>><?php echo $this->value; ?></textarea>
|
49 |
+
|
50 |
+
<?php
|
51 |
+
|
52 |
+
$this->display_description();
|
53 |
+
|
54 |
+
}
|
55 |
+
|
56 |
+
}
|
lib/simple-admin-pages/classes/AdminPageSetting.Toggle.class.php
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Register, display and save an option with a single checkbox.
|
5 |
+
*
|
6 |
+
* This setting accepts the following arguments in its constructor function.
|
7 |
+
*
|
8 |
+
* $args = array(
|
9 |
+
* 'id' => 'setting_id', // Unique id
|
10 |
+
* 'title' => 'My Setting', // Title or label for the setting
|
11 |
+
* 'description' => 'Description', // Help text description
|
12 |
+
* 'label' => 'Label', // Checkbox label text
|
13 |
+
* );
|
14 |
+
* );
|
15 |
+
*
|
16 |
+
* @since 1.0
|
17 |
+
* @package Simple Admin Pages
|
18 |
+
*/
|
19 |
+
|
20 |
+
class sapAdminPageSettingToggle_2_0_a_1 extends sapAdminPageSetting_2_0_a_1 {
|
21 |
+
|
22 |
+
public $sanitize_callback = 'sanitize_text_field';
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Display this setting
|
26 |
+
* @since 1.0
|
27 |
+
*/
|
28 |
+
public function display_setting() {
|
29 |
+
|
30 |
+
$input_name = $this->get_input_name();
|
31 |
+
|
32 |
+
?>
|
33 |
+
|
34 |
+
<input type="checkbox" name="<?php echo $input_name; ?>" id="<?php echo $input_name; ?>" value="1"<?php if( $this->value == '1' ) : ?> checked="checked"<?php endif; ?>>
|
35 |
+
<label for="<?php echo $input_name; ?>"><?php echo $this->label; ?></label>
|
36 |
+
|
37 |
+
<?php
|
38 |
+
|
39 |
+
$this->display_description();
|
40 |
+
|
41 |
+
}
|
42 |
+
|
43 |
+
}
|
lib/simple-admin-pages/classes/AdminPageSetting.class.php
ADDED
@@ -0,0 +1,225 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Register, display and save a setting on a custom admin menu
|
5 |
+
*
|
6 |
+
* All settings accept the following arguments in their constructor functions.
|
7 |
+
*
|
8 |
+
* $args = array(
|
9 |
+
* 'id' => 'setting_id', // Unique id
|
10 |
+
* 'title' => 'My Setting', // Title or label for the setting
|
11 |
+
* 'description' => 'Description' // Help text description
|
12 |
+
* );
|
13 |
+
*
|
14 |
+
* @since 1.0
|
15 |
+
* @package Simple Admin Pages
|
16 |
+
*/
|
17 |
+
|
18 |
+
abstract class sapAdminPageSetting_2_0_a_1 {
|
19 |
+
|
20 |
+
// Page defaults
|
21 |
+
public $id; // used in form fields and database to track and store setting
|
22 |
+
public $title; // setting label
|
23 |
+
public $description; // optional description of the setting
|
24 |
+
public $value; // value of the setting, if a value exists
|
25 |
+
|
26 |
+
// Array to store errors
|
27 |
+
public $errors = array();
|
28 |
+
|
29 |
+
/*
|
30 |
+
* Function to use when sanitizing the data
|
31 |
+
*
|
32 |
+
* We set this to a strict sanitization function as a default, but a
|
33 |
+
* setting should override this in an extended class when needed.
|
34 |
+
*
|
35 |
+
* @since 1.0
|
36 |
+
*/
|
37 |
+
public $sanitize_callback = 'sanitize_text_field';
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Initialize the setting
|
41 |
+
*
|
42 |
+
* By default, every setting takes an id, title and description in the $args
|
43 |
+
* array.
|
44 |
+
*
|
45 |
+
* @since 1.0
|
46 |
+
*/
|
47 |
+
public function __construct( $args ) {
|
48 |
+
|
49 |
+
// Parse the values passed
|
50 |
+
$this->parse_args( $args );
|
51 |
+
|
52 |
+
// Get any existing value
|
53 |
+
$this->set_value();
|
54 |
+
|
55 |
+
// Set an error if the object is missing necessary data
|
56 |
+
if ( $this->missing_data() ) {
|
57 |
+
$this->set_error();
|
58 |
+
}
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Parse the arguments passed in the construction and assign them to
|
63 |
+
* internal variables. This function will be overwritten for most subclasses
|
64 |
+
* @since 1.0
|
65 |
+
*/
|
66 |
+
private function parse_args( $args ) {
|
67 |
+
foreach ( $args as $key => $val ) {
|
68 |
+
switch ( $key ) {
|
69 |
+
|
70 |
+
case 'id' :
|
71 |
+
$this->{$key} = esc_attr( $val );
|
72 |
+
|
73 |
+
default :
|
74 |
+
$this->{$key} = $val;
|
75 |
+
|
76 |
+
}
|
77 |
+
}
|
78 |
+
}
|
79 |
+
|
80 |
+
/**
|
81 |
+
* Check for missing data when setup.
|
82 |
+
* @since 1.0
|
83 |
+
*/
|
84 |
+
private function missing_data( ) {
|
85 |
+
|
86 |
+
$error_type = 'missing_data';
|
87 |
+
|
88 |
+
// Required fields
|
89 |
+
if ( empty( $this->id ) ) {
|
90 |
+
$this->set_error(
|
91 |
+
array(
|
92 |
+
'type' => $error_type,
|
93 |
+
'data' => 'id'
|
94 |
+
)
|
95 |
+
);
|
96 |
+
}
|
97 |
+
if ( empty( $this->title ) ) {
|
98 |
+
$this->set_error(
|
99 |
+
array(
|
100 |
+
'type' => $error_type,
|
101 |
+
'data' => 'title'
|
102 |
+
)
|
103 |
+
);
|
104 |
+
}
|
105 |
+
}
|
106 |
+
|
107 |
+
/**
|
108 |
+
* Set a value
|
109 |
+
* @since 2.0
|
110 |
+
*/
|
111 |
+
public function set_value( $val = null ) {
|
112 |
+
|
113 |
+
if ( $val === null ) {
|
114 |
+
$option_group_value = get_option( $this->page );
|
115 |
+
$val = isset( $option_group_value[ $this->id ] ) ? $option_group_value[ $this->id ] : '';
|
116 |
+
}
|
117 |
+
|
118 |
+
$this->value = $this->esc_value( $val );
|
119 |
+
}
|
120 |
+
|
121 |
+
/**
|
122 |
+
* Escape the value to display it in text fields and other input fields
|
123 |
+
*
|
124 |
+
* We use esc_attr() here so that the default is quite strict, but other
|
125 |
+
* setting types should override this function with the appropriate escape
|
126 |
+
* function. See: http://codex.wordpress.org/Data_Validation
|
127 |
+
*
|
128 |
+
* @since 1.0
|
129 |
+
*/
|
130 |
+
public function esc_value( $val ) {
|
131 |
+
return esc_attr( $val );
|
132 |
+
}
|
133 |
+
|
134 |
+
/**
|
135 |
+
* Wrapper for the sanitization callback function.
|
136 |
+
*
|
137 |
+
* This just reduces code duplication for child classes that need a custom
|
138 |
+
* callback function.
|
139 |
+
* @since 1.0
|
140 |
+
*/
|
141 |
+
public function sanitize_callback_wrapper( $value ) {
|
142 |
+
return call_user_func( $this->sanitize_callback, $value );
|
143 |
+
}
|
144 |
+
|
145 |
+
/**
|
146 |
+
* Display this setting
|
147 |
+
* @since 1.0
|
148 |
+
*/
|
149 |
+
abstract public function display_setting();
|
150 |
+
|
151 |
+
/**
|
152 |
+
* Display a description for this setting
|
153 |
+
* @since 1.0
|
154 |
+
*/
|
155 |
+
public function display_description() {
|
156 |
+
|
157 |
+
if ( !empty( $this->description ) ) {
|
158 |
+
|
159 |
+
?>
|
160 |
+
|
161 |
+
<p class="description"><?php echo $this->description; ?></p>
|
162 |
+
|
163 |
+
<?php
|
164 |
+
|
165 |
+
}
|
166 |
+
}
|
167 |
+
|
168 |
+
/**
|
169 |
+
* Generate an option input field name, using the grouped schema:
|
170 |
+
* "page[option_name]"
|
171 |
+
* @since 1.2
|
172 |
+
*/
|
173 |
+
public function get_input_name() {
|
174 |
+
return esc_attr( $this->page ) . '[' . esc_attr( $this->id ) . ']';
|
175 |
+
}
|
176 |
+
|
177 |
+
/**
|
178 |
+
* Add and register this setting
|
179 |
+
*
|
180 |
+
* @since 1.0
|
181 |
+
*/
|
182 |
+
public function add_settings_field( $section_id ) {
|
183 |
+
|
184 |
+
// If no sanitization callback exists, don't register the setting.
|
185 |
+
if ( !$this->has_sanitize_callback() ) {
|
186 |
+
return;
|
187 |
+
}
|
188 |
+
|
189 |
+
add_settings_field(
|
190 |
+
$this->id,
|
191 |
+
$this->title,
|
192 |
+
array( $this, 'display_setting' ),
|
193 |
+
$this->tab,
|
194 |
+
$section_id
|
195 |
+
);
|
196 |
+
|
197 |
+
}
|
198 |
+
|
199 |
+
/**
|
200 |
+
* Check if this field has a sanitization callback set
|
201 |
+
* @since 1.2
|
202 |
+
*/
|
203 |
+
public function has_sanitize_callback() {
|
204 |
+
if ( isset( $this->sanitize_callback ) && trim( $this->sanitize_callback ) ) {
|
205 |
+
return true;
|
206 |
+
}
|
207 |
+
|
208 |
+
return false;
|
209 |
+
}
|
210 |
+
|
211 |
+
/**
|
212 |
+
* Set an error
|
213 |
+
* @since 1.0
|
214 |
+
*/
|
215 |
+
public function set_error( $error ) {
|
216 |
+
$this->errors[] = array_merge(
|
217 |
+
$error,
|
218 |
+
array(
|
219 |
+
'class' => get_class( $this ),
|
220 |
+
'id' => $this->id,
|
221 |
+
'backtrace' => debug_backtrace()
|
222 |
+
)
|
223 |
+
);
|
224 |
+
}
|
225 |
+
}
|
lib/simple-admin-pages/classes/Library.class.php
ADDED
@@ -0,0 +1,406 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
if ( !class_exists( 'sapLibrary_2_0_a_1' ) ) {
|
3 |
+
/**
|
4 |
+
* This library class loads and provides access to the correct version of the
|
5 |
+
* Simple Admin Pages library.
|
6 |
+
*
|
7 |
+
* @since 1.0
|
8 |
+
* @package Simple Admin Pages
|
9 |
+
*/
|
10 |
+
class sapLibrary_2_0_a_1 {
|
11 |
+
|
12 |
+
// Version of the library
|
13 |
+
private $version = '2.0.a.1';
|
14 |
+
|
15 |
+
// A full URL to the library which is used to correctly link scripts and
|
16 |
+
// stylesheets.
|
17 |
+
public $lib_url;
|
18 |
+
|
19 |
+
// A relative path to any custom library extension classes. When
|
20 |
+
// instantiating a custom setting class, the library will search in its own
|
21 |
+
// directory of classes adn also the $lib_extension_path. This way,
|
22 |
+
// developers can add on their own classes without mixing them with the
|
23 |
+
// default classes.
|
24 |
+
public $lib_extension_path;
|
25 |
+
|
26 |
+
// An array of pages to add to the admin menus
|
27 |
+
public $pages = array();
|
28 |
+
|
29 |
+
// Collects errors for debugging
|
30 |
+
public $errors = array();
|
31 |
+
|
32 |
+
// Set debug mode to true to stop and print errors found while processing.
|
33 |
+
// @note This is not related to your PHP error reporting setting, but is an
|
34 |
+
// internal error tracking mechanism to catch missing or malformed data
|
35 |
+
// during development.
|
36 |
+
public $debug_mode = false;
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Initialize the library with the appropriate version
|
40 |
+
* @since 1.0
|
41 |
+
*/
|
42 |
+
public function __construct( $args ) {
|
43 |
+
|
44 |
+
// If no URL path to the library is passed, we won't be able to add the
|
45 |
+
// CSS and Javascript to the admin panel
|
46 |
+
if ( !isset( $args['lib_url'] ) ) {
|
47 |
+
$this->set_error(
|
48 |
+
array(
|
49 |
+
'id' => 'no-lib-url',
|
50 |
+
'desc' => 'No URL path to the library provided when the libary was created.',
|
51 |
+
'var' => $args,
|
52 |
+
'line' => __LINE__,
|
53 |
+
'function' => __FUNCTION__
|
54 |
+
)
|
55 |
+
);
|
56 |
+
} else {
|
57 |
+
$this->lib_url = $args['lib_url'];
|
58 |
+
}
|
59 |
+
|
60 |
+
// Set a library extension path if passed
|
61 |
+
if ( isset( $args['lib_extension_path'] ) ) {
|
62 |
+
$this->lib_extension_path = $args['lib_extension_path'];
|
63 |
+
}
|
64 |
+
|
65 |
+
// Set the debug mode
|
66 |
+
if ( isset( $args['debug_mode'] ) && $args['debug_mode'] === true ) {
|
67 |
+
$this->debug_mode = true;
|
68 |
+
}
|
69 |
+
|
70 |
+
// Ensure we have access to WordPress' plugin functions
|
71 |
+
require_once(ABSPATH . '/wp-admin/includes/plugin.php');
|
72 |
+
|
73 |
+
// Load the required classes
|
74 |
+
$this->load_class( 'sapAdminPage', 'AdminPage.class.php' );
|
75 |
+
$this->load_class( 'sapAdminPageSection', 'AdminPageSection.class.php' );
|
76 |
+
$this->load_class( 'sapAdminPageSetting', 'AdminPageSetting.class.php' );
|
77 |
+
|
78 |
+
// Add the scripts to the admin pages
|
79 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
|
80 |
+
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* Load the class if it isn't already loaded
|
85 |
+
* @since 1.0
|
86 |
+
*/
|
87 |
+
private function load_class( $class, $file ) {
|
88 |
+
|
89 |
+
if ( !class_exists( $this->get_versioned_classname( $class ) ) ) {
|
90 |
+
require_once( $file );
|
91 |
+
}
|
92 |
+
}
|
93 |
+
|
94 |
+
/**
|
95 |
+
* Return the version suffix for a class
|
96 |
+
* @since 1.0
|
97 |
+
*/
|
98 |
+
private function get_versioned_classname( $class ) {
|
99 |
+
return $class . '_' . str_replace( '.', '_', $this->version );
|
100 |
+
}
|
101 |
+
|
102 |
+
/**
|
103 |
+
* Check if the correct version of a class exists
|
104 |
+
* @since 1.0
|
105 |
+
*/
|
106 |
+
private function versioned_class_exists( $class ) {
|
107 |
+
if ( class_exists( $this->get_versioned_classname( $class ) ) ) {
|
108 |
+
return true;
|
109 |
+
} else {
|
110 |
+
return false;
|
111 |
+
}
|
112 |
+
}
|
113 |
+
|
114 |
+
/**
|
115 |
+
* Load the files for a specific setting type and return the class
|
116 |
+
* to use when instantiating the setting object.
|
117 |
+
*
|
118 |
+
* @since 1.0
|
119 |
+
*/
|
120 |
+
private function get_setting_classname( $type ) {
|
121 |
+
|
122 |
+
switch( $type ) {
|
123 |
+
|
124 |
+
case 'text' :
|
125 |
+
require_once('AdminPageSetting.Text.class.php');
|
126 |
+
return $this->get_versioned_classname( 'sapAdminPageSettingText' );
|
127 |
+
|
128 |
+
case 'textarea' :
|
129 |
+
require_once('AdminPageSetting.Textarea.class.php');
|
130 |
+
return $this->get_versioned_classname( 'sapAdminPageSettingTextarea' );
|
131 |
+
|
132 |
+
case 'select' :
|
133 |
+
require_once('AdminPageSetting.Select.class.php');
|
134 |
+
return $this->get_versioned_classname( 'sapAdminPageSettingSelect' );
|
135 |
+
|
136 |
+
case 'toggle' :
|
137 |
+
require_once('AdminPageSetting.Toggle.class.php');
|
138 |
+
return $this->get_versioned_classname( 'sapAdminPageSettingToggle' );
|
139 |
+
|
140 |
+
case 'post' :
|
141 |
+
require_once('AdminPageSetting.SelectPost.class.php');
|
142 |
+
return $this->get_versioned_classname( 'sapAdminPageSettingSelectPost' );
|
143 |
+
|
144 |
+
case 'taxonomy' :
|
145 |
+
require_once('AdminPageSetting.SelectTaxonomy.class.php');
|
146 |
+
return $this->get_versioned_classname( 'sapAdminPageSettingSelectTaxonomy' );
|
147 |
+
|
148 |
+
case 'editor' :
|
149 |
+
require_once('AdminPageSetting.Editor.class.php');
|
150 |
+
return $this->get_versioned_classname( 'sapAdminPageSettingEditor' );
|
151 |
+
|
152 |
+
case 'html' :
|
153 |
+
require_once('AdminPageSetting.HTML.class.php');
|
154 |
+
return $this->get_versioned_classname( 'sapAdminPageSettingHTML' );
|
155 |
+
|
156 |
+
case 'scheduler' :
|
157 |
+
require_once('AdminPageSetting.Scheduler.class.php');
|
158 |
+
return $this->get_versioned_classname( 'sapAdminPageSettingScheduler' );
|
159 |
+
|
160 |
+
case 'opening-hours' :
|
161 |
+
require_once('AdminPageSetting.OpeningHours.class.php');
|
162 |
+
return $this->get_versioned_classname( 'sapAdminPageSettingOpeningHours' );
|
163 |
+
|
164 |
+
default :
|
165 |
+
|
166 |
+
// Exit early if a custom type is declared without providing the
|
167 |
+
// details to find the type class
|
168 |
+
if ( ( !is_array( $type ) || !isset( $type['id'] ) ) ||
|
169 |
+
( !isset( $type['class'] ) || !isset( $type['filename'] ) ) ) {
|
170 |
+
return false;
|
171 |
+
}
|
172 |
+
|
173 |
+
// Load the custom type file. Look for the file in the library's
|
174 |
+
// folder or check the custom library extension path.
|
175 |
+
if ( file_exists( $type['filename'] ) ) {
|
176 |
+
require_once( $type['filename'] );
|
177 |
+
} elseif ( isset( $this->lib_extension_path ) && file_exists( $this->lib_extension_path . $type['filename'] ) ) {
|
178 |
+
require_once( $this->lib_extension_path . '/' . $type['filename'] );
|
179 |
+
} else {
|
180 |
+
return false;
|
181 |
+
}
|
182 |
+
|
183 |
+
|
184 |
+
// Check that we've loaded the appropriate class
|
185 |
+
if ( !$this->versioned_class_exists( $type['class'] ) ) {
|
186 |
+
return false;
|
187 |
+
}
|
188 |
+
|
189 |
+
return $this->get_versioned_classname( $type['class'] );
|
190 |
+
|
191 |
+
}
|
192 |
+
|
193 |
+
}
|
194 |
+
|
195 |
+
/**
|
196 |
+
* Initialize a page
|
197 |
+
* @since 1.0
|
198 |
+
*
|
199 |
+
* @todo perform some checks on args to ensure a valid page can be constructed
|
200 |
+
*/
|
201 |
+
public function add_page( $menu_location, $args = array() ) {
|
202 |
+
|
203 |
+
// default should be 'options'
|
204 |
+
$class = $this->get_versioned_classname( 'sapAdminPage' );
|
205 |
+
|
206 |
+
if ( $menu_location == 'themes' ) {
|
207 |
+
$this->load_class( 'sapAdminPageThemes', 'AdminPage.Themes.class.php' );
|
208 |
+
$class = $this->get_versioned_classname( 'sapAdminPageThemes' );
|
209 |
+
} elseif ( $menu_location == 'submenu' ) {
|
210 |
+
$this->load_class( 'sapAdminPageSubmenu', 'AdminPage.Submenu.class.php' );
|
211 |
+
$class = $this->get_versioned_classname( 'sapAdminPageSubmenu' );
|
212 |
+
}
|
213 |
+
|
214 |
+
if ( class_exists( $class ) ) {
|
215 |
+
$this->pages[ $args['id'] ] = new $class( $args );
|
216 |
+
}
|
217 |
+
|
218 |
+
}
|
219 |
+
|
220 |
+
/**
|
221 |
+
* Initialize a section
|
222 |
+
* @since 1.0
|
223 |
+
*
|
224 |
+
* @todo perform some checks on args to ensure a valid section can be constructed
|
225 |
+
*/
|
226 |
+
public function add_section( $page, $args = array() ) {
|
227 |
+
|
228 |
+
if ( !isset( $this->pages[ $page ] ) ) {
|
229 |
+
return false;
|
230 |
+
} else {
|
231 |
+
$args['page'] = $page;
|
232 |
+
}
|
233 |
+
|
234 |
+
$class = $this->get_versioned_classname( 'sapAdminPageSection' );
|
235 |
+
if ( class_exists( $class ) ) {
|
236 |
+
$this->pages[ $page ]->add_section( new $class( $args ) );
|
237 |
+
}
|
238 |
+
|
239 |
+
}
|
240 |
+
|
241 |
+
/**
|
242 |
+
* Initialize a setting
|
243 |
+
*
|
244 |
+
* The type variable can be a string pointing to a pre-defined setting type,
|
245 |
+
* or an array consisting of an id, classname and filename which references
|
246 |
+
* a custom setting type. @sa get_setting_classname()
|
247 |
+
*
|
248 |
+
* @since 1.0
|
249 |
+
*/
|
250 |
+
public function add_setting( $page, $section, $type, $args = array() ) {
|
251 |
+
|
252 |
+
if ( !isset( $this->pages[ $page ] ) || !isset( $this->pages[ $page ]->sections[ $section ] ) ) {
|
253 |
+
return false;
|
254 |
+
} else {
|
255 |
+
$args['page'] = $page;
|
256 |
+
$args['tab'] = $this->pages[$page]->sections[ $section ]->get_page_slug();
|
257 |
+
}
|
258 |
+
|
259 |
+
$class = $this->get_setting_classname( $type );
|
260 |
+
if ( ( $class && class_exists( $class ) ) && is_subclass_of( $class, $this->get_versioned_classname( 'sapAdminPageSetting' ) ) ) {
|
261 |
+
$this->pages[ $page ]->sections[ $section ]->add_setting( new $class( $args ) );
|
262 |
+
}
|
263 |
+
|
264 |
+
}
|
265 |
+
|
266 |
+
/**
|
267 |
+
* Register all page, section and settings content with WordPress
|
268 |
+
* @since 1.0
|
269 |
+
*/
|
270 |
+
public function add_admin_menus() {
|
271 |
+
|
272 |
+
// If the library is run in debug mode, check for any errors in content,
|
273 |
+
// print any errors found, and don't add the menu if there are errors
|
274 |
+
if ( $this->debug_mode ) {
|
275 |
+
$errors = array();
|
276 |
+
foreach ( $this->pages as $page ) {
|
277 |
+
foreach ( $page->sections as $section ) {
|
278 |
+
if ( count( $section->errors ) ) {
|
279 |
+
array_merge( $errors, $section->errors );
|
280 |
+
}
|
281 |
+
foreach ( $section->settings as $setting ) {
|
282 |
+
if ( count( $setting->errors ) ) {
|
283 |
+
$errors = array_merge( $errors, $setting->errors );
|
284 |
+
}
|
285 |
+
}
|
286 |
+
}
|
287 |
+
}
|
288 |
+
if ( count( $errors ) ) {
|
289 |
+
print_r( $errors );
|
290 |
+
return;
|
291 |
+
}
|
292 |
+
}
|
293 |
+
|
294 |
+
// Add the action hooks
|
295 |
+
foreach ( $this->pages as $id => $page ) {
|
296 |
+
add_action( 'admin_menu', array( $page, 'add_admin_menu' ) );
|
297 |
+
add_action( 'admin_init', array( $page, 'register_admin_menu' ) );
|
298 |
+
}
|
299 |
+
}
|
300 |
+
|
301 |
+
/**
|
302 |
+
* Port data from a previous version to the current version
|
303 |
+
*
|
304 |
+
* Version 2.0 of the library changes the structure of how it stores data.
|
305 |
+
* In order to upgrade the version of the library your plugin/theme is
|
306 |
+
* using, this method must be called after all of your pages and settings
|
307 |
+
* have been declared but before you run add_admin_menus().
|
308 |
+
*
|
309 |
+
* This method will loop over all of the settings data and port any existing
|
310 |
+
* data to the new data structure. It will check if the data has been ported
|
311 |
+
* first before it updates the data. The old data will be removed to keep
|
312 |
+
* the database clean.
|
313 |
+
*
|
314 |
+
* @var int target_version Which data version the library should update to.
|
315 |
+
* @since 2.0
|
316 |
+
*/
|
317 |
+
public function port_data( $target_version, $delete_old_data = true ) {
|
318 |
+
|
319 |
+
// Port data to the storage structure in version 2
|
320 |
+
if ( $target_version == 2 ) {
|
321 |
+
|
322 |
+
foreach ( $this->pages as $page_id => $page ) {
|
323 |
+
|
324 |
+
// Skip if this page has already been ported
|
325 |
+
if ( get_option( $page_id ) !== false ) {
|
326 |
+
continue;
|
327 |
+
}
|
328 |
+
|
329 |
+
$page_values = array();
|
330 |
+
|
331 |
+
foreach ( $page->sections as $section ) {
|
332 |
+
foreach ( $section->settings as $setting ) {
|
333 |
+
$value = get_option( $setting->id );
|
334 |
+
if ( $value !== false ) {
|
335 |
+
$page_values[ $setting->id ] = $value;
|
336 |
+
}
|
337 |
+
}
|
338 |
+
}
|
339 |
+
|
340 |
+
if ( count( $page_values ) ) {
|
341 |
+
$result = add_option( $page_id, $page_values );
|
342 |
+
|
343 |
+
// Delete old data if the flag is set and the new data was
|
344 |
+
// saved successfully.
|
345 |
+
if ( $delete_old_data === true && $result !== false ) {
|
346 |
+
foreach( $page_values as $setting_id => $setting_value ) {
|
347 |
+
delete_option( $setting_id );
|
348 |
+
}
|
349 |
+
}
|
350 |
+
|
351 |
+
// Reset settings values
|
352 |
+
if ( $result === true ) {
|
353 |
+
|
354 |
+
foreach ( $page->sections as $section ) {
|
355 |
+
foreach ( $section->settings as $setting ) {
|
356 |
+
$setting->set_value();
|
357 |
+
}
|
358 |
+
}
|
359 |
+
|
360 |
+
}
|
361 |
+
}
|
362 |
+
}
|
363 |
+
}
|
364 |
+
|
365 |
+
}
|
366 |
+
|
367 |
+
/**
|
368 |
+
* Enqueue the CSS stylesheet
|
369 |
+
* @since 1.0
|
370 |
+
* @todo complex settings should enqueue their assets only when loaded
|
371 |
+
*/
|
372 |
+
public function enqueue_scripts() {
|
373 |
+
|
374 |
+
// Load the pickadate library
|
375 |
+
wp_enqueue_style( 'pickadate-default', $this->lib_url . 'lib/pickadate/themes/default.css' );
|
376 |
+
wp_enqueue_style( 'pickadate-date', $this->lib_url . 'lib/pickadate/themes/default.date.css' );
|
377 |
+
wp_enqueue_style( 'pickadate-time', $this->lib_url . 'lib/pickadate/themes/default.time.css' );
|
378 |
+
wp_enqueue_script( 'pickadate', $this->lib_url . 'lib/pickadate/picker.js', array( 'jquery' ), '', true );
|
379 |
+
wp_enqueue_script( 'pickadate-date', $this->lib_url . 'lib/pickadate/picker.date.js', array( 'jquery' ), '', true );
|
380 |
+
wp_enqueue_script( 'pickadate-time', $this->lib_url . 'lib/pickadate/picker.time.js', array( 'jquery' ), '', true );
|
381 |
+
wp_enqueue_script( 'pickadate-legacy', $this->lib_url . 'lib/pickadate/legacy.js', array( 'jquery' ), '', true );
|
382 |
+
// @todo is there some way I can enqueue this for RTL languages
|
383 |
+
// wp_enqueue_style( 'pickadate-rtl', $this->lib_url . 'lib/pickadate/themes/rtl.css' );
|
384 |
+
|
385 |
+
// Default styles and scripts
|
386 |
+
wp_enqueue_style( 'sap-admin-style', $this->lib_url . 'css/admin.css' );
|
387 |
+
wp_enqueue_script( 'sap-admin-script', $this->lib_url . 'js/admin.js', array( 'jquery' ), '1.0', true );
|
388 |
+
}
|
389 |
+
|
390 |
+
/**
|
391 |
+
* Set an error
|
392 |
+
* @since 1.0
|
393 |
+
*/
|
394 |
+
public function set_error( $error ) {
|
395 |
+
$this->errors[] = array_merge(
|
396 |
+
$error,
|
397 |
+
array(
|
398 |
+
'class' => get_class( $this ),
|
399 |
+
'id' => $this->id,
|
400 |
+
'backtrace' => debug_backtrace()
|
401 |
+
)
|
402 |
+
);
|
403 |
+
}
|
404 |
+
|
405 |
+
}
|
406 |
+
} // endif;
|
lib/simple-admin-pages/css/admin.css
ADDED
@@ -0,0 +1,225 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
* CSS Stylesheet for Simple Admin Pages library
|
3 |
+
*
|
4 |
+
* This stylesheet formats the display components placed into the WordPress
|
5 |
+
* admin menu. It attempts to replicate WordPress's existing admin menu style.
|
6 |
+
*
|
7 |
+
* @package Simple Admin Pages
|
8 |
+
*/
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Clear floats
|
12 |
+
*/
|
13 |
+
.clearfix:before,
|
14 |
+
.clearfix:after {
|
15 |
+
content: " ";
|
16 |
+
display: table;
|
17 |
+
}
|
18 |
+
.clearfix:after {
|
19 |
+
clear: both;
|
20 |
+
}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Textarea
|
24 |
+
*/
|
25 |
+
textarea.small-text {
|
26 |
+
width: 25em;
|
27 |
+
height: 10em;
|
28 |
+
}
|
29 |
+
textarea.large-text {
|
30 |
+
height: 30em;
|
31 |
+
}
|
32 |
+
|
33 |
+
/*
|
34 |
+
* Opening Hours
|
35 |
+
*/
|
36 |
+
.sap-opening-hours td {
|
37 |
+
padding-left: 0;
|
38 |
+
padding-right: 1em;
|
39 |
+
padding-top: 0;
|
40 |
+
}
|
41 |
+
input.sap-opening-hours-day {
|
42 |
+
width: 15em;
|
43 |
+
}
|
44 |
+
input.sap-opening-hours-hours {
|
45 |
+
width: 9em;
|
46 |
+
}
|
47 |
+
|
48 |
+
/*
|
49 |
+
* Scheduler
|
50 |
+
*/
|
51 |
+
.sap-add-scheduler {
|
52 |
+
margin-top: 1em;
|
53 |
+
}
|
54 |
+
.sap-scheduler-rule {
|
55 |
+
position: relative;
|
56 |
+
padding: 1em;
|
57 |
+
margin: 1em 0;
|
58 |
+
max-width: 800px;
|
59 |
+
background: #fff;
|
60 |
+
-webkit-box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
|
61 |
+
box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
|
62 |
+
}
|
63 |
+
.sap-selector {
|
64 |
+
display: inline-block;
|
65 |
+
margin-top: 0;
|
66 |
+
margin-right: 1em;
|
67 |
+
border-bottom: 1px solid #ccc;
|
68 |
+
}
|
69 |
+
.sap-selector li {
|
70 |
+
display: inline-block;
|
71 |
+
margin-right: 1em;
|
72 |
+
line-height: 20px;
|
73 |
+
}
|
74 |
+
.sap-selector a {
|
75 |
+
text-decoration: none;
|
76 |
+
}
|
77 |
+
.sap-selector .selected {
|
78 |
+
font-weight: 600;
|
79 |
+
color: #666;
|
80 |
+
}
|
81 |
+
.sap-scheduler-weekdays li,
|
82 |
+
.sap-scheduler-weeks li {
|
83 |
+
display: inline-block;
|
84 |
+
margin-right: 0.5em;
|
85 |
+
text-align: center;
|
86 |
+
}
|
87 |
+
.sap-scheduler-date .label,
|
88 |
+
.sap-scheduler-date-input label {
|
89 |
+
display: block;
|
90 |
+
font-style: italic;
|
91 |
+
text-align: left;
|
92 |
+
margin-bottom: 0;
|
93 |
+
}
|
94 |
+
.sap-scheduler-weekdays input,
|
95 |
+
.sap-scheduler-weeks input {
|
96 |
+
margin-top: 6px;
|
97 |
+
margin-bottom: 7px;
|
98 |
+
}
|
99 |
+
.sap-scheduler-date label {
|
100 |
+
display: block;
|
101 |
+
}
|
102 |
+
.sap-scheduler-date-input {
|
103 |
+
display: none;
|
104 |
+
margin-top: 1em;
|
105 |
+
}
|
106 |
+
.sap-scheduler-date.date .sap-scheduler-weeks,
|
107 |
+
.sap-scheduler-date.date .sap-scheduler-weekdays {
|
108 |
+
display: none;
|
109 |
+
}
|
110 |
+
.sap-scheduler-date.weekly .sap-scheduler-weeks,
|
111 |
+
.sap-scheduler-date.date .sap-scheduler-weeks {
|
112 |
+
display: none;
|
113 |
+
}
|
114 |
+
.sap-scheduler-date.date .sap-scheduler-date-input {
|
115 |
+
display: block;
|
116 |
+
}
|
117 |
+
.sap-scheduler-time {
|
118 |
+
margin-top: 4em;
|
119 |
+
}
|
120 |
+
.sap-scheduler-time.all-day .sap-scheduler-time-input {
|
121 |
+
display: none;
|
122 |
+
}
|
123 |
+
.sap-scheduler-time-input .start,
|
124 |
+
.sap-scheduler-time-input .end {
|
125 |
+
margin-top: 1em;
|
126 |
+
display: inline-block;
|
127 |
+
}
|
128 |
+
.sap-scheduler-time-input .start {
|
129 |
+
margin-right: 1em;
|
130 |
+
}
|
131 |
+
.sap-scheduler-time-input label {
|
132 |
+
display: block;
|
133 |
+
font-style: italic;
|
134 |
+
}
|
135 |
+
.sap-scheduler-time-input input {
|
136 |
+
max-width: 8em;
|
137 |
+
}
|
138 |
+
.sap-scheduler-all-day {
|
139 |
+
display: inline-block;
|
140 |
+
margin-top: 1.7em;
|
141 |
+
font-style: italic;
|
142 |
+
padding: 1em;
|
143 |
+
background: #eee;
|
144 |
+
}
|
145 |
+
.sap-scheduler-all-day p {
|
146 |
+
margin-top: 0;
|
147 |
+
}
|
148 |
+
.sap-scheduler-time.time-slot .sap-scheduler-all-day {
|
149 |
+
display: none;
|
150 |
+
}
|
151 |
+
.sap-scheduler-control {
|
152 |
+
clear: both;
|
153 |
+
line-height: 1.5em;
|
154 |
+
}
|
155 |
+
.sap-scheduler-control {
|
156 |
+
position: absolute;
|
157 |
+
top: 1em;
|
158 |
+
right: 1em;
|
159 |
+
}
|
160 |
+
.sap-scheduler-control a {
|
161 |
+
text-decoration: none;
|
162 |
+
}
|
163 |
+
.sap-scheduler-control .delete {
|
164 |
+
color: #a00;
|
165 |
+
}
|
166 |
+
.sap-scheduler-control .delete:hover,
|
167 |
+
.sap-scheduler-control .delete:focus {
|
168 |
+
color: red;
|
169 |
+
}
|
170 |
+
.sap-scheduler-rule.list .sap-scheduler-date,
|
171 |
+
.sap-scheduler-rule.list .sap-scheduler-time {
|
172 |
+
display: none;
|
173 |
+
}
|
174 |
+
.sap-scheduler-rule .sap-scheduler-brief {
|
175 |
+
display: none;
|
176 |
+
clear: both;
|
177 |
+
}
|
178 |
+
.sap-scheduler-rule.list .sap-scheduler-brief {
|
179 |
+
display: block;
|
180 |
+
}
|
181 |
+
.sap-scheduler-brief {
|
182 |
+
margin-right: 4em;
|
183 |
+
line-height: 1.5em;
|
184 |
+
}
|
185 |
+
.sap-scheduler-brief .date,
|
186 |
+
.sap-scheduler-brief .time {
|
187 |
+
display: inline-block;
|
188 |
+
}
|
189 |
+
.sap-scheduler-brief .date {
|
190 |
+
margin-right: 1em;
|
191 |
+
}
|
192 |
+
@media (min-width: 783px) {
|
193 |
+
.sap-selector li {
|
194 |
+
font-size: 13px;
|
195 |
+
}
|
196 |
+
.sap-scheduler-time {
|
197 |
+
margin-top: 0;
|
198 |
+
}
|
199 |
+
.sap-scheduler-date {
|
200 |
+
float: left;
|
201 |
+
width: 50%;
|
202 |
+
}
|
203 |
+
.sap-scheduler-date.full-width {
|
204 |
+
float: none;
|
205 |
+
width: 100%;
|
206 |
+
}
|
207 |
+
.sap-scheduler-time {
|
208 |
+
float: right;
|
209 |
+
width: 50%;
|
210 |
+
}
|
211 |
+
}
|
212 |
+
|
213 |
+
/**
|
214 |
+
* Adjustements to pickadate library css to override WP admin styles
|
215 |
+
*/
|
216 |
+
.picker__table {
|
217 |
+
table-layout: auto;
|
218 |
+
}
|
219 |
+
.picker__table th,
|
220 |
+
.picker__table td {
|
221 |
+
text-align: center;
|
222 |
+
display: table-cell;
|
223 |
+
padding: 0.5em;
|
224 |
+
font-size: 1em;
|
225 |
+
}
|
lib/simple-admin-pages/js/admin.js
ADDED
@@ -0,0 +1,340 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* Javascript functions for the admin interface components of Simple Admin Pages
|
3 |
+
*
|
4 |
+
* @package Simple Admin Pages
|
5 |
+
*/
|
6 |
+
|
7 |
+
jQuery(document).ready(function ($) {
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Opening Hours
|
11 |
+
***************/
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Update the name of each day when the select option is changed
|
15 |
+
*/
|
16 |
+
$( '.sap-opening-hours-day' ).change( function() {
|
17 |
+
$( $(this).data( 'target' ) ).val( $(this).children( 'option:selected' ).data( 'name' ) );
|
18 |
+
});
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Scheduler
|
22 |
+
***********/
|
23 |
+
|
24 |
+
if ( typeof sap_scheduler != 'undefined' ) {
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Register click events on load
|
28 |
+
*/
|
29 |
+
sap_scheduler_register_events();
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Enable datepickers on load
|
33 |
+
*/
|
34 |
+
if ( typeof sap_scheduler.settings != 'undefined' ) {
|
35 |
+
for ( var key in sap_scheduler.settings ) {
|
36 |
+
var obj = sap_scheduler.settings[key];
|
37 |
+
$( '#' + key + ' .sap-scheduler-date-input input' ).pickadate({
|
38 |
+
format: obj.date_format,
|
39 |
+
});
|
40 |
+
$( '#' + key + ' .sap-scheduler-time-input input' ).pickatime({
|
41 |
+
interval: obj.time_interval,
|
42 |
+
format: obj.time_format,
|
43 |
+
});
|
44 |
+
}
|
45 |
+
}
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Add a new scheduler panel
|
49 |
+
*/
|
50 |
+
$( '.sap-add-scheduler a' ).click( function() {
|
51 |
+
var scheduler = $(this).parent().siblings( '.sap-scheduler' );
|
52 |
+
var scheduler_id = scheduler.attr( 'id' );
|
53 |
+
var scheduler_settings = sap_scheduler.settings[ scheduler_id ];
|
54 |
+
scheduler.append( scheduler_settings.template.replace( /\[0\]/g, '[' + scheduler.children( '.sap-scheduler-rule' ).length + ']' ) );
|
55 |
+
scheduler.last( '#' + scheduler_id + ' .sap-scheduler-rule' ).find( '.sap-scheduler-date-input input' ).pickadate({
|
56 |
+
format: scheduler_settings.date_format,
|
57 |
+
});
|
58 |
+
scheduler.last( '#' + scheduler_id + ' .sap-scheduler-rule' ).find( '.sap-scheduler-time-input input' ).pickatime({
|
59 |
+
interval: scheduler_settings.time_interval,
|
60 |
+
format: scheduler_settings.time_format,
|
61 |
+
});
|
62 |
+
sap_scheduler_register_events();
|
63 |
+
|
64 |
+
return false;
|
65 |
+
});
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Register event handlers on the scheduler rules. This is run on page load
|
69 |
+
* and every time a rule is added.
|
70 |
+
*/
|
71 |
+
function sap_scheduler_register_events() {
|
72 |
+
|
73 |
+
/**
|
74 |
+
* Open and close the full view of a scheduling rule
|
75 |
+
*/
|
76 |
+
$( '.sap-scheduler-rule .toggle' ).off( 'click' ).click( function() {
|
77 |
+
|
78 |
+
var controls = $(this).parent();
|
79 |
+
|
80 |
+
if ( $(this).parent().parent().hasClass( 'list' ) ) {
|
81 |
+
controls.hide();
|
82 |
+
$(this).children( '.dashicons-edit' ).removeClass( 'dashicons-edit' ).addClass( 'dashicons-arrow-up-alt2' );
|
83 |
+
controls.siblings( '.sap-scheduler-brief' ).hide();
|
84 |
+
controls.siblings( '.sap-scheduler-date, .sap-scheduler-time' ).slideDown( function() {
|
85 |
+
$(this).parent().removeClass( 'list' );
|
86 |
+
controls.fadeIn();
|
87 |
+
});
|
88 |
+
|
89 |
+
} else {
|
90 |
+
controls.hide();
|
91 |
+
$(this).children( '.dashicons-arrow-up-alt2' ).removeClass( 'dashicons-arrow-up-alt2' ).addClass( 'dashicons-edit' );
|
92 |
+
controls.siblings( '.sap-scheduler-brief' ).fadeIn();
|
93 |
+
controls.siblings( '.sap-scheduler-time' ).slideUp();
|
94 |
+
controls.siblings( '.sap-scheduler-date' ).slideUp( function() {
|
95 |
+
|
96 |
+
var scheduler_rule = $(this).parent();
|
97 |
+
var scheduler_id = scheduler_rule.parent().attr( 'id' );
|
98 |
+
|
99 |
+
scheduler_rule.addClass( 'list' );
|
100 |
+
controls.fadeIn();
|
101 |
+
|
102 |
+
sap_scheduler_set_date_phrase( scheduler_rule, scheduler_id );
|
103 |
+
sap_scheduler_set_time_phrase( scheduler_rule, scheduler_id );
|
104 |
+
});
|
105 |
+
}
|
106 |
+
|
107 |
+
return false;
|
108 |
+
});
|
109 |
+
|
110 |
+
/**
|
111 |
+
* Update current selection for selector lists
|
112 |
+
*/
|
113 |
+
$( '.sap-selector a' ).off( 'switch.sap' ).on( 'switch.sap', function() {
|
114 |
+
$(this).parent().parent().find( 'a' ).removeClass( 'selected' );
|
115 |
+
$(this).addClass( 'selected' );
|
116 |
+
|
117 |
+
return false;
|
118 |
+
});
|
119 |
+
|
120 |
+
/**
|
121 |
+
* Switch between weekly, monthly and date options
|
122 |
+
*/
|
123 |
+
$( '.sap-scheduler-date .sap-selector a' ).off( 'click' ).click( function() {
|
124 |
+
|
125 |
+
$(this).trigger( 'switch.sap' );
|
126 |
+
|
127 |
+
var date = $(this).closest( '.sap-scheduler-date' );
|
128 |
+
|
129 |
+
if ( $(this).data( 'format' ) == 'weekly' && date.hasClass( 'weekly' ) === false ) {
|
130 |
+
date.children( '.sap-scheduler-weeks' ).slideUp( function() {
|
131 |
+
$(this).find( 'input' ).prop('checked', false);
|
132 |
+
});
|
133 |
+
date.children( '.sap-scheduler-date-input' ).slideUp( function() {
|
134 |
+
$(this).find( 'input' ).val( '' );
|
135 |
+
});
|
136 |
+
date.children( '.sap-scheduler-weekdays' ).slideDown( function() {
|
137 |
+
date.removeClass( 'monthly date' );
|
138 |
+
date.addClass( 'weekly' );
|
139 |
+
});
|
140 |
+
|
141 |
+
} else if ( $(this).data( 'format' ) == 'monthly' && date.hasClass( 'monthly' ) === false ) {
|
142 |
+
date.children( '.sap-scheduler-date-input' ).slideUp( function() {
|
143 |
+
$(this).find( 'input' ).val( '' );
|
144 |
+
});
|
145 |
+
date.children( '.sap-scheduler-weekdays' ).slideDown();
|
146 |
+
date.children( '.sap-scheduler-weeks' ).slideDown( function() {
|
147 |
+
date.removeClass( 'weekly date' );
|
148 |
+
date.addClass( 'monthly' );
|
149 |
+
});
|
150 |
+
|
151 |
+
} else if ( $(this).data( 'format' ) == 'date' && date.hasClass( 'date' ) === false ) {
|
152 |
+
date.children( '.sap-scheduler-weekdays' ).slideUp( function() {
|
153 |
+
$(this).find( 'input' ).prop('checked', false);
|
154 |
+
});
|
155 |
+
date.children( '.sap-scheduler-weeks' ).slideUp( function() {
|
156 |
+
$(this).find( 'input' ).prop('checked', false);
|
157 |
+
});
|
158 |
+
date.children( '.sap-scheduler-date-input' ).slideDown( function() {
|
159 |
+
date.removeClass( 'weekly monthly' );
|
160 |
+
date.addClass( 'date' );
|
161 |
+
});
|
162 |
+
}
|
163 |
+
|
164 |
+
return false;
|
165 |
+
});
|
166 |
+
|
167 |
+
/**
|
168 |
+
* Show or hide time slot options
|
169 |
+
*/
|
170 |
+
$( '.sap-scheduler-time .sap-selector a' ).off( 'click' ).click( function() {
|
171 |
+
|
172 |
+
$(this).trigger( 'switch.sap' );
|
173 |
+
|
174 |
+
var time = $(this).closest( '.sap-scheduler-time' );
|
175 |
+
|
176 |
+
if ( $(this).data( 'format' ) == 'time-slot' && time.hasClass( 'time-slot' ) === false ) {
|
177 |
+
time.children( '.sap-scheduler-time-input' ).slideDown();
|
178 |
+
time.children( '.sap-scheduler-all-day' ).slideUp( function() {
|
179 |
+
time.removeClass( 'all-day' );
|
180 |
+
time.addClass( 'time-slot' );
|
181 |
+
});
|
182 |
+
|
183 |
+
} else if ( $(this).data( 'format' ) == 'all-day' && time.hasClass( 'all-day' ) === false ) {
|
184 |
+
time.children( '.sap-scheduler-all-day' ).slideDown();
|
185 |
+
time.children( '.sap-scheduler-time-input' ).slideUp( function() {
|
186 |
+
time.removeClass( 'time-slot' );
|
187 |
+
time.addClass( 'all-day' );
|
188 |
+
time.find( 'input' ).val( '' );
|
189 |
+
});
|
190 |
+
}
|
191 |
+
|
192 |
+
return false;
|
193 |
+
});
|
194 |
+
|
195 |
+
/**
|
196 |
+
* Show time slot options from the link in the all-day notice
|
197 |
+
*/
|
198 |
+
$( '.sap-scheduler-all-day a' ).off( 'click' ).click( function() {
|
199 |
+
$(this).closest( '.sap-scheduler-time' ).children( '.sap-selector' ).find( 'a[data-format="time-slot"]' ).trigger( 'click' );
|
200 |
+
|
201 |
+
return false;
|
202 |
+
});
|
203 |
+
|
204 |
+
/**
|
205 |
+
* Delete a scheduling rule panel
|
206 |
+
*/
|
207 |
+
$( '.sap-scheduler-control .delete' ).off( 'click' ).click( function() {
|
208 |
+
var scheduler = $(this).closest( '.sap-scheduler' );
|
209 |
+
$(this).parent().parent().fadeOut( function() {
|
210 |
+
$(this).remove();
|
211 |
+
|
212 |
+
// Reset the index of each rule
|
213 |
+
// @todo optimize this excessive use of regex (32x per rule).
|
214 |
+
// maybe set a data-slug attribute on .sap-scheduler-rule and a
|
215 |
+
// data-slug prop on each input/select/label, then use these to
|
216 |
+
// construct the new attributes: rule-slug[index][input-slug]
|
217 |
+
scheduler.children( '.sap-scheduler-rule' ).each( function( i ) {
|
218 |
+
var index = i.toString();
|
219 |
+
$(this).find( 'input' ).each( function() {
|
220 |
+
var name = $(this).attr( 'name' ).replace( /\[\d*\]/g, '[' + index + ']' );
|
221 |
+
$(this).attr( 'name', name );
|
222 |
+
$(this).attr( 'id', name );
|
223 |
+
var aria_owns = $(this).attr( 'aria-owns' );
|
224 |
+
if ( typeof aria_owns !== 'undefined' && aria_owns !== false) {
|
225 |
+
$(this).attr( 'aria-owns', name );
|
226 |
+
}
|
227 |
+
});
|
228 |
+
$(this).find( 'label' ).each( function() {
|
229 |
+
var name = $(this).attr( 'for' ).replace( /\[\d*\]/g, '[' + index + ']' );
|
230 |
+
$(this).attr( 'for', name );
|
231 |
+
});
|
232 |
+
$(this).find( '.picker' ).each( function() {
|
233 |
+
var name = $(this).attr( 'id' ).replace( /\[\d*\]/g, '[' + index + ']' );
|
234 |
+
$(this).attr( 'id', name );
|
235 |
+
});
|
236 |
+
});
|
237 |
+
});
|
238 |
+
|
239 |
+
return false;
|
240 |
+
});
|
241 |
+
}
|
242 |
+
|
243 |
+
/**
|
244 |
+
* Set the summary phrase for a scheduler rule's date. This phrase is shown
|
245 |
+
* when the rule's view is minimized.
|
246 |
+
*/
|
247 |
+
function sap_scheduler_set_date_phrase( scheduler_rule, scheduler_id ) {
|
248 |
+
|
249 |
+
var date_value = scheduler_rule.find( '.sap-scheduler-date-input input' ).val();
|
250 |
+
if ( typeof date_value !== 'undefined' && date_value != '' ) {
|
251 |
+
scheduler_rule.find( '.sap-scheduler-brief .date .value' ).html( date_value );
|
252 |
+
|
253 |
+
return;
|
254 |
+
}
|
255 |
+
|
256 |
+
var weekdays = 0;
|
257 |
+
var weekday_arr = new Array();
|
258 |
+
scheduler_rule.find( '.sap-scheduler-weekdays input' ).each( function() {
|
259 |
+
if ( $(this).prop( 'checked' ) !== false ) {
|
260 |
+
weekdays += 1;
|
261 |
+
weekday_arr.push( sap_scheduler.settings[scheduler_id]['weekdays'][ $(this).data( 'day' ) ] );
|
262 |
+
}
|
263 |
+
});
|
264 |
+
|
265 |
+
if ( weekdays == 0 && sap_scheduler.settings[ scheduler_id ].disable_weekdays === false ) {
|
266 |
+
scheduler_rule.find( '.sap-scheduler-brief .date .value' ).html( sap_scheduler.settings[scheduler_id].summaries['never'] );
|
267 |
+
|
268 |
+
return;
|
269 |
+
|
270 |
+
} else if ( weekdays == 7 ) {
|
271 |
+
var weekday_string = sap_scheduler.settings[scheduler_id].summaries['weekly_always'];
|
272 |
+
|
273 |
+
} else {
|
274 |
+
var weekday_string = weekday_arr.join( ', ' );
|
275 |
+
}
|
276 |
+
|
277 |
+
var weeks = 0;
|
278 |
+
var weeks_arr = new Array();
|
279 |
+
scheduler_rule.find( '.sap-scheduler-weeks input' ).each( function() {
|
280 |
+
if ( $(this).prop( 'checked' ) !== false ) {
|
281 |
+
weeks +=1;
|
282 |
+
weeks_arr.push( sap_scheduler.settings[scheduler_id]['weeks'][ $(this).data( 'week' ) ] );
|
283 |
+
}
|
284 |
+
});
|
285 |
+
|
286 |
+
if ( ( weeks == 0 || weeks == 5 ) && sap_scheduler.settings[ scheduler_id ].disable_weekdays === false ) {
|
287 |
+
scheduler_rule.find( '.sap-scheduler-brief .date .value' ).html( weekday_string );
|
288 |
+
|
289 |
+
return;
|
290 |
+
}
|
291 |
+
|
292 |
+
if ( weeks == 0 ) {
|
293 |
+
scheduler_rule.find( '.sap-scheduler-brief .date .value' ).html( sap_scheduler.settings[scheduler_id].summaries['never'] );
|
294 |
+
|
295 |
+
return;
|
296 |
+
}
|
297 |
+
|
298 |
+
if ( weekday_string != '' ) {
|
299 |
+
scheduler_rule.find( '.sap-scheduler-brief .date .value' ).html( sap_scheduler.settings[scheduler_id].summaries['monthly_weekdays'].replace( '{days}', weekday_arr.join( ', ' ) ).replace( '{weeks}', weeks_arr.join( ', ' ) ) );
|
300 |
+
} else {
|
301 |
+
scheduler_rule.find( '.sap-scheduler-brief .date .value' ).html( sap_scheduler.settings[scheduler_id].summaries['monthly_weeks'].replace( '{weeks}', weeks_arr.join( ', ' ) ) );
|
302 |
+
}
|
303 |
+
}
|
304 |
+
|
305 |
+
/**
|
306 |
+
* Set the summary phrase for a scheduler rule's time. This phrase is shown
|
307 |
+
* when the rule's view is minimized.
|
308 |
+
*/
|
309 |
+
function sap_scheduler_set_time_phrase( scheduler_rule, scheduler_id ) {
|
310 |
+
|
311 |
+
var start = scheduler_rule.find( '.sap-scheduler-time-input .start input' ).val();
|
312 |
+
var end = scheduler_rule.find( '.sap-scheduler-time-input .end input' ).val();
|
313 |
+
|
314 |
+
if ( start == '' && ( end == '' || typeof end == 'undefined' ) ) {
|
315 |
+
scheduler_rule.find( '.sap-scheduler-brief .time .value' ).html( sap_scheduler.settings[scheduler_id].summaries['all_day'] );
|
316 |
+
|
317 |
+
return;
|
318 |
+
}
|
319 |
+
|
320 |
+
if ( start == '' ) {
|
321 |
+
scheduler_rule.find( '.sap-scheduler-brief .time .value' ).html( sap_scheduler.settings[scheduler_id].summaries['before'] + ' ' + end );
|
322 |
+
|
323 |
+
return;
|
324 |
+
}
|
325 |
+
|
326 |
+
if ( end == '' || typeof end == 'undefined' ) {
|
327 |
+
scheduler_rule.find( '.sap-scheduler-brief .time .value' ).html( sap_scheduler.settings[scheduler_id].summaries['after'] + ' ' + start );
|
328 |
+
|
329 |
+
return;
|
330 |
+
}
|
331 |
+
|
332 |
+
if ( typeof end == 'undefined' ) {
|
333 |
+
return scheduler_rule.find( '.sap-scheduler-brief .time .value' ).html( start );
|
334 |
+
} else {
|
335 |
+
return scheduler_rule.find( '.sap-scheduler-brief .time .value' ).html( start + sap_scheduler.settings[scheduler_id].summaries['separator'] + end );
|
336 |
+
}
|
337 |
+
}
|
338 |
+
}
|
339 |
+
|
340 |
+
});
|
lib/simple-admin-pages/lib/pickadate/legacy.js
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*!
|
2 |
+
* Legacy browser support
|
3 |
+
*/
|
4 |
+
[].map||(Array.prototype.map=function(a,b){for(var c=this,d=c.length,e=new Array(d),f=0;d>f;f++)f in c&&(e[f]=a.call(b,c[f],f,c));return e}),[].filter||(Array.prototype.filter=function(a){if(null==this)throw new TypeError;var b=Object(this),c=b.length>>>0;if("function"!=typeof a)throw new TypeError;for(var d=[],e=arguments[1],f=0;c>f;f++)if(f in b){var g=b[f];a.call(e,g,f,b)&&d.push(g)}return d}),[].indexOf||(Array.prototype.indexOf=function(a){if(null==this)throw new TypeError;var b=Object(this),c=b.length>>>0;if(0===c)return-1;var d=0;if(arguments.length>1&&(d=Number(arguments[1]),d!=d?d=0:0!==d&&1/0!=d&&d!=-1/0&&(d=(d>0||-1)*Math.floor(Math.abs(d)))),d>=c)return-1;for(var e=d>=0?d:Math.max(c-Math.abs(d),0);c>e;e++)if(e in b&&b[e]===a)return e;return-1});/*!
|
5 |
+
* Cross-Browser Split 1.1.1
|
6 |
+
* Copyright 2007-2012 Steven Levithan <stevenlevithan.com>
|
7 |
+
* Available under the MIT License
|
8 |
+
* http://blog.stevenlevithan.com/archives/cross-browser-split
|
9 |
+
*/
|
10 |
+
var nativeSplit=String.prototype.split,compliantExecNpcg=void 0===/()??/.exec("")[1];String.prototype.split=function(a,b){var c=this;if("[object RegExp]"!==Object.prototype.toString.call(a))return nativeSplit.call(c,a,b);var d,e,f,g,h=[],i=(a.ignoreCase?"i":"")+(a.multiline?"m":"")+(a.extended?"x":"")+(a.sticky?"y":""),j=0;for(a=new RegExp(a.source,i+"g"),c+="",compliantExecNpcg||(d=new RegExp("^"+a.source+"$(?!\\s)",i)),b=void 0===b?-1>>>0:b>>>0;(e=a.exec(c))&&(f=e.index+e[0].length,!(f>j&&(h.push(c.slice(j,e.index)),!compliantExecNpcg&&e.length>1&&e[0].replace(d,function(){for(var a=1;a<arguments.length-2;a++)void 0===arguments[a]&&(e[a]=void 0)}),e.length>1&&e.index<c.length&&Array.prototype.push.apply(h,e.slice(1)),g=e[0].length,j=f,h.length>=b)));)a.lastIndex===e.index&&a.lastIndex++;return j===c.length?(g||!a.test(""))&&h.push(""):h.push(c.slice(j)),h.length>b?h.slice(0,b):h};
|
lib/simple-admin-pages/lib/pickadate/picker.date.js
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*!
|
2 |
+
* Date picker for pickadate.js v3.5.0
|
3 |
+
* http://amsul.github.io/pickadate.js/date.htm
|
4 |
+
*/
|
5 |
+
!function(a){"function"==typeof define&&define.amd?define(["picker","jquery"],a):a(Picker,jQuery)}(function(a,b){function c(a,b){var c=this,d=a.$node[0].value,e=a.$node.data("value"),f=e||d,g=e?b.formatSubmit:b.format,h=function(){return"rtl"===getComputedStyle(a.$root[0]).direction};c.settings=b,c.$node=a.$node,c.queue={min:"measure create",max:"measure create",now:"now create",select:"parse create validate",highlight:"parse navigate create validate",view:"parse create validate viewset",disable:"deactivate",enable:"activate"},c.item={},c.item.clear=null,c.item.disable=(b.disable||[]).slice(0),c.item.enable=-function(a){return a[0]===!0?a.shift():-1}(c.item.disable),c.set("min",b.min).set("max",b.max).set("now"),f?c.set("select",f,{format:g}):c.set("select",null).set("highlight",c.item.now),c.key={40:7,38:-7,39:function(){return h()?-1:1},37:function(){return h()?1:-1},go:function(a){var b=c.item.highlight,d=new Date(b.year,b.month,b.date+a);c.set("highlight",[d.getFullYear(),d.getMonth(),d.getDate()],{interval:a}),this.render()}},a.on("render",function(){a.$root.find("."+b.klass.selectMonth).on("change",function(){var c=this.value;c&&(a.set("highlight",[a.get("view").year,c,a.get("highlight").date]),a.$root.find("."+b.klass.selectMonth).trigger("focus"))}),a.$root.find("."+b.klass.selectYear).on("change",function(){var c=this.value;c&&(a.set("highlight",[c,a.get("view").month,a.get("highlight").date]),a.$root.find("."+b.klass.selectYear).trigger("focus"))})}).on("open",function(){a.$root.find("button, select").attr("disabled",!1)}).on("close",function(){a.$root.find("button, select").attr("disabled",!0)})}var d=7,e=6,f=a._;c.prototype.set=function(a,b,c){var d=this,e=d.item;return null===b?("clear"==a&&(a="select"),e[a]=b,d):(e["enable"==a?"disable":"flip"==a?"enable":a]=d.queue[a].split(" ").map(function(e){return b=d[e](a,b,c)}).pop(),"select"==a?d.set("highlight",e.select,c):"highlight"==a?d.set("view",e.highlight,c):a.match(/^(flip|min|max|disable|enable)$/)&&(e.select&&d.disabled(e.select)&&d.set("select",e.select,c),e.highlight&&d.disabled(e.highlight)&&d.set("highlight",e.highlight,c)),d)},c.prototype.get=function(a){return this.item[a]},c.prototype.create=function(a,c,d){var e,g=this;return c=void 0===c?a:c,c==-1/0||1/0==c?e=c:b.isPlainObject(c)&&f.isInteger(c.pick)?c=c.obj:b.isArray(c)?(c=new Date(c[0],c[1],c[2]),c=f.isDate(c)?c:g.create().obj):c=f.isInteger(c)||f.isDate(c)?g.normalize(new Date(c),d):g.now(a,c,d),{year:e||c.getFullYear(),month:e||c.getMonth(),date:e||c.getDate(),day:e||c.getDay(),obj:e||c,pick:e||c.getTime()}},c.prototype.createRange=function(a,c){var d=this,e=function(a){return a===!0||b.isArray(a)||f.isDate(a)?d.create(a):a};return f.isInteger(a)||(a=e(a)),f.isInteger(c)||(c=e(c)),f.isInteger(a)&&b.isPlainObject(c)?a=[c.year,c.month,c.date+a]:f.isInteger(c)&&b.isPlainObject(a)&&(c=[a.year,a.month,a.date+c]),{from:e(a),to:e(c)}},c.prototype.withinRange=function(a,b){return a=this.createRange(a.from,a.to),b.pick>=a.from.pick&&b.pick<=a.to.pick},c.prototype.overlapRanges=function(a,b){var c=this;return a=c.createRange(a.from,a.to),b=c.createRange(b.from,b.to),c.withinRange(a,b.from)||c.withinRange(a,b.to)||c.withinRange(b,a.from)||c.withinRange(b,a.to)},c.prototype.now=function(a,b,c){return b=new Date,c&&c.rel&&b.setDate(b.getDate()+c.rel),this.normalize(b,c)},c.prototype.navigate=function(a,c,d){var e,f,g,h,i=b.isArray(c),j=b.isPlainObject(c),k=this.item.view;if(i||j){for(j?(f=c.year,g=c.month,h=c.date):(f=+c[0],g=+c[1],h=+c[2]),d&&d.nav&&k&&k.month!==g&&(f=k.year,g=k.month),e=new Date(f,g+(d&&d.nav?d.nav:0),1),f=e.getFullYear(),g=e.getMonth();new Date(f,g,h).getMonth()!==g;)h-=1;c=[f,g,h]}return c},c.prototype.normalize=function(a){return a.setHours(0,0,0,0),a},c.prototype.measure=function(a,b){var c=this;return b?f.isInteger(b)&&(b=c.now(a,b,{rel:b})):b="min"==a?-1/0:1/0,b},c.prototype.viewset=function(a,b){return this.create([b.year,b.month,1])},c.prototype.validate=function(a,c,d){var e,g,h,i,j=this,k=c,l=d&&d.interval?d.interval:1,m=-1===j.item.enable,n=j.item.min,o=j.item.max,p=m&&j.item.disable.filter(function(a){if(b.isArray(a)){var d=j.create(a).pick;d<c.pick?e=!0:d>c.pick&&(g=!0)}return f.isInteger(a)}).length;if((!d||!d.nav)&&(!m&&j.disabled(c)||m&&j.disabled(c)&&(p||e||g)||!m&&(c.pick<=n.pick||c.pick>=o.pick)))for(m&&!p&&(!g&&l>0||!e&&0>l)&&(l*=-1);j.disabled(c)&&(Math.abs(l)>1&&(c.month<k.month||c.month>k.month)&&(c=k,l=l>0?1:-1),c.pick<=n.pick?(h=!0,l=1,c=j.create([n.year,n.month,n.date+(c.pick===n.pick?0:-1)])):c.pick>=o.pick&&(i=!0,l=-1,c=j.create([o.year,o.month,o.date+(c.pick===o.pick?0:1)])),!h||!i);)c=j.create([c.year,c.month,c.date+l]);return c},c.prototype.disabled=function(a){var c=this,d=c.item.disable.filter(function(d){return f.isInteger(d)?a.day===(c.settings.firstDay?d:d-1)%7:b.isArray(d)||f.isDate(d)?a.pick===c.create(d).pick:b.isPlainObject(d)?c.withinRange(d,a):void 0});return d=d.length&&!d.filter(function(a){return b.isArray(a)&&"inverted"==a[3]||b.isPlainObject(a)&&a.inverted}).length,-1===c.item.enable?!d:d||a.pick<c.item.min.pick||a.pick>c.item.max.pick},c.prototype.parse=function(a,b,c){var d=this,e={};return b&&"string"==typeof b?(c&&c.format||(c=c||{},c.format=d.settings.format),d.formats.toArray(c.format).map(function(a){var c=d.formats[a],g=c?f.trigger(c,d,[b,e]):a.replace(/^!/,"").length;c&&(e[a]=b.substr(0,g)),b=b.substr(g)}),[e.yyyy||e.yy,+(e.mm||e.m)-1,e.dd||e.d]):b},c.prototype.formats=function(){function a(a,b,c){var d=a.match(/\w+/)[0];return c.mm||c.m||(c.m=b.indexOf(d)+1),d.length}function b(a){return a.match(/\w+/)[0].length}return{d:function(a,b){return a?f.digits(a):b.date},dd:function(a,b){return a?2:f.lead(b.date)},ddd:function(a,c){return a?b(a):this.settings.weekdaysShort[c.day]},dddd:function(a,c){return a?b(a):this.settings.weekdaysFull[c.day]},m:function(a,b){return a?f.digits(a):b.month+1},mm:function(a,b){return a?2:f.lead(b.month+1)},mmm:function(b,c){var d=this.settings.monthsShort;return b?a(b,d,c):d[c.month]},mmmm:function(b,c){var d=this.settings.monthsFull;return b?a(b,d,c):d[c.month]},yy:function(a,b){return a?2:(""+b.year).slice(2)},yyyy:function(a,b){return a?4:b.year},toArray:function(a){return a.split(/(d{1,4}|m{1,4}|y{4}|yy|!.)/g)},toString:function(a,b){var c=this;return c.formats.toArray(a).map(function(a){return f.trigger(c.formats[a],c,[0,b])||a.replace(/^!/,"")}).join("")}}}(),c.prototype.isDateExact=function(a,c){var d=this;return f.isInteger(a)&&f.isInteger(c)||"boolean"==typeof a&&"boolean"==typeof c?a===c:(f.isDate(a)||b.isArray(a))&&(f.isDate(c)||b.isArray(c))?d.create(a).pick===d.create(c).pick:b.isPlainObject(a)&&b.isPlainObject(c)?d.isDateExact(a.from,c.from)&&d.isDateExact(a.to,c.to):!1},c.prototype.isDateOverlap=function(a,c){var d=this;return f.isInteger(a)&&(f.isDate(c)||b.isArray(c))?a===d.create(c).day+1:f.isInteger(c)&&(f.isDate(a)||b.isArray(a))?c===d.create(a).day+1:b.isPlainObject(a)&&b.isPlainObject(c)?d.overlapRanges(a,c):!1},c.prototype.flipEnable=function(a){var b=this.item;b.enable=a||(-1==b.enable?1:-1)},c.prototype.deactivate=function(a,c){var d=this,e=d.item.disable.slice(0);return"flip"==c?d.flipEnable():c===!1?(d.flipEnable(1),e=[]):c===!0?(d.flipEnable(-1),e=[]):c.map(function(a){for(var c,g=0;g<e.length;g+=1)if(d.isDateExact(a,e[g])){c=!0;break}c||(f.isInteger(a)||f.isDate(a)||b.isArray(a)||b.isPlainObject(a)&&a.from&&a.to)&&e.push(a)}),e},c.prototype.activate=function(a,c){var d=this,e=d.item.disable,g=e.length;return"flip"==c?d.flipEnable():c===!0?(d.flipEnable(1),e=[]):c===!1?(d.flipEnable(-1),e=[]):c.map(function(a){var c,h,i,j;for(i=0;g>i;i+=1){if(h=e[i],d.isDateExact(h,a)){c=e[i]=null,j=!0;break}if(d.isDateOverlap(h,a)){b.isPlainObject(a)?(a.inverted=!0,c=a):b.isArray(a)?(c=a,c[3]||c.push("inverted")):f.isDate(a)&&(c=[a.getFullYear(),a.getMonth(),a.getDate(),"inverted"]);break}}if(c)for(i=0;g>i;i+=1)if(d.isDateExact(e[i],a)){e[i]=null;break}if(j)for(i=0;g>i;i+=1)if(d.isDateOverlap(e[i],a)){e[i]=null;break}c&&e.push(c)}),e.filter(function(a){return null!=a})},c.prototype.nodes=function(a){var b=this,c=b.settings,g=b.item,h=g.now,i=g.select,j=g.highlight,k=g.view,l=g.disable,m=g.min,n=g.max,o=function(a,b){return c.firstDay&&(a.push(a.shift()),b.push(b.shift())),f.node("thead",f.node("tr",f.group({min:0,max:d-1,i:1,node:"th",item:function(d){return[a[d],c.klass.weekdays,'scope=col title="'+b[d]+'"']}})))}((c.showWeekdaysFull?c.weekdaysFull:c.weekdaysShort).slice(0),c.weekdaysFull.slice(0)),p=function(a){return f.node("div"," ",c.klass["nav"+(a?"Next":"Prev")]+(a&&k.year>=n.year&&k.month>=n.month||!a&&k.year<=m.year&&k.month<=m.month?" "+c.klass.navDisabled:""),"data-nav="+(a||-1)+" "+f.ariaAttr({role:"button",controls:b.$node[0].id+"_table"})+' title="'+(a?c.labelMonthNext:c.labelMonthPrev)+'"')},q=function(){var d=c.showMonthsShort?c.monthsShort:c.monthsFull;return c.selectMonths?f.node("select",f.group({min:0,max:11,i:1,node:"option",item:function(a){return[d[a],0,"value="+a+(k.month==a?" selected":"")+(k.year==m.year&&a<m.month||k.year==n.year&&a>n.month?" disabled":"")]}}),c.klass.selectMonth,(a?"":"disabled")+" "+f.ariaAttr({controls:b.$node[0].id+"_table"})+' title="'+c.labelMonthSelect+'"'):f.node("div",d[k.month],c.klass.month)},r=function(){var d=k.year,e=c.selectYears===!0?5:~~(c.selectYears/2);if(e){var g=m.year,h=n.year,i=d-e,j=d+e;if(g>i&&(j+=g-i,i=g),j>h){var l=i-g,o=j-h;i-=l>o?o:l,j=h}return f.node("select",f.group({min:i,max:j,i:1,node:"option",item:function(a){return[a,0,"value="+a+(d==a?" selected":"")]}}),c.klass.selectYear,(a?"":"disabled")+" "+f.ariaAttr({controls:b.$node[0].id+"_table"})+' title="'+c.labelYearSelect+'"')}return f.node("div",d,c.klass.year)};return f.node("div",(c.selectYears?r()+q():q()+r())+p()+p(1),c.klass.header)+f.node("table",o+f.node("tbody",f.group({min:0,max:e-1,i:1,node:"tr",item:function(a){var e=c.firstDay&&0===b.create([k.year,k.month,1]).day?-7:0;return[f.group({min:d*a-k.day+e+1,max:function(){return this.min+d-1},i:1,node:"td",item:function(a){a=b.create([k.year,k.month,a+(c.firstDay?1:0)]);var d=i&&i.pick==a.pick,e=j&&j.pick==a.pick,g=l&&b.disabled(a)||a.pick<m.pick||a.pick>n.pick;return[f.node("div",a.date,function(b){return b.push(k.month==a.month?c.klass.infocus:c.klass.outfocus),h.pick==a.pick&&b.push(c.klass.now),d&&b.push(c.klass.selected),e&&b.push(c.klass.highlighted),g&&b.push(c.klass.disabled),b.join(" ")}([c.klass.day]),"data-pick="+a.pick+" "+f.ariaAttr({role:"gridcell",selected:d&&b.$node.val()===f.trigger(b.formats.toString,b,[c.format,a])?!0:null,activedescendant:e?!0:null,disabled:g?!0:null})),"",f.ariaAttr({role:"presentation"})]}})]}})),c.klass.table,'id="'+b.$node[0].id+'_table" '+f.ariaAttr({role:"grid",controls:b.$node[0].id,readonly:!0}))+f.node("div",f.node("button",c.today,c.klass.buttonToday,"type=button data-pick="+h.pick+(a?"":" disabled")+" "+f.ariaAttr({controls:b.$node[0].id}))+f.node("button",c.clear,c.klass.buttonClear,"type=button data-clear=1"+(a?"":" disabled")+" "+f.ariaAttr({controls:b.$node[0].id})),c.klass.footer)},c.defaults=function(a){return{labelMonthNext:"Next month",labelMonthPrev:"Previous month",labelMonthSelect:"Select a month",labelYearSelect:"Select a year",monthsFull:["January","February","March","April","May","June","July","August","September","October","November","December"],monthsShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],weekdaysFull:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],weekdaysShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],today:"Today",clear:"Clear",format:"d mmmm, yyyy",klass:{table:a+"table",header:a+"header",navPrev:a+"nav--prev",navNext:a+"nav--next",navDisabled:a+"nav--disabled",month:a+"month",year:a+"year",selectMonth:a+"select--month",selectYear:a+"select--year",weekdays:a+"weekday",day:a+"day",disabled:a+"day--disabled",selected:a+"day--selected",highlighted:a+"day--highlighted",now:a+"day--today",infocus:a+"day--infocus",outfocus:a+"day--outfocus",footer:a+"footer",buttonClear:a+"button--clear",buttonToday:a+"button--today"}}}(a.klasses().picker+"__"),a.extend("pickadate",c)});
|
lib/simple-admin-pages/lib/pickadate/picker.js
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*!
|
2 |
+
* pickadate.js v3.5.0, 2014/04/13
|
3 |
+
* By Amsul, http://amsul.ca
|
4 |
+
* Hosted on http://amsul.github.io/pickadate.js
|
5 |
+
* Licensed under MIT
|
6 |
+
*/
|
7 |
+
!function(a){"function"==typeof define&&define.amd?define("picker",["jquery"],a):this.Picker=a(jQuery)}(function(a){function b(f,g,i,j){function k(){return b._.node("div",b._.node("div",b._.node("div",b._.node("div",v.component.nodes(q.open),s.box),s.wrap),s.frame),s.holder)}function l(){t.data(g,v).addClass(s.input).val(t.data("value")?v.get("select",r.format):f.value).on("focus."+q.id+" click."+q.id,o),r.editable||t.on("keydown."+q.id,function(a){var b=a.keyCode,c=/^(8|46)$/.test(b);return 27==b?(v.close(),!1):void((32==b||c||!q.open&&v.component.key[b])&&(a.preventDefault(),a.stopPropagation(),c?v.clear().close():v.open()))}),e(f,{haspopup:!0,expanded:!1,readonly:!1,owns:f.id+"_root"+(v._hidden?" "+v._hidden.id:"")})}function m(){v.$root.on({focusin:function(a){v.$root.removeClass(s.focused),a.stopPropagation()},"mousedown click":function(b){var c=b.target;c!=v.$root.children()[0]&&(b.stopPropagation(),"mousedown"!=b.type||a(c).is(":input")||"OPTION"==c.nodeName||(b.preventDefault(),f.focus()))}}).on("click","[data-pick], [data-nav], [data-clear]",function(){var c=a(this),d=c.data(),e=c.hasClass(s.navDisabled)||c.hasClass(s.disabled),g=document.activeElement;g=g&&(g.type||g.href)&&g,(e||g&&!a.contains(v.$root[0],g))&&f.focus(),d.nav&&!e?v.set("highlight",v.component.item.highlight,{nav:d.nav}):b._.isInteger(d.pick)&&!e?v.set("select",d.pick).close(!0):d.clear&&v.clear().close(!0)}),e(v.$root[0],"hidden",!0)}function n(){var b,c;r.hiddenName===!0?(b=f.name+"_hidden",c=f.name,f.name=""):(c=["string"==typeof r.hiddenPrefix?r.hiddenPrefix:"","string"==typeof r.hiddenSuffix?r.hiddenSuffix:"_submit"],c=b=c[0]+f.name+c[1]),v._hidden=a('<input type=hidden name="'+c+'"id="'+b+'"'+(t.data("value")||f.value?' value="'+v.get("select",r.formatSubmit)+'"':"")+">")[0],t.on("change."+q.id,function(){v._hidden.value=f.value?v.get("select",r.formatSubmit):""}).after(v._hidden)}function o(a){a.stopPropagation(),"focus"==a.type&&v.$root.addClass(s.focused),v.open()}if(!f)return b;var p=!1,q={id:f.id||"P"+Math.abs(~~(Math.random()*new Date))},r=i?a.extend(!0,{},i.defaults,j):j||{},s=a.extend({},b.klasses(),r.klass),t=a(f),u=function(){return this.start()},v=u.prototype={constructor:u,$node:t,start:function(){return q&&q.start?v:(q.methods={},q.start=!0,q.open=!1,q.type=f.type,f.autofocus=f==document.activeElement,f.type="text",f.readOnly=!r.editable,f.id=f.id||q.id,v.component=new i(v,r),v.$root=a(b._.node("div",k(),s.picker,'id="'+f.id+'_root"')),m(),r.formatSubmit&&n(),l(),r.container?a(r.container).append(v.$root):t.after(v.$root),v.on({start:v.component.onStart,render:v.component.onRender,stop:v.component.onStop,open:v.component.onOpen,close:v.component.onClose,set:v.component.onSet}).on({start:r.onStart,render:r.onRender,stop:r.onStop,open:r.onOpen,close:r.onClose,set:r.onSet}),p=c(v.$root.children()[0]),f.autofocus&&v.open(),v.trigger("start").trigger("render"))},render:function(a){return a?v.$root.html(k()):v.$root.find("."+s.box).html(v.component.nodes(q.open)),v.trigger("render")},stop:function(){return q.start?(v.close(),v._hidden&&v._hidden.parentNode.removeChild(v._hidden),v.$root.remove(),t.removeClass(s.input).removeData(g),setTimeout(function(){t.off("."+q.id)},0),f.type=q.type,f.readOnly=!1,v.trigger("stop"),q.methods={},q.start=!1,v):v},open:function(c){return q.open?v:(t.addClass(s.active),e(f,"expanded",!0),setTimeout(function(){v.$root.addClass(s.opened),e(v.$root[0],"hidden",!1)},0),c!==!1&&(q.open=!0,p&&a("html").css("overflow","hidden").css("padding-right","+="+d()),t.trigger("focus"),h.on("click."+q.id+" focusin."+q.id,function(a){var b=a.target;b!=f&&b!=document&&3!=a.which&&v.close(b===v.$root.children()[0])}).on("keydown."+q.id,function(c){var d=c.keyCode,e=v.component.key[d],g=c.target;27==d?v.close(!0):g!=f||!e&&13!=d?a.contains(v.$root[0],g)&&13==d&&(c.preventDefault(),g.click()):(c.preventDefault(),e?b._.trigger(v.component.key.go,v,[b._.trigger(e)]):v.$root.find("."+s.highlighted).hasClass(s.disabled)||v.set("select",v.component.item.highlight).close())})),v.trigger("open"))},close:function(b){return b&&(t.off("focus."+q.id).trigger("focus"),setTimeout(function(){t.on("focus."+q.id,o)},0)),t.removeClass(s.active),e(f,"expanded",!1),setTimeout(function(){v.$root.removeClass(s.opened+" "+s.focused),e(v.$root[0],"hidden",!0)},0),q.open?(q.open=!1,p&&a("html").css("overflow","").css("padding-right","-="+d()),h.off("."+q.id),v.trigger("close")):v},clear:function(){return v.set("clear")},set:function(b,c,d){var e,f,g=a.isPlainObject(b),h=g?b:{};if(d=g&&a.isPlainObject(c)?c:d||{},b){g||(h[b]=c);for(e in h)f=h[e],e in v.component.item&&(void 0===f&&(f=null),v.component.set(e,f,d)),("select"==e||"clear"==e)&&t.val("clear"==e?"":v.get(e,r.format)).trigger("change");v.render()}return d.muted?v:v.trigger("set",h)},get:function(a,c){return a=a||"value",null!=q[a]?q[a]:"value"==a?f.value:a in v.component.item?"string"==typeof c?b._.trigger(v.component.formats.toString,v.component,[c,v.component.get(a)]):v.component.get(a):void 0},on:function(b,c){var d,e,f=a.isPlainObject(b),g=f?b:{};if(b){f||(g[b]=c);for(d in g)e=g[d],q.methods[d]=q.methods[d]||[],q.methods[d].push(e)}return v},off:function(){var a,b,c=arguments;for(a=0,namesCount=c.length;namesCount>a;a+=1)b=c[a],b in q.methods&&delete q.methods[b];return v},trigger:function(a,c){var d=q.methods[a];return d&&d.map(function(a){b._.trigger(a,v,[c])}),v}};return new u}function c(a){var b,c="position";return a.currentStyle?b=a.currentStyle[c]:window.getComputedStyle&&(b=getComputedStyle(a)[c]),"fixed"==b}function d(){var b=a('<div style="visibility:hidden;width:100px" />').appendTo("body"),c=b[0].offsetWidth;b.css("overflow","scroll");var d=a('<div style="width:100%" />').appendTo(b),e=d[0].offsetWidth;return b.remove(),c-e}function e(b,c,d){if(a.isPlainObject(c))for(var e in c)f(b,e,c[e]);else f(b,c,d)}function f(a,b,c){a.setAttribute(("role"==b?"":"aria-")+b,c)}function g(b,c){a.isPlainObject(b)||(b={attribute:c}),c="";for(var d in b){var e=("role"==d?"":"aria-")+d,f=b[d];c+=null==f?"":e+'="'+b[d]+'"'}return c}var h=a(document);return b.klasses=function(a){return a=a||"picker",{picker:a,opened:a+"--opened",focused:a+"--focused",input:a+"__input",active:a+"__input--active",holder:a+"__holder",frame:a+"__frame",wrap:a+"__wrap",box:a+"__box"}},b._={group:function(a){for(var c,d="",e=b._.trigger(a.min,a);e<=b._.trigger(a.max,a,[e]);e+=a.i)c=b._.trigger(a.item,a,[e]),d+=b._.node(a.node,c[0],c[1],c[2]);return d},node:function(b,c,d,e){return c?(c=a.isArray(c)?c.join(""):c,d=d?' class="'+d+'"':"",e=e?" "+e:"","<"+b+d+e+">"+c+"</"+b+">"):""},lead:function(a){return(10>a?"0":"")+a},trigger:function(a,b,c){return"function"==typeof a?a.apply(b,c||[]):a},digits:function(a){return/\d/.test(a[1])?2:1},isDate:function(a){return{}.toString.call(a).indexOf("Date")>-1&&this.isInteger(a.getDate())},isInteger:function(a){return{}.toString.call(a).indexOf("Number")>-1&&a%1===0},ariaAttr:g},b.extend=function(c,d){a.fn[c]=function(e,f){var g=this.data(c);return"picker"==e?g:g&&"string"==typeof e?b._.trigger(g[e],g,[f]):this.each(function(){var f=a(this);f.data(c)||new b(this,c,d,e)})},a.fn[c].defaults=d.defaults},b});
|
lib/simple-admin-pages/lib/pickadate/picker.time.js
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*!
|
2 |
+
* Time picker for pickadate.js v3.5.0
|
3 |
+
* http://amsul.github.io/pickadate.js/time.htm
|
4 |
+
*/
|
5 |
+
!function(a){"function"==typeof define&&define.amd?define(["picker","jquery"],a):a(Picker,jQuery)}(function(a,b){function c(a,b){var c=this,d=a.$node[0].value,e=a.$node.data("value"),f=e||d,g=e?b.formatSubmit:b.format;c.settings=b,c.$node=a.$node,c.queue={interval:"i",min:"measure create",max:"measure create",now:"now create",select:"parse create validate",highlight:"parse create validate",view:"parse create validate",disable:"deactivate",enable:"activate"},c.item={},c.item.clear=null,c.item.interval=b.interval||30,c.item.disable=(b.disable||[]).slice(0),c.item.enable=-function(a){return a[0]===!0?a.shift():-1}(c.item.disable),c.set("min",b.min).set("max",b.max).set("now"),f?c.set("select",f,{format:g,fromValue:!!d}):c.set("select",null).set("highlight",c.item.now),c.key={40:1,38:-1,39:1,37:-1,go:function(a){c.set("highlight",c.item.highlight.pick+a*c.item.interval,{interval:a*c.item.interval}),this.render()}},a.on("render",function(){var c=a.$root.children(),d=c.find("."+b.klass.viewset);d.length&&(c[0].scrollTop=~~d.position().top-2*d[0].clientHeight)}).on("open",function(){a.$root.find("button").attr("disabled",!1)}).on("close",function(){a.$root.find("button").attr("disabled",!0)})}var d=24,e=60,f=12,g=d*e,h=a._;c.prototype.set=function(a,b,c){var d=this,e=d.item;return null===b?("clear"==a&&(a="select"),e[a]=b,d):(e["enable"==a?"disable":"flip"==a?"enable":a]=d.queue[a].split(" ").map(function(e){return b=d[e](a,b,c)}).pop(),"select"==a?d.set("highlight",e.select,c):"highlight"==a?d.set("view",e.highlight,c):"interval"==a?d.set("min",e.min,c).set("max",e.max,c):a.match(/^(flip|min|max|disable|enable)$/)&&("min"==a&&d.set("max",e.max,c),e.select&&d.disabled(e.select)&&d.set("select",e.select,c),e.highlight&&d.disabled(e.highlight)&&d.set("highlight",e.highlight,c)),d)},c.prototype.get=function(a){return this.item[a]},c.prototype.create=function(a,c,f){var i=this;return c=void 0===c?a:c,h.isDate(c)&&(c=[c.getHours(),c.getMinutes()]),b.isPlainObject(c)&&h.isInteger(c.pick)?c=c.pick:b.isArray(c)?c=+c[0]*e+ +c[1]:h.isInteger(c)||(c=i.now(a,c,f)),"max"==a&&c<i.item.min.pick&&(c+=g),"min"!=a&&"max"!=a&&(c-i.item.min.pick)%i.item.interval!==0&&(c+=i.item.interval),c=i.normalize(a,c,f),{hour:~~(d+c/e)%d,mins:(e+c%e)%e,time:(g+c)%g,pick:c}},c.prototype.createRange=function(a,c){var d=this,e=function(a){return a===!0||b.isArray(a)||h.isDate(a)?d.create(a):a};return h.isInteger(a)||(a=e(a)),h.isInteger(c)||(c=e(c)),h.isInteger(a)&&b.isPlainObject(c)?a=[c.hour,c.mins+a*d.settings.interval]:h.isInteger(c)&&b.isPlainObject(a)&&(c=[a.hour,a.mins+c*d.settings.interval]),{from:e(a),to:e(c)}},c.prototype.withinRange=function(a,b){return a=this.createRange(a.from,a.to),b.pick>=a.from.pick&&b.pick<=a.to.pick},c.prototype.overlapRanges=function(a,b){var c=this;return a=c.createRange(a.from,a.to),b=c.createRange(b.from,b.to),c.withinRange(a,b.from)||c.withinRange(a,b.to)||c.withinRange(b,a.from)||c.withinRange(b,a.to)},c.prototype.now=function(a,b){var c,d=this.item.interval,f=new Date,g=f.getHours()*e+f.getMinutes(),i=h.isInteger(b);return g-=g%d,c=0>b&&-d>=d*b+g,g+="min"==a&&c?0:d,i&&(g+=d*(c&&"max"!=a?b+1:b)),g},c.prototype.normalize=function(a,b){var c=this.item.interval,d=this.item.min&&this.item.min.pick||0;return b-="min"==a?0:(b-d)%c},c.prototype.measure=function(a,c,f){var g=this;return c?c===!0||h.isInteger(c)?c=g.now(a,c,f):b.isPlainObject(c)&&h.isInteger(c.pick)&&(c=g.normalize(a,c.pick,f)):c="min"==a?[0,0]:[d-1,e-1],c},c.prototype.validate=function(a,b,c){var d=this,e=c&&c.interval?c.interval:d.item.interval;return d.disabled(b)&&(b=d.shift(b,e)),b=d.scope(b),d.disabled(b)&&(b=d.shift(b,-1*e)),b},c.prototype.disabled=function(a){var c=this,d=c.item.disable.filter(function(d){return h.isInteger(d)?a.hour==d:b.isArray(d)||h.isDate(d)?a.pick==c.create(d).pick:b.isPlainObject(d)?c.withinRange(d,a):void 0});return d=d.length&&!d.filter(function(a){return b.isArray(a)&&"inverted"==a[2]||b.isPlainObject(a)&&a.inverted}).length,-1===c.item.enable?!d:d||a.pick<c.item.min.pick||a.pick>c.item.max.pick},c.prototype.shift=function(a,b){var c=this,d=c.item.min.pick,e=c.item.max.pick;for(b=b||c.item.interval;c.disabled(a)&&(a=c.create(a.pick+=b),!(a.pick<=d||a.pick>=e)););return a},c.prototype.scope=function(a){var b=this.item.min.pick,c=this.item.max.pick;return this.create(a.pick>c?c:a.pick<b?b:a)},c.prototype.parse=function(a,b,c){var d,f,g,i,j,k=this,l={};if(!b||"string"!=typeof b)return b;c&&c.format||(c=c||{},c.format=k.settings.format),k.formats.toArray(c.format).map(function(a){var c,d=k.formats[a],e=d?h.trigger(d,k,[b,l]):a.replace(/^!/,"").length;d&&(c=b.substr(0,e),l[a]=c.match(/^\d+$/)?+c:c),b=b.substr(e)});for(i in l)j=l[i],h.isInteger(j)?i.match(/^(h|hh)$/i)?(d=j,("h"==i||"hh"==i)&&(d%=12)):"i"==i&&(f=j):i.match(/^a$/i)&&j.match(/^p/i)&&("h"in l||"hh"in l)&&(g=!0);return(g?d+12:d)*e+f},c.prototype.formats={h:function(a,b){return a?h.digits(a):b.hour%f||f},hh:function(a,b){return a?2:h.lead(b.hour%f||f)},H:function(a,b){return a?h.digits(a):""+b.hour%24},HH:function(a,b){return a?h.digits(a):h.lead(b.hour%24)},i:function(a,b){return a?2:h.lead(b.mins)},a:function(a,b){return a?4:g/2>b.time%g?"a.m.":"p.m."},A:function(a,b){return a?2:g/2>b.time%g?"AM":"PM"},toArray:function(a){return a.split(/(h{1,2}|H{1,2}|i|a|A|!.)/g)},toString:function(a,b){var c=this;return c.formats.toArray(a).map(function(a){return h.trigger(c.formats[a],c,[0,b])||a.replace(/^!/,"")}).join("")}},c.prototype.isTimeExact=function(a,c){var d=this;return h.isInteger(a)&&h.isInteger(c)||"boolean"==typeof a&&"boolean"==typeof c?a===c:(h.isDate(a)||b.isArray(a))&&(h.isDate(c)||b.isArray(c))?d.create(a).pick===d.create(c).pick:b.isPlainObject(a)&&b.isPlainObject(c)?d.isTimeExact(a.from,c.from)&&d.isTimeExact(a.to,c.to):!1},c.prototype.isTimeOverlap=function(a,c){var d=this;return h.isInteger(a)&&(h.isDate(c)||b.isArray(c))?a===d.create(c).hour:h.isInteger(c)&&(h.isDate(a)||b.isArray(a))?c===d.create(a).hour:b.isPlainObject(a)&&b.isPlainObject(c)?d.overlapRanges(a,c):!1},c.prototype.flipEnable=function(a){var b=this.item;b.enable=a||(-1==b.enable?1:-1)},c.prototype.deactivate=function(a,c){var d=this,e=d.item.disable.slice(0);return"flip"==c?d.flipEnable():c===!1?(d.flipEnable(1),e=[]):c===!0?(d.flipEnable(-1),e=[]):c.map(function(a){for(var c,f=0;f<e.length;f+=1)if(d.isTimeExact(a,e[f])){c=!0;break}c||(h.isInteger(a)||h.isDate(a)||b.isArray(a)||b.isPlainObject(a)&&a.from&&a.to)&&e.push(a)}),e},c.prototype.activate=function(a,c){var d=this,e=d.item.disable,f=e.length;return"flip"==c?d.flipEnable():c===!0?(d.flipEnable(1),e=[]):c===!1?(d.flipEnable(-1),e=[]):c.map(function(a){var c,g,i,j;for(i=0;f>i;i+=1){if(g=e[i],d.isTimeExact(g,a)){c=e[i]=null,j=!0;break}if(d.isTimeOverlap(g,a)){b.isPlainObject(a)?(a.inverted=!0,c=a):b.isArray(a)?(c=a,c[2]||c.push("inverted")):h.isDate(a)&&(c=[a.getFullYear(),a.getMonth(),a.getDate(),"inverted"]);break}}if(c)for(i=0;f>i;i+=1)if(d.isTimeExact(e[i],a)){e[i]=null;break}if(j)for(i=0;f>i;i+=1)if(d.isTimeOverlap(e[i],a)){e[i]=null;break}c&&e.push(c)}),e.filter(function(a){return null!=a})},c.prototype.i=function(a,b){return h.isInteger(b)&&b>0?b:this.item.interval},c.prototype.nodes=function(a){var b=this,c=b.settings,d=b.item.select,e=b.item.highlight,f=b.item.view,g=b.item.disable;return h.node("ul",h.group({min:b.item.min.pick,max:b.item.max.pick,i:b.item.interval,node:"li",item:function(a){a=b.create(a);var i=a.pick,j=d&&d.pick==i,k=e&&e.pick==i,l=g&&b.disabled(a);return[h.trigger(b.formats.toString,b,[h.trigger(c.formatLabel,b,[a])||c.format,a]),function(a){return j&&a.push(c.klass.selected),k&&a.push(c.klass.highlighted),f&&f.pick==i&&a.push(c.klass.viewset),l&&a.push(c.klass.disabled),a.join(" ")}([c.klass.listItem]),"data-pick="+a.pick+" "+h.ariaAttr({role:"option",selected:j&&b.$node.val()===h.trigger(b.formats.toString,b,[c.format,a])?!0:null,activedescendant:k?!0:null,disabled:l?!0:null})]}})+h.node("li",h.node("button",c.clear,c.klass.buttonClear,"type=button data-clear=1"+(a?"":" disabled")+" "+h.ariaAttr({controls:b.$node[0].id})),"",h.ariaAttr({role:"presentation"})),c.klass.list,h.ariaAttr({role:"listbox",controls:b.$node[0].id}))},c.defaults=function(a){return{clear:"Clear",format:"h:i A",interval:30,klass:{picker:a+" "+a+"--time",holder:a+"__holder",list:a+"__list",listItem:a+"__list-item",disabled:a+"__list-item--disabled",selected:a+"__list-item--selected",highlighted:a+"__list-item--highlighted",viewset:a+"__list-item--viewset",now:a+"__list-item--now",buttonClear:a+"__button--clear"}}}(a.klasses().picker),a.extend("pickatime",c)});
|
lib/simple-admin-pages/lib/pickadate/themes/default.css
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
1 |
+
.picker{font-size:16px;text-align:left;line-height:1.2;color:#000;position:absolute;z-index:10000;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.picker__input{cursor:default}.picker__input.picker__input--active{border-color:#0089ec}.picker__holder{width:100%;overflow-y:auto;-webkit-overflow-scrolling:touch}/*!
|
2 |
+
* Default mobile-first, responsive styling for pickadate.js
|
3 |
+
* Demo: http://amsul.github.io/pickadate.js
|
4 |
+
*/.picker__frame,.picker__holder{bottom:0;left:0;right:0;top:100%}.picker__holder{position:fixed;-webkit-transition:background .15s ease-out,top 0s .15s;-moz-transition:background .15s ease-out,top 0s .15s;transition:background .15s ease-out,top 0s .15s}.picker__frame{position:absolute;margin:0 auto;min-width:256px;max-width:666px;width:100%;-ms-filter:"alpha(Opacity=0)";filter:alpha(opacity=0);-moz-opacity:0;opacity:0;-webkit-transition:all .15s ease-out;-moz-transition:all .15s ease-out;transition:all .15s ease-out}@media (min-height:33.875em){.picker__frame{overflow:visible;top:auto;bottom:-100%;max-height:80%}}@media (min-height:40.125em){.picker__frame{margin-bottom:7.5%}}.picker__wrap{display:table;width:100%;height:100%}@media (min-height:33.875em){.picker__wrap{display:block}}.picker__box{background:#fff;display:table-cell;vertical-align:middle}@media (min-height:26.5em){.picker__box{font-size:1.25em}}@media (min-height:33.875em){.picker__box{display:block;font-size:1.33em;border:1px solid #777;border-top-color:#898989;border-bottom-width:0;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0;-webkit-box-shadow:0 12px 36px 16px rgba(0,0,0,.24);-moz-box-shadow:0 12px 36px 16px rgba(0,0,0,.24);box-shadow:0 12px 36px 16px rgba(0,0,0,.24)}}@media (min-height:40.125em){.picker__box{font-size:1.5em;border-bottom-width:1px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}}.picker--opened .picker__holder{top:0;zoom:1;background:rgba(0,0,0,.32);-webkit-transition:background .15s ease-out;-moz-transition:background .15s ease-out;transition:background .15s ease-out}.picker--opened .picker__frame{top:0;-ms-filter:"alpha(Opacity=100)";filter:alpha(opacity=100);-moz-opacity:1;opacity:1}@media (min-height:33.875em){.picker--opened .picker__frame{top:auto;bottom:0}}
|
lib/simple-admin-pages/lib/pickadate/themes/default.date.css
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
.picker__box{padding:0 1em}.picker__header{text-align:center;position:relative;margin-top:.75em}.picker__month,.picker__year{font-weight:500;display:inline-block;margin-left:.25em;margin-right:.25em}.picker__year{color:#999;font-size:.8em;font-style:italic}.picker__select--month,.picker__select--year{border:1px solid #b7b7b7;height:2em;padding:.5em;margin-left:.25em;margin-right:.25em}@media (min-width:24.5em){.picker__select--month,.picker__select--year{margin-top:-.5em}}.picker__select--month{width:35%}.picker__select--year{width:22.5%}.picker__select--month:focus,.picker__select--year:focus{border-color:#0089ec}.picker__nav--next,.picker__nav--prev{position:absolute;padding:.5em 1.25em;width:1em;height:1em;top:-.25em}@media (min-width:24.5em){.picker__nav--next,.picker__nav--prev{top:-.33em}}.picker__nav--prev{left:-1em;padding-right:1.25em}@media (min-width:24.5em){.picker__nav--prev{padding-right:1.5em}}.picker__nav--next{right:-1em;padding-left:1.25em}@media (min-width:24.5em){.picker__nav--next{padding-left:1.5em}}.picker__nav--next:before,.picker__nav--prev:before{content:" ";border-top:.5em solid transparent;border-bottom:.5em solid transparent;border-right:.75em solid #000;width:0;height:0;display:block;margin:0 auto}.picker__nav--next:before{border-right:0;border-left:.75em solid #000}.picker__nav--next:hover,.picker__nav--prev:hover{cursor:pointer;color:#000;background:#b1dcfb}.picker__nav--disabled,.picker__nav--disabled:before,.picker__nav--disabled:before:hover,.picker__nav--disabled:hover{cursor:default;background:0 0;border-right-color:#f5f5f5;border-left-color:#f5f5f5}.picker__table{text-align:center;border-collapse:collapse;border-spacing:0;table-layout:fixed;font-size:inherit;width:100%;margin-top:.75em;margin-bottom:.5em}@media (min-height:33.875em){.picker__table{margin-bottom:.75em}}.picker__table td{margin:0;padding:0}.picker__weekday{width:14.285714286%;font-size:.75em;padding-bottom:.25em;color:#999;font-weight:500}@media (min-height:33.875em){.picker__weekday{padding-bottom:.5em}}.picker__day{padding:.3125em 0;font-weight:200;border:1px solid transparent}.picker__day--today{color:#0089ec;position:relative}.picker__day--today:before{content:" ";position:absolute;top:2px;right:2px;width:0;height:0;border-top:.5em solid #0059bc;border-left:.5em solid transparent}.picker__day--selected,.picker__day--selected:hover{border-color:#0089ec}.picker__day--highlighted{background:#b1dcfb}.picker__day--disabled:before{border-top-color:#aaa}.picker__day--outfocus{color:#ddd}.picker__day--infocus:hover,.picker__day--outfocus:hover{cursor:pointer;color:#000;background:#b1dcfb}.picker--focused .picker__day--highlighted,.picker__day--highlighted:hover{background:#0089ec;color:#fff}.picker__day--disabled,.picker__day--disabled:hover{background:#f5f5f5;border-color:#f5f5f5;color:#ddd;cursor:default}.picker__day--highlighted.picker__day--disabled,.picker__day--highlighted.picker__day--disabled:hover{background:#bbb}.picker__footer{text-align:center}.picker__button--clear,.picker__button--today{border:1px solid #fff;background:#fff;font-size:.8em;padding:.66em 0;font-weight:700;width:50%;display:inline-block;vertical-align:bottom}.picker__button--clear:hover,.picker__button--today:hover{cursor:pointer;color:#000;background:#b1dcfb;border-bottom-color:#b1dcfb}.picker__button--clear:focus,.picker__button--today:focus{background:#b1dcfb;border-color:#0089ec;outline:0}.picker__button--clear:before,.picker__button--today:before{position:relative;display:inline-block;height:0}.picker__button--today:before{content:" ";margin-right:.45em;top:-.05em;width:0;border-top:.66em solid #0059bc;border-left:.66em solid transparent}.picker__button--clear:before{content:"\D7";margin-right:.35em;top:-.1em;color:#e20;vertical-align:top;font-size:1.1em}
|
lib/simple-admin-pages/lib/pickadate/themes/default.time.css
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
.picker__list{list-style:none;padding:.75em 0 4.2em;margin:0}.picker__list-item{border-bottom:1px solid #ddd;border-top:1px solid #ddd;margin-bottom:-1px;position:relative;background:#fff;padding:.75em 1.25em}@media (min-height:46.75em){.picker__list-item{padding:.5em 1em}}.picker__list-item:hover{cursor:pointer;color:#000;background:#b1dcfb;border-color:#0089ec;z-index:10}.picker__list-item--selected,.picker__list-item--selected:hover{border-color:#0089ec;z-index:10}.picker__list-item--highlighted{background:#b1dcfb}.picker--focused .picker__list-item--highlighted,.picker__list-item--highlighted:hover{background:#0089ec;color:#fff}.picker--focused .picker__list-item--disabled,.picker__list-item--disabled,.picker__list-item--disabled:hover{background:#f5f5f5;color:#ddd;cursor:default;border-color:#ddd;z-index:auto}.picker--time .picker__button--clear{display:block;width:80%;margin:1em auto 0;padding:1em 1.25em;background:0 0;border:0;font-weight:500;font-size:.67em;text-align:center;text-transform:uppercase;color:#666}.picker--time .picker__button--clear:focus,.picker--time .picker__button--clear:hover{background:#b1dcfb;background:#e20;border-color:#e20;cursor:pointer;color:#fff;outline:0}.picker--time .picker__button--clear:before{top:-.25em;color:#666;font-size:1.25em;font-weight:700}.picker--time .picker__button--clear:focus:before,.picker--time .picker__button--clear:hover:before{color:#fff}.picker--time .picker__frame{min-width:256px;max-width:320px}.picker--time .picker__box{font-size:1em;background:#f2f2f2;padding:0}@media (min-height:40.125em){.picker--time .picker__box{margin-bottom:5em}}
|
lib/simple-admin-pages/lib/pickadate/themes/rtl.css
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
/*!
|
2 |
+
* Styling for RTL (right-to-left) languages using pickadate.js
|
3 |
+
*/.picker{direction:rtl}.picker__nav--next{right:auto;left:-1em}.picker__nav--prev{left:auto;right:-1em}.picker__nav--next:before{border-left:0;border-right:.75em solid #000}.picker__nav--prev:before{border-right:0;border-left:.75em solid #000}
|
lib/simple-admin-pages/lib/pickadate/translations/ar.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
$.extend($.fn.pickadate.defaults,{monthsFull:["يناير","فبراير","مارس","ابريل","مايو","يونيو","يوليو","اغسطس","سبتمبر","اكتوبر","نوفمبر","ديسمبر"],monthsShort:["يناير","فبراير","مارس","ابريل","مايو","يونيو","يوليو","اغسطس","سبتمبر","اكتوبر","نوفمبر","ديسمبر"],weekdaysFull:["الاحد","الاثنين","الثلاثاء","الاربعاء","الخميس","الجمعة","السبت"],weekdaysShort:["الاحد","الاثنين","الثلاثاء","الاربعاء","الخميس","الجمعة","السبت"],today:"اليوم",clear:"مسح",format:"yyyy mmmm dd",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/bg_BG.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["януари","февруари","март","април","май","юни","юли","август","септември","октомври","ноември","декември"],monthsShort:["янр","фев","мар","апр","май","юни","юли","авг","сеп","окт","ное","дек"],weekdaysFull:["неделя","понеделник","вторник","сряда","четвъртък","петък","събота"],weekdaysShort:["нд","пн","вт","ср","чт","пт","сб"],today:"днес",clear:"изтривам",firstDay:1,format:"d mmmm yyyy г.",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/bs_BA.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["januar","februar","mart","april","maj","juni","juli","august","septembar","oktobar","novembar","decembar"],monthsShort:["jan","feb","mar","apr","maj","jun","jul","aug","sep","okt","nov","dec"],weekdaysFull:["nedjelja","ponedjeljak","utorak","srijeda","cetvrtak","petak","subota"],weekdaysShort:["ne","po","ut","sr","če","pe","su"],today:"danas",clear:"izbrisati",firstDay:1,format:"dd. mmmm yyyy.",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/ca_ES.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["Gener","Febrer","Març","Abril","Maig","juny","Juliol","Agost","Setembre","Octubre","Novembre","Desembre"],monthsShort:["Gen","Feb","Mar","Abr","Mai","Jun","Jul","Ago","Set","Oct","Nov","Des"],weekdaysFull:["diumenge","dilluns","dimarts","dimecres","dijous","divendres","dissabte"],weekdaysShort:["diu","dil","dim","dmc","dij","div","dis"],today:"avui",clear:"esborrar",firstDay:1,format:"dddd d !de mmmm !de yyyy",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/cs_CZ.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["leden","únor","březen","duben","květen","červen","červenec","srpen","září","říjen","listopad","prosinec"],monthsShort:["led","úno","bře","dub","kvě","čer","čvc","srp","zář","říj","lis","pro"],weekdaysFull:["neděle","pondělí","úterý","středa","čtvrtek","pátek","sobota"],weekdaysShort:["ne","po","út","st","čt","pá","so"],today:"dnes",clear:"vymazat",firstDay:1,format:"d. mmmm yyyy",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/da_DK.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["januar","februar","marts","april","maj","juni","juli","august","september","oktober","november","december"],monthsShort:["jan","feb","mar","apr","maj","jun","jul","aug","sep","okt","nov","dec"],weekdaysFull:["søndag","mandag","tirsdag","onsdag","torsdag","fredag","lørdag"],weekdaysShort:["søn","man","tir","ons","tor","fre","lør"],today:"i dag",clear:"slet",firstDay:1,format:"d. mmmm yyyy",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/de_DE.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],monthsShort:["Jan","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],weekdaysFull:["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"],weekdaysShort:["So","Mo","Di","Mi","Do","Fr","Sa"],today:"Heute",clear:"Löschen",firstDay:1,format:"dddd, dd. mmmm yyyy",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/el_GR.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["Ιανουάριος","Φεβρουάριος","Μάρτιος","Απρίλιος","Μάιος","Ιούνιος","Ιούλιος","Αύγουστος","Σεπτέμβριος","Οκτώβριος","Νοέμβριος","Δεκέμβριος"],monthsShort:["Ιαν","Φεβ","Μαρ","Απρ","Μαι","Ιουν","Ιουλ","Αυγ","Σεπ","Οκτ","Νοε","Δεκ"],weekdaysFull:["Κυριακή","Δευτέρα","Τρίτη","Τετάρτη","Πέμπτη","Παρασκευή","Σάββατο"],weekdaysShort:["Κυρ","Δευ","Τρι","Τετ","Πεμ","Παρ","Σαβ"],today:"σήμερα",clear:"Διαγραφή",firstDay:1,format:"d mmmm yyyy",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/es_ES.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["enero","febrero","marzo","abril","mayo","junio","julio","agosto","septiembre","octubre","noviembre","diciembre"],monthsShort:["ene","feb","mar","abr","may","jun","jul","ago","sep","oct","nov","dic"],weekdaysFull:["domingo","lunes","martes","miércoles","jueves","viernes","sábado"],weekdaysShort:["dom","lun","mar","mié","jue","vie","sáb"],today:"hoy",clear:"borrar",firstDay:1,format:"dddd d !de mmmm !de yyyy",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/et_EE.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["jaanuar","veebruar","märts","aprill","mai","juuni","juuli","august","september","oktoober","november","detsember"],monthsShort:["jaan","veebr","märts","apr","mai","juuni","juuli","aug","sept","okt","nov","dets"],weekdaysFull:["pühapäev","esmaspäev","teisipäev","kolmapäev","neljapäev","reede","laupäev"],weekdaysShort:["püh","esm","tei","kol","nel","ree","lau"],today:"täna",clear:"kustutama",firstDay:1,format:"d. mmmm yyyy. a",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/eu_ES.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["urtarrila","otsaila","martxoa","apirila","maiatza","ekaina","uztaila","abuztua","iraila","urria","azaroa","abendua"],monthsShort:["urt","ots","mar","api","mai","eka","uzt","abu","ira","urr","aza","abe"],weekdaysFull:["igandea","astelehena","asteartea","asteazkena","osteguna","ostirala","larunbata"],weekdaysShort:["ig.","al.","ar.","az.","og.","or.","lr."],today:"gaur",clear:"garbitu",firstDay:1,format:"dddd, yyyy(e)ko mmmmren da",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/fi_FI.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["tammikuu","helmikuu","maaliskuu","huhtikuu","toukokuu","kesäkuu","heinäkuu","elokuu","syyskuu","lokakuu","marraskuu","joulukuu"],monthsShort:["tammi","helmi","maalis","huhti","touko","kesä","heinä","elo","syys","loka","marras","joulu"],weekdaysFull:["sunnuntai","maanantai","tiistai","keskiviikko","torstai","perjantai","lauantai"],weekdaysShort:["su","ma","ti","ke","to","pe","la"],today:"tänään",clear:"tyhjennä",firstDay:1,format:"d.m.yyyy",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/fr_FR.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["Janvier","Février","Mars","Avril","Mai","Juin","Juillet","Août","Septembre","Octobre","Novembre","Décembre"],monthsShort:["Jan","Fev","Mar","Avr","Mai","Juin","Juil","Aou","Sep","Oct","Nov","Dec"],weekdaysFull:["Dimanche","Lundi","Mardi","Mercredi","Jeudi","Vendredi","Samedi"],weekdaysShort:["Dim","Lun","Mar","Mer","Jeu","Ven","Sam"],today:"Aujourd'hui",clear:"Effacer",firstDay:1,format:"dd mmmm yyyy",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/gl_ES.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["Xaneiro","Febreiro","Marzo","Abril","Maio","Xuño","Xullo","Agosto","Setembro","Outubro","Novembro","Decembro"],monthsShort:["xan","feb","mar","abr","mai","xun","xul","ago","sep","out","nov","dec"],weekdaysFull:["domingo","luns","martes","mércores","xoves","venres","sábado"],weekdaysShort:["dom","lun","mar","mér","xov","ven","sab"],today:"hoxe",clear:"borrar",firstDay:1,format:"dddd d !de mmmm !de yyyy",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/he_IL.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["ינואר","פברואר","מרץ","אפריל","מאי","יוני","יולי","אוגוסט","ספטמבר","אוקטובר","נובמבר","דצמבר"],monthsShort:["ינו","פבר","מרץ","אפר","מאי","יונ","יול","אוג","ספט","אוק","נוב","דצמ"],weekdaysFull:["יום ראשון","יום שני","יום שלישי","יום רביעי","יום חמישי","יום ששי","יום שבת"],weekdaysShort:["א","ב","ג","ד","ה","ו","ש"],today:"היום",clear:"למחוק",format:"yyyy mmmmב d dddd",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/hr_HR.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["sijećanj","veljača","ožujak","travanj","svibanj","lipanj","srpanj","kolovoz","rujan","listopad","studeni","prosinac"],monthsShort:["sij","velj","ožu","tra","svi","lip","srp","kol","ruj","lis","stu","pro"],weekdaysFull:["nedjelja","ponedjeljak","utorak","srijeda","četvrtak","petak","subota"],weekdaysShort:["ned","pon","uto","sri","čet","pet","sub"],today:"danas",clear:"izbrisati",firstDay:1,format:"d. mmmm yyyy.",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/hu_HU.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["január","február","március","aprilis","május","június","július","augusztus","szeptember","október","november","december"],monthsShort:["jan","febr","márc","apr","máj","jún","júl","aug","szept","okt","nov","dec"],weekdaysFull:["vasámap","hétfö","kedd","szerda","csütörtök","péntek","szombat"],weekdaysShort:["V","H","K","SZ","CS","P","SZ"],today:"ma",clear:"töröl",firstDay:1,format:"yyyy. mmmm dd.",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/id_ID.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["Januari","Februari","Maret","April","Mei","Juni","Juli","Agustus","September","Oktober","November","Desember"],monthsShort:["Jan","Feb","Mar","Apr","Mei","Jun","Jul","Agu","Sep","Okt","Nov","Des"],weekdaysFull:["Minggu","Senin","Selasa","Rabu","Kamis","Jumat","Sabtu"],weekdaysShort:["Min","Sen","Sel","Rab","Kam","Jum","Sab"],today:"hari ini",clear:"menghapus",firstDay:1,format:"d mmmm yyyy",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/is_IS.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["janúar","febrúar","mars","apríl","maí","júní","júlí","ágúst","september","október","nóvember","desember"],monthsShort:["jan","feb","mar","apr","maí","jún","júl","ágú","sep","okt","nóv","des"],weekdaysFull:["sunnudagur","mánudagur","þriðjudagur","miðvikudagur","fimmtudagur","föstudagur","laugardagur"],weekdaysShort:["sun","mán","þri","mið","fim","fös","lau"],today:"Í dag",clear:"Hreinsa",firstDay:1,format:"dd. mmmm yyyy",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/it_IT.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["gennaio","febbraio","marzo","aprile","maggio","giugno","luglio","agosto","settembre","ottobre","novembre","dicembre"],monthsShort:["gen","feb","mar","apr","mag","giu","lug","ago","set","ott","nov","dic"],weekdaysFull:["domenica","lunedì","martedì","mercoledì","giovedì","venerdì","sabato"],weekdaysShort:["dom","lun","mar","mer","gio","ven","sab"],today:"oggi",clear:"cancellare",firstDay:1,format:"dddd d mmmm yyyy",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/ja_JP.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],monthsShort:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],weekdaysFull:["日曜日","月曜日","火曜日","水曜日","木曜日","金曜日","土曜日"],weekdaysShort:["日曜日","月曜日","火曜日","水曜日","木曜日","金曜日","土曜日"],today:"今日",clear:"消去",firstDay:1,format:"yyyy mm dd",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/ko_KR.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["1월","2월","3월","4월","5월","6월","7월","8월","9월","10월","11월","12월"],monthsShort:["1월","2월","3월","4월","5월","6월","7월","8월","9월","10월","11월","12월"],weekdaysFull:["일요일","월요일","화요일","수요일","목요일","금요일","토요일"],weekdaysShort:["일","월","화","수","목","금","토"],today:"오늘",clear:"취소",firstDay:1,format:"yyyy 년 mm 월 dd 일",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/nl_NL.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["januari","februari","maart","april","mei","juni","juli","augustus","september","oktober","november","december"],monthsShort:["jan","feb","maa","apr","mei","jun","jul","aug","sep","okt","nov","dec"],weekdaysFull:["zondag","maandag","dinsdag","woensdag","donderdag","vrijdag","zaterdag"],weekdaysShort:["zo","ma","di","wo","do","vr","za"],today:"vandaag",clear:"verwijderen",firstDay:1,format:"dddd d mmmm yyyy",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/no_NO.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["januar","februar","mars","april","mai","juni","juli","august","september","oktober","november","desember"],monthsShort:["jan","feb","mar","apr","mai","jun","jul","aug","sep","okt","nov","des"],weekdaysFull:["søndag","mandag","tirsdag","onsdag","torsdag","fredag","lørdag"],weekdaysShort:["søn","man","tir","ons","tor","fre","lør"],today:"i dag",clear:"nullstill",firstDay:1,format:"dd. mmm. yyyy",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/pl_PL.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["styczeń","luty","marzec","kwiecień","maj","czerwiec","lipiec","sierpień","wrzesień","październik","listopad","grudzień"],monthsShort:["sty","lut","mar","kwi","maj","cze","lip","sie","wrz","paź","lis","gru"],weekdaysFull:["niedziela","poniedziałek","wtorek","środa","czwartek","piątek","sobota"],weekdaysShort:["N","Pn","Wt","Śr","Cz","Pt","So"],today:"dzisiaj",clear:"usunąć",firstDay:1,format:"d mmmm yyyy",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/pt_BR.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["janeiro","fevereiro","março","abril","maio","junho","julho","agosto","setembro","outubro","novembro","dezembro"],monthsShort:["jan","fev","mar","abr","mai","jun","jul","ago","set","out","nov","dez"],weekdaysFull:["domingo","segunda-feira","terça-feira","quarta-feira","quinta-feira","sexta-feira","sábado"],weekdaysShort:["dom","seg","ter","qua","qui","sex","sab"],today:"hoje",clear:"excluir",format:"dddd, d !de mmmm !de yyyy",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/pt_PT.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["janeiro","fevereiro","março","abril","maio","junho","julho","agosto","setembro","outubro","novembro","dezembro"],monthsShort:["jan","fev","mar","abr","mai","jun","jul","ago","set","out","nov","dez"],weekdaysFull:["domingo","segunda","terça","quarta","quinta","sexta","sábado"],weekdaysShort:["dom","seg","ter","qua","qui","sex","sab"],today:"hoje",clear:"excluir",format:"d !de mmmm !de yyyy",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/ro_RO.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["ianuarie","februarie","martie","aprilie","mai","iunie","iulie","august","septembrie","octombrie","noiembrie","decembrie"],monthsShort:["ian","feb","mar","apr","mai","iun","iul","aug","sep","oct","noi","dec"],weekdaysFull:["duminică","luni","marţi","miercuri","joi","vineri","sâmbătă"],weekdaysShort:["D","L","Ma","Mi","J","V","S"],today:"azi",clear:"șterge",firstDay:1,format:"dd mmmm yyyy",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/ru_RU.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["Января","Февраля","Марта","Апреля","Мая","Июня","Июля","Августа","Сентября","Октября","Ноября","Декабря"],monthsShort:["Янв","Фев","Мар","Апр","Май","Июн","Июл","Авг","Сен","Окт","Ноя","Дек"],weekdaysFull:["воскресенье","понедельник","вторник","среда","четверг","пятница","суббота"],weekdaysShort:["вс","пн","вт","ср","чт","пт","сб"],today:"сегодня",clear:"удалить",firstDay:1,format:"d mmmm yyyy г.",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/sk_SK.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["január","február","marec","apríl","máj","jún","júl","august","september","október","november","december"],monthsShort:["I","II","III","IV","V","VI","VII","VIII","IX","X","XI","XII"],weekdaysFull:["nedeļľa","pondelok","utorok","streda","š̌švrtok","piatok","sobota"],weekdaysShort:["Ne","Po","Ut","St","Št","Pi","So"],today:"dnes",clear:"vymazať",firstDay:1,format:"d. mmmm yyyy",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/sl_SI.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["januar","februar","marec","april","maj","junij","julij","avgust","september","oktober","november","december"],monthsShort:["jan","feb","mar","apr","maj","jun","jul","avg","sep","okt","nov","dec"],weekdaysFull:["nedelja","ponedeljek","torek","sreda","četrtek","petek","sobota"],weekdaysShort:["ned","pon","tor","sre","čet","pet","sob"],today:"danes",clear:"izbriši",firstDay:1,format:"d. mmmm yyyy",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/sv_SE.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["januari","februari","mars","april","maj","juni","juli","augusti","september","oktober","november","december"],monthsShort:["jan","feb","mar","apr","maj","jun","jul","aug","sep","okt","nov","dec"],weekdaysFull:["söndag","måndag","tisdag","onsdag","torsdag","fredag","lördag"],weekdaysShort:["sön","mån","tis","ons","tor","fre","lör"],today:"i dag",clear:"bort",firstDay:1,format:"d/m yyyy",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/th_TH.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["มกราคม","กุมภาพันธ์","มีนาคม","เมษายน","พฤษภาคม","มิถุนายน","กรกฎาคม","สิงหาคม","กันยายน","ตุลาคม","พฤศจิกายน","ธันวาคม"],monthsShort:["ม.ค.","ก.พ.","มี.ค.","เม.ย.","พ.ค.","มิ.ย.","ก.ค.","ส.ค.","ก.ย.","ต.ค.","พ.ย.","ธ.ค."],weekdaysFull:["อาทติย","จันทร","องัคาร","พุธ","พฤหสั บดี","ศกุร","เสาร"],weekdaysShort:["อ.","จ.","อ.","พ.","พฤ.","ศ.","ส."],today:"วันนี้",clear:"ลบ",format:"d mmmm yyyy",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/tr_TR.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["Ocak","Şubat","Mart","Nisan","Mayıs","Haziran","Temmuz","Ağustos","Eylül","Ekim","Kasım","Aralık"],monthsShort:["Oca","Şub","Mar","Nis","May","Haz","Tem","Ağu","Eyl","Eki","Kas","Ara"],weekdaysFull:["Pazar","Pazartesi","Salı","Çarşamba","Perşembe","Cuma","Cumartesi"],weekdaysShort:["Pzr","Pzt","Sal","Çrş","Prş","Cum","Cmt"],today:"bugün",clear:"sil",firstDay:1,format:"dd mmmm yyyy dddd",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/uk_UA.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["січень","лютий","березень","квітень","травень","червень","липень","серпень","вересень","жовтень","листопад","грудень"],monthsShort:["січ","лют","бер","кві","тра","чер","лип","сер","вер","жов","лис","гру"],weekdaysFull:["неділя","понеділок","вівторок","середа","четвер","п‘ятниця","субота"],weekdaysShort:["нд","пн","вт","ср","чт","пт","сб"],today:"сьогодні",clear:"викреслити",firstDay:1,format:"dd mmmm yyyy p.",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/zh_CN.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],monthsShort:["一","二","三","四","五","六","七","八","九","十","十一","十二"],weekdaysFull:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],weekdaysShort:["日","一","二","三","四","五","六"],today:"今日",clear:"删",firstDay:1,format:"yyyy 年 mm 月 dd 日",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/lib/pickadate/translations/zh_TW.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery.extend(jQuery.fn.pickadate.defaults,{monthsFull:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],monthsShort:["一","二","三","四","五","六","七","八","九","十","十一","十二"],weekdaysFull:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],weekdaysShort:["日","一","二","三","四","五","六"],today:"今天",clear:"清除",firstDay:1,format:"yyyy 年 mm 月 dd 日",formatSubmit:"yyyy/mm/dd"});
|
lib/simple-admin-pages/simple-admin-pages.php
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Simple Admin Pages
|
5 |
+
*
|
6 |
+
* This is a very small utility library to easily add new admin pages to the
|
7 |
+
* WordPress admin interface. It simply collects WordPress' useful Settings API
|
8 |
+
* into reuseable classes.
|
9 |
+
*
|
10 |
+
* Created by Nate Wright
|
11 |
+
*
|
12 |
+
*
|
13 |
+
* @since 1.0
|
14 |
+
* @package Simple Admin Pages
|
15 |
+
* @license GNU GPL 2 or later
|
16 |
+
*/
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Always load the library files attached to this copy of SAP
|
20 |
+
*
|
21 |
+
* This fixes a compatibility bug if two versions of the library are being
|
22 |
+
* loaded by different plugins/themes. However, versions prior to 2.0 do not
|
23 |
+
* include this and can cause compatibility issues.
|
24 |
+
*/
|
25 |
+
require_once( 'classes/Library.class.php' );
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Initialize the appropriate version of the libary.
|
29 |
+
*
|
30 |
+
* This function should remain backwards compatible at all times, so that the
|
31 |
+
* initialization function from version 1.0 will still be able to initialize
|
32 |
+
* the library appropriately for version 2.0. This way, if two plugins exist
|
33 |
+
* with different versions, the plugin creator will still be able to initialize
|
34 |
+
* their library.
|
35 |
+
*
|
36 |
+
* @since 1.0
|
37 |
+
*/
|
38 |
+
if ( !function_exists( 'sap_initialize_library' ) ) {
|
39 |
+
|
40 |
+
function sap_initialize_library( $args = array() ) {
|
41 |
+
|
42 |
+
// Exit early if no version was provided
|
43 |
+
if ( !isset( $args['version'] ) ) {
|
44 |
+
return null;
|
45 |
+
}
|
46 |
+
|
47 |
+
// Set the textdomain for translation
|
48 |
+
if ( !defined( 'SAP_TEXTDOMAIN' ) ) {
|
49 |
+
define( 'SAP_TEXTDOMAIN', 'sapdomain' );
|
50 |
+
}
|
51 |
+
|
52 |
+
$lib_class_name = 'sapLibrary_' . str_replace( '.', '_', $args['version'] );
|
53 |
+
|
54 |
+
return new $lib_class_name( $args );
|
55 |
+
}
|
56 |
+
|
57 |
+
}
|
readme.txt
ADDED
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
=== Restaurant Reservations ===
|
2 |
+
Contributors: NateWr
|
3 |
+
Author URI: https://github.com/NateWr
|
4 |
+
Plugin URL: http://themeofthecrop.com
|
5 |
+
Requires at Least: 3.8
|
6 |
+
Tested Up To: 3.9
|
7 |
+
Tags: restaurant, reservations, bookings, table bookings, restaurant reservation, table reservation
|
8 |
+
Stable tag: 1.0.1
|
9 |
+
License: GPLv2 or later
|
10 |
+
Donate link: http://themeofthecrop.com
|
11 |
+
|
12 |
+
Accept restaurant reservations and table bookings online. Quickly confirm or reject bookings, send email notifications, set booking times and more.
|
13 |
+
|
14 |
+
== Description ==
|
15 |
+
|
16 |
+
Accept restaurant reservations and table bookings online. Quickly confirm or reject bookings, send out custom email notifications, restrict booking times and more.
|
17 |
+
|
18 |
+
* Quickly confirm or reject a booking
|
19 |
+
* Admin email notification when a booking request is made
|
20 |
+
* Customer email notifications when their request is confirmed or rejected
|
21 |
+
* Limit available booking times
|
22 |
+
* Custom user role to manage bookings
|
23 |
+
* Add your booking form to any page, post or widget area
|
24 |
+
* Customize all notification messages, and date and time formats
|
25 |
+
|
26 |
+
This plugin is under active development. More features will be added to this plugin and addons will be created which extend the functionality or integrate with third-party services. Follow future developments at [Theme of the Crop](http://themeofthecrop.com/?utm_source=Plugin&utm_medium=Plugin%20Description&utm_campaign=Restaurant%20Reservations) or read the Upgrade Notices when you see updates for this plugin in your WordPress admin panel.
|
27 |
+
|
28 |
+
= How to use =
|
29 |
+
|
30 |
+
There is a short guide to using the plugin in the /docs/ folder. It can be accessed by following the Help link listed under the plugin on the Plugins page in your WordPress admin area. Not sure where that is? One of the screenshots for this plugin will show you where to find it.
|
31 |
+
|
32 |
+
= Developers =
|
33 |
+
|
34 |
+
This plugin is packed with hooks so you can extend it, customize it and rebrand it as needed. Development takes place on [GitHub](https://github.com/NateWr/restaurant-reservations), so fork it up.
|
35 |
+
|
36 |
+
== Installation ==
|
37 |
+
|
38 |
+
1. Unzip `restaurant-reservations.zip`
|
39 |
+
2. Upload the contents of `restaurant-reservations.zip` to the `/wp-content/plugins/` directory
|
40 |
+
3. Activate the plugin through the 'Plugins' menu in WordPress
|
41 |
+
4. Go to Bookings > Settings to set up the page to display your booking form.
|
42 |
+
|
43 |
+
== Screenshots ==
|
44 |
+
|
45 |
+
1. Easily manage bookings. View today's bookings or upcoming bookings at-a-glance. Confirm or reject bookings quickly.
|
46 |
+
2. Easy-to-use booking form with a simple, clear style that is compatible with many themes. Minimal CSS is used to make it easier to style.
|
47 |
+
3. Great, mobile-friendly date and time pickers to make it easy for your customers.
|
48 |
+
4. Clear settings page to get it working how you need.
|
49 |
+
5. Easily adjust when customers can request a booking.
|
50 |
+
6. Customize the admin notification email when a new booking request is made. Add a quick link to confirm or reject a request straight from the email.
|
51 |
+
7. Customize the notification email sent to a user when they make a new booking request.
|
52 |
+
8. Customize the notification email sent to a user when their booking is confirmed. You can also customize the email sent when a booking is rejected.
|
53 |
+
9. Access a short guide from your Plugins list to help you get started quickly.
|
54 |
+
|
55 |
+
== Changelog ==
|
56 |
+
|
57 |
+
= 1.0.1 (2014-05-08) =
|
58 |
+
* Replace dashicons caret with CSS-only caret in booking form
|
59 |
+
|
60 |
+
= 1.0 (2014-05-07) =
|
61 |
+
* Initial release
|
restaurant-reservations.php
ADDED
@@ -0,0 +1,334 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Plugin Name: Restaurant Reservations
|
4 |
+
* Plugin URI: http://themeofthecrop.com
|
5 |
+
* Description: Accept restaurant reservations and bookings online.
|
6 |
+
* Version: 1.0.1
|
7 |
+
* Author: Theme of the Crop
|
8 |
+
* Author URI: http://themeofthecrop.com
|
9 |
+
* License: GNU General Public License v2.0 or later
|
10 |
+
* License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
11 |
+
* Requires at least: 3.8
|
12 |
+
* Tested up to: 3.9
|
13 |
+
*
|
14 |
+
* Text Domain: rtbdomain
|
15 |
+
* Domain Path: /languages/
|
16 |
+
*
|
17 |
+
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU
|
18 |
+
* General Public License as published by the Free Software Foundation; either version 2 of the License,
|
19 |
+
* or (at your option) any later version.
|
20 |
+
*
|
21 |
+
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
22 |
+
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
23 |
+
*
|
24 |
+
* You should have received a copy of the GNU General Public License along with this program; if not, write
|
25 |
+
* to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
26 |
+
*/
|
27 |
+
if ( ! defined( 'ABSPATH' ) )
|
28 |
+
exit;
|
29 |
+
|
30 |
+
if ( !class_exists( 'rtbInit' ) ) {
|
31 |
+
class rtbInit {
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Set a flag which tracks whether the form has already been rendered on
|
35 |
+
* the page. Only one form per page for now.
|
36 |
+
* @todo support multiple forms per page
|
37 |
+
*/
|
38 |
+
public $form_rendered = false;
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Initialize the plugin and register hooks
|
42 |
+
*/
|
43 |
+
public function __construct() {
|
44 |
+
|
45 |
+
// Common strings
|
46 |
+
define( 'RTB_TEXTDOMAIN', 'rtbdomain' );
|
47 |
+
define( 'RTB_PLUGIN_DIR', untrailingslashit( plugin_dir_path( __FILE__ ) ) );
|
48 |
+
define( 'RTB_PLUGIN_URL', untrailingslashit( plugins_url( basename( plugin_dir_path( __FILE__ ) ), basename( __FILE__ ) ) ) );
|
49 |
+
define( 'RTB_PLUGIN_FNAME', plugin_basename( __FILE__ ) );
|
50 |
+
define( 'RTB_BOOKING_POST_TYPE', 'rtb-booking' );
|
51 |
+
define( 'RTB_BOOKING_POST_TYPE_SLUG', 'booking' );
|
52 |
+
define( 'RTB_LOAD_FRONTEND_ASSETS', apply_filters( 'rtb-load-frontend-assets', true ) );
|
53 |
+
|
54 |
+
|
55 |
+
// Initialize the plugin
|
56 |
+
add_action( 'init', array( $this, 'load_textdomain' ) );
|
57 |
+
|
58 |
+
// Add custom roles and capabilities
|
59 |
+
add_action( 'init', array( $this, 'add_roles' ) );
|
60 |
+
|
61 |
+
// Load custom post types
|
62 |
+
require_once( RTB_PLUGIN_DIR . '/includes/CustomPostTypes.class.php' );
|
63 |
+
$this->cpts = new rtbCustomPostTypes();
|
64 |
+
|
65 |
+
// Add the admin menu
|
66 |
+
add_action( 'admin_menu', array( $this, 'add_menu_page' ) );
|
67 |
+
|
68 |
+
// Flush the rewrite rules for the custom post types
|
69 |
+
register_activation_hook( __FILE__, array( $this, 'rewrite_flush' ) );
|
70 |
+
|
71 |
+
// Load the template functions which print the booking form, etc
|
72 |
+
require_once( RTB_PLUGIN_DIR . '/includes/template-functions.php' );
|
73 |
+
|
74 |
+
// Load assets
|
75 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_assets' ) );
|
76 |
+
add_action( 'wp_enqueue_scripts', array( $this, 'register_assets' ) );
|
77 |
+
|
78 |
+
// Handle notifications
|
79 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Notifications.class.php' );
|
80 |
+
$this->notifications = new rtbNotifications();
|
81 |
+
|
82 |
+
// Load settings
|
83 |
+
require_once( RTB_PLUGIN_DIR . '/includes/Settings.class.php' );
|
84 |
+
$this->settings = new rtbSettings();
|
85 |
+
|
86 |
+
// Append booking form to a post's $content variable
|
87 |
+
add_filter( 'the_content', array( $this, 'append_to_content' ) );
|
88 |
+
|
89 |
+
// Register the widget
|
90 |
+
add_action( 'widgets_init', array( $this, 'register_widgets' ) );
|
91 |
+
|
92 |
+
// Add links to plugin listing
|
93 |
+
add_filter('plugin_action_links', array( $this, 'plugin_action_links' ), 10, 2);
|
94 |
+
|
95 |
+
// Development tool
|
96 |
+
// @todo maybe split off this sort of thing to another file
|
97 |
+
add_action( 'init', array( $this, 'dev_add_bookings_data' ) );
|
98 |
+
|
99 |
+
}
|
100 |
+
|
101 |
+
/**
|
102 |
+
* Flush the rewrite rules when this plugin is activated to update with
|
103 |
+
* custom post types
|
104 |
+
* @since 0.0.1
|
105 |
+
*/
|
106 |
+
public function rewrite_flush() {
|
107 |
+
$this->cpts->load_cpts();
|
108 |
+
flush_rewrite_rules();
|
109 |
+
}
|
110 |
+
|
111 |
+
/**
|
112 |
+
* Load the plugin textdomain for localistion
|
113 |
+
* @since 0.0.1
|
114 |
+
*/
|
115 |
+
public function load_textdomain() {
|
116 |
+
load_plugin_textdomain( RTB_TEXTDOMAIN, false, plugin_basename( dirname( __FILE__ ) ) . "/languages" );
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* Add a role to manage the bookings and add the capability to Editors,
|
121 |
+
* Administrators and Super Admins
|
122 |
+
* @since 0.0.1
|
123 |
+
*/
|
124 |
+
public function add_roles() {
|
125 |
+
|
126 |
+
// The booking manager should be able to access the bookings list and
|
127 |
+
// update booking statuses, but shouldn't be able to touch anything else
|
128 |
+
// in the account.
|
129 |
+
$booking_manager = add_role(
|
130 |
+
'rtb_booking_manager',
|
131 |
+
__( 'Booking Manager', RTB_TEXTDOMAIN ),
|
132 |
+
array(
|
133 |
+
'read' => true,
|
134 |
+
'manage_bookings' => true,
|
135 |
+
)
|
136 |
+
);
|
137 |
+
|
138 |
+
$manage_bookings_roles = apply_filters(
|
139 |
+
'rtb_manage_bookings_roles',
|
140 |
+
array(
|
141 |
+
'administrator',
|
142 |
+
'editor',
|
143 |
+
)
|
144 |
+
);
|
145 |
+
|
146 |
+
global $wp_roles;
|
147 |
+
foreach ( $manage_bookings_roles as $role ) {
|
148 |
+
$wp_roles->add_cap( $role, 'manage_bookings' );
|
149 |
+
}
|
150 |
+
}
|
151 |
+
|
152 |
+
/**
|
153 |
+
* Add the top-level admin menu page
|
154 |
+
* @since 0.0.1
|
155 |
+
*/
|
156 |
+
public function add_menu_page() {
|
157 |
+
|
158 |
+
add_menu_page(
|
159 |
+
_x( 'Bookings', 'Title of admin page that lists bookings', RTB_TEXTDOMAIN ),
|
160 |
+
_x( 'Bookings', 'Title of bookings admin menu item', RTB_TEXTDOMAIN ),
|
161 |
+
'manage_bookings',
|
162 |
+
'rtb-bookings',
|
163 |
+
array( $this, 'show_admin_bookings_page' ),
|
164 |
+
'dashicons-calendar',
|
165 |
+
'26.2987'
|
166 |
+
);
|
167 |
+
|
168 |
+
}
|
169 |
+
|
170 |
+
/**
|
171 |
+
* Display the admin bookings page
|
172 |
+
* @since 0.0.1
|
173 |
+
*/
|
174 |
+
public function show_admin_bookings_page() {
|
175 |
+
|
176 |
+
require_once( RTB_PLUGIN_DIR . '/includes/WP_List_Table.BookingsTable.class.php' );
|
177 |
+
$bookings_table = new rtbBookingsTable();
|
178 |
+
$bookings_table->prepare_items();
|
179 |
+
?>
|
180 |
+
|
181 |
+
<div class="wrap">
|
182 |
+
<h2><?php _e( 'Restaurant Bookings', RTB_TEXTDOMAIN ); ?></h2>
|
183 |
+
<?php do_action( 'rtb_bookings_table_top' ); ?>
|
184 |
+
<form id="rtb-bookings-table" method="POST" action="">
|
185 |
+
<input type="hidden" name="post_type" value="<?php echo RTB_BOOKING_POST_TYPE; ?>" />
|
186 |
+
<input type="hidden" name="page" value="rtb-bookings">
|
187 |
+
|
188 |
+
<?php $bookings_table->views(); ?>
|
189 |
+
<?php $bookings_table->advanced_filters(); ?>
|
190 |
+
<?php $bookings_table->display(); ?>
|
191 |
+
</form>
|
192 |
+
<?php do_action( 'rtb_bookings_table_btm' ); ?>
|
193 |
+
</div>
|
194 |
+
|
195 |
+
<?php
|
196 |
+
}
|
197 |
+
|
198 |
+
/**
|
199 |
+
* Append booking form to a post's $content variable
|
200 |
+
* @since 0.0.1
|
201 |
+
*/
|
202 |
+
function append_to_content( $content ) {
|
203 |
+
|
204 |
+
if ( !is_main_query() || !in_the_loop() ) {
|
205 |
+
return $content;
|
206 |
+
}
|
207 |
+
|
208 |
+
$booking_page = $this->settings->get_setting( 'booking-page' );
|
209 |
+
if ( empty( $booking_page ) ) {
|
210 |
+
return $content;
|
211 |
+
}
|
212 |
+
|
213 |
+
global $post;
|
214 |
+
if ( $post->ID !== $this->settings->get_setting( 'booking-page' ) ) {
|
215 |
+
return $content;
|
216 |
+
}
|
217 |
+
|
218 |
+
return $content . rtb_print_booking_form();
|
219 |
+
}
|
220 |
+
|
221 |
+
/**
|
222 |
+
* Development tool to populate the database with lots of bookings
|
223 |
+
* @since 0.0.1
|
224 |
+
*/
|
225 |
+
public function dev_add_bookings_data() {
|
226 |
+
|
227 |
+
if ( !WP_DEBUG || !isset( $_GET['rtb_devmode'] ) || $_GET['rtb_devmode'] !== 'add_bookings' ) {
|
228 |
+
return;
|
229 |
+
}
|
230 |
+
|
231 |
+
$lorem = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam feugiat consequat diam, in tincidunt purus convallis vel. Morbi sed dapibus diam. Vestibulum laoreet mi at neque varius consequat. Nam non mi erat. Donec nec semper velit. Maecenas id tortor orci. Aenean viverra suscipit urna, egestas adipiscing felis varius vitae. Curabitur et accumsan turpis. Suspendisse sed risus ac mi lobortis aliquam vel vel dolor. Nulla facilisi. In feugiat tempus massa, sed pulvinar neque bibendum ut. Nullam nibh eros, consectetur et orci non, condimentum tempor nunc. Maecenas sit amet libero sed diam pulvinar iaculis eget vitae odio. Quisque ac luctus metus, sit amet fringilla magna. Aliquam commodo odio eu eros imperdiet, ut auctor odio faucibus.';
|
232 |
+
$words = explode( ' ', str_replace( array( ',', '.'), '', $lorem ) );
|
233 |
+
for ( $i = 0; $i < 100; $i++ ) {
|
234 |
+
|
235 |
+
shuffle( $words );
|
236 |
+
|
237 |
+
$phone = '(';
|
238 |
+
for( $p = 0; $p < 3; $p++ ) {
|
239 |
+
$phone .= rand(0,9);
|
240 |
+
}
|
241 |
+
$phone .= ') ';
|
242 |
+
for( $p = 0; $p < 3; $p++ ) {
|
243 |
+
$phone .= rand(0,9);
|
244 |
+
}
|
245 |
+
$phone .= '-';
|
246 |
+
for( $p = 0; $p < 4; $p++ ) {
|
247 |
+
$phone .= rand(0,9);
|
248 |
+
}
|
249 |
+
|
250 |
+
$status = rand(0, 100) > 30 ? 'confirmed' : 'pending';
|
251 |
+
$status = rand(0, 100) > 90 ? 'closed' : $status;
|
252 |
+
|
253 |
+
// Get the date formatted for MySQL
|
254 |
+
$date = new DateTime( date('Y-m-d H:i:s', current_time() + rand( 0, 7776000 ) ) ); // 90 days in advance
|
255 |
+
|
256 |
+
$id = wp_insert_post(
|
257 |
+
array(
|
258 |
+
'post_type' => RTB_BOOKING_POST_TYPE,
|
259 |
+
'post_title' => $words[0] . ' ' . $words[1],
|
260 |
+
'post_content' => rand(0,10) < 3 ? $lorem : '',
|
261 |
+
'post_date' => $date->format( 'Y-m-d H:i:s' ),
|
262 |
+
'post_status' => $status,
|
263 |
+
)
|
264 |
+
);
|
265 |
+
|
266 |
+
$meta = array(
|
267 |
+
'party' => rand(1, 20),
|
268 |
+
'email' => $words[2] . '@email.com',
|
269 |
+
'phone' => $phone,
|
270 |
+
'date_submission' => current_time(), // keep track of when it was submitted for logs
|
271 |
+
);
|
272 |
+
|
273 |
+
$meta = apply_filters( 'rtb_sanitize_post_metadata_devmode', $meta, $id );
|
274 |
+
|
275 |
+
update_post_meta( $id, 'rtb', $meta );
|
276 |
+
|
277 |
+
}
|
278 |
+
}
|
279 |
+
|
280 |
+
/**
|
281 |
+
* Enqueue the admin-only CSS and Javascript
|
282 |
+
* @since 0.0.1
|
283 |
+
*/
|
284 |
+
public function enqueue_admin_assets() {
|
285 |
+
|
286 |
+
$screen = get_current_screen();
|
287 |
+
if ( $screen->base == 'toplevel_page_rtb-bookings' || $screen->base == 'bookings_page_rtb-settings' ) {
|
288 |
+
wp_enqueue_style( 'rtb-admin', RTB_PLUGIN_URL . '/assets/css/admin.css' );
|
289 |
+
wp_enqueue_script( 'rtb-admin', RTB_PLUGIN_URL . '/assets/js/admin.js', array( 'jquery' ), '', true );
|
290 |
+
}
|
291 |
+
}
|
292 |
+
|
293 |
+
/**
|
294 |
+
* Register the front-end CSS and Javascript for the booking form
|
295 |
+
* @since 0.0.1
|
296 |
+
*/
|
297 |
+
function register_assets() {
|
298 |
+
|
299 |
+
if ( !RTB_LOAD_FRONTEND_ASSETS ) {
|
300 |
+
return;
|
301 |
+
}
|
302 |
+
|
303 |
+
wp_register_style( 'rtb-booking-form', RTB_PLUGIN_URL . '/assets/css/booking-form.css' );
|
304 |
+
wp_register_script( 'rtb-booking-form', RTB_PLUGIN_URL . '/assets/js/booking-form.js', array( 'jquery' ) );
|
305 |
+
}
|
306 |
+
|
307 |
+
/**
|
308 |
+
* Register the widgets
|
309 |
+
* @since 0.0.1
|
310 |
+
*/
|
311 |
+
public function register_widgets() {
|
312 |
+
require_once( RTB_PLUGIN_DIR . '/includes/WP_Widget.BookingFormWidget.class.php' );
|
313 |
+
register_widget( 'rtbBookingFormWidget' );
|
314 |
+
}
|
315 |
+
|
316 |
+
/**
|
317 |
+
* Add links to the plugin listing on the installed plugins page
|
318 |
+
* @since 0.0.1
|
319 |
+
*/
|
320 |
+
public function plugin_action_links( $links, $plugin ) {
|
321 |
+
|
322 |
+
if ( $plugin == RTB_PLUGIN_FNAME ) {
|
323 |
+
|
324 |
+
$links['help'] = '<a href="' . RTB_PLUGIN_URL . '/docs" title="' . __( 'View the help documentation for Restaurant Reservations', RTB_TEXTDOMAIN ) . '">' . __( 'Help', RTB_TEXTDOMAIN ) . '</a>';
|
325 |
+
}
|
326 |
+
|
327 |
+
return $links;
|
328 |
+
|
329 |
+
}
|
330 |
+
|
331 |
+
}
|
332 |
+
} // endif;
|
333 |
+
|
334 |
+
$rtb_controller = new rtbInit();
|
screenshot-1.PNG
ADDED
Binary file
|
screenshot-2.PNG
ADDED
Binary file
|
screenshot-3.PNG
ADDED
Binary file
|
screenshot-4.PNG
ADDED
Binary file
|
screenshot-5.PNG
ADDED
Binary file
|
screenshot-6.PNG
ADDED
Binary file
|
screenshot-7.PNG
ADDED
Binary file
|
screenshot-8.PNG
ADDED
Binary file
|
screenshot-9.PNG
ADDED
Binary file
|