Business Profile - Version 1.0

Version Description

(2014-07-16) = * Initial public release on WordPress.org * Add an option to display a link to a booking form if the Restaurant Reservations plugin is active * Fix: skip a scheduling rule if no weekdays are set. h/t @jasonhobbsllc

Download this release

Release Info

Developer NateWr
Plugin Icon 128x128 Business Profile
Version 1.0
Comparing to
See all releases

Version 1.0

Files changed (83) hide show
  1. GPL.txt +280 -0
  2. assets/css/contact-card.css +110 -0
  3. assets/js/map.js +62 -0
  4. business-profile.php +117 -0
  5. docs/index.html +141 -0
  6. docs/style.css +31 -0
  7. includes/Integrations.php +140 -0
  8. includes/Settings.class.php +255 -0
  9. includes/WP_Widget.ContactCardWidget.class.php +104 -0
  10. includes/template-functions.php +455 -0
  11. lib/simple-admin-pages/README.md +396 -0
  12. lib/simple-admin-pages/classes/AdminPage.Menu.class.php +32 -0
  13. lib/simple-admin-pages/classes/AdminPage.Submenu.class.php +38 -0
  14. lib/simple-admin-pages/classes/AdminPage.Themes.class.php +15 -0
  15. lib/simple-admin-pages/classes/AdminPage.class.php +215 -0
  16. lib/simple-admin-pages/classes/AdminPageSection.class.php +132 -0
  17. lib/simple-admin-pages/classes/AdminPageSetting.Address.class.php +138 -0
  18. lib/simple-admin-pages/classes/AdminPageSetting.Editor.class.php +44 -0
  19. lib/simple-admin-pages/classes/AdminPageSetting.HTML.class.php +33 -0
  20. lib/simple-admin-pages/classes/AdminPageSetting.OpeningHours.class.php +180 -0
  21. lib/simple-admin-pages/classes/AdminPageSetting.Scheduler.class.php +664 -0
  22. lib/simple-admin-pages/classes/AdminPageSetting.Select.class.php +60 -0
  23. lib/simple-admin-pages/classes/AdminPageSetting.SelectPost.class.php +61 -0
  24. lib/simple-admin-pages/classes/AdminPageSetting.SelectTaxonomy.class.php +66 -0
  25. lib/simple-admin-pages/classes/AdminPageSetting.Text.class.php +35 -0
  26. lib/simple-admin-pages/classes/AdminPageSetting.Textarea.class.php +56 -0
  27. lib/simple-admin-pages/classes/AdminPageSetting.Toggle.class.php +43 -0
  28. lib/simple-admin-pages/classes/AdminPageSetting.class.php +261 -0
  29. lib/simple-admin-pages/classes/Library.class.php +415 -0
  30. lib/simple-admin-pages/css/admin.css +225 -0
  31. lib/simple-admin-pages/js/address.js +96 -0
  32. lib/simple-admin-pages/js/opening-hours.js +20 -0
  33. lib/simple-admin-pages/js/scheduler.js +329 -0
  34. lib/simple-admin-pages/lib/pickadate/legacy.js +10 -0
  35. lib/simple-admin-pages/lib/pickadate/picker.date.js +5 -0
  36. lib/simple-admin-pages/lib/pickadate/picker.js +7 -0
  37. lib/simple-admin-pages/lib/pickadate/picker.time.js +5 -0
  38. lib/simple-admin-pages/lib/pickadate/themes/default.css +4 -0
  39. lib/simple-admin-pages/lib/pickadate/themes/default.date.css +1 -0
  40. lib/simple-admin-pages/lib/pickadate/themes/default.time.css +1 -0
  41. lib/simple-admin-pages/lib/pickadate/themes/rtl.css +3 -0
  42. lib/simple-admin-pages/lib/pickadate/translations/ar.js +1 -0
  43. lib/simple-admin-pages/lib/pickadate/translations/bg_BG.js +1 -0
  44. lib/simple-admin-pages/lib/pickadate/translations/bs_BA.js +1 -0
  45. lib/simple-admin-pages/lib/pickadate/translations/ca_ES.js +1 -0
  46. lib/simple-admin-pages/lib/pickadate/translations/cs_CZ.js +1 -0
  47. lib/simple-admin-pages/lib/pickadate/translations/da_DK.js +1 -0
  48. lib/simple-admin-pages/lib/pickadate/translations/de_DE.js +1 -0
  49. lib/simple-admin-pages/lib/pickadate/translations/el_GR.js +1 -0
  50. lib/simple-admin-pages/lib/pickadate/translations/es_ES.js +1 -0
  51. lib/simple-admin-pages/lib/pickadate/translations/et_EE.js +1 -0
  52. lib/simple-admin-pages/lib/pickadate/translations/eu_ES.js +1 -0
  53. lib/simple-admin-pages/lib/pickadate/translations/fi_FI.js +1 -0
  54. lib/simple-admin-pages/lib/pickadate/translations/fr_FR.js +1 -0
  55. lib/simple-admin-pages/lib/pickadate/translations/gl_ES.js +1 -0
  56. lib/simple-admin-pages/lib/pickadate/translations/he_IL.js +1 -0
  57. lib/simple-admin-pages/lib/pickadate/translations/hr_HR.js +1 -0
  58. lib/simple-admin-pages/lib/pickadate/translations/hu_HU.js +1 -0
  59. lib/simple-admin-pages/lib/pickadate/translations/id_ID.js +1 -0
  60. lib/simple-admin-pages/lib/pickadate/translations/is_IS.js +1 -0
  61. lib/simple-admin-pages/lib/pickadate/translations/it_IT.js +1 -0
  62. lib/simple-admin-pages/lib/pickadate/translations/ja_JP.js +1 -0
  63. lib/simple-admin-pages/lib/pickadate/translations/ko_KR.js +1 -0
  64. lib/simple-admin-pages/lib/pickadate/translations/nl_NL.js +1 -0
  65. lib/simple-admin-pages/lib/pickadate/translations/no_NO.js +1 -0
  66. lib/simple-admin-pages/lib/pickadate/translations/pl_PL.js +1 -0
  67. lib/simple-admin-pages/lib/pickadate/translations/pt_BR.js +1 -0
  68. lib/simple-admin-pages/lib/pickadate/translations/pt_PT.js +1 -0
  69. lib/simple-admin-pages/lib/pickadate/translations/ro_RO.js +1 -0
  70. lib/simple-admin-pages/lib/pickadate/translations/ru_RU.js +1 -0
  71. lib/simple-admin-pages/lib/pickadate/translations/sk_SK.js +1 -0
  72. lib/simple-admin-pages/lib/pickadate/translations/sl_SI.js +1 -0
  73. lib/simple-admin-pages/lib/pickadate/translations/sv_SE.js +1 -0
  74. lib/simple-admin-pages/lib/pickadate/translations/th_TH.js +1 -0
  75. lib/simple-admin-pages/lib/pickadate/translations/tr_TR.js +1 -0
  76. lib/simple-admin-pages/lib/pickadate/translations/uk_UA.js +1 -0
  77. lib/simple-admin-pages/lib/pickadate/translations/zh_CN.js +1 -0
  78. lib/simple-admin-pages/lib/pickadate/translations/zh_TW.js +1 -0
  79. lib/simple-admin-pages/simple-admin-pages.php +57 -0
  80. readme.txt +66 -0
  81. screenshot-1.png +0 -0
  82. screenshot-2.png +0 -0
  83. screenshot-3.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/contact-card.css ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Frontend CSS Stylesheet for Business Profile contact cards */
2
+
3
+ .bp-contact-card {
4
+ font-style: normal;
5
+ }
6
+ .bp-contact-card>div {
7
+ margin: 1em 0;
8
+ }
9
+ .bp-name {
10
+ font-weight: bold;
11
+ }
12
+ .bp-opening-hours {
13
+ max-width: 25em;
14
+ }
15
+ .bp-opening-hours .bp-title {
16
+ text-align: center;
17
+ display: block;
18
+ }
19
+ .bp-weekday:before,
20
+ .bp-weekday:after {
21
+ content: " ";
22
+ display: table;
23
+ }
24
+ .bp-weekday:after {
25
+ clear: both;
26
+ }
27
+ .bp-weekday-name {
28
+ float: left;
29
+ clear: left;
30
+ width: 45%;
31
+ }
32
+ .bp-times {
33
+ float: right;
34
+ clear: right;
35
+ width: 45%;
36
+ text-align: right;
37
+ }
38
+ .bp-times .bp-time {
39
+ display: block;
40
+ }
41
+ .bp-map {
42
+ height: 20em;
43
+ color: #333;
44
+ font-weight: 400;
45
+ }
46
+ .bp-map a {
47
+ color: #3a84df;
48
+ }
49
+ .bp-map a:hover {
50
+ text-decoration: underline;
51
+ }
52
+
53
+ .bp-map p {
54
+ margin: 4px;
55
+ line-height: 16px;
56
+ font-weight: 400;
57
+ }
58
+
59
+ .bp-map img {
60
+ max-width: none; /* Fixes Google Maps info window close button */
61
+ }
62
+
63
+ .bp-map-info-window {
64
+ width: 200px;
65
+ max-width: 100%;
66
+ }
67
+
68
+ .bp-directions:before,
69
+ .bp-phone:before,
70
+ .bp-contact:before,
71
+ .bp-booking:before,
72
+ .bp-opening-hours-brief:before,
73
+ .bp-opening-hours .bp-title:before {
74
+ display: inline-block;
75
+ width: 1.5em;
76
+ height: 1em;
77
+ font-size: 1.5em;
78
+ line-height: 1;
79
+ font-family: dashicons;
80
+ text-decoration: inherit;
81
+ font-weight: 400;
82
+ font-style: normal;
83
+ vertical-align: top;
84
+ text-align: center;
85
+ -webkit-transition: color .1s ease-in 0;
86
+ transition: color .1s ease-in 0;
87
+ -webkit-font-smoothing: antialiased;
88
+ -moz-osx-font-smoothing: grayscale;
89
+ }
90
+
91
+ .bp-directions:before {
92
+ content: "\f230";
93
+ }
94
+
95
+ .bp-phone:before {
96
+ content: "\f470";
97
+ }
98
+
99
+ .bp-contact:before {
100
+ content: "\f466";
101
+ }
102
+
103
+ .bp-booking:before {
104
+ content: "\f145";
105
+ }
106
+
107
+ .bp-opening-hours-brief:before,
108
+ .bp-opening-hours .bp-title:before {
109
+ content: "\f469";
110
+ }
assets/js/map.js ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Frontend Javascript for Business Profile maps */
2
+ jQuery(document).ready(function ($) {
3
+
4
+ // Load Google Maps API and initialize maps
5
+ var bp_map_script = document.createElement( 'script' );
6
+ bp_map_script.type = 'text/javascript';
7
+ bp_map_script.src = 'https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&callback=bp_initialize_map';
8
+ document.body.appendChild( bp_map_script );
9
+
10
+ });
11
+
12
+ function bp_initialize_map() {
13
+
14
+ var bp_maps = [];
15
+ var bp_info_windows = [];
16
+
17
+ jQuery( '.bp-map' ).each( function() {
18
+ var id = jQuery(this).attr( 'id' );
19
+ var data = jQuery(this).data();
20
+
21
+ // Google Maps API v3
22
+ if ( typeof data.lat !== 'undefined' ) {
23
+ var latlon = new google.maps.LatLng( data.lat, data.lon );
24
+ var bp_map_options = {
25
+ zoom: 15,
26
+ center: latlon,
27
+ };
28
+
29
+ bp_maps[ id ] = new google.maps.Map( document.getElementById( id ), bp_map_options );
30
+
31
+ var content = '<div class="bp-map-info-window">' +
32
+ '<p><strong>' + data.name + '</strong></p>' +
33
+ '<p>' + data.address + '</p>';
34
+
35
+ if ( typeof data.phone !== 'undefined' ) {
36
+ content += '<p>' + data.phone + '</p>';
37
+ }
38
+ content += '<p><a target="_blank" href="//maps.google.com/maps?saddr=current+location&daddr=' + encodeURIComponent( data.address ) + '">Get Directions</a></p>' +
39
+ '</div>';
40
+
41
+ bp_info_windows[ id ] = new google.maps.InfoWindow({
42
+ position: latlon,
43
+ content: content,
44
+ });
45
+ bp_info_windows[ id ].open( bp_maps[ id ]);
46
+
47
+ // Google Maps iframe embed (fallback if no lat/lon data available)
48
+ } else if ( typeof data.address !== '' ) {
49
+ var bp_map_iframe = document.createElement( 'iframe' );
50
+ bp_map_iframe.frameBorder = 0;
51
+ bp_map_iframe.style.width = '100%';
52
+ bp_map_iframe.style.height = '100%';
53
+
54
+ if ( typeof data.name !== '' ) {
55
+ data.address = data.name + ',' + data.address;
56
+ }
57
+ bp_map_iframe.src = '//maps.google.com/maps?output=embed&q=' + encodeURIComponent( data.address );
58
+
59
+ jQuery(this).html( bp_map_iframe );
60
+ }
61
+ });
62
+ }
business-profile.php ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Plugin Name: Business Profile
4
+ * Plugin URI: http://themeofthecrop.com
5
+ * Description: Contact information, Google Maps and opening hours made easy for businesses.
6
+ * Version: 1.0
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
+ *
12
+ * Text Domain: bpfwpdomain
13
+ * Domain Path: /languages/
14
+ *
15
+ * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
16
+ * General Public License as published by the Free Software Foundation; either version 2 of the License,
17
+ * or (at your option) any later version.
18
+ *
19
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
20
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
21
+ *
22
+ * You should have received a copy of the GNU General Public License along with this program; if not, write
23
+ * to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24
+ */
25
+ if ( ! defined( 'ABSPATH' ) )
26
+ exit;
27
+
28
+ if ( !class_exists( 'bpfwpInit' ) ) {
29
+ class bpfwpInit {
30
+
31
+ /**
32
+ * Settings for displaying the contact card currently being handled
33
+ * @since 0.0.1
34
+ */
35
+ public $display_settings = array();
36
+
37
+ /**
38
+ * Initialize the plugin and register hooks
39
+ */
40
+ public function __construct() {
41
+
42
+ // Common strings
43
+ define( 'BPFWP_TEXTDOMAIN', 'bpfwpdomain' );
44
+ define( 'BPFWP_PLUGIN_DIR', untrailingslashit( plugin_dir_path( __FILE__ ) ) );
45
+ define( 'BPFWP_PLUGIN_URL', untrailingslashit( plugins_url( basename( plugin_dir_path( __FILE__ ) ), basename( __FILE__ ) ) ) );
46
+ define( 'BPFWP_PLUGIN_FNAME', plugin_basename( __FILE__ ) );
47
+ define( 'BPFWP_VERSION', 1 );
48
+
49
+ // Load the textdomain
50
+ add_action( 'init', array( $this, 'load_textdomain' ) );
51
+
52
+ // Load settings
53
+ require_once( BPFWP_PLUGIN_DIR . '/includes/Settings.class.php' );
54
+ $this->settings = new bpfwpSettings();
55
+
56
+ // Load the template functions which print the contact cards
57
+ require_once( BPFWP_PLUGIN_DIR . '/includes/template-functions.php' );
58
+
59
+ // Load integrations with third-party plugins/apps
60
+ require_once( BPFWP_PLUGIN_DIR . '/includes/integrations.php' );
61
+
62
+ // Load assets
63
+ add_action( 'wp_enqueue_scripts', array( $this, 'register_assets' ) );
64
+
65
+ // Register the widget
66
+ add_action( 'widgets_init', array( $this, 'register_widgets' ) );
67
+
68
+ // Add links to plugin listing
69
+ add_filter('plugin_action_links', array( $this, 'plugin_action_links' ), 10, 2);
70
+
71
+ }
72
+
73
+ /**
74
+ * Load the plugin textdomain for localistion
75
+ * @since 0.0.1
76
+ */
77
+ public function load_textdomain() {
78
+ load_plugin_textdomain( BPFWP_TEXTDOMAIN, false, plugin_basename( dirname( __FILE__ ) ) . "/languages" );
79
+ }
80
+
81
+ /**
82
+ * Register the front-end CSS styles
83
+ * @since 0.0.1
84
+ */
85
+ function register_assets() {
86
+ wp_register_style( 'bpfwp-default', BPFWP_PLUGIN_URL . '/assets/css/contact-card.css' );
87
+ wp_register_script( 'bpfwp-map', BPFWP_PLUGIN_URL . '/assets/js/map.js' );
88
+ }
89
+
90
+ /**
91
+ * Register the widgets
92
+ * @since 0.0.1
93
+ */
94
+ public function register_widgets() {
95
+ require_once( BPFWP_PLUGIN_DIR . '/includes/WP_Widget.ContactCardWidget.class.php' );
96
+ register_widget( 'bpfwpContactCardWidget' );
97
+ }
98
+
99
+ /**
100
+ * Add links to the plugin listing on the installed plugins page
101
+ * @since 0.0.1
102
+ */
103
+ public function plugin_action_links( $links, $plugin ) {
104
+
105
+ if ( $plugin == BPFWP_PLUGIN_FNAME ) {
106
+
107
+ $links['help'] = '<a href="' . BPFWP_PLUGIN_URL . '/docs" title="' . __( 'View the help documentation for Business Profile', BPFWP_TEXTDOMAIN ) . '">' . __( 'Help', BPFWP_TEXTDOMAIN ) . '</a>';
108
+ }
109
+
110
+ return $links;
111
+
112
+ }
113
+
114
+ }
115
+ } // endif;
116
+
117
+ $bpfwp_controller = new bpfwpInit();
docs/index.html ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>Business Profile | 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="#shortcode">Shortcode</a>
26
+ </li>
27
+ <li>
28
+ <a href="#extend">Extend this plugin</a>
29
+ </li>
30
+ </ul>
31
+ </div>
32
+
33
+ <div id="content">
34
+
35
+ <h1>Guide to the Business Profile plugin for WordPress</h1>
36
+ <p>This guide will help you install and configure the plugin for your site.</p>
37
+ <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=Business%Profile">themeofthecrop.com</a>.</p>
38
+
39
+ <a name="install"></a>
40
+ <h2>Installation</h2>
41
+ <p>The following steps describe how to upload the Business Profile plugin to an
42
+ existing WordPress installation. If you have not yet installed WordPress, consult the
43
+ <a href="http://codex.wordpress.org/Installing_WordPress">WordPress
44
+ documentation</a> for more information.</p>
45
+ <ol>
46
+ <li>
47
+ Unpack the business-profile.zip file. It should create a folder
48
+ named <code>/business-profile/</code>.
49
+ </li>
50
+ <li>
51
+ Upload the <code>/business-profile/</code> folder to your
52
+ WordPress plugin directory at <code>/wp-content/plugins/</code>.
53
+ </li>
54
+ <li>
55
+ Go to your WordPress admin dashboard at yoursite.com/wp-admin and
56
+ click the <strong>Plugins</strong> item in the menu on the left.
57
+ </li>
58
+ <li>
59
+ Find <strong>Business Profile</strong> in the list of Plugins
60
+ and click the <strong>Activate</strong> link below the plugin name.
61
+ </li>
62
+ </ol>
63
+ <p>The plugin is now active. Next you'll need to get it setup.</p>
64
+
65
+ <a name="setup"></a>
66
+ <h2>Quick Setup</h2>
67
+ <p>To get this plugin working as quickly as possible, all you need to do is
68
+ fill out your business details and add the widget</p>
69
+ <ol>
70
+ <li>
71
+ Go to the <strong>Business Profile</strong> page in your admin
72
+ dashboard. It should appear near the bottom of the menu on the left.
73
+ </li>
74
+ <li>
75
+ Fill out your business details on this page and click the Save
76
+ button at the bottom.
77
+ </li>
78
+ <li>
79
+ Simply paste <code>[contact-card]</code> into the content of any
80
+ page or post. Or add the new Contact Card widget to any sidebar.
81
+ </li>
82
+ </ol>
83
+ <p>Once you've done that, your users can view your business details. Read
84
+ the <a href="#shortcode">Shortcode</a> section to learn about customizing
85
+ what details are displayed.</p>
86
+
87
+ <a name="shortcode"></a>
88
+ <h2>Shortcode</h2>
89
+ <p>Prints the contact details to your site. All details will be displayed with the default shortcode <code>[contact-card]</code>. To hide any of the components, set the attribute to 0.</p>
90
+ <p><code>[contact-card show_name=1 show_address=1 show_get_directions=1 show_phone=1 show_contact=1 show_opening_hours=1 show_opening_hours_brief=0 show_map=1]</code></p>
91
+ <ul>
92
+ <li>
93
+ <strong>show_name</strong><br>
94
+ Whether or not to show the name of the business.
95
+ </li>
96
+ <li>
97
+ <strong>show_address</strong><br>
98
+ Whether or not to show the address.
99
+ </li>
100
+ <li>
101
+ <strong>show_get_directions</strong><br>
102
+ Whether or not to show a link to Google Maps with directions from the user's current location to your address.
103
+ </li>
104
+ <li>
105
+ <strong>show_phone</strong><br>
106
+ Whether or not to show your phone number.
107
+ </li>
108
+ <li>
109
+ <strong>show_contact</strong><br>
110
+ Whether or not to show a link to your contact page if set. Otherwise it will show the email address if one has been added to the Business Profile settings page.
111
+ </li>
112
+ <li>
113
+ <strong>show_opening_hours</strong><br>
114
+ Whether or not to show your opening hours.
115
+ </li>
116
+ <li>
117
+ <strong>show_opening_hours_brief</strong><br>
118
+ Whether or not to show a short, one-line version of your opening hours. This is turned off by default. Set it to 1 to turn it on.
119
+ </li>
120
+ <li>
121
+ <strong>show_map</strong><br>
122
+ Whether or not to show a Google Map with your location.
123
+ </li>
124
+ </ul>
125
+
126
+ <a name="extend"></a>
127
+ <h2>Extend this plugin</h2>
128
+ <p>This plugin has been designed from the ground up to be extensible and
129
+ customizable. There are several hooks to allow you to customize the
130
+ functionality and output.</p>
131
+ <p>If you have any questions, please
132
+ <a href="http://themeofthecrop.com/about/support/?utm_source=Plugin&utm_medium=Plugin%20Help%20Documentation&utm_campaign=Business%Profile">contact me</a>.</p>
133
+ <p>This plugin is available on
134
+ <a href="https://github.com/NateWr/business-profile">GitHub</a>
135
+ if you would like to contribute pull requests.</p>
136
+
137
+ </div>
138
+
139
+ </body>
140
+
141
+ </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/Integrations.php ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Initialize third-party integrations
4
+ *
5
+ * This file loads and runs code to help the theme that can be found in
6
+ * wp-content/themes/plate-up/includes. This code is separated from the normal
7
+ * functions.php file to make that file easier to read. Ideally, all code that
8
+ * is typically customized by users will be accessible through functions.php.
9
+ * The code that is loaded here should only pertain to more advanced features
10
+ * and functions that few users will ever touch if they are using this theme.
11
+ */
12
+ if ( ! defined( 'ABSPATH' ) )
13
+ exit;
14
+
15
+ if ( !class_exists( 'bpfwpIntegrations' ) ) {
16
+ class bpfwpIntegrations {
17
+
18
+ public function __construct() {
19
+
20
+ add_action( 'plugins_loaded', array( $this, 'plugins_loaded' ) );
21
+
22
+ }
23
+
24
+ /**
25
+ * Integrations run after the plugins are loaded
26
+ * @since 0.0.1
27
+ */
28
+ public function plugins_loaded() {
29
+
30
+ // Restaurant Reservations plugin
31
+ if ( defined( 'RTB_PLUGIN_DIR' ) ) {
32
+
33
+ // Add default setting for booking link to template function/shortcode
34
+ add_filter( 'bpwfp_contact_card_defaults', array( $this, 'bpwfp_booking_link_default' ) );
35
+
36
+ // Add the callback to print the booking link
37
+ add_filter( 'bpwfwp_component_callbacks', array( $this, 'bpwfp_booking_link_callback' ) );
38
+
39
+ // Add display toggle for the booking link to the widget options
40
+ add_filter( 'bpfwp_widget_display_toggles', array( $this, 'bpfwp_booking_link_widget_option' ) );
41
+
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Add default setting for booking link to template function/shortcode
47
+ * Restaurant Reservations plugin
48
+ * @since 0.0.1
49
+ */
50
+ public function bpwfp_booking_link_default( $defaults ) {
51
+
52
+ $defaults['show_booking_link'] = true;
53
+
54
+ return $defaults;
55
+ }
56
+
57
+ /**
58
+ * Add the callback to print the booking link
59
+ * Restaurant Reservations plugin
60
+ * @since 0.0.1
61
+ */
62
+ public function bpwfp_booking_link_callback( $data ) {
63
+
64
+ global $rtb_controller;
65
+ $booking_page = $rtb_controller->settings->get_setting( 'booking-page' );
66
+
67
+ if ( !empty( $booking_page ) ) {
68
+
69
+ // Place the link at the end of other short links if they're
70
+ // displayed
71
+ if ( isset( $data['contact'] ) ) {
72
+ $pos = array_search( 'contact', array_keys( $data ) );
73
+ } elseif ( isset( $data['phone'] ) ) {
74
+ $pos = array_search( 'phone', array_keys( $data ) );
75
+ } elseif ( isset( $data['address'] ) ) {
76
+ $pos = array_search( 'address', array_keys( $data ) );
77
+ }
78
+
79
+ if ( !empty( $pos ) ) {
80
+ $a = array_slice( $data, 0, $pos );
81
+ $b = array_slice( $data, $pos + 1 );
82
+ $data = array_merge( $a, array( 'booking_page' => array( $this, 'bpfwp_print_booking_link' ) ) , $b );
83
+
84
+ // If no short links are being displayed, just add it to the bottom.
85
+ } else {
86
+ $data['booking_page'] = array( $this, 'bpfwp_print_booking_link' );
87
+ }
88
+ }
89
+
90
+ return $data;
91
+ }
92
+
93
+ /**
94
+ * Print the booking link
95
+ * Restaurant Reservations plugin
96
+ * @since 0.0.1
97
+ */
98
+ public function bpfwp_print_booking_link() {
99
+
100
+ global $bpfwp_controller;
101
+
102
+ if ( $bpfwp_controller->display_settings['show_booking_link'] ) :
103
+ global $rtb_controller;
104
+ ?>
105
+
106
+ <div class="bp-booking">
107
+ <a href="<?php echo get_permalink( $rtb_controller->settings->get_setting( 'booking-page' ) ); ?>"><?php _e( 'Book a table', BPFWP_TEXTDOMAIN ); ?></a>
108
+ </div>
109
+
110
+ <?php
111
+ endif;
112
+ }
113
+
114
+ /**
115
+ * Add the booking page display option to the widget options
116
+ * Restaurant Reservations plugin
117
+ * @since 0.0.1
118
+ */
119
+ public function bpfwp_booking_link_widget_option( $toggles ) {
120
+
121
+ // Place the option below the contact option
122
+ $pos = array_search( 'show_contact', array_keys( $toggles ) );
123
+
124
+ if ( !empty( $pos ) ) {
125
+ $a = array_slice( $toggles, 0, $pos );
126
+ $b = array_slice( $toggles, $pos + 1 );
127
+ $toggles = array_merge( $a, array( 'show_booking_link' => __( 'Show book a table link', BPFWP_TEXTDOMAIN ) ) , $b );
128
+
129
+ // If no short links are being displayed, just add it to the bottom.
130
+ } else {
131
+ $toggles['show_booking_link'] = __( 'Show book a table link', BPFWP_TEXTDOMAIN );
132
+ }
133
+
134
+ return $toggles;
135
+ }
136
+
137
+ }
138
+ } // endif;
139
+
140
+ new bpfwpIntegrations();
includes/Settings.class.php ADDED
@@ -0,0 +1,255 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( !defined( 'ABSPATH' ) ) exit;
3
+
4
+ if ( !class_exists( 'bpfwpSettings' ) ) {
5
+ /**
6
+ * Class to handle configurable settings for Business Profile
7
+ *
8
+ * @since 0.0.1
9
+ */
10
+ class bpfwpSettings {
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
+ 'schema_type' => 'Organization',
40
+ 'name' => get_bloginfo( 'name' ),
41
+ );
42
+
43
+ $this->defaults = apply_filters( 'bpfwp_defaults', $this->defaults );
44
+ }
45
+
46
+ /**
47
+ * Get a setting's value or fallback to a default if one exists
48
+ * @since 0.0.1
49
+ */
50
+ public function get_setting( $setting ) {
51
+
52
+ if ( empty( $this->settings ) ) {
53
+ $this->settings = get_option( 'bpfwp-settings' );
54
+ }
55
+
56
+ if ( !empty( $this->settings[ $setting ] ) ) {
57
+ return $this->settings[ $setting ];
58
+ }
59
+
60
+ if ( !empty( $this->defaults[ $setting ] ) ) {
61
+ return $this->defaults[ $setting ];
62
+ }
63
+
64
+ return null;
65
+ }
66
+
67
+ /**
68
+ * Load the admin settings page
69
+ * @since 0.0.1
70
+ * @sa https://github.com/NateWr/simple-admin-pages
71
+ */
72
+ public function load_settings_panel() {
73
+
74
+ require_once( BPFWP_PLUGIN_DIR . '/lib/simple-admin-pages/simple-admin-pages.php' );
75
+ $sap = sap_initialize_library(
76
+ $args = array(
77
+ 'version' => '2.0.a.5',
78
+ 'lib_url' => BPFWP_PLUGIN_URL . '/lib/simple-admin-pages/',
79
+ )
80
+ );
81
+
82
+ $sap->add_page(
83
+ 'menu',
84
+ array(
85
+ 'id' => 'bpfwp-settings',
86
+ 'title' => __( 'Business Profile', BPFWP_TEXTDOMAIN ),
87
+ 'menu_title' => __( 'Business Profile', BPFWP_TEXTDOMAIN ),
88
+ 'capability' => 'manage_options',
89
+ 'icon' => 'dashicons-businessman',
90
+ 'position' => null
91
+ )
92
+ );
93
+
94
+ $sap->add_section(
95
+ 'bpfwp-settings',
96
+ array(
97
+ 'id' => 'bpfwp-seo',
98
+ 'title' => __( 'Search Engine Optimization', BPFWP_TEXTDOMAIN ),
99
+ )
100
+ );
101
+
102
+ $sap->add_setting(
103
+ 'bpfwp-settings',
104
+ 'bpfwp-seo',
105
+ 'select',
106
+ array(
107
+ 'id' => 'schema_type',
108
+ 'title' => __( 'Schema Type', BPFWP_TEXTDOMAIN ),
109
+ 'description' => __( 'Select the option that best describes your business to improve how search engines understand your website', BPFWP_TEXTDOMAIN ) . ' <a href="http://schema.org/" target="_blank">Schema.org</a>',
110
+ 'blank_option' => false,
111
+ 'options' => array(
112
+ 'Organization' => 'Organization',
113
+ 'Corporation' => 'Corporation',
114
+ 'EducationalOrganization' => 'Educational Organization',
115
+ 'GovernmentOrganization' => 'Government Organization',
116
+ 'LocalBusiness' => 'Local Business',
117
+ 'AnimalShelter' => '- Animal Shelter',
118
+ 'AutomotiveBusiness' => '- Automotive Business',
119
+ 'ChildCare' => '- Child Care',
120
+ 'DryCleaningOrLaundry' => '- Dry Cleaning or Laundry',
121
+ 'EmergencyService' => '- Emergency Service',
122
+ 'EmploymentAgency' => '- Employment Agency',
123
+ 'EntertainmentBusiness' => '- Entertainment Business',
124
+ 'FinancialService' => '- Financial Service',
125
+ 'FoodEstablishment' => '- Food Establishment',
126
+ 'GovernmentOffice' => '- Government Office',
127
+ 'HealthAndBeautyBusiness' => '- Health and Beauty Business',
128
+ 'HomeAndConstructionBusiness' => '- Home and Construction Business',
129
+ 'InternetCafe' => '- Internet Cafe',
130
+ 'Library' => '- Library',
131
+ 'LodgingBusiness' => '- Lodging Business',
132
+ 'MedicalOrganization' => '- Medical Organization',
133
+ 'RadioStation' => '- Radio Station',
134
+ 'RealEstateAgent' => '- Real Estate Agent',
135
+ 'RecyclingCenter' => '- Recycling Center',
136
+ 'SelfStorage' => '- Self Storage',
137
+ 'SportsActivityLocation' => '- Sports Activity Location',
138
+ 'Store' => '- Store',
139
+ 'TouristInformationCenter' => '- Tourist Information Center',
140
+ 'TravelAgency' => '- Travel Agency',
141
+ 'NGO' => 'NGO',
142
+ 'PerformingGroup' => 'PerformingGroup',
143
+ 'SportsTeam' => 'SportsTeam',
144
+ ),
145
+ )
146
+ );
147
+
148
+ $sap->add_section(
149
+ 'bpfwp-settings',
150
+ array(
151
+ 'id' => 'rtb-contact',
152
+ 'title' => __( 'Contact Information', BPFWP_TEXTDOMAIN ),
153
+ )
154
+ );
155
+
156
+ $sap->add_setting(
157
+ 'bpfwp-settings',
158
+ 'rtb-contact',
159
+ 'text',
160
+ array(
161
+ 'id' => 'name',
162
+ 'title' => __( 'Name', BPFWP_TEXTDOMAIN ),
163
+ 'description' => __( 'Enter the name of your business if it is different than the website name.', BPFWP_TEXTDOMAIN ),
164
+ 'placeholder' => $this->defaults['name'],
165
+ )
166
+ );
167
+
168
+ $sap->add_setting(
169
+ 'bpfwp-settings',
170
+ 'rtb-contact',
171
+ 'address',
172
+ array(
173
+ 'id' => 'address',
174
+ 'title' => __( 'Address', BPFWP_TEXTDOMAIN ),
175
+ )
176
+ );
177
+
178
+ $sap->add_setting(
179
+ 'bpfwp-settings',
180
+ 'rtb-contact',
181
+ 'text',
182
+ array(
183
+ 'id' => 'phone',
184
+ 'title' => __( 'Phone', BPFWP_TEXTDOMAIN ),
185
+ )
186
+ );
187
+
188
+ $sap->add_setting(
189
+ 'bpfwp-settings',
190
+ 'rtb-contact',
191
+ 'post',
192
+ array(
193
+ 'id' => 'contact-page',
194
+ 'title' => __( 'Contact Page', BPFWP_TEXTDOMAIN ),
195
+ 'description' => __( 'Select a page on your site where users can reach you, such as a contact form.', BPFWP_TEXTDOMAIN ),
196
+ 'blank_option' => true,
197
+ 'args' => array(
198
+ 'post_type' => 'page',
199
+ 'posts_per_page' => -1,
200
+ 'post_status' => 'publish',
201
+ ),
202
+ )
203
+ );
204
+
205
+ $sap->add_setting(
206
+ 'bpfwp-settings',
207
+ 'rtb-contact',
208
+ 'text',
209
+ array(
210
+ 'id' => 'contact-email',
211
+ 'title' => __( 'Email Address (optional)', BPFWP_TEXTDOMAIN ),
212
+ 'description' => __( 'Enter an email address only if you want to display this publicly. Showing your email address on your site may cause you to receive excessive spam.', BPFWP_TEXTDOMAIN ),
213
+ )
214
+ );
215
+
216
+ $sap->add_section(
217
+ 'bpfwp-settings',
218
+ array(
219
+ 'id' => 'rtb-schedule',
220
+ 'title' => __( 'Schedule', BPFWP_TEXTDOMAIN ),
221
+ )
222
+ );
223
+
224
+ $sap->add_setting(
225
+ 'bpfwp-settings',
226
+ 'rtb-schedule',
227
+ 'scheduler',
228
+ array(
229
+ 'id' => 'opening-hours',
230
+ 'title' => __( 'Opening Hours', BPFWP_TEXTDOMAIN ),
231
+ 'description' => __( 'Define your weekly opening hours by adding scheduling rules.', BPFWP_TEXTDOMAIN ),
232
+ 'weekdays' => array(
233
+ 'monday' => _x( 'Mo', 'Monday abbreviation', BPFWP_TEXTDOMAIN ),
234
+ 'tuesday' => _x( 'Tu', 'Tuesday abbreviation', BPFWP_TEXTDOMAIN ),
235
+ 'wednesday' => _x( 'We', 'Wednesday abbreviation', BPFWP_TEXTDOMAIN ),
236
+ 'thursday' => _x( 'Th', 'Thursday abbreviation', BPFWP_TEXTDOMAIN ),
237
+ 'friday' => _x( 'Fr', 'Friday abbreviation', BPFWP_TEXTDOMAIN ),
238
+ 'saturday' => _x( 'Sa', 'Saturday abbreviation', BPFWP_TEXTDOMAIN ),
239
+ 'sunday' => _x( 'Su', 'Sunday abbreviation', BPFWP_TEXTDOMAIN )
240
+ ),
241
+ 'time_format' => _x( 'h:i A', 'Time format displayed in the opening hours setting panel in your admin area. Must match formatting rules at http://amsul.ca/pickadate.js/time.htm#formats', BPFWP_TEXTDOMAIN ),
242
+ 'date_format' => _x( 'mmmm d, yyyy', 'Date format displayed in the opening hours setting panel in your admin area. Must match formatting rules at http://amsul.ca/pickadate.js/date.htm#formatting-rules', BPFWP_TEXTDOMAIN ),
243
+ 'disable_weeks' => true,
244
+ 'disable_date' => true,
245
+ )
246
+ );
247
+
248
+ $sap = apply_filters( 'bpfwp_settings_page', $sap );
249
+
250
+ $sap->add_admin_menus();
251
+
252
+ }
253
+
254
+ }
255
+ } // endif;
includes/WP_Widget.ContactCardWidget.class.php ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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( 'bpfwpContactCardWidget' ) ) {
9
+ /**
10
+ * Contact card widget
11
+ *
12
+ * Extends WP_Widget to display a contact card in a widget.
13
+ * @since 0.0.1
14
+ */
15
+ class bpfwpContactCardWidget extends WP_Widget {
16
+
17
+ /**
18
+ * Register widget with WordPress.
19
+ * @since 0.0.1
20
+ */
21
+ function __construct() {
22
+
23
+ // Display toggles
24
+ $this->toggles = apply_filters( 'bpfwp_widget_display_toggles', array(
25
+ 'show_name' => __( 'Show Name', BPFWP_TEXTDOMAIN ),
26
+ 'show_address' => __( 'Show Address', BPFWP_TEXTDOMAIN ),
27
+ 'show_get_directions' => __( 'Show link to get directions on Google Maps', BPFWP_TEXTDOMAIN ),
28
+ 'show_phone' => __( 'Show Phone number', BPFWP_TEXTDOMAIN ),
29
+ 'show_contact' => __( 'Show contact details', BPFWP_TEXTDOMAIN ),
30
+ 'show_opening_hours' => __( 'Show Opening Hours', BPFWP_TEXTDOMAIN ),
31
+ 'show_opening_hours_brief' => __( 'Show brief opening hours on one line', BPFWP_TEXTDOMAIN ),
32
+ 'show_map' => __( 'Show Google Map', BPFWP_TEXTDOMAIN ),
33
+ )
34
+ );
35
+
36
+ parent::__construct(
37
+ 'bpfwp_contact_card_widget',
38
+ __('Contact Card', BPFWP_TEXTDOMAIN),
39
+ array( 'description' => __( 'Display a contact card with your name, address, phone number, opening hours and map.', BPFWP_TEXTDOMAIN ), )
40
+ );
41
+
42
+ }
43
+
44
+ /**
45
+ * Print the widget content
46
+ * @since 0.0.1
47
+ */
48
+ public function widget( $args, $instance ) {
49
+
50
+ global $bpfwp_controller;
51
+
52
+ // Print the widget's HTML markup
53
+ echo $args['before_widget'];
54
+ if( isset( $instance['title'] ) ) {
55
+ $title = apply_filters( 'widget_title', $instance['title'] );
56
+ echo $args['before_title'] . $title . $args['after_title'];
57
+ }
58
+ echo bpwfwp_print_contact_card( $instance );
59
+ echo $args['after_widget'];
60
+
61
+ }
62
+
63
+ /**
64
+ * Print the form to configure this widget in the admin panel
65
+ * @since 1.0
66
+ */
67
+ public function form( $instance ) {
68
+ ?>
69
+
70
+ <p>
71
+ <label for="<?php echo $this->get_field_id( 'title' ); ?>"> <?php _e( 'Title' ); ?></label>
72
+ <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; ?>>
73
+ </p>
74
+
75
+ <?php foreach( $this->toggles as $id => $label ) : ?>
76
+
77
+ <p>
78
+ <label for="<?php echo $this->get_field_id( $id ); ?>"> <?php echo $label; ?></label>
79
+ <input type="checkbox" id="<?php echo $this->get_field_id( $id ); ?>" name="<?php echo $this->get_field_name( $id ); ?>" value="1"<?php if ( !empty( $instance[$id] ) ) : ?> checked="checked"<?php endif; ?>>
80
+ </p>
81
+
82
+ <?php endforeach;
83
+ }
84
+
85
+ /**
86
+ * Sanitize and save the widget form values.
87
+ * @since 1.0
88
+ */
89
+ public function update( $new_instance, $old_instance ) {
90
+
91
+ $instance = array();
92
+ if ( !empty( $new_instance['title'] ) ) {
93
+ $instance['title'] = strip_tags( $new_instance['title'] );
94
+ }
95
+
96
+ foreach( $this->toggles as $id => $label ) {
97
+ $instance[ $id ] = empty( $new_instance[ $id ] ) ? false : true;
98
+ }
99
+
100
+ return $instance;
101
+ }
102
+
103
+ }
104
+ } // endif
includes/template-functions.php ADDED
@@ -0,0 +1,455 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Template functions for rendering contact cards
4
+ */
5
+
6
+ /**
7
+ * Print a contact card and add a shortcode
8
+ * @since 0.0.1
9
+ */
10
+ if ( !function_exists( 'bpwfwp_print_contact_card' ) ) {
11
+ function bpwfwp_print_contact_card( $args = array() ) {
12
+
13
+ // Define shortcode attributes
14
+ $defaults = array(
15
+ 'show_name' => true,
16
+ 'show_address' => true,
17
+ 'show_get_directions' => true,
18
+ 'show_phone' => true,
19
+ 'show_contact' => true,
20
+ 'show_opening_hours' => true,
21
+ 'show_opening_hours_brief' => false,
22
+ 'show_map' => true,
23
+ );
24
+
25
+ $defaults = apply_filters( 'bpwfp_contact_card_defaults', $defaults );
26
+
27
+ global $bpfwp_controller;
28
+ $bpfwp_controller->display_settings = shortcode_atts( $defaults, $args, 'contact-card' );
29
+
30
+ // Setup components and callback functions to render them
31
+ $data = array();
32
+
33
+ if ( $bpfwp_controller->settings->get_setting( 'name' ) ) {
34
+ $data['name'] = 'bpwfwp_print_name';
35
+ }
36
+
37
+ if ( $bpfwp_controller->settings->get_setting( 'address' ) ) {
38
+ $data['address'] = 'bpwfwp_print_address';
39
+ }
40
+
41
+ if ( $bpfwp_controller->settings->get_setting( 'phone' ) ) {
42
+ $data['phone'] = 'bpwfwp_print_phone';
43
+ }
44
+
45
+ if ( $bpfwp_controller->display_settings['show_contact'] &&
46
+ ( $bpfwp_controller->settings->get_setting( 'contact-email' ) || $bpfwp_controller->settings->get_setting( 'contact-page' ) ) ) {
47
+ $data['contact'] = 'bpwfwp_print_contact';
48
+ }
49
+
50
+ if ( $bpfwp_controller->settings->get_setting( 'opening-hours' ) ) {
51
+ $data['opening_hours'] = 'bpwfwp_print_opening_hours';
52
+ }
53
+
54
+ if ( $bpfwp_controller->display_settings['show_map'] && $bpfwp_controller->settings->get_setting( 'address' ) ) {
55
+ $data['map'] = 'bpwfwp_print_map';
56
+ }
57
+
58
+ $data = apply_filters( 'bpwfwp_component_callbacks', $data );
59
+
60
+
61
+ if ( apply_filters( 'bpfwp-load-frontend-assets', true ) ) {
62
+ wp_enqueue_style( 'dashicons' );
63
+ wp_enqueue_style( 'bpfwp-default' );
64
+ }
65
+
66
+ ob_start();
67
+ ?>
68
+
69
+ <address class="bp-contact-card" itemscope itemtype="http://schema.org/<?php echo $bpfwp_controller->settings->get_setting( 'schema_type' ); ?>">
70
+ <?php foreach ( $data as $data => $callback ) { call_user_func( $callback ); } ?>
71
+ </address>
72
+
73
+ <?php
74
+ $output = ob_get_clean();
75
+
76
+ return apply_filters( 'bpwfwp_contact_card_output', $output );
77
+ }
78
+ if ( !shortcode_exists( 'contact-card' ) ) {
79
+ add_shortcode( 'contact-card', 'bpwfwp_print_contact_card' );
80
+ }
81
+ } // endif;
82
+
83
+ /**
84
+ * Print the name
85
+ * @since 0.0.1
86
+ */
87
+ if ( !function_exists( 'bpwfwp_print_name' ) ) {
88
+ function bpwfwp_print_name() {
89
+
90
+ global $bpfwp_controller;
91
+
92
+ if ( $bpfwp_controller->display_settings['show_name'] ) :
93
+ ?>
94
+ <div class="bp-name" itemprop="name">
95
+ <?php echo esc_attr( $bpfwp_controller->settings->get_setting( 'name' ) ); ?>
96
+ </div>
97
+
98
+ <?php else : ?>
99
+ <meta itemprop="name" content="<?php echo esc_attr( $bpfwp_controller->settings->get_setting( 'name' ) ); ?>">
100
+
101
+ <?php endif; ?>
102
+
103
+ <meta itemprop="description" content="<?php echo esc_attr( get_bloginfo( 'url' ) ) ?>">
104
+ <meta itemprop="url" content="<?php echo esc_attr( get_bloginfo( 'description' ) ); ?>">
105
+
106
+ <?php
107
+ }
108
+ } // endif;
109
+
110
+ /**
111
+ * Print the address with a get directions link to Google Maps
112
+ * @since 0.0.1
113
+ */
114
+ if ( !function_exists( 'bpwfwp_print_address' ) ) {
115
+ function bpwfwp_print_address() {
116
+
117
+ global $bpfwp_controller;
118
+
119
+ $address = $bpfwp_controller->settings->get_setting( 'address' );
120
+
121
+ if ( $bpfwp_controller->display_settings['show_address'] ) :
122
+ ?>
123
+
124
+ <div class="bp-address" itemprop="address" itemscope itemtype="http://schema.org/PostalAddress">
125
+ <?php echo nl2br( $address['text'] ); ?>
126
+ </div>
127
+
128
+ <?php else : ?>
129
+ <meta itemprop="address" content="<?php echo esc_attr( $address['text'] ); ?>">
130
+
131
+ <?php endif; ?>
132
+
133
+ <?php if ( $bpfwp_controller->display_settings['show_get_directions'] ) : ?>
134
+ <div class="bp-directions">
135
+ <a href="//maps.google.com/maps?saddr=current+location&daddr=<?php echo urlencode( esc_attr( $address['text'] ) ); ?>"><?php _e( 'Get directions', BPFWP_TEXTDOMAIN ); ?></a>
136
+ </div>
137
+ <?php endif;
138
+
139
+ }
140
+ } // endif;
141
+
142
+ /**
143
+ * Print the phone number
144
+ * @since 0.0.1
145
+ */
146
+ if ( !function_exists( 'bpwfwp_print_phone' ) ) {
147
+ function bpwfwp_print_phone() {
148
+
149
+ global $bpfwp_controller;
150
+
151
+ if ( $bpfwp_controller->display_settings['show_phone'] ) :
152
+ ?>
153
+
154
+ <div class="bp-phone" itemprop="telephone">
155
+ <?php echo $bpfwp_controller->settings->get_setting( 'phone' ); ?>
156
+ </div>
157
+
158
+ <?php else : ?>
159
+ <meta itemprop="telephone" content="<?php echo esc_attr( $bpfwp_controller->settings->get_setting( 'phone' ) ); ?>">
160
+
161
+ <?php endif;
162
+ }
163
+ } // endif;
164
+
165
+ /**
166
+ * Print the contact link
167
+ * @since 0.0.1
168
+ */
169
+ if ( !function_exists( 'bpwfwp_print_contact' ) ) {
170
+ function bpwfwp_print_contact() {
171
+
172
+ global $bpfwp_controller;
173
+
174
+ $email = $bpfwp_controller->settings->get_setting( 'contact-email' );
175
+ if ( !empty( $email ) ) :
176
+ ?>
177
+
178
+ <div class="bp-contact bp-contact-email" itemprop="email">
179
+ <a href="mailto:<?php echo esc_attr( $email ); ?>"><?php echo $email; ?></a>
180
+ </div>
181
+
182
+ <?php
183
+ return;
184
+ endif;
185
+
186
+ $contact = $bpfwp_controller->settings->get_setting( 'contact-page' );
187
+ if ( !empty( $contact ) ) :
188
+ ?>
189
+
190
+ <div class="bp-contact bp-contact-page" itemprop="ContactPoint" itemscope itemtype="http://schema.org/ContactPoint">
191
+ <a href="<?php echo get_post_permalink( $contact ); ?>"><?php _e( 'Contact', BPFWP_TEXTDOMAIN ); ?></a>
192
+ </div>
193
+
194
+ <?php endif;
195
+
196
+ }
197
+ } // endif;
198
+
199
+ /**
200
+ * Print the opening hours
201
+ * @since 0.0.1
202
+ */
203
+ if ( !function_exists( 'bpwfwp_print_opening_hours' ) ) {
204
+ function bpwfwp_print_opening_hours() {
205
+
206
+ global $bpfwp_controller;
207
+
208
+ $weekdays_schema = array(
209
+ 'monday' => 'Mo',
210
+ 'tuesday' => 'Tu',
211
+ 'wednesday' => 'We',
212
+ 'thursday' => 'Th',
213
+ 'friday' => 'Fr',
214
+ 'saturday' => 'Sa',
215
+ 'sunday' => 'Su',
216
+ );
217
+
218
+ $hours = $bpfwp_controller->settings->get_setting( 'opening-hours' );
219
+
220
+ // Output proper schema.org format
221
+ foreach( $hours as $slot ) {
222
+
223
+ // Skip this entry if no weekdays are set
224
+ if ( empty( $slot['weekdays'] ) ) {
225
+ continue;
226
+ }
227
+
228
+ $days = array();
229
+ foreach( $slot['weekdays'] as $day => $val ) {
230
+ $days[] = $weekdays_schema[ $day ];
231
+ }
232
+ $string = !empty( $days ) ? join( ',', $days ) : '';
233
+
234
+ if ( !empty( $string) && !empty( $slot['time'] ) ) {
235
+
236
+ if ( empty( $slot['time']['start'] ) ) {
237
+ $start = '00:00';
238
+ } else {
239
+ $start = trim( substr( $slot['time']['start'], 0, -2 ) );
240
+ if ( substr( $slot['time']['start'], -2 ) == 'PM' && $start !== '12:00' ) {
241
+ $split = explode( ':', $start );
242
+ $split[0] += 12;
243
+ $start = join( ':', $split );
244
+ }
245
+ if ( substr( $slot['time']['start'], -2 ) == 'AM' && $start == '12:00' ) {
246
+ $start = '00:00';
247
+ }
248
+ }
249
+
250
+ if ( empty( $slot['time']['end'] ) ) {
251
+ $end = '24:00';
252
+ } else {
253
+ $end = trim( substr( $slot['time']['end'], 0, -2 ) );
254
+ if ( substr( $slot['time']['end'], -2 ) == 'PM' ) {
255
+ $split = explode( ':', $end );
256
+ $split[0] += 12;
257
+ $end = join( ':', $split );
258
+ }
259
+ if ( substr( $slot['time']['start'], -2 ) == 'AM' && $start == '12:00' ) {
260
+ $end = '24:00';
261
+ }
262
+ }
263
+
264
+ $string .= ' ' . $start . '-' . $end;
265
+ }
266
+ echo '<meta itemprop="openingHours" content="' . esc_attr( $string ) . '">';
267
+ }
268
+
269
+ // Output display format
270
+ if ( !$bpfwp_controller->display_settings['show_opening_hours'] ) {
271
+ return;
272
+ }
273
+
274
+ if ( $bpfwp_controller->display_settings['show_opening_hours_brief'] ) :
275
+ ?>
276
+
277
+ <div class="bp-opening-hours-brief">
278
+
279
+ <?php
280
+ $slots = array();
281
+ foreach( $hours as $slot ) {
282
+
283
+ // Skip this entry if no weekdays are set
284
+ if ( empty( $slot['weekdays'] ) ) {
285
+ continue;
286
+ }
287
+
288
+ $days = array();
289
+ foreach( $slot['weekdays'] as $day => $val ) {
290
+ $days[] = $weekdays_schema[ $day ];
291
+ }
292
+ $string = !empty( $days ) ? join( ',', $days ) : '';
293
+
294
+
295
+ if ( empty( $slot['time'] ) ) {
296
+ $string .= __( ' all day', BPFWP_TEXTDOMAIN );
297
+ } else {
298
+ if ( !empty( $slot['time']['start'] ) ) {
299
+ $start = new DateTime( $slot['time']['start'] );
300
+ }
301
+ if ( !empty( $slot['time']['end'] ) ) {
302
+ $end = new DateTime( $slot['time']['end'] );
303
+ }
304
+
305
+ if ( empty( $start ) ) {
306
+ $string .= __( ' open until ', BPFWP_TEXTDOMAIN ) . $end->format( get_option( 'time_format' ) );
307
+ } elseif ( empty( $end ) ) {
308
+ $string .= __( ' open from ', BPFWP_TEXTDOMAIN ) . $start->format( get_option( 'time_format' ) );
309
+ } else {
310
+ $string .= ' ' . $start->format( get_option( 'time_format' ) ) . _x( '-', 'Separator between opening and closing times. Example: 9:00am-5:00pm', BPFWP_TEXTDOMAIN ) . $end->format( get_option( 'time_format' ) );
311
+ }
312
+ }
313
+
314
+ $slots[] = $string;
315
+ }
316
+
317
+ echo join( _x( '; ', 'Separator between multiple opening times in the brief opening hours. Example: Mo,We 9:00 AM - 5:00 PM; Tu,Th 10:00 AM - 5:00 PM', BPFWP_TEXTDOMAIN ), $slots );
318
+ ?>
319
+
320
+ </div>
321
+
322
+ <?php
323
+ return;
324
+ endif; // brief opening hours
325
+
326
+ $weekdays_display = array(
327
+ 'monday' => __( 'Monday' ),
328
+ 'tuesday' => __( 'Tuesday' ),
329
+ 'wednesday' => __( 'Wednesday' ),
330
+ 'thursday' => __( 'Thursday' ),
331
+ 'friday' => __( 'Friday' ),
332
+ 'saturday' => __( 'Saturday' ),
333
+ 'sunday' => __( 'Sunday' ),
334
+ );
335
+
336
+ $weekdays = array();
337
+ foreach( $hours as $rule ) {
338
+
339
+ // Skip this entry if no weekdays are set
340
+ if ( empty( $rule['weekdays'] ) ) {
341
+ continue;
342
+ }
343
+
344
+ if ( empty( $rule['time'] ) ) {
345
+ $time = __( 'Open', BPFWP_TEXTDOMAIN );
346
+
347
+ } else {
348
+
349
+ if ( !empty( $rule['time']['start'] ) ) {
350
+ $start = new DateTime( $rule['time']['start'] );
351
+ }
352
+ if ( !empty( $rule['time']['end'] ) ) {
353
+ $end = new DateTime( $rule['time']['end'] );
354
+ }
355
+
356
+ if ( empty( $start ) ) {
357
+ $time = __( 'Open until ', BPFWP_TEXTDOMAIN ) . $end->format( get_option( 'time_format' ) );
358
+ } elseif ( empty( $end ) ) {
359
+ $time = __( 'Open from ', BPFWP_TEXTDOMAIN ) . $start->format( get_option( 'time_format' ) );
360
+ } else {
361
+ $time = $start->format( get_option( 'time_format' ) ) . _x( '-', 'Separator between opening and closing times. Example: 9:00am-5:00pm', BPFWP_TEXTDOMAIN ) . $end->format( get_option( 'time_format' ) );
362
+ }
363
+ }
364
+
365
+ foreach( $rule['weekdays'] as $day => $val ) {
366
+
367
+ if ( !array_key_exists( $day, $weekdays ) ) {
368
+ $weekdays[$day] = array();
369
+ }
370
+
371
+ $weekdays[$day][] = $time;
372
+ }
373
+ }
374
+
375
+ if ( count( $weekdays ) ) :
376
+
377
+ // Order the weekdays and add any missing days as "closed"
378
+ $weekdays_ordered = array();
379
+ foreach( $weekdays_display as $slug => $name ) {
380
+ if ( !array_key_exists( $slug, $weekdays ) ) {
381
+ $weekdays_ordered[$slug] = array( __( 'Closed', BPFWP_TEXTDOMAIN ) );
382
+ } else {
383
+ $weekdays_ordered[$slug] = $weekdays[$slug];
384
+ }
385
+ }
386
+ ?>
387
+
388
+ <div class="bp-opening-hours">
389
+ <span class="bp-title"><?php _e( 'Opening Hours', BPFWP_TEXTDOMAIN ); ?></span>
390
+ <?php foreach ( $weekdays_ordered as $weekday => $times ) : ?>
391
+ <div class="bp-weekday">
392
+ <span class="bp-weekday-name bp-weekday-<?php echo $weekday; ?>"><?php echo $weekdays_display[$weekday]; ?></span>
393
+ <span class="bp-times">
394
+ <?php foreach ( $times as $time ) : ?>
395
+ <span class="bp-time"><?php echo $time; ?></span>
396
+ <?php endforeach; ?>
397
+ </span>
398
+ </div>
399
+ <?php endforeach; ?>
400
+ </div>
401
+
402
+ <?php
403
+ endif;
404
+ }
405
+ } // endif;
406
+
407
+ /**
408
+ * Print a map to the address
409
+ * @since 0.0.1
410
+ */
411
+ if ( !function_exists( 'bpwfwp_print_map' ) ) {
412
+ function bpwfwp_print_map() {
413
+
414
+ global $bpfwp_controller;
415
+
416
+ $address = $bpfwp_controller->settings->get_setting( 'address' );
417
+
418
+ wp_enqueue_script( 'bpfwp-map' );
419
+ wp_localize_script(
420
+ 'bpfwp-map',
421
+ 'bpfwp_map',
422
+ array(
423
+ 'strings' => array(
424
+ 'get_directions' => __( 'Get directions', BPFWP_TEXTDOMAIN ),
425
+ )
426
+ )
427
+ );
428
+
429
+ global $bpfwp_map_ids;
430
+ if ( empty( $bpfwp_map_ids ) ) {
431
+ $bpfwp_map_ids = array();
432
+ }
433
+
434
+ $id = count( $bpfwp_map_ids );
435
+ $bpfwp_map_ids[] = $id;
436
+
437
+
438
+ $attr = '';
439
+
440
+ $phone = $bpfwp_controller->settings->get_setting( 'phone' );
441
+ if ( !empty( $phone ) ) {
442
+ $attr .= ' data-phone="' . esc_attr( $phone ) . '"';
443
+ }
444
+
445
+ if ( !empty( $address['lat'] ) && !empty( $address['lon'] ) ) {
446
+ $attr .= ' data-lat="' . esc_attr( $address['lat'] ) . '" data-lon="' . esc_attr( $address['lon'] ) . '"';
447
+ }
448
+ ?>
449
+
450
+ <div id="bp-map-<?php echo $id; ?>" class="bp-map" itemprop="map" data-name="<?php echo esc_attr( $bpfwp_controller->settings->get_setting( 'name' ) ); ?>" data-address="<?php echo esc_attr( nl2br( $address['text'] ) ); ?>"<?php echo $attr; ?>>
451
+ </div>
452
+
453
+ <?php
454
+ }
455
+ } // endif;
lib/simple-admin-pages/README.md ADDED
@@ -0,0 +1,396 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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.5', // 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.5', // 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.5 - 2014-05-15
372
+ - Fix a bug with the Textarea component callback
373
+
374
+ - 2.0.a.4 - 2014-05-15
375
+ - Only load assets when component is called
376
+ - Revert adding version number to script handles
377
+ - Fix localized script handler for Scheduler
378
+ - Fix pickadate CSS rule specificity
379
+
380
+ - 2.0.a.3 - 2014-05-14
381
+ - Fix undefined function error in Scheduler javascript when using Firefox
382
+ - Add version number to style and script handles so different versions will be enqueued
383
+
384
+ - 2.0.a.2 - 2014-05-11
385
+ - Add support for top-level menus
386
+ - Support line breaks in textarea components
387
+
388
+ - 2.0.a.1 - 2014-04-03
389
+ - Save all data on a page as one row in wp_options
390
+
391
+ - 1.1 - never released
392
+ - Support themes pages
393
+ - Support submenu pages for custom menu items
394
+
395
+ - 1.0 - 2013-11-20
396
+ - Initial release
lib/simple-admin-pages/classes/AdminPage.Menu.class.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 sapAdminPageMenu_2_0_a_5 extends sapAdminPage_2_0_a_5 {
12
+
13
+ public $setup_function = 'add_menu_page'; // WP function to register the page
14
+
15
+ /**
16
+ * Add the page to the appropriate menu slot.
17
+ * @since 1.0
18
+ */
19
+ public function add_admin_menu() {
20
+
21
+ call_user_func(
22
+ $this->setup_function,
23
+ $this->title,
24
+ $this->menu_title,
25
+ $this->capability,
26
+ $this->id,
27
+ array( $this, 'display_admin_menu' ),
28
+ $this->icon,
29
+ $this->position
30
+ );
31
+ }
32
+ }
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_5 extends sapAdminPage_2_0_a_5 {
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_5 extends sapAdminPage_2_0_a_5 {
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_5 {
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_5 {
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.Address.class.php ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Register, display and save a textarea field setting in the admin menu
5
+ *
6
+ * @since 2.0.a.5
7
+ * @package Simple Admin Pages
8
+ */
9
+
10
+ class sapAdminPageSettingAddress_2_0_a_5 extends sapAdminPageSetting_2_0_a_5 {
11
+
12
+ /*
13
+ * Size of this textarea
14
+ *
15
+ * This is put directly into a css class [size]-text,
16
+ * and setting this to 'large' will link into WordPress's existing textarea
17
+ * style for full-width textareas.
18
+ */
19
+ public $size = 'small';
20
+
21
+ /**
22
+ * Scripts that must be loaded for this component
23
+ * @since 2.0.a.5
24
+ */
25
+ public $scripts = array(
26
+ 'sap-address' => array(
27
+ 'path' => 'js/address.js',
28
+ 'dependencies' => array( 'jquery' ),
29
+ 'version' => '2.0.a.5',
30
+ 'footer' => true,
31
+ ),
32
+ );
33
+
34
+ public $sanitize_callback = 'sanitize_text_field';
35
+
36
+ /**
37
+ * Escape the value to display it safely HTML textarea fields
38
+ * @since 2.0.a.5
39
+ */
40
+ public function esc_value( $val ) {
41
+
42
+ $escaped = array();
43
+ $escaped['text'] = empty( $val['text'] ) ? '' : esc_textarea( $val['text'] );
44
+ $escaped['lat'] = empty( $val['lat'] ) ? '' : esc_textarea( $val['lat'] );
45
+ $escaped['lon'] = empty( $val['lon'] ) ? '' : esc_textarea( $val['lon'] );
46
+
47
+ return $escaped;
48
+ }
49
+
50
+ /**
51
+ * Set the size of this textarea field
52
+ * @since 1.0
53
+ */
54
+ public function set_size( $size ) {
55
+ $this->size = esc_attr( $size );
56
+ }
57
+
58
+ /**
59
+ * Wrapper for the sanitization callback function.
60
+ *
61
+ * This just reduces code duplication for child classes that need a custom
62
+ * callback function.
63
+ * @since 2.0.a.5
64
+ */
65
+ public function sanitize_callback_wrapper( $value ) {
66
+
67
+ $sanitized = array();
68
+ $sanitized['text'] = empty( $value['text'] ) ? '' : wp_kses_post( $value['text'] );
69
+ $sanitized['lat'] = empty( $value['lat'] ) ? '' : sanitize_text_field( $value['lat'] );
70
+ $sanitized['lon'] = empty( $value['lon'] ) ? '' : sanitize_text_field( $value['lon'] );
71
+
72
+ return $sanitized;
73
+ }
74
+
75
+ /**
76
+ * Display this setting
77
+ * @since 2.0.a.5
78
+ */
79
+ public function display_setting() {
80
+
81
+ $strings = array(
82
+ 'sep-lat-lon' => _x( ', ', 'separates latitude and longitude', SAP_TEXTDOMAIN ),
83
+ 'no-setting' => __( 'No map coordinates set.', SAP_TEXTDOMAIN ),
84
+ 'retrieving' => __( 'Requesting new coordinates', SAP_TEXTDOMAIN ),
85
+ 'select' => __( 'Select a match below', SAP_TEXTDOMAIN ),
86
+ 'view' => __( 'View', SAP_TEXTDOMAIN ),
87
+ 'retrieve' => __( 'Retrieve map coordinates', SAP_TEXTDOMAIN ),
88
+ 'remove' => __( 'Remove map coordinates', SAP_TEXTDOMAIN ),
89
+ 'try_again' => __( 'Try again?', SAP_TEXTDOMAIN ),
90
+ 'result_error' => __( 'Error', SAP_TEXTDOMAIN ),
91
+ 'result_invalid' => __( 'Invalid request. Be sure to fill out the address field before retrieving coordinates.', SAP_TEXTDOMAIN ),
92
+ 'result_denied' => __( 'Request denied.', SAP_TEXTDOMAIN ),
93
+ 'result_limit' => __( 'Request denied because you are over your request quota.', SAP_TEXTDOMAIN ),
94
+ 'result_empty' => __( 'Nothing was found at that address', SAP_TEXTDOMAIN ),
95
+ );
96
+
97
+ wp_localize_script(
98
+ 'sap-address',
99
+ 'sap_address',
100
+ array(
101
+ 'strings' => $strings,
102
+ )
103
+ );
104
+
105
+ $this->display_description();
106
+
107
+ ?>
108
+
109
+ <div class="sap-address" id="<?php echo $this->id; ?>">
110
+ <textarea name="<?php echo $this->get_input_name(); ?>[text]" 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['text']; ?></textarea>
111
+ <p class="sap-map-coords-wrapper">
112
+ <span class="dashicons dashicons-location-alt"></span>
113
+ <span class="sap-map-coords">
114
+ <?php if ( empty( $this->value['lat'] ) || empty( $this->value['lon'] ) ) : ?>
115
+ <?php echo $strings['no-setting']; ?>
116
+ <?php else : ?>
117
+ <?php echo $this->value['lat'] . $strings['sep-lat-lon'] . $this->value['lon']; ?>
118
+ <a href="//maps.google.com/maps?q=<?php echo esc_attr( $this->value['lat'] ) . ',' . esc_attr( $this->value['lon'] ); ?>" class="sap-view-coords" target="_blank"><?php echo $strings['view']; ?></a>
119
+ <?php endif; ?>
120
+ </span>
121
+ </p>
122
+ <p class="sap-coords-action-wrapper">
123
+ <a href="#" class="sap-get-coords">
124
+ <?php echo $strings['retrieve']; ?>
125
+ </a>
126
+ <?php _ex( ' | ', 'separator between admin action links in address component', SAP_TEXTDOMAIN ); ?>
127
+ <a href="#" class="sap-remove-coords">
128
+ <?php echo $strings['remove']; ?>
129
+ </a>
130
+ </p>
131
+ <input type="hidden" class="lat" name="<?php echo $this->get_input_name(); ?>[lat]" value="<?php echo $this->value['lat']; ?>">
132
+ <input type="hidden" class="lon" name="<?php echo $this->get_input_name(); ?>[lon]" value="<?php echo $this->value['lon']; ?>">
133
+ </div>
134
+
135
+ <?php
136
+ }
137
+
138
+ }
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_5 extends sapAdminPageSetting_2_0_a_5 {
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_5 extends sapAdminPageSetting_2_0_a_5 {
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,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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_5 extends sapAdminPageSetting_2_0_a_5 {
29
+
30
+ public $sanitize_callback = 'sanitize_text_field';
31
+
32
+ /**
33
+ * Scripts that must be loaded for this component
34
+ * @since 2.0.a.4
35
+ */
36
+ public $scripts = array(
37
+ 'sap-opening-hours' => array(
38
+ 'path' => 'js/opening-hours.js',
39
+ 'dependencies' => array( 'jquery' ),
40
+ 'version' => '2.0.a.5',
41
+ 'footer' => true,
42
+ ),
43
+ );
44
+
45
+ // Array of days of the week
46
+ public $weekdays = array(
47
+ 'monday' => 'Monday',
48
+ 'tuesday' => 'Tuesday',
49
+ 'wednesday' => 'Wednesday',
50
+ 'thursday' => 'Thursday',
51
+ 'friday' => 'Friday',
52
+ 'saturday' => 'Saturday',
53
+ 'sunday' => 'Sunday'
54
+ );
55
+
56
+ /**
57
+ * Parse the arguments passed in the construction and assign them to
58
+ * internal variables.
59
+ * @since 1.0
60
+ */
61
+ private function parse_args( $args ) {
62
+ foreach ( $args as $key => $val ) {
63
+ switch ( $key ) {
64
+
65
+ case 'id' :
66
+ $this->{$key} = esc_attr( $val );
67
+
68
+ case 'weekdays' :
69
+
70
+ $this->weekdays = $val;
71
+
72
+ default :
73
+ $this->{$key} = $val;
74
+
75
+ }
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Escape the value to display it in text fields and other input fields
81
+ *
82
+ * @since 1.0
83
+ */
84
+ public function esc_value( $val ) {
85
+
86
+ $value = array();
87
+
88
+ // Loop over the values and sanitize them
89
+ for ( $i = 0; $i < 7; $i++ ) {
90
+ $value[$i]['day'] = isset( $val[$i] ) && isset( $val[$i]['day'] ) ? esc_attr( $val[$i]['day'] ) : '';
91
+ $value[$i]['hours'] = isset( $val[$i] ) && isset( $val[$i]['hours'] ) ? esc_attr( $val[$i]['hours'] ) : '';
92
+ }
93
+
94
+ return $value;
95
+ }
96
+
97
+ /**
98
+ * Get a day's display name
99
+ * @since 1.0
100
+ */
101
+ private function get_day_name( $day ) {
102
+ foreach ( $this->weekdays as $id => $name ) {
103
+ if ( $day == $id ) {
104
+ return $name;
105
+ }
106
+ }
107
+
108
+ return '';
109
+ }
110
+
111
+ /**
112
+ * Display this setting
113
+ * @since 1.0
114
+ * @todo integrate time picker
115
+ */
116
+ public function display_setting() {
117
+
118
+ $this->display_description();
119
+
120
+ for ($i = 0; $i < 7; $i++) {
121
+
122
+ ?>
123
+
124
+ <table class="sap-opening-hours">
125
+ <tr>
126
+ <td>
127
+ <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'] ) ); ?>">
128
+ <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">
129
+ <option value=""></option>
130
+
131
+ <?php foreach ( $this->weekdays as $id => $name ) : ?>
132
+
133
+ <option value="<?php echo $id; ?>" data-name="<?php echo esc_attr( $name ); ?>"<?php if ( $this->value[$i]['day'] == $id ) : ?> selected<?php endif; ?>>
134
+ <?php echo $name; ?>
135
+ </option>
136
+
137
+ <?php endforeach; ?>
138
+
139
+ </select>
140
+ </td>
141
+ <td>
142
+ <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" />
143
+ </td>
144
+ </tr>
145
+ </table>
146
+
147
+ <?php
148
+
149
+ }
150
+
151
+ }
152
+
153
+ /**
154
+ * Sanitize the array of text inputs for this setting
155
+ * @since 1.0
156
+ */
157
+ public function sanitize_callback_wrapper( $values ) {
158
+
159
+ // If no sanitization callback exists, don't register the setting.
160
+ if ( !isset( $this->sanitize_callback ) || !trim( $this->sanitize_callback ) ) {
161
+ return;
162
+ }
163
+
164
+ // If this isn't an array, just sanitize it as a string
165
+ if (!is_array( $values ) ) {
166
+ return call_user_func( $this->sanitize_callback, $values );
167
+ }
168
+
169
+ // Loop over the values and sanitize them
170
+ for ( $i = 0; $i < 7; $i++ ) {
171
+ if ( isset( $values[ $i ] ) && is_array( $values[ $i ] ) ) {
172
+ $values[$i]['day'] = call_user_func( $this->sanitize_callback, $values[$i]['day'] );
173
+ $values[$i]['hours'] = call_user_func( $this->sanitize_callback, $values[$i]['hours'] );
174
+ }
175
+ }
176
+
177
+ return $values;
178
+ }
179
+
180
+ }
lib/simple-admin-pages/classes/AdminPageSetting.Scheduler.class.php ADDED
@@ -0,0 +1,664 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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_5 extends sapAdminPageSetting_2_0_a_5 {
14
+
15
+ public $sanitize_callback = 'sanitize_text_field';
16
+
17
+ /**
18
+ * Scripts that must be loaded for this component
19
+ * @since 2.0.a.4
20
+ */
21
+ public $scripts = array(
22
+ 'pickadate' => array(
23
+ 'path' => 'lib/pickadate/picker.js',
24
+ 'dependencies' => array( 'jquery' ),
25
+ 'version' => '3.5.0',
26
+ 'footer' => true,
27
+ ),
28
+ 'pickadate-date' => array(
29
+ 'path' => 'lib/pickadate/picker.date.js',
30
+ 'dependencies' => array( 'jquery' ),
31
+ 'version' => '3.5.0',
32
+ 'footer' => true,
33
+ ),
34
+ 'pickadate-time' => array(
35
+ 'path' => 'lib/pickadate/picker.time.js',
36
+ 'dependencies' => array( 'jquery' ),
37
+ 'version' => '3.5.0',
38
+ 'footer' => true,
39
+ ),
40
+ 'pickadate-legacy' => array(
41
+ 'path' => 'lib/pickadate/legacy.js',
42
+ 'dependencies' => array( 'jquery' ),
43
+ 'version' => '3.5.0',
44
+ 'footer' => true,
45
+ ),
46
+ 'sap-scheduler' => array(
47
+ 'path' => 'js/scheduler.js',
48
+ 'dependencies' => array( 'jquery' ),
49
+ 'version' => '2.0.a.5',
50
+ 'footer' => true,
51
+ ),
52
+ // @todo there should be some way to load alternate language .js files
53
+ // and RTL CSS scripts
54
+ );
55
+
56
+ /**
57
+ * Styles that must be loaded for this component
58
+ * @since 2.0.a.4
59
+ */
60
+ public $styles = array(
61
+ 'pickadate-default' => array(
62
+ 'path' => 'lib/pickadate/themes/default.css',
63
+ 'dependencies' => '',
64
+ 'version' => '3.5.0',
65
+ 'media' => null,
66
+ ),
67
+ 'pickadate-date' => array(
68
+ 'path' => 'lib/pickadate/themes/default.date.css',
69
+ 'dependencies' => '',
70
+ 'version' => '3.5.0',
71
+ 'media' => null,
72
+ ),
73
+ 'pickadate-time' => array(
74
+ 'path' => 'lib/pickadate/themes/default.time.css',
75
+ 'dependencies' => '',
76
+ 'version' => '3.5.0',
77
+ 'media' => null,
78
+ ),
79
+ );
80
+
81
+ public $weekdays = array(
82
+ 'monday' => 'Mo',
83
+ 'tuesday' => 'Tu',
84
+ 'wednesday' => 'We',
85
+ 'thursday' => 'Th',
86
+ 'friday' => 'Fr',
87
+ 'saturday' => 'Sa',
88
+ 'sunday' => 'Su',
89
+ );
90
+
91
+ public $weeks = array(
92
+ 'first' => '1st',
93
+ 'second' => '2nd',
94
+ 'third' => '3rd',
95
+ 'fourth' => '4th',
96
+ 'last' => 'last',
97
+ );
98
+
99
+ /**
100
+ * Number of minutes between time selection intervals
101
+ */
102
+ public $time_interval = 15;
103
+
104
+ /**
105
+ * Display format for time selection
106
+ * See http://amsul.ca/pickadate.js/ for formatting options
107
+ */
108
+ public $time_format = 'h:i A';
109
+
110
+ /**
111
+ * Display format for date selection
112
+ * See http://amsul.ca/pickadate.js/ for formatting options
113
+ */
114
+ public $date_format = 'd mmmm, yyyy';
115
+
116
+ /**
117
+ * Boolean to disable the weekday selection option
118
+ */
119
+ public $disable_weekdays = false;
120
+
121
+ /**
122
+ * Boolean to disable the weeks selection option
123
+ */
124
+ public $disable_weeks = false;
125
+
126
+ /**
127
+ * Boolean to disable the date selection option
128
+ */
129
+ public $disable_date = false;
130
+
131
+ /**
132
+ * Boolean to disable the time selection option
133
+ */
134
+ public $disable_time = false;
135
+
136
+ /**
137
+ * Boolean to disable the end time selection option
138
+ */
139
+ public $disable_end_time = false;
140
+
141
+ /**
142
+ * Array containing strings for schedule summaries. See the default
143
+ * summaries at set_schedule_summaries(). Use this to overwrite the defaults
144
+ * with anything more useful to this instance.
145
+ */
146
+ public $instance_schedule_summaries;
147
+
148
+ /**
149
+ * Escape the value to display it in text fields and other input fields
150
+ * @since 2.0
151
+ */
152
+ public function esc_value( $val ) {
153
+
154
+ $value = array();
155
+
156
+ if ( empty( $val ) ) {
157
+ return $value;
158
+ }
159
+
160
+ foreach ( $val as $i => $rule ) {
161
+
162
+ if ( !empty( $rule['weekdays'] ) ) {
163
+ $value[$i]['weekdays'] = array();
164
+ foreach ( $rule['weekdays'] as $day => $flag ) {
165
+ if ( $flag !== '1' ) {
166
+ continue;
167
+ }
168
+
169
+ $value[$i]['weekdays'][$day] = $flag;
170
+ }
171
+ }
172
+
173
+ if ( !empty( $rule['weeks'] ) ) {
174
+ $value[$i]['weeks'] = array();
175
+ foreach ( $rule['weeks'] as $week => $flag ) {
176
+ if ( $flag !== '1' ) {
177
+ continue;
178
+ }
179
+
180
+ $value[$i]['weeks'][$week] = $flag;
181
+ }
182
+ }
183
+
184
+ if ( !empty( $rule['date'] ) ) {
185
+ $value[$i]['date'] = esc_attr( $rule['date'] );
186
+ }
187
+
188
+ if ( !empty( $rule['time']['start'] ) ) {
189
+ $value[$i]['time']['start'] = esc_attr( $rule['time']['start'] );
190
+ }
191
+ if ( !empty( $rule['time']['end'] ) ) {
192
+ $value[$i]['time']['end'] = esc_attr( $rule['time']['end'] );
193
+ }
194
+ }
195
+
196
+ return $value;
197
+ }
198
+
199
+ /**
200
+ * Compile and pass configurable variables to the javascript file, so they
201
+ * can be used when we initialize the pickadate components
202
+ * @since 2.0
203
+ */
204
+ public function pass_to_scripts() {
205
+
206
+ // Create a global variable containing settings for all schedulers
207
+ // that are being rendered on the page. This allows us to pass different
208
+ // settings for different schedulers on the same page.
209
+ global $sap_scheduler_settings;
210
+
211
+ if ( !isset( $sap_scheduler_settings ) ) {
212
+ $sap_scheduler_settings = array();
213
+ }
214
+
215
+ $sap_scheduler_settings[ $this->id ] = array(
216
+ 'time_interval' => $this->time_interval,
217
+ 'time_format' => $this->time_format,
218
+ 'date_format' => $this->date_format,
219
+ 'template' => $this->get_template(),
220
+ 'weekdays' => $this->weekdays,
221
+ 'weeks' => $this->weeks,
222
+ 'disable_weekdays' => $this->disable_weekdays,
223
+ 'disable_weeks' => $this->disable_weeks,
224
+ 'disable_date' => $this->disable_date,
225
+ 'disable_time' => $this->disable_time,
226
+ 'summaries' => $this->schedule_summaries,
227
+ );
228
+
229
+ // This gets called multiple times, but only the last call is actually
230
+ // pushed to the script.
231
+ wp_localize_script(
232
+ 'sap-scheduler',
233
+ 'sap_scheduler',
234
+ array(
235
+ 'settings' => $sap_scheduler_settings
236
+ )
237
+ );
238
+
239
+ }
240
+
241
+ /**
242
+ * Display this setting
243
+ * @since 2.0
244
+ */
245
+ public function display_setting() {
246
+
247
+ $this->display_description();
248
+
249
+ // Define summary text to use when a rule is displayed in brief
250
+ $this->set_schedule_summaries();
251
+
252
+ // Pass data to the script files to handle js interactions
253
+ $this->pass_to_scripts();
254
+
255
+ ?>
256
+
257
+ <div class="sap-scheduler" id="<?php echo $this->id; ?>">
258
+ <?php
259
+ foreach ( $this->value as $id => $rule ) {
260
+ echo $this->get_template( $id, $rule, true );
261
+ }
262
+ ?>
263
+ </div>
264
+ <div class="sap-add-scheduler">
265
+ <a href="#" class="button">
266
+ <?php _e( 'Add new scheduling rule', SAP_TEXTDOMAIN ); ?>
267
+ </a>
268
+ </div>
269
+
270
+ <?php
271
+ }
272
+
273
+ /**
274
+ * Retrieve the template for a scheduling rule
275
+ * @since 2.0
276
+ */
277
+ public function get_template( $id = 0, $values = array(), $list = false ) {
278
+
279
+ $date_format = $this->get_date_format( $values );
280
+ $time_format = $this->get_time_format( $values );
281
+
282
+ ob_start();
283
+ ?>
284
+
285
+ <div class="sap-scheduler-rule clearfix<?php echo $list ? ' list' : ''; ?>">
286
+ <div class="sap-scheduler-date <?php echo $date_format; echo $this->disable_time === true ? ' full-width' : ''; ?>">
287
+ <ul class="sap-selector">
288
+
289
+ <?php if ( !$this->has_multiple_date_formats() ) : ?>
290
+ <li>
291
+ <div class="dashicons dashicons-calendar"></div>
292
+ <?php if ( $date_format == 'weekly' ) : ?>
293
+ <?php _ex( 'Weekly', 'Format of a scheduling rule', SAP_TEXTDOMAIN ); ?>
294
+ <?php elseif ( $date_format == 'monthly' ) : ?>
295
+ <?php _ex( 'Monthly', 'Format of a scheduling rule', SAP_TEXTDOMAIN ); ?>
296
+ <?php elseif ( $date_format == 'date' ) : ?>
297
+ <?php _ex( 'Date', 'Format of a scheduling rule', SAP_TEXTDOMAIN ); ?>
298
+ <?php endif; ?>
299
+ </li>
300
+ <?php else : ?>
301
+
302
+ <?php if ( $this->disable_weekdays === false ) : ?>
303
+ <li>
304
+ <div class="dashicons dashicons-calendar"></div>
305
+ <a href="#" data-format="weekly"<?php echo $date_format == 'weekly' ? ' class="selected"' : ''; ?>>
306
+ <?php _ex( 'Weekly', 'Format of a scheduling rule', SAP_TEXTDOMAIN ); ?>
307
+ </a>
308
+ </li>
309
+ <?php endif; ?>
310
+
311
+ <?php if ( $this->disable_weeks === false ) : ?>
312
+ <li>
313
+ <a href="#" data-format="monthly"<?php echo $date_format == 'monthly' ? ' class="selected"' : ''; ?>>
314
+ <?php _ex( 'Monthly', 'Format of a scheduling rule', SAP_TEXTDOMAIN ); ?>
315
+ </a>
316
+ </li>
317
+ <?php endif; ?>
318
+
319
+ <?php if ( $this->disable_date === false ) : ?>
320
+ <li>
321
+ <a href="#" data-format="date"<?php echo $date_format == 'date' ? ' class="selected"' : ''; ?>>
322
+ <?php _ex( 'Date', 'Format of a scheduling rule', SAP_TEXTDOMAIN ); ?>
323
+ </a>
324
+ </li>
325
+ <?php endif; ?>
326
+
327
+ <?php endif; ?>
328
+ </ul>
329
+
330
+ <?php if ( $this->disable_weekdays === false ) : ?>
331
+ <ul class="sap-scheduler-weekdays">
332
+ <li class="label">
333
+ <?php _ex( 'Days of the week', 'Label for selecting days of the week in a scheduling rule', SAP_TEXTDOMAIN ); ?>
334
+ </li>
335
+ <?php
336
+ foreach ( $this->weekdays as $slug => $label ) :
337
+ $input_name = $this->get_input_name() . '[' . $id . '][weekdays][' . esc_attr( $slug ) . ']';
338
+ ?>
339
+ <li>
340
+ &nbsp;<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>
341
+ </li>
342
+ <?php endforeach; ?>
343
+ </ul>
344
+ <?php endif; ?>
345
+
346
+ <?php if ( $this->disable_weeks === false ) : ?>
347
+ <ul class="sap-scheduler-weeks">
348
+ <li class="label">
349
+ <?php _ex( 'Weeks of the month', 'Label for selecting weeks of the month in a scheduling rule', SAP_TEXTDOMAIN ); ?>
350
+ </li>
351
+ <?php
352
+ foreach ( $this->weeks as $slug => $label ) :
353
+ $input_name = $this->get_input_name() . '[' . $id . '][weeks][' . esc_attr( $slug ) . ']';
354
+ ?>
355
+ <li>
356
+ &nbsp;<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>
357
+ </li>
358
+ <?php endforeach; ?>
359
+ </ul>
360
+ <?php endif; ?>
361
+
362
+ <?php if ( $this->disable_date === false ) : ?>
363
+ <div class="sap-scheduler-date-input">
364
+ <label for="<?php echo $this->get_input_name(); ?>[<?php echo $id; ?>][date]">
365
+ <?php _e( 'Date', SAP_TEXTDOMAIN ); ?>
366
+ </label>
367
+ <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']; ?>">
368
+ </div>
369
+ <?php endif; ?>
370
+
371
+ </div>
372
+
373
+ <?php if ( $this->disable_time === false ) : ?>
374
+ <div class="sap-scheduler-time <?php echo $time_format; ?>">
375
+
376
+ <ul class="sap-selector">
377
+ <li>
378
+ <div class="dashicons dashicons-clock"></div>
379
+ <a href="#" data-format="time-slot"<?php echo $time_format == 'time-slot' ? ' class="selected"' : ''; ?>>
380
+ <?php _ex( 'Time', 'Label to select time slot for a scheduling rule', SAP_TEXTDOMAIN ); ?>
381
+ </a>
382
+ </li>
383
+ <li>
384
+ <a href="#" data-format="all-day"<?php echo $time_format == 'all-day' ? ' class="selected"' : ''; ?>>
385
+ <?php _ex( 'All day', 'Label to set a scheduling rule to last all day', SAP_TEXTDOMAIN ); ?>
386
+ </a>
387
+ </li>
388
+ </ul>
389
+
390
+ <div class="sap-scheduler-time-input clearfix">
391
+
392
+ <div class="start">
393
+ <label for="<?php echo $this->get_input_name(); ?>[<?php echo $id; ?>][time][start]">
394
+ <?php _ex( 'Start', 'Label for the starting time of a scheduling rule', SAP_TEXTDOMAIN ); ?>
395
+ </label>
396
+ <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']; ?>">
397
+ </div>
398
+
399
+ <?php if ( $this->disable_end_time === false ) : ?>
400
+ <div class="end">
401
+ <label for="<?php echo $this->get_input_name(); ?>[<?php echo $id; ?>][time][end]">
402
+ <?php _ex( 'End', 'Label for the ending time of a scheduling rule', SAP_TEXTDOMAIN ); ?>
403
+ </label>
404
+ <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']; ?>">
405
+ </div>
406
+ <?php endif; ?>
407
+
408
+ </div>
409
+
410
+ <div class="sap-scheduler-all-day">
411
+ <?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 ); ?>
412
+ </div>
413
+
414
+ </div>
415
+ <?php endif; ?>
416
+
417
+ <div class="sap-scheduler-brief">
418
+ <div class="date">
419
+ <div class="dashicons dashicons-calendar"></div>
420
+ <span class="value"><?php echo $this->get_date_summary( $values ); ?></span>
421
+ </div>
422
+ <?php if ( $this->disable_time === false ) : ?>
423
+ <div class="time">
424
+ <div class="dashicons dashicons-clock"></div>
425
+ <span class="value"><?php echo $this->get_time_summary( $values ); ?></span>
426
+ </div>
427
+ <?php endif; ?>
428
+ </div>
429
+ <div class="sap-scheduler-control">
430
+ <a href="#" class="toggle" title="<?php _e( 'Open and close this rule', SAP_TEXTDOMAIN ); ?>">
431
+ <div class="dashicons dashicons-<?php echo $list ? 'edit' : 'arrow-up-alt2'; ?>"></div>
432
+ <span class="screen-reader-text">
433
+ <?php _e( 'Open and close this rule', SAP_TEXTDOMAIN ); ?>
434
+ </span>
435
+ </a>
436
+ <a href="#" class="delete" title="<?php _e( 'Delete rule', SAP_TEXTDOMAIN ); ?>">
437
+ <div class="dashicons dashicons-dismiss"></div>
438
+ <span class="screen-reader-text">
439
+ <?php _e( 'Delete scheduling rule', SAP_TEXTDOMAIN ); ?>
440
+ </span>
441
+ </a>
442
+ </div>
443
+ </div>
444
+
445
+ <?php
446
+ $output = ob_get_clean();
447
+
448
+ return $output;
449
+ }
450
+
451
+ /**
452
+ * Determine the date format of a rule (weeky/monthly/date)
453
+ * @since 2.0
454
+ */
455
+ public function get_date_format( $values ) {
456
+
457
+ if ( !empty( $values['date'] ) ) {
458
+ return 'date';
459
+ } elseif ( !empty( $values['weeks'] ) ) {
460
+ return 'monthly';
461
+ } elseif ( !empty( $values['weekdays'] ) ) {
462
+ return 'weekly';
463
+ }
464
+
465
+ if ( $this->disable_weekdays === false ) {
466
+ return 'weekly';
467
+ }
468
+ if ( $this->disable_weeks === false ) {
469
+ return 'monthly';
470
+ }
471
+ if ( $this->disable_date === false ) {
472
+ return 'date';
473
+ }
474
+ }
475
+
476
+ /**
477
+ * Determine the time format of a rule (time-slot/all-day)
478
+ * @since 2.0
479
+ */
480
+ public function get_time_format( $values ) {
481
+ if ( empty( $values['time']['start'] ) && empty( $values['time']['end'] ) ) {
482
+ return 'all-day';
483
+ }
484
+
485
+ return 'time-slot';
486
+ }
487
+
488
+ /**
489
+ * Determine if multiple date formats are enabled
490
+ * @since 2.0
491
+ */
492
+ public function has_multiple_date_formats() {
493
+ $i = 0;
494
+ if ( $this->disable_weekdays === false ) {
495
+ $i++;
496
+ }
497
+ if ( $this->disable_weeks === false ) {
498
+ $i++;
499
+ }
500
+ if ( $this->disable_date === false ) {
501
+ $i++;
502
+ }
503
+
504
+ if ( $i > 1 ) {
505
+ return true;
506
+ } else {
507
+ return false;
508
+ }
509
+ }
510
+
511
+ /**
512
+ * Set some default summary strings that can be used when the scheduler
513
+ * rule is shown in brief
514
+ * @since 2.0
515
+ */
516
+ public function set_schedule_summaries() {
517
+
518
+ if ( !empty( $this->schedule_summaries ) ) {
519
+ return;
520
+ }
521
+
522
+ $this->schedule_summaries = array(
523
+ 'never' => _x( 'Never', 'Brief default description of a scheduling rule when no weekdays or weeks are included in the rule.', SAP_TEXTDOMAIN ),
524
+ 'weekly_always' => _x( 'Every day', 'Brief default description of a scheduling rule when all the weekdays/weeks are included in the rule.', SAP_TEXTDOMAIN ),
525
+ '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 ),
526
+ '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 ),
527
+ 'all_day' => _x( 'All day', 'Brief default description of a scheduling rule when no times are set', SAP_TEXTDOMAIN ),
528
+ '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 ),
529
+ '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 ),
530
+ 'separator' => _x( '&mdash;', 'Default separator between times of a scheduling rule.', SAP_TEXTDOMAIN ),
531
+ );
532
+
533
+ if ( !empty( $this->instance_schedule_summaries ) ) {
534
+ $this->schedule_summaries = array_merge( $this->schedule_summaries, $this->instance_schedule_summaries );
535
+ }
536
+ }
537
+
538
+ /**
539
+ * Print the date phrase, a brief description of the date settings
540
+ * @since 2.0
541
+ */
542
+ public function get_date_summary( $values = array() ) {
543
+
544
+ if ( !empty( $values['date'] ) ) {
545
+ return $values['date'];
546
+ }
547
+
548
+ if ( empty( $values['weekdays'] ) && $this->disable_weekdays === false ) {
549
+ return $this->schedule_summaries['never'];
550
+ }
551
+
552
+ if ( empty( $values['weekdays'] ) ) {
553
+ $weekdays = '';
554
+ } elseif ( count( $values['weekdays'] ) == 7 ) {
555
+ $weekdays = $this->schedule_summaries['weekly_always'];
556
+ } else {
557
+ $arr = array();
558
+ foreach ( $values['weekdays'] as $weekday => $state ) {
559
+ $arr[] = $this->weekdays[$weekday];
560
+ }
561
+ $weekdays = join( ', ', $arr );
562
+ }
563
+
564
+ if ( ( empty( $values['weeks'] ) || count( $values['weeks'] ) == 5 ) && $this->disable_weekdays === false ) {
565
+ return $weekdays;
566
+ }
567
+
568
+ if ( empty( $values['weeks'] ) ) {
569
+ return $this->schedule_summaries['never'];
570
+ }
571
+
572
+ $arr = array();
573
+ foreach ( $values['weeks'] as $weeks => $state ) {
574
+ $arr[] = $this->weeks[$weeks];
575
+ }
576
+ $weeks = join( ', ', $arr );
577
+
578
+ if ( !empty( $weekdays ) ) {
579
+ return str_replace( array( '{days}', '{weeks}' ), array( $weekdays, $weeks ), $this->schedule_summaries['monthly_weekdays'] );
580
+ } else {
581
+ return str_replace( '{weeks}', ucfirst( $weeks ), $this->schedule_summaries['monthly_weeks'] );
582
+ }
583
+
584
+ }
585
+
586
+ /**
587
+ * Print the time phrase, a brief description of the time settings
588
+ * @since 2.0
589
+ */
590
+ public function get_time_summary( $values = array() ) {
591
+
592
+ if ( empty( $values['time']['start'] ) && empty( $values['time']['end'] ) ) {
593
+ return $this->schedule_summaries['all_day'];
594
+ }
595
+
596
+ if ( empty( $values['time']['start'] ) ) {
597
+ return $this->schedule_summaries['before'] . ' ' . $values['time']['end'];
598
+ }
599
+
600
+ if ( empty( $values['time']['end'] ) ) {
601
+ return $this->schedule_summaries['after'] . ' ' . $values['time']['start'];
602
+ }
603
+
604
+ return $values['time']['start'] . $this->schedule_summaries['separator'] . $values['time']['end'];
605
+
606
+ }
607
+
608
+ /**
609
+ * Sanitize the array of text inputs for this setting
610
+ * @since 2.0
611
+ */
612
+ public function sanitize_callback_wrapper( $values ) {
613
+
614
+ $output = array();
615
+
616
+ if ( !is_array( $values ) || !count( $values ) ) {
617
+ return $output;
618
+ }
619
+
620
+ foreach ( $values as $i => $rule ) {
621
+
622
+ if ( !empty( $rule['weekdays'] ) ) {
623
+ $output[$i]['weekdays'] = array();
624
+ foreach ( $rule['weekdays'] as $day => $flag ) {
625
+ if ( $flag !== '1' ||
626
+ ( $day !== 'monday' && $day !== 'tuesday' && $day !== 'wednesday' && $day !== 'thursday' && $day !== 'friday' && $day !== 'saturday' && $day !== 'sunday' ) ) {
627
+ continue;
628
+ }
629
+
630
+ $output[$i]['weekdays'][$day] = $flag;
631
+ }
632
+ }
633
+
634
+ if ( !empty( $rule['weeks'] ) ) {
635
+ $output[$i]['weeks'] = array();
636
+ foreach ( $rule['weeks'] as $week => $flag ) {
637
+ if ( $flag !== '1' ||
638
+ ( $week !== 'first' && $week !== 'second' && $week !== 'third' && $week !== 'fourth' && $week !== 'last' ) ) {
639
+ continue;
640
+ }
641
+
642
+ $output[$i]['weeks'][$week] = $flag;
643
+ }
644
+ }
645
+
646
+ if ( !empty( $rule['date'] ) ) {
647
+ $date = new DateTime( $rule['date'] );
648
+ if ( checkdate( $date->format( 'n' ), $date->format( 'j' ), $date->format( 'Y' ) ) ) {
649
+ $output[$i]['date'] = call_user_func( $this->sanitize_callback, $rule['date'] );
650
+ }
651
+ }
652
+
653
+ if ( !empty( $rule['time']['start'] ) ) {
654
+ $output[$i]['time']['start'] = call_user_func( $this->sanitize_callback, $rule['time']['start'] );
655
+ }
656
+ if ( !empty( $rule['time']['end'] ) ) {
657
+ $output[$i]['time']['end'] = call_user_func( $this->sanitize_callback, $rule['time']['end'] );
658
+ }
659
+ }
660
+
661
+ return $output;
662
+ }
663
+
664
+ }
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_5 extends sapAdminPageSetting_2_0_a_5 {
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_5 extends sapAdminPageSetting_2_0_a_5 {
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_5 extends sapAdminPageSetting_2_0_a_5 {
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_5 extends sapAdminPageSetting_2_0_a_5 {
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_5 extends sapAdminPageSetting_2_0_a_5 {
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 = 'wp_kses_post';
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_5 extends sapAdminPageSetting_2_0_a_5 {
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,261 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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_5 {
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
+ * Scripts that must be loaded for this component
41
+ * @since 2.0.a.4
42
+ */
43
+ public $scripts = array(
44
+ /**
45
+ * Example
46
+ * See: http://codex.wordpress.org/Function_Reference/wp_enqueue_script
47
+ *
48
+ 'handle' => array(
49
+ 'path' => 'path/from/simple-admin-pages/file.js',
50
+ 'dependencies' => array( 'jquery' ),
51
+ 'version' => '3.5.0',
52
+ 'footer' => true,
53
+ ),
54
+ */
55
+ );
56
+
57
+ /**
58
+ * Styles that must be loaded for this component
59
+ * @since 2.0.a.4
60
+ */
61
+ public $styles = array(
62
+ /**
63
+ * Example
64
+ * See: http://codex.wordpress.org/Function_Reference/wp_enqueue_style
65
+ *
66
+ 'handle' => array(
67
+ 'path' => 'path/from/simple-admin-pages/file.css',
68
+ 'dependencies' => 'array( 'another-handle')', // or empty string
69
+ 'version' => '3.5.0',
70
+ 'media' => null,
71
+ ),
72
+ */
73
+ );
74
+
75
+ /**
76
+ * Initialize the setting
77
+ *
78
+ * By default, every setting takes an id, title and description in the $args
79
+ * array.
80
+ *
81
+ * @since 1.0
82
+ */
83
+ public function __construct( $args ) {
84
+
85
+ // Parse the values passed
86
+ $this->parse_args( $args );
87
+
88
+ // Get any existing value
89
+ $this->set_value();
90
+
91
+ // Set an error if the object is missing necessary data
92
+ if ( $this->missing_data() ) {
93
+ $this->set_error();
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Parse the arguments passed in the construction and assign them to
99
+ * internal variables. This function will be overwritten for most subclasses
100
+ * @since 1.0
101
+ */
102
+ private function parse_args( $args ) {
103
+ foreach ( $args as $key => $val ) {
104
+ switch ( $key ) {
105
+
106
+ case 'id' :
107
+ $this->{$key} = esc_attr( $val );
108
+
109
+ default :
110
+ $this->{$key} = $val;
111
+
112
+ }
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Check for missing data when setup.
118
+ * @since 1.0
119
+ */
120
+ private function missing_data( ) {
121
+
122
+ $error_type = 'missing_data';
123
+
124
+ // Required fields
125
+ if ( empty( $this->id ) ) {
126
+ $this->set_error(
127
+ array(
128
+ 'type' => $error_type,
129
+ 'data' => 'id'
130
+ )
131
+ );
132
+ }
133
+ if ( empty( $this->title ) ) {
134
+ $this->set_error(
135
+ array(
136
+ 'type' => $error_type,
137
+ 'data' => 'title'
138
+ )
139
+ );
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Set a value
145
+ * @since 2.0
146
+ */
147
+ public function set_value( $val = null ) {
148
+
149
+ if ( $val === null ) {
150
+ $option_group_value = get_option( $this->page );
151
+ $val = isset( $option_group_value[ $this->id ] ) ? $option_group_value[ $this->id ] : '';
152
+ }
153
+
154
+ $this->value = $this->esc_value( $val );
155
+ }
156
+
157
+ /**
158
+ * Escape the value to display it in text fields and other input fields
159
+ *
160
+ * We use esc_attr() here so that the default is quite strict, but other
161
+ * setting types should override this function with the appropriate escape
162
+ * function. See: http://codex.wordpress.org/Data_Validation
163
+ *
164
+ * @since 1.0
165
+ */
166
+ public function esc_value( $val ) {
167
+ return esc_attr( $val );
168
+ }
169
+
170
+ /**
171
+ * Wrapper for the sanitization callback function.
172
+ *
173
+ * This just reduces code duplication for child classes that need a custom
174
+ * callback function.
175
+ * @since 1.0
176
+ */
177
+ public function sanitize_callback_wrapper( $value ) {
178
+ return call_user_func( $this->sanitize_callback, $value );
179
+ }
180
+
181
+ /**
182
+ * Display this setting
183
+ * @since 1.0
184
+ */
185
+ abstract public function display_setting();
186
+
187
+ /**
188
+ * Display a description for this setting
189
+ * @since 1.0
190
+ */
191
+ public function display_description() {
192
+
193
+ if ( !empty( $this->description ) ) {
194
+
195
+ ?>
196
+
197
+ <p class="description"><?php echo $this->description; ?></p>
198
+
199
+ <?php
200
+
201
+ }
202
+ }
203
+
204
+ /**
205
+ * Generate an option input field name, using the grouped schema:
206
+ * "page[option_name]"
207
+ * @since 1.2
208
+ */
209
+ public function get_input_name() {
210
+ return esc_attr( $this->page ) . '[' . esc_attr( $this->id ) . ']';
211
+ }
212
+
213
+ /**
214
+ * Add and register this setting
215
+ *
216
+ * @since 1.0
217
+ */
218
+ public function add_settings_field( $section_id ) {
219
+
220
+ // If no sanitization callback exists, don't register the setting.
221
+ if ( !$this->has_sanitize_callback() ) {
222
+ return;
223
+ }
224
+
225
+ add_settings_field(
226
+ $this->id,
227
+ $this->title,
228
+ array( $this, 'display_setting' ),
229
+ $this->tab,
230
+ $section_id
231
+ );
232
+
233
+ }
234
+
235
+ /**
236
+ * Check if this field has a sanitization callback set
237
+ * @since 1.2
238
+ */
239
+ public function has_sanitize_callback() {
240
+ if ( isset( $this->sanitize_callback ) && trim( $this->sanitize_callback ) ) {
241
+ return true;
242
+ }
243
+
244
+ return false;
245
+ }
246
+
247
+ /**
248
+ * Set an error
249
+ * @since 1.0
250
+ */
251
+ public function set_error( $error ) {
252
+ $this->errors[] = array_merge(
253
+ $error,
254
+ array(
255
+ 'class' => get_class( $this ),
256
+ 'id' => $this->id,
257
+ 'backtrace' => debug_backtrace()
258
+ )
259
+ );
260
+ }
261
+ }
lib/simple-admin-pages/classes/Library.class.php ADDED
@@ -0,0 +1,415 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( !class_exists( 'sapLibrary_2_0_a_5' ) ) {
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_5 {
11
+
12
+ // Version of the library
13
+ private $version = '2.0.a.5';
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
+ case 'address' :
165
+ require_once('AdminPageSetting.Address.class.php');
166
+ return $this->get_versioned_classname( 'sapAdminPageSettingAddress' );
167
+
168
+ default :
169
+
170
+ // Exit early if a custom type is declared without providing the
171
+ // details to find the type class
172
+ if ( ( !is_array( $type ) || !isset( $type['id'] ) ) ||
173
+ ( !isset( $type['class'] ) || !isset( $type['filename'] ) ) ) {
174
+ return false;
175
+ }
176
+
177
+ // Load the custom type file. Look for the file in the library's
178
+ // folder or check the custom library extension path.
179
+ if ( file_exists( $type['filename'] ) ) {
180
+ require_once( $type['filename'] );
181
+ } elseif ( isset( $this->lib_extension_path ) && file_exists( $this->lib_extension_path . $type['filename'] ) ) {
182
+ require_once( $this->lib_extension_path . '/' . $type['filename'] );
183
+ } else {
184
+ return false;
185
+ }
186
+
187
+
188
+ // Check that we've loaded the appropriate class
189
+ if ( !$this->versioned_class_exists( $type['class'] ) ) {
190
+ return false;
191
+ }
192
+
193
+ return $this->get_versioned_classname( $type['class'] );
194
+
195
+ }
196
+
197
+ }
198
+
199
+ /**
200
+ * Initialize a page
201
+ * @since 1.0
202
+ *
203
+ * @todo perform some checks on args to ensure a valid page can be constructed
204
+ */
205
+ public function add_page( $menu_location, $args = array() ) {
206
+
207
+ // default should be 'options'
208
+ $class = $this->get_versioned_classname( 'sapAdminPage' );
209
+
210
+ if ( $menu_location == 'themes' ) {
211
+ $this->load_class( 'sapAdminPageThemes', 'AdminPage.Themes.class.php' );
212
+ $class = $this->get_versioned_classname( 'sapAdminPageThemes' );
213
+ } elseif ( $menu_location == 'menu' ) {
214
+ $this->load_class( 'sapAdminPageMenu', 'AdminPage.Menu.class.php' );
215
+ $class = $this->get_versioned_classname( 'sapAdminPageMenu' );
216
+ } elseif ( $menu_location == 'submenu' ) {
217
+ $this->load_class( 'sapAdminPageSubmenu', 'AdminPage.Submenu.class.php' );
218
+ $class = $this->get_versioned_classname( 'sapAdminPageSubmenu' );
219
+ }
220
+
221
+ if ( class_exists( $class ) ) {
222
+ $this->pages[ $args['id'] ] = new $class( $args );
223
+ }
224
+
225
+ }
226
+
227
+ /**
228
+ * Initialize a section
229
+ * @since 1.0
230
+ *
231
+ * @todo perform some checks on args to ensure a valid section can be constructed
232
+ */
233
+ public function add_section( $page, $args = array() ) {
234
+
235
+ if ( !isset( $this->pages[ $page ] ) ) {
236
+ return false;
237
+ } else {
238
+ $args['page'] = $page;
239
+ }
240
+
241
+ $class = $this->get_versioned_classname( 'sapAdminPageSection' );
242
+ if ( class_exists( $class ) ) {
243
+ $this->pages[ $page ]->add_section( new $class( $args ) );
244
+ }
245
+
246
+ }
247
+
248
+ /**
249
+ * Initialize a setting
250
+ *
251
+ * The type variable can be a string pointing to a pre-defined setting type,
252
+ * or an array consisting of an id, classname and filename which references
253
+ * a custom setting type. @sa get_setting_classname()
254
+ *
255
+ * @since 1.0
256
+ */
257
+ public function add_setting( $page, $section, $type, $args = array() ) {
258
+
259
+ if ( !isset( $this->pages[ $page ] ) || !isset( $this->pages[ $page ]->sections[ $section ] ) ) {
260
+ return false;
261
+ } else {
262
+ $args['page'] = $page;
263
+ $args['tab'] = $this->pages[$page]->sections[ $section ]->get_page_slug();
264
+ }
265
+
266
+ $class = $this->get_setting_classname( $type );
267
+ if ( ( $class && class_exists( $class ) ) && is_subclass_of( $class, $this->get_versioned_classname( 'sapAdminPageSetting' ) ) ) {
268
+ $this->pages[ $page ]->sections[ $section ]->add_setting( new $class( $args ) );
269
+ }
270
+
271
+ }
272
+
273
+ /**
274
+ * Register all page, section and settings content with WordPress
275
+ * @since 1.0
276
+ */
277
+ public function add_admin_menus() {
278
+
279
+ // If the library is run in debug mode, check for any errors in content,
280
+ // print any errors found, and don't add the menu if there are errors
281
+ if ( $this->debug_mode ) {
282
+ $errors = array();
283
+ foreach ( $this->pages as $page ) {
284
+ foreach ( $page->sections as $section ) {
285
+ if ( count( $section->errors ) ) {
286
+ array_merge( $errors, $section->errors );
287
+ }
288
+ foreach ( $section->settings as $setting ) {
289
+ if ( count( $setting->errors ) ) {
290
+ $errors = array_merge( $errors, $setting->errors );
291
+ }
292
+ }
293
+ }
294
+ }
295
+ if ( count( $errors ) ) {
296
+ print_r( $errors );
297
+ return;
298
+ }
299
+ }
300
+
301
+ // Add the action hooks
302
+ foreach ( $this->pages as $id => $page ) {
303
+ add_action( 'admin_menu', array( $page, 'add_admin_menu' ) );
304
+ add_action( 'admin_init', array( $page, 'register_admin_menu' ) );
305
+ }
306
+ }
307
+
308
+ /**
309
+ * Port data from a previous version to the current version
310
+ *
311
+ * Version 2.0 of the library changes the structure of how it stores data.
312
+ * In order to upgrade the version of the library your plugin/theme is
313
+ * using, this method must be called after all of your pages and settings
314
+ * have been declared but before you run add_admin_menus().
315
+ *
316
+ * This method will loop over all of the settings data and port any existing
317
+ * data to the new data structure. It will check if the data has been ported
318
+ * first before it updates the data. The old data will be removed to keep
319
+ * the database clean.
320
+ *
321
+ * @var int target_version Which data version the library should update to.
322
+ * @since 2.0
323
+ */
324
+ public function port_data( $target_version, $delete_old_data = true ) {
325
+
326
+ // Port data to the storage structure in version 2
327
+ if ( $target_version == 2 ) {
328
+
329
+ foreach ( $this->pages as $page_id => $page ) {
330
+
331
+ // Skip if this page has already been ported
332
+ if ( get_option( $page_id ) !== false ) {
333
+ continue;
334
+ }
335
+
336
+ $page_values = array();
337
+
338
+ foreach ( $page->sections as $section ) {
339
+ foreach ( $section->settings as $setting ) {
340
+ $value = get_option( $setting->id );
341
+ if ( $value !== false ) {
342
+ $page_values[ $setting->id ] = $value;
343
+ }
344
+ }
345
+ }
346
+
347
+ if ( count( $page_values ) ) {
348
+ $result = add_option( $page_id, $page_values );
349
+
350
+ // Delete old data if the flag is set and the new data was
351
+ // saved successfully.
352
+ if ( $delete_old_data === true && $result !== false ) {
353
+ foreach( $page_values as $setting_id => $setting_value ) {
354
+ delete_option( $setting_id );
355
+ }
356
+ }
357
+
358
+ // Reset settings values
359
+ if ( $result === true ) {
360
+
361
+ foreach ( $page->sections as $section ) {
362
+ foreach ( $section->settings as $setting ) {
363
+ $setting->set_value();
364
+ }
365
+ }
366
+
367
+ }
368
+ }
369
+ }
370
+ }
371
+
372
+ }
373
+
374
+ /**
375
+ * Enqueue the stylesheets and scripts
376
+ * @since 1.0
377
+ * @todo complex settings should enqueue their assets only when loaded
378
+ */
379
+ public function enqueue_scripts() {
380
+
381
+ // Enqueue assets for specific settings
382
+ foreach ( $this->pages as $page ) {
383
+ foreach ( $page->sections as $section ) {
384
+ foreach ( $section->settings as $setting ) {
385
+ foreach( $setting->scripts as $handle => $script ) {
386
+ wp_enqueue_script( $handle, $this->lib_url . $script['path'], $script['dependencies'], $script['version'], $script['footer'] );
387
+ }
388
+ foreach( $setting->styles as $handle => $style ) {
389
+ wp_enqueue_style( $handle . '-' . $this->version, $this->lib_url . $style['path'], $style['dependencies'], $style['version'], $style['media'] );
390
+ }
391
+ }
392
+ }
393
+ }
394
+
395
+ // Default styles and scripts
396
+ wp_enqueue_style( 'sap-admin-style-' . $this->version, $this->lib_url . 'css/admin.css' );
397
+ }
398
+
399
+ /**
400
+ * Set an error
401
+ * @since 1.0
402
+ */
403
+ public function set_error( $error ) {
404
+ $this->errors[] = array_merge(
405
+ $error,
406
+ array(
407
+ 'class' => get_class( $this ),
408
+ 'id' => $this->id,
409
+ 'backtrace' => debug_backtrace()
410
+ )
411
+ );
412
+ }
413
+
414
+ }
415
+ } // 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: 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 .picker__table {
217
+ table-layout: auto;
218
+ }
219
+ .picker .picker__table th,
220
+ .picker .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/address.js ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Javascript functions for Address component
3
+ *
4
+ * @package Simple Admin Pages
5
+ */
6
+
7
+ jQuery(document).ready(function ($) {
8
+
9
+ /**
10
+ * Set coordinate that have been received
11
+ */
12
+ function sap_address_set_coords( control, lat, lon ) {
13
+ control.find( '.sap-coords-result' ).remove();
14
+ control.find( 'input.lat' ).val( lat );
15
+ control.find( 'input.lon' ).val( lon );
16
+
17
+ if ( lat == '' && lon == '' ) {
18
+ control.find( '.sap-map-coords' ).text( lat + sap_address.strings['no-setting'] + lon ).attr( 'style', '' );
19
+ } else {
20
+ control.find( '.sap-map-coords' ).text( lat + sap_address.strings['sep-lat-lon'] + lon ).attr( 'style', '' );
21
+ }
22
+
23
+ var url = '//maps.google.com/maps?q=' + lat + ',' + lon;
24
+ if ( control.find( '.sap-view-coords' ).length ) {
25
+ control.find( '.sap-view-coords' ).attr( 'href', url );
26
+ } else {
27
+ control.find( '.sap-map-coords-wrapper' ).append( '<a class="sap-view-coords" href="' + url + '" target="_blank">' + sap_address.strings.view + '</a>' );
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Retrieve coordinates
33
+ */
34
+ $('.sap-get-coords').click( function() {
35
+ var control = $(this).parent().parent();
36
+ var address = control.find( 'textarea' ).val();
37
+
38
+ // Reset messages
39
+ control.find( '.sap-coords-result' ).remove();
40
+ control.find( '.error' ).remove();
41
+ control.find( '.sap-view-coords' ).remove();
42
+ control.find( '.sap-map-coords' ).text( sap_address.strings.retrieving ).attr( 'style', 'opacity: 0.3' );
43
+
44
+ // Call Google Maps geocoding API
45
+ // See: https://developers.google.com/maps/documentation/geocoding/
46
+ var req = $.get(
47
+ '//maps.googleapis.com/maps/api/geocode/json',
48
+ { sensor: false, address: address },
49
+ function( data ) {
50
+
51
+ if ( data.status == 'OK' ) {
52
+ if ( data.results.length == 1 ) {
53
+ sap_address_set_coords( control, data.results[0].geometry.location.lat, data.results[0].geometry.location.lng );
54
+
55
+ } else {
56
+ for ( var key in data.results ) {
57
+ control.append( '<p class="sap-coords-result">' + data.results[key].formatted_address + ' <span class="dashicons dashicons-arrow-right"></span> <a href="#" data-lat="' + data.results[key].geometry.location.lat + '" data-lon="' + data.results[key].geometry.location.lng + '">Set</a></p>' );
58
+ }
59
+ control.find( '.sap-map-coords' ).text( sap_address.strings.select ).attr( 'style', '' );
60
+
61
+ control.find( '.sap-coords-result a' ).click( function() {
62
+ sap_address_set_coords( control, $(this).data( 'lat' ), $(this).data( 'lon' ) );
63
+ });
64
+
65
+ }
66
+
67
+ } else {
68
+ sap_address_set_coords( control, control.find( 'input.lat' ).val(), control.find( 'input.lon' ).val() );
69
+
70
+ if ( data.status == 'UNKNOWN_ERROR' ) {
71
+ control.find( '.sap-coords-action-wrapper' ).prepend( '<div class="error">' + sap_address.strings.result_error + '</div>' );
72
+ } else if ( data.status == 'INVALID_REQUEST' ) {
73
+ control.find( '.sap-coords-action-wrapper' ).prepend( '<div class="error">' + sap_address.strings.result_invalid + '</div>' );
74
+ } else if ( data.status == 'INVALID_REQUEST' ) {
75
+ control.find( '.sap-coords-action-wrapper' ).prepend( '<div class="error">' + sap_address.strings.result_error + '</div>' );
76
+ } else if ( data.status == 'REQUEST_DENIED' ) {
77
+ control.find( '.sap-coords-action-wrapper' ).prepend( '<div class="error">' + sap_address.strings.result_denied + '</div>' );
78
+ } else if ( data.status == 'OVER_QUERY_LIMIT' ) {
79
+ control.find( '.sap-coords-action-wrapper' ).prepend( '<div class="error">' + sap_address.strings.result_limit + '</div>' );
80
+ } else if ( data.status == 'ZERO_RESULTS' ) {
81
+ control.find( '.sap-coords-action-wrapper' ).prepend( '<div class="error">' + sap_address.strings.result_empty + '</div>' );
82
+ }
83
+ }
84
+ }
85
+ )
86
+ });
87
+
88
+ /**
89
+ * Remove coordinates from settings
90
+ */
91
+ $('.sap-remove-coords').click( function() {
92
+ var control = $(this).parent().parent();
93
+ sap_address_set_coords( control, '', '' );
94
+ });
95
+
96
+ });
lib/simple-admin-pages/js/opening-hours.js ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Javascript functions for Opening Hours component
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
+ });
lib/simple-admin-pages/js/scheduler.js ADDED
@@ -0,0 +1,329 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Javascript functions for the Scheduler component
3
+ *
4
+ * @package Simple Admin Pages
5
+ */
6
+
7
+ jQuery(document).ready(function ($) {
8
+
9
+ /**
10
+ * Scheduler
11
+ ***********/
12
+
13
+ if ( typeof sap_scheduler != 'undefined' ) {
14
+
15
+ /**
16
+ * Register event handlers on the scheduler rules. This is run on page load
17
+ * and every time a rule is added.
18
+ */
19
+ function sap_scheduler_register_events() {
20
+
21
+ /**
22
+ * Open and close the full view of a scheduling rule
23
+ */
24
+ $( '.sap-scheduler-rule .toggle' ).off( 'click' ).click( function() {
25
+
26
+ var controls = $(this).parent();
27
+
28
+ if ( $(this).parent().parent().hasClass( 'list' ) ) {
29
+ controls.hide();
30
+ $(this).children( '.dashicons-edit' ).removeClass( 'dashicons-edit' ).addClass( 'dashicons-arrow-up-alt2' );
31
+ controls.siblings( '.sap-scheduler-brief' ).hide();
32
+ controls.siblings( '.sap-scheduler-date, .sap-scheduler-time' ).slideDown( function() {
33
+ $(this).parent().removeClass( 'list' );
34
+ controls.fadeIn();
35
+ });
36
+
37
+ } else {
38
+ controls.hide();
39
+ $(this).children( '.dashicons-arrow-up-alt2' ).removeClass( 'dashicons-arrow-up-alt2' ).addClass( 'dashicons-edit' );
40
+ controls.siblings( '.sap-scheduler-brief' ).fadeIn();
41
+ controls.siblings( '.sap-scheduler-time' ).slideUp();
42
+ controls.siblings( '.sap-scheduler-date' ).slideUp( function() {
43
+
44
+ var scheduler_rule = $(this).parent();
45
+ var scheduler_id = scheduler_rule.parent().attr( 'id' );
46
+
47
+ scheduler_rule.addClass( 'list' );
48
+ controls.fadeIn();
49
+
50
+ sap_scheduler_set_date_phrase( scheduler_rule, scheduler_id );
51
+ sap_scheduler_set_time_phrase( scheduler_rule, scheduler_id );
52
+ });
53
+ }
54
+
55
+ return false;
56
+ });
57
+
58
+ /**
59
+ * Update current selection for selector lists
60
+ */
61
+ $( '.sap-selector a' ).off( 'switch.sap' ).on( 'switch.sap', function() {
62
+ $(this).parent().parent().find( 'a' ).removeClass( 'selected' );
63
+ $(this).addClass( 'selected' );
64
+
65
+ return false;
66
+ });
67
+
68
+ /**
69
+ * Switch between weekly, monthly and date options
70
+ */
71
+ $( '.sap-scheduler-date .sap-selector a' ).off( 'click' ).click( function() {
72
+
73
+ $(this).trigger( 'switch.sap' );
74
+
75
+ var date = $(this).closest( '.sap-scheduler-date' );
76
+
77
+ if ( $(this).data( 'format' ) == 'weekly' && date.hasClass( 'weekly' ) === false ) {
78
+ date.children( '.sap-scheduler-weeks' ).slideUp( function() {
79
+ $(this).find( 'input' ).prop('checked', false);
80
+ });
81
+ date.children( '.sap-scheduler-date-input' ).slideUp( function() {
82
+ $(this).find( 'input' ).val( '' );
83
+ });
84
+ date.children( '.sap-scheduler-weekdays' ).slideDown( function() {
85
+ date.removeClass( 'monthly date' );
86
+ date.addClass( 'weekly' );
87
+ });
88
+
89
+ } else if ( $(this).data( 'format' ) == 'monthly' && date.hasClass( 'monthly' ) === false ) {
90
+ date.children( '.sap-scheduler-date-input' ).slideUp( function() {
91
+ $(this).find( 'input' ).val( '' );
92
+ });
93
+ date.children( '.sap-scheduler-weekdays' ).slideDown();
94
+ date.children( '.sap-scheduler-weeks' ).slideDown( function() {
95
+ date.removeClass( 'weekly date' );
96
+ date.addClass( 'monthly' );
97
+ });
98
+
99
+ } else if ( $(this).data( 'format' ) == 'date' && date.hasClass( 'date' ) === false ) {
100
+ date.children( '.sap-scheduler-weekdays' ).slideUp( function() {
101
+ $(this).find( 'input' ).prop('checked', false);
102
+ });
103
+ date.children( '.sap-scheduler-weeks' ).slideUp( function() {
104
+ $(this).find( 'input' ).prop('checked', false);
105
+ });
106
+ date.children( '.sap-scheduler-date-input' ).slideDown( function() {
107
+ date.removeClass( 'weekly monthly' );
108
+ date.addClass( 'date' );
109
+ });
110
+ }
111
+
112
+ return false;
113
+ });
114
+
115
+ /**
116
+ * Show or hide time slot options
117
+ */
118
+ $( '.sap-scheduler-time .sap-selector a' ).off( 'click' ).click( function() {
119
+
120
+ $(this).trigger( 'switch.sap' );
121
+
122
+ var time = $(this).closest( '.sap-scheduler-time' );
123
+
124
+ if ( $(this).data( 'format' ) == 'time-slot' && time.hasClass( 'time-slot' ) === false ) {
125
+ time.children( '.sap-scheduler-time-input' ).slideDown();
126
+ time.children( '.sap-scheduler-all-day' ).slideUp( function() {
127
+ time.removeClass( 'all-day' );
128
+ time.addClass( 'time-slot' );
129
+ });
130
+
131
+ } else if ( $(this).data( 'format' ) == 'all-day' && time.hasClass( 'all-day' ) === false ) {
132
+ time.children( '.sap-scheduler-all-day' ).slideDown();
133
+ time.children( '.sap-scheduler-time-input' ).slideUp( function() {
134
+ time.removeClass( 'time-slot' );
135
+ time.addClass( 'all-day' );
136
+ time.find( 'input' ).val( '' );
137
+ });
138
+ }
139
+
140
+ return false;
141
+ });
142
+
143
+ /**
144
+ * Show time slot options from the link in the all-day notice
145
+ */
146
+ $( '.sap-scheduler-all-day a' ).off( 'click' ).click( function() {
147
+ $(this).closest( '.sap-scheduler-time' ).children( '.sap-selector' ).find( 'a[data-format="time-slot"]' ).trigger( 'click' );
148
+
149
+ return false;
150
+ });
151
+
152
+ /**
153
+ * Delete a scheduling rule panel
154
+ */
155
+ $( '.sap-scheduler-control .delete' ).off( 'click' ).click( function() {
156
+ var scheduler = $(this).closest( '.sap-scheduler' );
157
+ $(this).parent().parent().fadeOut( function() {
158
+ $(this).remove();
159
+
160
+ // Reset the index of each rule
161
+ // @todo optimize this excessive use of regex (32x per rule).
162
+ // maybe set a data-slug attribute on .sap-scheduler-rule and a
163
+ // data-slug prop on each input/select/label, then use these to
164
+ // construct the new attributes: rule-slug[index][input-slug]
165
+ scheduler.children( '.sap-scheduler-rule' ).each( function( i ) {
166
+ var index = i.toString();
167
+ $(this).find( 'input' ).each( function() {
168
+ var name = $(this).attr( 'name' ).replace( /\[\d*\]/g, '[' + index + ']' );
169
+ $(this).attr( 'name', name );
170
+ $(this).attr( 'id', name );
171
+ var aria_owns = $(this).attr( 'aria-owns' );
172
+ if ( typeof aria_owns !== 'undefined' && aria_owns !== false) {
173
+ $(this).attr( 'aria-owns', name );
174
+ }
175
+ });
176
+ $(this).find( 'label' ).each( function() {
177
+ var name = $(this).attr( 'for' ).replace( /\[\d*\]/g, '[' + index + ']' );
178
+ $(this).attr( 'for', name );
179
+ });
180
+ $(this).find( '.picker' ).each( function() {
181
+ var name = $(this).attr( 'id' ).replace( /\[\d*\]/g, '[' + index + ']' );
182
+ $(this).attr( 'id', name );
183
+ });
184
+ });
185
+ });
186
+
187
+ return false;
188
+ });
189
+ }
190
+
191
+ /**
192
+ * Set the summary phrase for a scheduler rule's date. This phrase is shown
193
+ * when the rule's view is minimized.
194
+ */
195
+ function sap_scheduler_set_date_phrase( scheduler_rule, scheduler_id ) {
196
+
197
+ var date_value = scheduler_rule.find( '.sap-scheduler-date-input input' ).val();
198
+ if ( typeof date_value !== 'undefined' && date_value != '' ) {
199
+ scheduler_rule.find( '.sap-scheduler-brief .date .value' ).html( date_value );
200
+
201
+ return;
202
+ }
203
+
204
+ var weekdays = 0;
205
+ var weekday_arr = new Array();
206
+ scheduler_rule.find( '.sap-scheduler-weekdays input' ).each( function() {
207
+ if ( $(this).prop( 'checked' ) !== false ) {
208
+ weekdays += 1;
209
+ weekday_arr.push( sap_scheduler.settings[scheduler_id]['weekdays'][ $(this).data( 'day' ) ] );
210
+ }
211
+ });
212
+
213
+ if ( weekdays == 0 && sap_scheduler.settings[ scheduler_id ].disable_weekdays === false ) {
214
+ scheduler_rule.find( '.sap-scheduler-brief .date .value' ).html( sap_scheduler.settings[scheduler_id].summaries['never'] );
215
+
216
+ return;
217
+
218
+ } else if ( weekdays == 7 ) {
219
+ var weekday_string = sap_scheduler.settings[scheduler_id].summaries['weekly_always'];
220
+
221
+ } else {
222
+ var weekday_string = weekday_arr.join( ', ' );
223
+ }
224
+
225
+ var weeks = 0;
226
+ var weeks_arr = new Array();
227
+ scheduler_rule.find( '.sap-scheduler-weeks input' ).each( function() {
228
+ if ( $(this).prop( 'checked' ) !== false ) {
229
+ weeks +=1;
230
+ weeks_arr.push( sap_scheduler.settings[scheduler_id]['weeks'][ $(this).data( 'week' ) ] );
231
+ }
232
+ });
233
+
234
+ if ( ( weeks == 0 || weeks == 5 ) && sap_scheduler.settings[ scheduler_id ].disable_weekdays === false ) {
235
+ scheduler_rule.find( '.sap-scheduler-brief .date .value' ).html( weekday_string );
236
+
237
+ return;
238
+ }
239
+
240
+ if ( weeks == 0 ) {
241
+ scheduler_rule.find( '.sap-scheduler-brief .date .value' ).html( sap_scheduler.settings[scheduler_id].summaries['never'] );
242
+
243
+ return;
244
+ }
245
+
246
+ if ( weekday_string != '' ) {
247
+ 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( ', ' ) ) );
248
+ } else {
249
+ scheduler_rule.find( '.sap-scheduler-brief .date .value' ).html( sap_scheduler.settings[scheduler_id].summaries['monthly_weeks'].replace( '{weeks}', weeks_arr.join( ', ' ) ) );
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Set the summary phrase for a scheduler rule's time. This phrase is shown
255
+ * when the rule's view is minimized.
256
+ */
257
+ function sap_scheduler_set_time_phrase( scheduler_rule, scheduler_id ) {
258
+
259
+ var start = scheduler_rule.find( '.sap-scheduler-time-input .start input' ).val();
260
+ var end = scheduler_rule.find( '.sap-scheduler-time-input .end input' ).val();
261
+
262
+ if ( start == '' && ( end == '' || typeof end == 'undefined' ) ) {
263
+ scheduler_rule.find( '.sap-scheduler-brief .time .value' ).html( sap_scheduler.settings[scheduler_id].summaries['all_day'] );
264
+
265
+ return;
266
+ }
267
+
268
+ if ( start == '' ) {
269
+ scheduler_rule.find( '.sap-scheduler-brief .time .value' ).html( sap_scheduler.settings[scheduler_id].summaries['before'] + ' ' + end );
270
+
271
+ return;
272
+ }
273
+
274
+ if ( end == '' || typeof end == 'undefined' ) {
275
+ scheduler_rule.find( '.sap-scheduler-brief .time .value' ).html( sap_scheduler.settings[scheduler_id].summaries['after'] + ' ' + start );
276
+
277
+ return;
278
+ }
279
+
280
+ if ( typeof end == 'undefined' ) {
281
+ return scheduler_rule.find( '.sap-scheduler-brief .time .value' ).html( start );
282
+ } else {
283
+ return scheduler_rule.find( '.sap-scheduler-brief .time .value' ).html( start + sap_scheduler.settings[scheduler_id].summaries['separator'] + end );
284
+ }
285
+ }
286
+
287
+ /**
288
+ * Register click events on load
289
+ */
290
+ sap_scheduler_register_events();
291
+
292
+ /**
293
+ * Enable datepickers on load
294
+ */
295
+ if ( typeof sap_scheduler.settings != 'undefined' ) {
296
+ for ( var key in sap_scheduler.settings ) {
297
+ var obj = sap_scheduler.settings[key];
298
+ $( '#' + key + ' .sap-scheduler-date-input input' ).pickadate({
299
+ format: obj.date_format,
300
+ });
301
+ $( '#' + key + ' .sap-scheduler-time-input input' ).pickatime({
302
+ interval: obj.time_interval,
303
+ format: obj.time_format,
304
+ });
305
+ }
306
+ }
307
+
308
+ /**
309
+ * Add a new scheduler panel
310
+ */
311
+ $( '.sap-add-scheduler a' ).click( function() {
312
+ var scheduler = $(this).parent().siblings( '.sap-scheduler' );
313
+ var scheduler_id = scheduler.attr( 'id' );
314
+ var scheduler_settings = sap_scheduler.settings[ scheduler_id ];
315
+ scheduler.append( scheduler_settings.template.replace( /\[0\]/g, '[' + scheduler.children( '.sap-scheduler-rule' ).length + ']' ) );
316
+ scheduler.last( '#' + scheduler_id + ' .sap-scheduler-rule' ).find( '.sap-scheduler-date-input input' ).pickadate({
317
+ format: scheduler_settings.date_format,
318
+ });
319
+ scheduler.last( '#' + scheduler_id + ' .sap-scheduler-rule' ).find( '.sap-scheduler-time-input input' ).pickatime({
320
+ interval: scheduler_settings.time_interval,
321
+ format: scheduler_settings.time_format,
322
+ });
323
+ sap_scheduler_register_events();
324
+
325
+ return false;
326
+ });
327
+ }
328
+
329
+ });
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,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === Business Profile ===
2
+ Contributors: NateWr
3
+ Author URI: https://github.com/NateWr
4
+ Plugin URL: http://themeofthecrop.com
5
+ Requires at Least: 3.9
6
+ Tested Up To: 3.9.1
7
+ Tags: business profile, address, google map, schema, contact, phone, address, seo
8
+ Stable tag: 1.0
9
+ License: GPLv2 or later
10
+ Donate link: http://themeofthecrop.com
11
+
12
+ Display your business's contact details with seo-friendly Schema.org markup. Supports a Google Map, opening hours and more.
13
+
14
+ == Description ==
15
+
16
+ Add your business contact details to your site with seo-friendly Schema.org markup. This plugin adds a Contact Card widget and a `[contact-card]` shortcode. You can use these to display the following on any page:
17
+
18
+ * Business name
19
+ * Address
20
+ * Phone number
21
+ * Contact page link or email address
22
+ * Link to Google Map with directions to your establishment
23
+ * Google Map showing your location
24
+ * Opening hours
25
+
26
+ Schema.org markup helps search engines like Google discover your address, phone number and opening hours so that they can display them with your listing on Google.
27
+
28
+ **Sorry, it does not support multiple locations.**
29
+
30
+ This plugin is part of a group of plugins in development for restaurants. Check out the [Food and Drink Menu](http://wordpress.org/plugins/food-and-drink-menu/) and [Restaurant Reservations](http://wordpress.org/plugins/restaurant-reservations/) plugins as well.
31
+
32
+ = How to use =
33
+
34
+ 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.
35
+
36
+ = Developers =
37
+
38
+ This plugin is packed with hooks so you can extend it as needed. Development takes place on [GitHub](https://github.com/NateWr/business-profile/), so fork it up.
39
+
40
+ == Installation ==
41
+
42
+ 1. Unzip `business-profile.zip`
43
+ 2. Upload the contents of `business-profile.zip` to the `/wp-content/plugins/` directory
44
+ 3. Activate the plugin through the 'Plugins' menu in WordPress
45
+ 4. Go to the Business Profile page in your admin menu. You will find it near the bottom.
46
+
47
+ == Screenshots ==
48
+
49
+ 1. Display a full contact card on the front-end with the shortcode [contact-card] or use the widget to add it to a sidebar.
50
+ 2. An easy-to-use form lets you add all of the information, locate the correct map coordinates and set up your opening hours.
51
+ 3. Choose what information to display with the widget, or check out the shortcode attributes in the help document included.
52
+
53
+ == Changelog ==
54
+
55
+ = 1.0 (2014-07-16) =
56
+ * Initial public release on WordPress.org
57
+ * Add an option to display a link to a booking form if the Restaurant Reservations plugin is active
58
+ * Fix: skip a scheduling rule if no weekdays are set. h/t @jasonhobbsllc
59
+
60
+ = 0.0.1 (2014-05-26) =
61
+ * Initial release
62
+
63
+ == Upgrade Notice ==
64
+
65
+ = 1.0 =
66
+ This initial public release adds an integration with the Restaurant Reservations plugin. If the plugin is active it will now let you display a link to the booking form.
screenshot-1.png ADDED
Binary file
screenshot-2.png ADDED
Binary file
screenshot-3.png ADDED
Binary file