Version Description
- Update: Custom permalink structure option integration for WordPress 6.1+.
Download this release
Release Info
Developer | Archetyped |
Plugin | Cornerstone |
Version | 0.8.0 |
Comparing to | |
See all releases |
Code changes from version 0.7.8 to 0.8.0
- COPYING +339 -339
- Gruntfile.js +0 -49
- README.md +12 -12
- controller.php +235 -235
- functions.php +255 -255
- grunt/jshint.js +0 -38
- grunt/phplint.js +0 -14
- grunt/sass.js +0 -35
- grunt/uglify.js +0 -21
- grunt/watch.js +0 -57
- includes/class.content_base.php +614 -614
- includes/class.content_type.php +401 -401
- includes/class.content_utilities.php +1247 -1247
- includes/class.field.php +3 -3
- includes/class.field_type.php +697 -697
- includes/class.post.php +247 -247
- includes/class.post_query.php +438 -438
- js/lib.structure.admin.js +98 -24
- load.php +64 -64
- main.php +3 -3
- package.json +20 -20
- readme.txt +141 -137
COPYING
CHANGED
@@ -1,339 +1,339 @@
|
|
1 |
-
GNU GENERAL PUBLIC LICENSE
|
2 |
-
Version 2, June 1991
|
3 |
-
|
4 |
-
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
5 |
-
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
6 |
-
Everyone is permitted to copy and distribute verbatim copies
|
7 |
-
of this license document, but changing it is not allowed.
|
8 |
-
|
9 |
-
Preamble
|
10 |
-
|
11 |
-
The licenses for most software are designed to take away your
|
12 |
-
freedom to share and change it. By contrast, the GNU General Public
|
13 |
-
License is intended to guarantee your freedom to share and change free
|
14 |
-
software--to make sure the software is free for all its users. This
|
15 |
-
General Public License applies to most of the Free Software
|
16 |
-
Foundation's software and to any other program whose authors commit to
|
17 |
-
using it. (Some other Free Software Foundation software is covered by
|
18 |
-
the GNU Lesser General Public License instead.) You can apply it to
|
19 |
-
your programs, too.
|
20 |
-
|
21 |
-
When we speak of free software, we are referring to freedom, not
|
22 |
-
price. Our General Public Licenses are designed to make sure that you
|
23 |
-
have the freedom to distribute copies of free software (and charge for
|
24 |
-
this service if you wish), that you receive source code or can get it
|
25 |
-
if you want it, that you can change the software or use pieces of it
|
26 |
-
in new free programs; and that you know you can do these things.
|
27 |
-
|
28 |
-
To protect your rights, we need to make restrictions that forbid
|
29 |
-
anyone to deny you these rights or to ask you to surrender the rights.
|
30 |
-
These restrictions translate to certain responsibilities for you if you
|
31 |
-
distribute copies of the software, or if you modify it.
|
32 |
-
|
33 |
-
For example, if you distribute copies of such a program, whether
|
34 |
-
gratis or for a fee, you must give the recipients all the rights that
|
35 |
-
you have. You must make sure that they, too, receive or can get the
|
36 |
-
source code. And you must show them these terms so they know their
|
37 |
-
rights.
|
38 |
-
|
39 |
-
We protect your rights with two steps: (1) copyright the software, and
|
40 |
-
(2) offer you this license which gives you legal permission to copy,
|
41 |
-
distribute and/or modify the software.
|
42 |
-
|
43 |
-
Also, for each author's protection and ours, we want to make certain
|
44 |
-
that everyone understands that there is no warranty for this free
|
45 |
-
software. If the software is modified by someone else and passed on, we
|
46 |
-
want its recipients to know that what they have is not the original, so
|
47 |
-
that any problems introduced by others will not reflect on the original
|
48 |
-
authors' reputations.
|
49 |
-
|
50 |
-
Finally, any free program is threatened constantly by software
|
51 |
-
patents. We wish to avoid the danger that redistributors of a free
|
52 |
-
program will individually obtain patent licenses, in effect making the
|
53 |
-
program proprietary. To prevent this, we have made it clear that any
|
54 |
-
patent must be licensed for everyone's free use or not licensed at all.
|
55 |
-
|
56 |
-
The precise terms and conditions for copying, distribution and
|
57 |
-
modification follow.
|
58 |
-
|
59 |
-
GNU GENERAL PUBLIC LICENSE
|
60 |
-
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
61 |
-
|
62 |
-
0. This License applies to any program or other work which contains
|
63 |
-
a notice placed by the copyright holder saying it may be distributed
|
64 |
-
under the terms of this General Public License. The "Program", below,
|
65 |
-
refers to any such program or work, and a "work based on the Program"
|
66 |
-
means either the Program or any derivative work under copyright law:
|
67 |
-
that is to say, a work containing the Program or a portion of it,
|
68 |
-
either verbatim or with modifications and/or translated into another
|
69 |
-
language. (Hereinafter, translation is included without limitation in
|
70 |
-
the term "modification".) Each licensee is addressed as "you".
|
71 |
-
|
72 |
-
Activities other than copying, distribution and modification are not
|
73 |
-
covered by this License; they are outside its scope. The act of
|
74 |
-
running the Program is not restricted, and the output from the Program
|
75 |
-
is covered only if its contents constitute a work based on the
|
76 |
-
Program (independent of having been made by running the Program).
|
77 |
-
Whether that is true depends on what the Program does.
|
78 |
-
|
79 |
-
1. You may copy and distribute verbatim copies of the Program's
|
80 |
-
source code as you receive it, in any medium, provided that you
|
81 |
-
conspicuously and appropriately publish on each copy an appropriate
|
82 |
-
copyright notice and disclaimer of warranty; keep intact all the
|
83 |
-
notices that refer to this License and to the absence of any warranty;
|
84 |
-
and give any other recipients of the Program a copy of this License
|
85 |
-
along with the Program.
|
86 |
-
|
87 |
-
You may charge a fee for the physical act of transferring a copy, and
|
88 |
-
you may at your option offer warranty protection in exchange for a fee.
|
89 |
-
|
90 |
-
2. You may modify your copy or copies of the Program or any portion
|
91 |
-
of it, thus forming a work based on the Program, and copy and
|
92 |
-
distribute such modifications or work under the terms of Section 1
|
93 |
-
above, provided that you also meet all of these conditions:
|
94 |
-
|
95 |
-
a) You must cause the modified files to carry prominent notices
|
96 |
-
stating that you changed the files and the date of any change.
|
97 |
-
|
98 |
-
b) You must cause any work that you distribute or publish, that in
|
99 |
-
whole or in part contains or is derived from the Program or any
|
100 |
-
part thereof, to be licensed as a whole at no charge to all third
|
101 |
-
parties under the terms of this License.
|
102 |
-
|
103 |
-
c) If the modified program normally reads commands interactively
|
104 |
-
when run, you must cause it, when started running for such
|
105 |
-
interactive use in the most ordinary way, to print or display an
|
106 |
-
announcement including an appropriate copyright notice and a
|
107 |
-
notice that there is no warranty (or else, saying that you provide
|
108 |
-
a warranty) and that users may redistribute the program under
|
109 |
-
these conditions, and telling the user how to view a copy of this
|
110 |
-
License. (Exception: if the Program itself is interactive but
|
111 |
-
does not normally print such an announcement, your work based on
|
112 |
-
the Program is not required to print an announcement.)
|
113 |
-
|
114 |
-
These requirements apply to the modified work as a whole. If
|
115 |
-
identifiable sections of that work are not derived from the Program,
|
116 |
-
and can be reasonably considered independent and separate works in
|
117 |
-
themselves, then this License, and its terms, do not apply to those
|
118 |
-
sections when you distribute them as separate works. But when you
|
119 |
-
distribute the same sections as part of a whole which is a work based
|
120 |
-
on the Program, the distribution of the whole must be on the terms of
|
121 |
-
this License, whose permissions for other licensees extend to the
|
122 |
-
entire whole, and thus to each and every part regardless of who wrote it.
|
123 |
-
|
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
|
281 |
-
|
282 |
-
How to Apply These Terms to Your New Programs
|
283 |
-
|
284 |
-
If you develop a new program, and you want it to be of the greatest
|
285 |
-
possible use to the public, the best way to achieve this is to make it
|
286 |
-
free software which everyone can redistribute and change under these terms.
|
287 |
-
|
288 |
-
To do so, attach the following notices to the program. It is safest
|
289 |
-
to attach them to the start of each source file to most effectively
|
290 |
-
convey the exclusion of warranty; and each file should have at least
|
291 |
-
the "copyright" line and a pointer to where the full notice is found.
|
292 |
-
|
293 |
-
<one line to give the program's name and a brief idea of what it does.>
|
294 |
-
Copyright (C) <year> <name of author>
|
295 |
-
|
296 |
-
This program is free software; you can redistribute it and/or modify
|
297 |
-
it under the terms of the GNU General Public License as published by
|
298 |
-
the Free Software Foundation; either version 2 of the License, or
|
299 |
-
(at your option) any later version.
|
300 |
-
|
301 |
-
This program is distributed in the hope that it will be useful,
|
302 |
-
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
303 |
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
304 |
-
GNU General Public License for more details.
|
305 |
-
|
306 |
-
You should have received a copy of the GNU General Public License along
|
307 |
-
with this program; if not, write to the Free Software Foundation, Inc.,
|
308 |
-
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
309 |
-
|
310 |
-
Also add information on how to contact you by electronic and paper mail.
|
311 |
-
|
312 |
-
If the program is interactive, make it output a short notice like this
|
313 |
-
when it starts in an interactive mode:
|
314 |
-
|
315 |
-
Gnomovision version 69, Copyright (C) year name of author
|
316 |
-
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
317 |
-
This is free software, and you are welcome to redistribute it
|
318 |
-
under certain conditions; type `show c' for details.
|
319 |
-
|
320 |
-
The hypothetical commands `show w' and `show c' should show the appropriate
|
321 |
-
parts of the General Public License. Of course, the commands you use may
|
322 |
-
be called something other than `show w' and `show c'; they could even be
|
323 |
-
mouse-clicks or menu items--whatever suits your program.
|
324 |
-
|
325 |
-
You should also get your employer (if you work as a programmer) or your
|
326 |
-
school, if any, to sign a "copyright disclaimer" for the program, if
|
327 |
-
necessary. Here is a sample; alter the names:
|
328 |
-
|
329 |
-
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
330 |
-
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
331 |
-
|
332 |
-
<signature of Ty Coon>, 1 April 1989
|
333 |
-
Ty Coon, President of Vice
|
334 |
-
|
335 |
-
This General Public License does not permit incorporating your program into
|
336 |
-
proprietary programs. If your program is a subroutine library, you may
|
337 |
-
consider it more useful to permit linking proprietary applications with the
|
338 |
-
library. If this is what you want to do, use the GNU Lesser General
|
339 |
-
Public License instead of this License.
|
1 |
+
GNU GENERAL PUBLIC LICENSE
|
2 |
+
Version 2, June 1991
|
3 |
+
|
4 |
+
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
5 |
+
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
6 |
+
Everyone is permitted to copy and distribute verbatim copies
|
7 |
+
of this license document, but changing it is not allowed.
|
8 |
+
|
9 |
+
Preamble
|
10 |
+
|
11 |
+
The licenses for most software are designed to take away your
|
12 |
+
freedom to share and change it. By contrast, the GNU General Public
|
13 |
+
License is intended to guarantee your freedom to share and change free
|
14 |
+
software--to make sure the software is free for all its users. This
|
15 |
+
General Public License applies to most of the Free Software
|
16 |
+
Foundation's software and to any other program whose authors commit to
|
17 |
+
using it. (Some other Free Software Foundation software is covered by
|
18 |
+
the GNU Lesser General Public License instead.) You can apply it to
|
19 |
+
your programs, too.
|
20 |
+
|
21 |
+
When we speak of free software, we are referring to freedom, not
|
22 |
+
price. Our General Public Licenses are designed to make sure that you
|
23 |
+
have the freedom to distribute copies of free software (and charge for
|
24 |
+
this service if you wish), that you receive source code or can get it
|
25 |
+
if you want it, that you can change the software or use pieces of it
|
26 |
+
in new free programs; and that you know you can do these things.
|
27 |
+
|
28 |
+
To protect your rights, we need to make restrictions that forbid
|
29 |
+
anyone to deny you these rights or to ask you to surrender the rights.
|
30 |
+
These restrictions translate to certain responsibilities for you if you
|
31 |
+
distribute copies of the software, or if you modify it.
|
32 |
+
|
33 |
+
For example, if you distribute copies of such a program, whether
|
34 |
+
gratis or for a fee, you must give the recipients all the rights that
|
35 |
+
you have. You must make sure that they, too, receive or can get the
|
36 |
+
source code. And you must show them these terms so they know their
|
37 |
+
rights.
|
38 |
+
|
39 |
+
We protect your rights with two steps: (1) copyright the software, and
|
40 |
+
(2) offer you this license which gives you legal permission to copy,
|
41 |
+
distribute and/or modify the software.
|
42 |
+
|
43 |
+
Also, for each author's protection and ours, we want to make certain
|
44 |
+
that everyone understands that there is no warranty for this free
|
45 |
+
software. If the software is modified by someone else and passed on, we
|
46 |
+
want its recipients to know that what they have is not the original, so
|
47 |
+
that any problems introduced by others will not reflect on the original
|
48 |
+
authors' reputations.
|
49 |
+
|
50 |
+
Finally, any free program is threatened constantly by software
|
51 |
+
patents. We wish to avoid the danger that redistributors of a free
|
52 |
+
program will individually obtain patent licenses, in effect making the
|
53 |
+
program proprietary. To prevent this, we have made it clear that any
|
54 |
+
patent must be licensed for everyone's free use or not licensed at all.
|
55 |
+
|
56 |
+
The precise terms and conditions for copying, distribution and
|
57 |
+
modification follow.
|
58 |
+
|
59 |
+
GNU GENERAL PUBLIC LICENSE
|
60 |
+
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
61 |
+
|
62 |
+
0. This License applies to any program or other work which contains
|
63 |
+
a notice placed by the copyright holder saying it may be distributed
|
64 |
+
under the terms of this General Public License. The "Program", below,
|
65 |
+
refers to any such program or work, and a "work based on the Program"
|
66 |
+
means either the Program or any derivative work under copyright law:
|
67 |
+
that is to say, a work containing the Program or a portion of it,
|
68 |
+
either verbatim or with modifications and/or translated into another
|
69 |
+
language. (Hereinafter, translation is included without limitation in
|
70 |
+
the term "modification".) Each licensee is addressed as "you".
|
71 |
+
|
72 |
+
Activities other than copying, distribution and modification are not
|
73 |
+
covered by this License; they are outside its scope. The act of
|
74 |
+
running the Program is not restricted, and the output from the Program
|
75 |
+
is covered only if its contents constitute a work based on the
|
76 |
+
Program (independent of having been made by running the Program).
|
77 |
+
Whether that is true depends on what the Program does.
|
78 |
+
|
79 |
+
1. You may copy and distribute verbatim copies of the Program's
|
80 |
+
source code as you receive it, in any medium, provided that you
|
81 |
+
conspicuously and appropriately publish on each copy an appropriate
|
82 |
+
copyright notice and disclaimer of warranty; keep intact all the
|
83 |
+
notices that refer to this License and to the absence of any warranty;
|
84 |
+
and give any other recipients of the Program a copy of this License
|
85 |
+
along with the Program.
|
86 |
+
|
87 |
+
You may charge a fee for the physical act of transferring a copy, and
|
88 |
+
you may at your option offer warranty protection in exchange for a fee.
|
89 |
+
|
90 |
+
2. You may modify your copy or copies of the Program or any portion
|
91 |
+
of it, thus forming a work based on the Program, and copy and
|
92 |
+
distribute such modifications or work under the terms of Section 1
|
93 |
+
above, provided that you also meet all of these conditions:
|
94 |
+
|
95 |
+
a) You must cause the modified files to carry prominent notices
|
96 |
+
stating that you changed the files and the date of any change.
|
97 |
+
|
98 |
+
b) You must cause any work that you distribute or publish, that in
|
99 |
+
whole or in part contains or is derived from the Program or any
|
100 |
+
part thereof, to be licensed as a whole at no charge to all third
|
101 |
+
parties under the terms of this License.
|
102 |
+
|
103 |
+
c) If the modified program normally reads commands interactively
|
104 |
+
when run, you must cause it, when started running for such
|
105 |
+
interactive use in the most ordinary way, to print or display an
|
106 |
+
announcement including an appropriate copyright notice and a
|
107 |
+
notice that there is no warranty (or else, saying that you provide
|
108 |
+
a warranty) and that users may redistribute the program under
|
109 |
+
these conditions, and telling the user how to view a copy of this
|
110 |
+
License. (Exception: if the Program itself is interactive but
|
111 |
+
does not normally print such an announcement, your work based on
|
112 |
+
the Program is not required to print an announcement.)
|
113 |
+
|
114 |
+
These requirements apply to the modified work as a whole. If
|
115 |
+
identifiable sections of that work are not derived from the Program,
|
116 |
+
and can be reasonably considered independent and separate works in
|
117 |
+
themselves, then this License, and its terms, do not apply to those
|
118 |
+
sections when you distribute them as separate works. But when you
|
119 |
+
distribute the same sections as part of a whole which is a work based
|
120 |
+
on the Program, the distribution of the whole must be on the terms of
|
121 |
+
this License, whose permissions for other licensees extend to the
|
122 |
+
entire whole, and thus to each and every part regardless of who wrote it.
|
123 |
+
|
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
|
281 |
+
|
282 |
+
How to Apply These Terms to Your New Programs
|
283 |
+
|
284 |
+
If you develop a new program, and you want it to be of the greatest
|
285 |
+
possible use to the public, the best way to achieve this is to make it
|
286 |
+
free software which everyone can redistribute and change under these terms.
|
287 |
+
|
288 |
+
To do so, attach the following notices to the program. It is safest
|
289 |
+
to attach them to the start of each source file to most effectively
|
290 |
+
convey the exclusion of warranty; and each file should have at least
|
291 |
+
the "copyright" line and a pointer to where the full notice is found.
|
292 |
+
|
293 |
+
<one line to give the program's name and a brief idea of what it does.>
|
294 |
+
Copyright (C) <year> <name of author>
|
295 |
+
|
296 |
+
This program is free software; you can redistribute it and/or modify
|
297 |
+
it under the terms of the GNU General Public License as published by
|
298 |
+
the Free Software Foundation; either version 2 of the License, or
|
299 |
+
(at your option) any later version.
|
300 |
+
|
301 |
+
This program is distributed in the hope that it will be useful,
|
302 |
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
303 |
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
304 |
+
GNU General Public License for more details.
|
305 |
+
|
306 |
+
You should have received a copy of the GNU General Public License along
|
307 |
+
with this program; if not, write to the Free Software Foundation, Inc.,
|
308 |
+
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
309 |
+
|
310 |
+
Also add information on how to contact you by electronic and paper mail.
|
311 |
+
|
312 |
+
If the program is interactive, make it output a short notice like this
|
313 |
+
when it starts in an interactive mode:
|
314 |
+
|
315 |
+
Gnomovision version 69, Copyright (C) year name of author
|
316 |
+
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
317 |
+
This is free software, and you are welcome to redistribute it
|
318 |
+
under certain conditions; type `show c' for details.
|
319 |
+
|
320 |
+
The hypothetical commands `show w' and `show c' should show the appropriate
|
321 |
+
parts of the General Public License. Of course, the commands you use may
|
322 |
+
be called something other than `show w' and `show c'; they could even be
|
323 |
+
mouse-clicks or menu items--whatever suits your program.
|
324 |
+
|
325 |
+
You should also get your employer (if you work as a programmer) or your
|
326 |
+
school, if any, to sign a "copyright disclaimer" for the program, if
|
327 |
+
necessary. Here is a sample; alter the names:
|
328 |
+
|
329 |
+
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
330 |
+
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
331 |
+
|
332 |
+
<signature of Ty Coon>, 1 April 1989
|
333 |
+
Ty Coon, President of Vice
|
334 |
+
|
335 |
+
This General Public License does not permit incorporating your program into
|
336 |
+
proprietary programs. If your program is a subroutine library, you may
|
337 |
+
consider it more useful to permit linking proprietary applications with the
|
338 |
+
library. If this is what you want to do, use the GNU Lesser General
|
339 |
+
Public License instead of this License.
|
Gruntfile.js
DELETED
@@ -1,49 +0,0 @@
|
|
1 |
-
module.exports = function(grunt) {
|
2 |
-
// Load tasks
|
3 |
-
require('load-grunt-tasks')(grunt);
|
4 |
-
// Display task timing
|
5 |
-
require('time-grunt')(grunt);
|
6 |
-
// Project configuration.
|
7 |
-
grunt.initConfig({
|
8 |
-
// Metadata
|
9 |
-
pkg : grunt.file.readJSON('package.json'),
|
10 |
-
// Variables
|
11 |
-
paths : {
|
12 |
-
// Base dir assets dir
|
13 |
-
base : 'client',
|
14 |
-
|
15 |
-
// PHP assets
|
16 |
-
php : {
|
17 |
-
files_std : ['*.php', '**/*.php', '!node_modules/**/*.php'], // Standard file match
|
18 |
-
files : '<%= paths.php.files_std %>' // Dynamic file match
|
19 |
-
},
|
20 |
-
|
21 |
-
// JavaScript assets
|
22 |
-
js : {
|
23 |
-
base : 'js', // Base dir
|
24 |
-
src : '<%= paths.js.base %>/dev', // Development code
|
25 |
-
dest : '<%= paths.js.base %>/prod', // Production code
|
26 |
-
files_std : '**/<%= paths.js.src %>/**/*.js', // Standard file match
|
27 |
-
files : '<%= paths.js.files_std %>' // Dynamic file match
|
28 |
-
},
|
29 |
-
|
30 |
-
// Sass assets
|
31 |
-
sass : {
|
32 |
-
src : 'sass', // Source files dir
|
33 |
-
dest : 'css', // Compiled files dir
|
34 |
-
ext : '.css', // Compiled extension
|
35 |
-
target : '*.scss', // Only Sass files in CWD
|
36 |
-
exclude : '!_*.scss', // Do not process partials
|
37 |
-
base_src : '<%= paths.base %>/<%= paths.sass.src %>', // Base source dir
|
38 |
-
base_dest : '<%= paths.base %>/<%= paths.sass.dest %>', // Base compile dir
|
39 |
-
}
|
40 |
-
},
|
41 |
-
});
|
42 |
-
|
43 |
-
// Load task configurations
|
44 |
-
grunt.loadTasks('grunt');
|
45 |
-
|
46 |
-
// Default Tasks
|
47 |
-
grunt.registerTask('build', ['phplint', 'jshint:all', 'uglify', 'sass']);
|
48 |
-
grunt.registerTask('watch_all', ['watch:js', 'watch:sass']);
|
49 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
README.md
CHANGED
@@ -1,12 +1,12 @@
|
|
1 |
-
# Cornerstone
|
2 |
-
Enhanced Content Management for WordPress
|
3 |
-
|
4 |
-
Cornerstone transforms WordPress into a full-fledged Content Management System. Say *adios* to the hacks and tricks used to shoehorn your content into something that resembles a non-blog site and say hello to content management simplified.
|
5 |
-
|
6 |
-
[Learn more][home]
|
7 |
-
|
8 |
-
## Support
|
9 |
-
Found a bug or otherwise experiencing an issue with Cornerstone? [Report the issue here][issue-report]
|
10 |
-
|
11 |
-
[issue-report]: https://github.com/archetyped/cornerstone/wiki/
|
12 |
-
[home]: http://archetyped.com/tools/cornerstone/ "Cornerstone home page"
|
1 |
+
# Cornerstone
|
2 |
+
Enhanced Content Management for WordPress
|
3 |
+
|
4 |
+
Cornerstone transforms WordPress into a full-fledged Content Management System. Say *adios* to the hacks and tricks used to shoehorn your content into something that resembles a non-blog site and say hello to content management simplified.
|
5 |
+
|
6 |
+
[Learn more][home]
|
7 |
+
|
8 |
+
## Support
|
9 |
+
Found a bug or otherwise experiencing an issue with Cornerstone? [Report the issue here][issue-report]
|
10 |
+
|
11 |
+
[issue-report]: https://github.com/archetyped/cornerstone/wiki/Support-&-Feedback "Report an issue"
|
12 |
+
[home]: http://archetyped.com/tools/cornerstone/ "Cornerstone home page"
|
controller.php
CHANGED
@@ -1,235 +1,235 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* @package Cornerstone
|
5 |
-
*/
|
6 |
-
class Cornerstone extends CNR_Base {
|
7 |
-
/* Variables */
|
8 |
-
|
9 |
-
/**
|
10 |
-
* Script files
|
11 |
-
* @var array
|
12 |
-
* @see CNR_Base::files
|
13 |
-
*/
|
14 |
-
var $scripts = array (
|
15 |
-
'core' => array (
|
16 |
-
'file' => 'js/lib.core.js',
|
17 |
-
'deps' => 'jquery'
|
18 |
-
),
|
19 |
-
'admin' => array (
|
20 |
-
'file' => 'js/lib.admin.js',
|
21 |
-
'deps' => array('jquery', '[core]'),
|
22 |
-
'context' => 'admin'
|
23 |
-
),
|
24 |
-
'inline_edit' => array (
|
25 |
-
'file' => 'js/lib.posts.inline_edit.js',
|
26 |
-
'deps' => array('inline-edit-post','jquery', '[posts]'),
|
27 |
-
'context' => 'admin_page_edit'
|
28 |
-
)
|
29 |
-
);
|
30 |
-
|
31 |
-
/**
|
32 |
-
* Style files
|
33 |
-
* @var array
|
34 |
-
* @see CNR_Base::files
|
35 |
-
*/
|
36 |
-
var $styles = array (
|
37 |
-
'admin' => array (
|
38 |
-
'file' => 'css/admin.css',
|
39 |
-
'context' => 'admin'
|
40 |
-
)
|
41 |
-
);
|
42 |
-
|
43 |
-
/* Featured Content variables */
|
44 |
-
|
45 |
-
/**
|
46 |
-
* Category slug value that denotes a "featured" post
|
47 |
-
* @var string
|
48 |
-
* @see posts_featured_cat()
|
49 |
-
* @todo Remove need for this property
|
50 |
-
*/
|
51 |
-
var $posts_featured_cat = "feature";
|
52 |
-
|
53 |
-
/**
|
54 |
-
* Featured posts container
|
55 |
-
* @var CNR_Post_Query
|
56 |
-
*/
|
57 |
-
var $posts_featured = null;
|
58 |
-
|
59 |
-
/* Children Content Variables */
|
60 |
-
|
61 |
-
/**
|
62 |
-
* Children posts
|
63 |
-
* @var CNR_Post_Query
|
64 |
-
*/
|
65 |
-
var $post_children_collection = null;
|
66 |
-
|
67 |
-
/* Instance Variables */
|
68 |
-
|
69 |
-
/**
|
70 |
-
* Structure instance
|
71 |
-
* @var CNR_Structure
|
72 |
-
*/
|
73 |
-
var $structure = null;
|
74 |
-
|
75 |
-
/**
|
76 |
-
* Media instance
|
77 |
-
* @var CNR_Media
|
78 |
-
*/
|
79 |
-
var $media = null;
|
80 |
-
|
81 |
-
/**
|
82 |
-
* Post class instance
|
83 |
-
* @var CNR_Post
|
84 |
-
*/
|
85 |
-
var $post = null;
|
86 |
-
|
87 |
-
/**
|
88 |
-
* Feeds instance
|
89 |
-
* @var CNR_Feeds
|
90 |
-
*/
|
91 |
-
var $feeds = null;
|
92 |
-
|
93 |
-
/* Constructor */
|
94 |
-
|
95 |
-
function __construct() {
|
96 |
-
//Parent Constructor
|
97 |
-
parent::__construct();
|
98 |
-
|
99 |
-
//Init
|
100 |
-
$this->init();
|
101 |
-
|
102 |
-
//Special Queries
|
103 |
-
$this->posts_featured = new CNR_Post_Query( array( 'category' => $this->posts_featured_get_cat_id(), 'numberposts' => 4 ) );
|
104 |
-
$this->post_children_collection = new CNR_Post_Query();
|
105 |
-
|
106 |
-
$this->post = new CNR_Post();
|
107 |
-
$this->post->init();
|
108 |
-
|
109 |
-
|
110 |
-
//Init class instances
|
111 |
-
$this->structure = new CNR_Structure();
|
112 |
-
$this->structure->init();
|
113 |
-
|
114 |
-
$this->media = new CNR_Media();
|
115 |
-
$this->media->init();
|
116 |
-
|
117 |
-
$this->feeds = new CNR_Feeds();
|
118 |
-
$this->feeds->init();
|
119 |
-
}
|
120 |
-
|
121 |
-
/* Init */
|
122 |
-
|
123 |
-
/**
|
124 |
-
* Initialize environment
|
125 |
-
* Overrides parent method
|
126 |
-
* @see parent::init_env
|
127 |
-
* @return void
|
128 |
-
*/
|
129 |
-
function init_env() {
|
130 |
-
//Localization
|
131 |
-
$ldir = 'l10n';
|
132 |
-
$lpath = $this->util->get_plugin_file_path($ldir, array(false, false));
|
133 |
-
$lpath_abs = $this->util->get_file_path($ldir);
|
134 |
-
if ( is_dir($lpath_abs) ) {
|
135 |
-
load_plugin_textdomain($this->util->get_plugin_textdomain(), false, $lpath);
|
136 |
-
}
|
137 |
-
|
138 |
-
//Context
|
139 |
-
add_action(( is_admin() ) ? 'admin_head' : 'wp_head', $this->m('set_client_context'));
|
140 |
-
}
|
141 |
-
|
142 |
-
/* Methods */
|
143 |
-
|
144 |
-
/*-** Request **-*/
|
145 |
-
|
146 |
-
/**
|
147 |
-
* Output current context to client-side
|
148 |
-
* @uses `wp_head` action hook
|
149 |
-
* @uses `admin_head` action hook
|
150 |
-
* @return void
|
151 |
-
*/
|
152 |
-
function set_client_context() {
|
153 |
-
$ctx = new stdClass();
|
154 |
-
$ctx->context = $this->util->get_context();
|
155 |
-
$this->util->extend_client_object($ctx, true);
|
156 |
-
}
|
157 |
-
|
158 |
-
/*-** Child Content **-*/
|
159 |
-
|
160 |
-
/**
|
161 |
-
* Gets children posts of specified page and stores them for later use
|
162 |
-
* This method hooks into 'the_posts' filter to retrieve child posts for any single page retrieved by WP
|
163 |
-
* @return array $posts Posts array (required by 'the_posts' filter)
|
164 |
-
* @param array $posts Array of Posts (@see WP_QUERY)
|
165 |
-
*/
|
166 |
-
function post_children_get($posts) {
|
167 |
-
//Global variables
|
168 |
-
global $wp_query;
|
169 |
-
|
170 |
-
//Reset post children collection
|
171 |
-
$this->post_children_collection->init();
|
172 |
-
|
173 |
-
//Stop here if post is not a page
|
174 |
-
if ( ! is_page() || empty($posts) )
|
175 |
-
return $posts;
|
176 |
-
|
177 |
-
//Get children posts
|
178 |
-
$post =& $posts[0];
|
179 |
-
$this->post_children_collection =& CNR_Post::get_children($post);
|
180 |
-
|
181 |
-
//Return posts (required by filter)
|
182 |
-
return $posts;
|
183 |
-
}
|
184 |
-
|
185 |
-
/*-** Featured Content **-*/
|
186 |
-
|
187 |
-
/**
|
188 |
-
* Retrieves featured post category object
|
189 |
-
* @return object Featured post category object
|
190 |
-
* @todo integrate into CNR_Post_Query
|
191 |
-
*/
|
192 |
-
function posts_featured_get_cat() {
|
193 |
-
static $cat = null;
|
194 |
-
|
195 |
-
//Only fetch category object if it hasn't already been retrieved
|
196 |
-
if (is_null($cat) || !is_object($cat)) {
|
197 |
-
//Retrieve category object
|
198 |
-
if (is_int($this->posts_featured_cat)) {
|
199 |
-
$cat = get_category((int)$this->posts_featured_cat);
|
200 |
-
}
|
201 |
-
elseif (is_string($this->posts_featured_cat) && strlen($this->posts_featured_cat) > 0) {
|
202 |
-
$cat = get_category_by_slug($this->posts_featured_cat);
|
203 |
-
}
|
204 |
-
}
|
205 |
-
|
206 |
-
return $cat;
|
207 |
-
}
|
208 |
-
|
209 |
-
/**
|
210 |
-
* @todo integrate into CNR_Post_Query
|
211 |
-
*/
|
212 |
-
function posts_featured_get_cat_id() {
|
213 |
-
static $id = '';
|
214 |
-
if ($id == '') {
|
215 |
-
$cat = $this->posts_featured_get_cat();
|
216 |
-
if (!is_null($cat) && is_object($cat) && $this->util->property_exists($cat, 'cat_ID'))
|
217 |
-
$id = $cat->cat_ID;
|
218 |
-
}
|
219 |
-
return $id;
|
220 |
-
}
|
221 |
-
|
222 |
-
/**
|
223 |
-
* Checks if post has content to display
|
224 |
-
* @param object $post (optional) Post object
|
225 |
-
* @return bool TRUE if post has content, FALSE otherwise
|
226 |
-
* @todo Review for deletion/relocation
|
227 |
-
*/
|
228 |
-
function post_has_content($post = null) {
|
229 |
-
if ( !$this->util->check_post($post) )
|
230 |
-
return false;
|
231 |
-
if ( isset($post->post_content) && trim($post->post_content) != '' )
|
232 |
-
return true;
|
233 |
-
return false;
|
234 |
-
}
|
235 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package Cornerstone
|
5 |
+
*/
|
6 |
+
class Cornerstone extends CNR_Base {
|
7 |
+
/* Variables */
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Script files
|
11 |
+
* @var array
|
12 |
+
* @see CNR_Base::files
|
13 |
+
*/
|
14 |
+
var $scripts = array (
|
15 |
+
'core' => array (
|
16 |
+
'file' => 'js/lib.core.js',
|
17 |
+
'deps' => 'jquery'
|
18 |
+
),
|
19 |
+
'admin' => array (
|
20 |
+
'file' => 'js/lib.admin.js',
|
21 |
+
'deps' => array('jquery', '[core]'),
|
22 |
+
'context' => 'admin'
|
23 |
+
),
|
24 |
+
'inline_edit' => array (
|
25 |
+
'file' => 'js/lib.posts.inline_edit.js',
|
26 |
+
'deps' => array('inline-edit-post','jquery', '[posts]'),
|
27 |
+
'context' => 'admin_page_edit'
|
28 |
+
)
|
29 |
+
);
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Style files
|
33 |
+
* @var array
|
34 |
+
* @see CNR_Base::files
|
35 |
+
*/
|
36 |
+
var $styles = array (
|
37 |
+
'admin' => array (
|
38 |
+
'file' => 'css/admin.css',
|
39 |
+
'context' => 'admin'
|
40 |
+
)
|
41 |
+
);
|
42 |
+
|
43 |
+
/* Featured Content variables */
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Category slug value that denotes a "featured" post
|
47 |
+
* @var string
|
48 |
+
* @see posts_featured_cat()
|
49 |
+
* @todo Remove need for this property
|
50 |
+
*/
|
51 |
+
var $posts_featured_cat = "feature";
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Featured posts container
|
55 |
+
* @var CNR_Post_Query
|
56 |
+
*/
|
57 |
+
var $posts_featured = null;
|
58 |
+
|
59 |
+
/* Children Content Variables */
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Children posts
|
63 |
+
* @var CNR_Post_Query
|
64 |
+
*/
|
65 |
+
var $post_children_collection = null;
|
66 |
+
|
67 |
+
/* Instance Variables */
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Structure instance
|
71 |
+
* @var CNR_Structure
|
72 |
+
*/
|
73 |
+
var $structure = null;
|
74 |
+
|
75 |
+
/**
|
76 |
+
* Media instance
|
77 |
+
* @var CNR_Media
|
78 |
+
*/
|
79 |
+
var $media = null;
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Post class instance
|
83 |
+
* @var CNR_Post
|
84 |
+
*/
|
85 |
+
var $post = null;
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Feeds instance
|
89 |
+
* @var CNR_Feeds
|
90 |
+
*/
|
91 |
+
var $feeds = null;
|
92 |
+
|
93 |
+
/* Constructor */
|
94 |
+
|
95 |
+
function __construct() {
|
96 |
+
//Parent Constructor
|
97 |
+
parent::__construct();
|
98 |
+
|
99 |
+
//Init
|
100 |
+
$this->init();
|
101 |
+
|
102 |
+
//Special Queries
|
103 |
+
$this->posts_featured = new CNR_Post_Query( array( 'category' => $this->posts_featured_get_cat_id(), 'numberposts' => 4 ) );
|
104 |
+
$this->post_children_collection = new CNR_Post_Query();
|
105 |
+
|
106 |
+
$this->post = new CNR_Post();
|
107 |
+
$this->post->init();
|
108 |
+
|
109 |
+
|
110 |
+
//Init class instances
|
111 |
+
$this->structure = new CNR_Structure();
|
112 |
+
$this->structure->init();
|
113 |
+
|
114 |
+
$this->media = new CNR_Media();
|
115 |
+
$this->media->init();
|
116 |
+
|
117 |
+
$this->feeds = new CNR_Feeds();
|
118 |
+
$this->feeds->init();
|
119 |
+
}
|
120 |
+
|
121 |
+
/* Init */
|
122 |
+
|
123 |
+
/**
|
124 |
+
* Initialize environment
|
125 |
+
* Overrides parent method
|
126 |
+
* @see parent::init_env
|
127 |
+
* @return void
|
128 |
+
*/
|
129 |
+
function init_env() {
|
130 |
+
//Localization
|
131 |
+
$ldir = 'l10n';
|
132 |
+
$lpath = $this->util->get_plugin_file_path($ldir, array(false, false));
|
133 |
+
$lpath_abs = $this->util->get_file_path($ldir);
|
134 |
+
if ( is_dir($lpath_abs) ) {
|
135 |
+
load_plugin_textdomain($this->util->get_plugin_textdomain(), false, $lpath);
|
136 |
+
}
|
137 |
+
|
138 |
+
//Context
|
139 |
+
add_action(( is_admin() ) ? 'admin_head' : 'wp_head', $this->m('set_client_context'));
|
140 |
+
}
|
141 |
+
|
142 |
+
/* Methods */
|
143 |
+
|
144 |
+
/*-** Request **-*/
|
145 |
+
|
146 |
+
/**
|
147 |
+
* Output current context to client-side
|
148 |
+
* @uses `wp_head` action hook
|
149 |
+
* @uses `admin_head` action hook
|
150 |
+
* @return void
|
151 |
+
*/
|
152 |
+
function set_client_context() {
|
153 |
+
$ctx = new stdClass();
|
154 |
+
$ctx->context = $this->util->get_context();
|
155 |
+
$this->util->extend_client_object($ctx, true);
|
156 |
+
}
|
157 |
+
|
158 |
+
/*-** Child Content **-*/
|
159 |
+
|
160 |
+
/**
|
161 |
+
* Gets children posts of specified page and stores them for later use
|
162 |
+
* This method hooks into 'the_posts' filter to retrieve child posts for any single page retrieved by WP
|
163 |
+
* @return array $posts Posts array (required by 'the_posts' filter)
|
164 |
+
* @param array $posts Array of Posts (@see WP_QUERY)
|
165 |
+
*/
|
166 |
+
function post_children_get($posts) {
|
167 |
+
//Global variables
|
168 |
+
global $wp_query;
|
169 |
+
|
170 |
+
//Reset post children collection
|
171 |
+
$this->post_children_collection->init();
|
172 |
+
|
173 |
+
//Stop here if post is not a page
|
174 |
+
if ( ! is_page() || empty($posts) )
|
175 |
+
return $posts;
|
176 |
+
|
177 |
+
//Get children posts
|
178 |
+
$post =& $posts[0];
|
179 |
+
$this->post_children_collection =& CNR_Post::get_children($post);
|
180 |
+
|
181 |
+
//Return posts (required by filter)
|
182 |
+
return $posts;
|
183 |
+
}
|
184 |
+
|
185 |
+
/*-** Featured Content **-*/
|
186 |
+
|
187 |
+
/**
|
188 |
+
* Retrieves featured post category object
|
189 |
+
* @return object Featured post category object
|
190 |
+
* @todo integrate into CNR_Post_Query
|
191 |
+
*/
|
192 |
+
function posts_featured_get_cat() {
|
193 |
+
static $cat = null;
|
194 |
+
|
195 |
+
//Only fetch category object if it hasn't already been retrieved
|
196 |
+
if (is_null($cat) || !is_object($cat)) {
|
197 |
+
//Retrieve category object
|
198 |
+
if (is_int($this->posts_featured_cat)) {
|
199 |
+
$cat = get_category((int)$this->posts_featured_cat);
|
200 |
+
}
|
201 |
+
elseif (is_string($this->posts_featured_cat) && strlen($this->posts_featured_cat) > 0) {
|
202 |
+
$cat = get_category_by_slug($this->posts_featured_cat);
|
203 |
+
}
|
204 |
+
}
|
205 |
+
|
206 |
+
return $cat;
|
207 |
+
}
|
208 |
+
|
209 |
+
/**
|
210 |
+
* @todo integrate into CNR_Post_Query
|
211 |
+
*/
|
212 |
+
function posts_featured_get_cat_id() {
|
213 |
+
static $id = '';
|
214 |
+
if ($id == '') {
|
215 |
+
$cat = $this->posts_featured_get_cat();
|
216 |
+
if (!is_null($cat) && is_object($cat) && $this->util->property_exists($cat, 'cat_ID'))
|
217 |
+
$id = $cat->cat_ID;
|
218 |
+
}
|
219 |
+
return $id;
|
220 |
+
}
|
221 |
+
|
222 |
+
/**
|
223 |
+
* Checks if post has content to display
|
224 |
+
* @param object $post (optional) Post object
|
225 |
+
* @return bool TRUE if post has content, FALSE otherwise
|
226 |
+
* @todo Review for deletion/relocation
|
227 |
+
*/
|
228 |
+
function post_has_content($post = null) {
|
229 |
+
if ( !$this->util->check_post($post) )
|
230 |
+
return false;
|
231 |
+
if ( isset($post->post_content) && trim($post->post_content) != '' )
|
232 |
+
return true;
|
233 |
+
return false;
|
234 |
+
}
|
235 |
+
}
|
functions.php
CHANGED
@@ -1,256 +1,256 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* Functions
|
4 |
-
* Provides global access to specific functionality
|
5 |
-
* @package Cornerstone
|
6 |
-
* @author Archetyped
|
7 |
-
*/
|
8 |
-
|
9 |
-
/* Template tags */
|
10 |
-
|
11 |
-
/**
|
12 |
-
* Outputs feed links based on current page
|
13 |
-
* @return void
|
14 |
-
*/
|
15 |
-
function cnr_the_feed_links() {
|
16 |
-
global $cnr;
|
17 |
-
$cnr->feeds->the_links();
|
18 |
-
}
|
19 |
-
|
20 |
-
/*-** Child Content **-*/
|
21 |
-
|
22 |
-
function cnr_is_section() {
|
23 |
-
return ( is_page() && cnr_have_children() ) ? true : false;
|
24 |
-
}
|
25 |
-
|
26 |
-
/**
|
27 |
-
* Checks if current post/page has children elements
|
28 |
-
* @return bool TRUE if post/page has children, FALSE otherwise
|
29 |
-
*/
|
30 |
-
function cnr_have_children() {
|
31 |
-
global $cnr;
|
32 |
-
return $cnr->post_children_collection->has();
|
33 |
-
}
|
34 |
-
|
35 |
-
/**
|
36 |
-
* Prepares next child post for output to page
|
37 |
-
*
|
38 |
-
* @return void
|
39 |
-
*/
|
40 |
-
function cnr_next_child() {
|
41 |
-
global $cnr;
|
42 |
-
$cnr->post_children_collection->next();
|
43 |
-
}
|
44 |
-
|
45 |
-
/**
|
46 |
-
* Returns number of children in current request
|
47 |
-
* May not return total number of existing children (e.g. if output is paged, etc.)
|
48 |
-
* @return int Number of children returned in current request
|
49 |
-
*/
|
50 |
-
function cnr_children_count() {
|
51 |
-
global $cnr;
|
52 |
-
return $cnr->post_children_collection->count();
|
53 |
-
}
|
54 |
-
|
55 |
-
/**
|
56 |
-
* Returns total number of existing children
|
57 |
-
* @return int Total number of children
|
58 |
-
*/
|
59 |
-
function cnr_children_found() {
|
60 |
-
global $cnr;
|
61 |
-
return $cnr->post_children_collection->found();
|
62 |
-
}
|
63 |
-
|
64 |
-
/**
|
65 |
-
* Returns total number of pages of children
|
66 |
-
* Based on 'posts_per_page' option
|
67 |
-
* @return int Maximum number of pages
|
68 |
-
*/
|
69 |
-
function cnr_children_max_num_pages() {
|
70 |
-
global $cnr;
|
71 |
-
return $cnr->post_children_collection->max_num_pages();
|
72 |
-
}
|
73 |
-
|
74 |
-
/**
|
75 |
-
* Checks if current child item is the first child item
|
76 |
-
* @return bool TRUE if current item is first, FALSE otherwise
|
77 |
-
*/
|
78 |
-
function cnr_is_first_child() {
|
79 |
-
global $cnr;
|
80 |
-
return $cnr->post_children_collection->is_first();
|
81 |
-
}
|
82 |
-
|
83 |
-
/**
|
84 |
-
* Checks if current child item is the last child item
|
85 |
-
* @return bool TRUE if current item is last, FALSE otherwise
|
86 |
-
*/
|
87 |
-
function cnr_is_last_child() {
|
88 |
-
global $cnr;
|
89 |
-
return $cnr->post_children_collection->is_last();
|
90 |
-
}
|
91 |
-
|
92 |
-
/*-** Featured Content **-*/
|
93 |
-
|
94 |
-
/**
|
95 |
-
* Retrieves featured posts
|
96 |
-
* @return array Featured posts matching criteria
|
97 |
-
* @param int $limit (optional) Maximum number of featured posts to retrieve
|
98 |
-
* @param int|bool $parent (optional) Section to get featured posts of (Defaults to current section). FALSE if latest featured posts should be retrieved regardless of section
|
99 |
-
*/
|
100 |
-
function cnr_get_featured($limit = 0, $parent = null) {
|
101 |
-
global $cnr;
|
102 |
-
return $cnr->posts_featured->get($limit, $parent);
|
103 |
-
}
|
104 |
-
|
105 |
-
function cnr_in_featured($post_id = null) {
|
106 |
-
global $cnr;
|
107 |
-
return $cnr->posts_featured->contains($post_id);
|
108 |
-
}
|
109 |
-
|
110 |
-
function cnr_have_featured() {
|
111 |
-
global $cnr;
|
112 |
-
return $cnr->posts_featured->has();
|
113 |
-
}
|
114 |
-
|
115 |
-
function cnr_next_featured() {
|
116 |
-
global $cnr;
|
117 |
-
return $cnr->posts_featured->next();
|
118 |
-
|
119 |
-
}
|
120 |
-
|
121 |
-
function cnr_current_featured() {
|
122 |
-
global $cnr;
|
123 |
-
return $cnr->posts_featured->current();
|
124 |
-
}
|
125 |
-
|
126 |
-
function cnr_is_first_featured() {
|
127 |
-
global $cnr;
|
128 |
-
return $cnr->posts_featured->is_first();
|
129 |
-
}
|
130 |
-
|
131 |
-
function cnr_is_last_featured() {
|
132 |
-
global $cnr;
|
133 |
-
return $cnr->posts_featured->is_last();
|
134 |
-
}
|
135 |
-
|
136 |
-
function cnr_featured_count() {
|
137 |
-
global $cnr;
|
138 |
-
return $cnr->posts_featured->count();
|
139 |
-
}
|
140 |
-
|
141 |
-
/**
|
142 |
-
* Returns total number of found posts
|
143 |
-
* @return int Total number of posts
|
144 |
-
*/
|
145 |
-
function cnr_featured_found() {
|
146 |
-
global $cnr;
|
147 |
-
return $cnr->posts_featured->found();
|
148 |
-
}
|
149 |
-
|
150 |
-
/*-** Post-Specific **-*/
|
151 |
-
|
152 |
-
/**
|
153 |
-
* Checks if post has content to display
|
154 |
-
* @param object $post (optional) Post object
|
155 |
-
* @return bool TRUE if post has content, FALSE otherwise
|
156 |
-
*/
|
157 |
-
function cnr_has_content($post = null) {
|
158 |
-
global $cnr;
|
159 |
-
return $cnr->post_has_content($post);
|
160 |
-
}
|
161 |
-
|
162 |
-
/* Images */
|
163 |
-
|
164 |
-
function cnr_get_attachments($post = null) {
|
165 |
-
$m = new CNR_Media();
|
166 |
-
return $m->post_get_attachments($post);
|
167 |
-
}
|
168 |
-
|
169 |
-
function cnr_get_filesize($post = null, $formatted = true) {
|
170 |
-
$m = new CNR_Media();
|
171 |
-
return $m->get_attachment_filesize($post, $formatted);
|
172 |
-
}
|
173 |
-
|
174 |
-
/* Section */
|
175 |
-
|
176 |
-
/**
|
177 |
-
* Retrieves the post's section data
|
178 |
-
* @uses CNR_Post::get_section()
|
179 |
-
* @param string $data (optional) Type of data to return (Default: ID)
|
180 |
-
* Possible values:
|
181 |
-
* NULL Full section post object
|
182 |
-
* Column name Post column data (if exists)
|
183 |
-
*
|
184 |
-
* @param int $id (optional) Post ID (Default: current post)
|
185 |
-
* @return mixed post's section (or column data if specified via $data parameter)
|
186 |
-
*/
|
187 |
-
function cnr_get_the_section($data = 'ID', $id = null) {
|
188 |
-
return CNR_Post::get_section($id, $data);
|
189 |
-
}
|
190 |
-
|
191 |
-
/**
|
192 |
-
* Prints the post's section data
|
193 |
-
* @uses CNR_Post::the_section()
|
194 |
-
* @param string $data (optional) Type of data to return (Default: ID)
|
195 |
-
*/
|
196 |
-
function cnr_the_section($data = 'ID') {
|
197 |
-
CNR_Post::the_section(null, $data);
|
198 |
-
}
|
199 |
-
|
200 |
-
/* Content Types */
|
201 |
-
|
202 |
-
/**
|
203 |
-
* Register handler for a placeholder in a content type template
|
204 |
-
* Placeholders allow templates to be populated with dynamic content at runtime
|
205 |
-
* Multiple handlers can be registered for a placeholder,
|
206 |
-
* thus allowing custom handlers to override default processing, etc.
|
207 |
-
* @uses CNR_Field_Type::register_placeholder_handler() to register placeholder
|
208 |
-
* @param string $placeholder Placeholder identifier
|
209 |
-
* @param callback $handler Callback function to use as handler for placeholder
|
210 |
-
* @param int $priority (optional) Priority of registered handler (Default: 10)
|
211 |
-
*/
|
212 |
-
function cnr_register_placeholder_handler($placeholder, $handler, $priority = 10) {
|
213 |
-
CNR_Field_Type::register_placeholder_handler($placeholder, $handler, $priority);
|
214 |
-
}
|
215 |
-
|
216 |
-
/**
|
217 |
-
* Checks if data exists for specified field
|
218 |
-
* @global $cnr_content_utilities
|
219 |
-
* @param string $field_id ID of field to check for data
|
220 |
-
* @param int|obj $item (optional) Post ID or object to check for field data (Default: global post)
|
221 |
-
* @return bool TRUE if field data exists
|
222 |
-
*/
|
223 |
-
function cnr_has_data($field_id = null, $item = null) {
|
224 |
-
global $cnr_content_utilities;
|
225 |
-
return $cnr_content_utilities->has_item_data($item, $field_id);
|
226 |
-
}
|
227 |
-
|
228 |
-
/**
|
229 |
-
* Retrieve data from a field
|
230 |
-
* @global $cnr_content_utilities
|
231 |
-
* @see CNR_Content_Utilities::get_item_data() for more information
|
232 |
-
* @param string $field_id ID of field to retrieve
|
233 |
-
* @param string $layout (optional) Name of layout to use when returning data
|
234 |
-
* @param array $attr (optional) Additional attributes to pass to field
|
235 |
-
* @param int|object $item (optional) Post object to retrieve data from (Default: global post object)
|
236 |
-
* @param mixed $default Default value to return in case of errors (invalid field, no data, etc.)
|
237 |
-
* @return mixed Specified field data
|
238 |
-
*/
|
239 |
-
function cnr_get_data($field_id = null, $layout = 'display', $attr = null, $item = null, $default = '') {
|
240 |
-
global $cnr_content_utilities;
|
241 |
-
return $cnr_content_utilities->get_item_data($item, $field_id, $layout, $default, $attr);
|
242 |
-
}
|
243 |
-
|
244 |
-
/**
|
245 |
-
* Prints an item's field data
|
246 |
-
* @see CNR_Content_Utilities::the_item_data() for more information
|
247 |
-
* @param string $field_id Name of field to retrieve
|
248 |
-
* @param string $layout(optional) Layout to use when returning field data (Default: display)
|
249 |
-
* @param array $attr Additional items to pass to field
|
250 |
-
* @param int|object $item(optional) Content item to retrieve field from (Default: null - global $post object will be used)
|
251 |
-
* @param mixed $default Default value to return in case of errors, etc.
|
252 |
-
*/
|
253 |
-
function cnr_the_data($field_id = null, $layout = 'display', $attr = null, $item = null, $default = '') {
|
254 |
-
global $cnr_content_utilities;
|
255 |
-
$cnr_content_utilities->the_item_data($item, $field_id, $layout, $default, $attr);
|
256 |
}
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Functions
|
4 |
+
* Provides global access to specific functionality
|
5 |
+
* @package Cornerstone
|
6 |
+
* @author Archetyped
|
7 |
+
*/
|
8 |
+
|
9 |
+
/* Template tags */
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Outputs feed links based on current page
|
13 |
+
* @return void
|
14 |
+
*/
|
15 |
+
function cnr_the_feed_links() {
|
16 |
+
global $cnr;
|
17 |
+
$cnr->feeds->the_links();
|
18 |
+
}
|
19 |
+
|
20 |
+
/*-** Child Content **-*/
|
21 |
+
|
22 |
+
function cnr_is_section() {
|
23 |
+
return ( is_page() && cnr_have_children() ) ? true : false;
|
24 |
+
}
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Checks if current post/page has children elements
|
28 |
+
* @return bool TRUE if post/page has children, FALSE otherwise
|
29 |
+
*/
|
30 |
+
function cnr_have_children() {
|
31 |
+
global $cnr;
|
32 |
+
return $cnr->post_children_collection->has();
|
33 |
+
}
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Prepares next child post for output to page
|
37 |
+
*
|
38 |
+
* @return void
|
39 |
+
*/
|
40 |
+
function cnr_next_child() {
|
41 |
+
global $cnr;
|
42 |
+
$cnr->post_children_collection->next();
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Returns number of children in current request
|
47 |
+
* May not return total number of existing children (e.g. if output is paged, etc.)
|
48 |
+
* @return int Number of children returned in current request
|
49 |
+
*/
|
50 |
+
function cnr_children_count() {
|
51 |
+
global $cnr;
|
52 |
+
return $cnr->post_children_collection->count();
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Returns total number of existing children
|
57 |
+
* @return int Total number of children
|
58 |
+
*/
|
59 |
+
function cnr_children_found() {
|
60 |
+
global $cnr;
|
61 |
+
return $cnr->post_children_collection->found();
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Returns total number of pages of children
|
66 |
+
* Based on 'posts_per_page' option
|
67 |
+
* @return int Maximum number of pages
|
68 |
+
*/
|
69 |
+
function cnr_children_max_num_pages() {
|
70 |
+
global $cnr;
|
71 |
+
return $cnr->post_children_collection->max_num_pages();
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Checks if current child item is the first child item
|
76 |
+
* @return bool TRUE if current item is first, FALSE otherwise
|
77 |
+
*/
|
78 |
+
function cnr_is_first_child() {
|
79 |
+
global $cnr;
|
80 |
+
return $cnr->post_children_collection->is_first();
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* Checks if current child item is the last child item
|
85 |
+
* @return bool TRUE if current item is last, FALSE otherwise
|
86 |
+
*/
|
87 |
+
function cnr_is_last_child() {
|
88 |
+
global $cnr;
|
89 |
+
return $cnr->post_children_collection->is_last();
|
90 |
+
}
|
91 |
+
|
92 |
+
/*-** Featured Content **-*/
|
93 |
+
|
94 |
+
/**
|
95 |
+
* Retrieves featured posts
|
96 |
+
* @return array Featured posts matching criteria
|
97 |
+
* @param int $limit (optional) Maximum number of featured posts to retrieve
|
98 |
+
* @param int|bool $parent (optional) Section to get featured posts of (Defaults to current section). FALSE if latest featured posts should be retrieved regardless of section
|
99 |
+
*/
|
100 |
+
function cnr_get_featured($limit = 0, $parent = null) {
|
101 |
+
global $cnr;
|
102 |
+
return $cnr->posts_featured->get($limit, $parent);
|
103 |
+
}
|
104 |
+
|
105 |
+
function cnr_in_featured($post_id = null) {
|
106 |
+
global $cnr;
|
107 |
+
return $cnr->posts_featured->contains($post_id);
|
108 |
+
}
|
109 |
+
|
110 |
+
function cnr_have_featured() {
|
111 |
+
global $cnr;
|
112 |
+
return $cnr->posts_featured->has();
|
113 |
+
}
|
114 |
+
|
115 |
+
function cnr_next_featured() {
|
116 |
+
global $cnr;
|
117 |
+
return $cnr->posts_featured->next();
|
118 |
+
|
119 |
+
}
|
120 |
+
|
121 |
+
function cnr_current_featured() {
|
122 |
+
global $cnr;
|
123 |
+
return $cnr->posts_featured->current();
|
124 |
+
}
|
125 |
+
|
126 |
+
function cnr_is_first_featured() {
|
127 |
+
global $cnr;
|
128 |
+
return $cnr->posts_featured->is_first();
|
129 |
+
}
|
130 |
+
|
131 |
+
function cnr_is_last_featured() {
|
132 |
+
global $cnr;
|
133 |
+
return $cnr->posts_featured->is_last();
|
134 |
+
}
|
135 |
+
|
136 |
+
function cnr_featured_count() {
|
137 |
+
global $cnr;
|
138 |
+
return $cnr->posts_featured->count();
|
139 |
+
}
|
140 |
+
|
141 |
+
/**
|
142 |
+
* Returns total number of found posts
|
143 |
+
* @return int Total number of posts
|
144 |
+
*/
|
145 |
+
function cnr_featured_found() {
|
146 |
+
global $cnr;
|
147 |
+
return $cnr->posts_featured->found();
|
148 |
+
}
|
149 |
+
|
150 |
+
/*-** Post-Specific **-*/
|
151 |
+
|
152 |
+
/**
|
153 |
+
* Checks if post has content to display
|
154 |
+
* @param object $post (optional) Post object
|
155 |
+
* @return bool TRUE if post has content, FALSE otherwise
|
156 |
+
*/
|
157 |
+
function cnr_has_content($post = null) {
|
158 |
+
global $cnr;
|
159 |
+
return $cnr->post_has_content($post);
|
160 |
+
}
|
161 |
+
|
162 |
+
/* Images */
|
163 |
+
|
164 |
+
function cnr_get_attachments($post = null) {
|
165 |
+
$m = new CNR_Media();
|
166 |
+
return $m->post_get_attachments($post);
|
167 |
+
}
|
168 |
+
|
169 |
+
function cnr_get_filesize($post = null, $formatted = true) {
|
170 |
+
$m = new CNR_Media();
|
171 |
+
return $m->get_attachment_filesize($post, $formatted);
|
172 |
+
}
|
173 |
+
|
174 |
+
/* Section */
|
175 |
+
|
176 |
+
/**
|
177 |
+
* Retrieves the post's section data
|
178 |
+
* @uses CNR_Post::get_section()
|
179 |
+
* @param string $data (optional) Type of data to return (Default: ID)
|
180 |
+
* Possible values:
|
181 |
+
* NULL Full section post object
|
182 |
+
* Column name Post column data (if exists)
|
183 |
+
*
|
184 |
+
* @param int $id (optional) Post ID (Default: current post)
|
185 |
+
* @return mixed post's section (or column data if specified via $data parameter)
|
186 |
+
*/
|
187 |
+
function cnr_get_the_section($data = 'ID', $id = null) {
|
188 |
+
return CNR_Post::get_section($id, $data);
|
189 |
+
}
|
190 |
+
|
191 |
+
/**
|
192 |
+
* Prints the post's section data
|
193 |
+
* @uses CNR_Post::the_section()
|
194 |
+
* @param string $data (optional) Type of data to return (Default: ID)
|
195 |
+
*/
|
196 |
+
function cnr_the_section($data = 'ID') {
|
197 |
+
CNR_Post::the_section(null, $data);
|
198 |
+
}
|
199 |
+
|
200 |
+
/* Content Types */
|
201 |
+
|
202 |
+
/**
|
203 |
+
* Register handler for a placeholder in a content type template
|
204 |
+
* Placeholders allow templates to be populated with dynamic content at runtime
|
205 |
+
* Multiple handlers can be registered for a placeholder,
|
206 |
+
* thus allowing custom handlers to override default processing, etc.
|
207 |
+
* @uses CNR_Field_Type::register_placeholder_handler() to register placeholder
|
208 |
+
* @param string $placeholder Placeholder identifier
|
209 |
+
* @param callback $handler Callback function to use as handler for placeholder
|
210 |
+
* @param int $priority (optional) Priority of registered handler (Default: 10)
|
211 |
+
*/
|
212 |
+
function cnr_register_placeholder_handler($placeholder, $handler, $priority = 10) {
|
213 |
+
CNR_Field_Type::register_placeholder_handler($placeholder, $handler, $priority);
|
214 |
+
}
|
215 |
+
|
216 |
+
/**
|
217 |
+
* Checks if data exists for specified field
|
218 |
+
* @global $cnr_content_utilities
|
219 |
+
* @param string $field_id ID of field to check for data
|
220 |
+
* @param int|obj $item (optional) Post ID or object to check for field data (Default: global post)
|
221 |
+
* @return bool TRUE if field data exists
|
222 |
+
*/
|
223 |
+
function cnr_has_data($field_id = null, $item = null) {
|
224 |
+
global $cnr_content_utilities;
|
225 |
+
return $cnr_content_utilities->has_item_data($item, $field_id);
|
226 |
+
}
|
227 |
+
|
228 |
+
/**
|
229 |
+
* Retrieve data from a field
|
230 |
+
* @global $cnr_content_utilities
|
231 |
+
* @see CNR_Content_Utilities::get_item_data() for more information
|
232 |
+
* @param string $field_id ID of field to retrieve
|
233 |
+
* @param string $layout (optional) Name of layout to use when returning data
|
234 |
+
* @param array $attr (optional) Additional attributes to pass to field
|
235 |
+
* @param int|object $item (optional) Post object to retrieve data from (Default: global post object)
|
236 |
+
* @param mixed $default Default value to return in case of errors (invalid field, no data, etc.)
|
237 |
+
* @return mixed Specified field data
|
238 |
+
*/
|
239 |
+
function cnr_get_data($field_id = null, $layout = 'display', $attr = null, $item = null, $default = '') {
|
240 |
+
global $cnr_content_utilities;
|
241 |
+
return $cnr_content_utilities->get_item_data($item, $field_id, $layout, $default, $attr);
|
242 |
+
}
|
243 |
+
|
244 |
+
/**
|
245 |
+
* Prints an item's field data
|
246 |
+
* @see CNR_Content_Utilities::the_item_data() for more information
|
247 |
+
* @param string $field_id Name of field to retrieve
|
248 |
+
* @param string $layout(optional) Layout to use when returning field data (Default: display)
|
249 |
+
* @param array $attr Additional items to pass to field
|
250 |
+
* @param int|object $item(optional) Content item to retrieve field from (Default: null - global $post object will be used)
|
251 |
+
* @param mixed $default Default value to return in case of errors, etc.
|
252 |
+
*/
|
253 |
+
function cnr_the_data($field_id = null, $layout = 'display', $attr = null, $item = null, $default = '') {
|
254 |
+
global $cnr_content_utilities;
|
255 |
+
$cnr_content_utilities->the_item_data($item, $field_id, $layout, $default, $attr);
|
256 |
}
|
grunt/jshint.js
DELETED
@@ -1,38 +0,0 @@
|
|
1 |
-
module.exports = function(grunt) {
|
2 |
-
|
3 |
-
grunt.config('jshint', {
|
4 |
-
options : {
|
5 |
-
reporter: require('jshint-stylish'),
|
6 |
-
curly : true,
|
7 |
-
eqeqeq : true,
|
8 |
-
immed : true,
|
9 |
-
latedef : true,
|
10 |
-
newcap : false,
|
11 |
-
noarg : true,
|
12 |
-
sub : true,
|
13 |
-
undef : true,
|
14 |
-
unused : true,
|
15 |
-
boss : true,
|
16 |
-
eqnull : true,
|
17 |
-
browser : true,
|
18 |
-
jquery : true,
|
19 |
-
globals : {}
|
20 |
-
},
|
21 |
-
grunt : {
|
22 |
-
options : {
|
23 |
-
node : true
|
24 |
-
},
|
25 |
-
src : ['Gruntfile.js', 'grunt/*.js']
|
26 |
-
},
|
27 |
-
all : {
|
28 |
-
options : {
|
29 |
-
globals : {
|
30 |
-
'SLB' : true,
|
31 |
-
'console' : true
|
32 |
-
}
|
33 |
-
},
|
34 |
-
src : ['<%= paths.js.files %>']
|
35 |
-
},
|
36 |
-
});
|
37 |
-
|
38 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
grunt/phplint.js
DELETED
@@ -1,14 +0,0 @@
|
|
1 |
-
module.exports = function(grunt) {
|
2 |
-
|
3 |
-
grunt.config('phplint', {
|
4 |
-
options : {
|
5 |
-
phpArgs : {
|
6 |
-
'-lf': null
|
7 |
-
}
|
8 |
-
},
|
9 |
-
all : {
|
10 |
-
src : '<%= paths.php.files %>'
|
11 |
-
}
|
12 |
-
});
|
13 |
-
|
14 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
grunt/sass.js
DELETED
@@ -1,35 +0,0 @@
|
|
1 |
-
module.exports = function(grunt) {
|
2 |
-
|
3 |
-
grunt.config('sass', {
|
4 |
-
options : {
|
5 |
-
outputStyle : 'compressed',
|
6 |
-
},
|
7 |
-
core : {
|
8 |
-
files : [{
|
9 |
-
expand : true,
|
10 |
-
cwd : '<%= paths.sass.base_src %>/',
|
11 |
-
dest : '<%= paths.sass.base_dest %>/',
|
12 |
-
src : ['<%= paths.sass.target %>', '<%= paths.sass.exclude %>'],
|
13 |
-
ext : '<%= paths.sass.ext %>'
|
14 |
-
}]
|
15 |
-
},
|
16 |
-
themes : {
|
17 |
-
options : {
|
18 |
-
//includePaths : require('node-bourbon').includePaths
|
19 |
-
},
|
20 |
-
files : [{
|
21 |
-
expand : true,
|
22 |
-
cwd : 'themes/',
|
23 |
-
src : ['*/**/*.scss', '<%= paths.sass.exclude %>'],
|
24 |
-
dest : '<%= paths.sass.dest %>/',
|
25 |
-
srcd : '<%= paths.sass.src %>/',
|
26 |
-
ext : '<%= paths.sass.ext %>',
|
27 |
-
rename : function(dest, matchedSrcPath, options) {
|
28 |
-
var path = [options.cwd, matchedSrcPath.replace(options.srcd, dest)].join('');
|
29 |
-
return path;
|
30 |
-
}
|
31 |
-
}]
|
32 |
-
}
|
33 |
-
});
|
34 |
-
|
35 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
grunt/uglify.js
DELETED
@@ -1,21 +0,0 @@
|
|
1 |
-
module.exports = function(grunt) {
|
2 |
-
|
3 |
-
grunt.config('uglify', {
|
4 |
-
options : {
|
5 |
-
mangle: false,
|
6 |
-
report: 'min'
|
7 |
-
},
|
8 |
-
all : {
|
9 |
-
files : [{
|
10 |
-
expand : true,
|
11 |
-
cwd : '',
|
12 |
-
dest : '',
|
13 |
-
src : ['<%= paths.js.files %>'],
|
14 |
-
rename : function(dest, srcPath) {
|
15 |
-
return srcPath.replace('/' + grunt.config.get('paths.js.src') + '/', '/' + grunt.config.get('paths.js.dest') + '/');
|
16 |
-
}
|
17 |
-
}]
|
18 |
-
},
|
19 |
-
});
|
20 |
-
|
21 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
grunt/watch.js
DELETED
@@ -1,57 +0,0 @@
|
|
1 |
-
module.exports = function(grunt) {
|
2 |
-
|
3 |
-
grunt.config('watch', {
|
4 |
-
phplint : {
|
5 |
-
files : '<%= paths.php.files_std %>',
|
6 |
-
tasks : ['phplint'],
|
7 |
-
options : {
|
8 |
-
spawn : false
|
9 |
-
}
|
10 |
-
},
|
11 |
-
sass_core : {
|
12 |
-
files : ['<%= paths.sass.base_src %>/**/*.scss'],
|
13 |
-
tasks : ['sass:core']
|
14 |
-
},
|
15 |
-
sass_themes : {
|
16 |
-
files : ['themes/**/<%= paths.sass.src %>/**/*.scss'],
|
17 |
-
tasks : ['sass:themes']
|
18 |
-
},
|
19 |
-
jshint : {
|
20 |
-
files : '<%= paths.js.files_std %>',
|
21 |
-
tasks : ['jshint:all'],
|
22 |
-
options : {
|
23 |
-
spawn : false
|
24 |
-
}
|
25 |
-
},
|
26 |
-
js : {
|
27 |
-
files : '<%= paths.js.files_std %>',
|
28 |
-
tasks : ['jshint:all', 'uglify:all'],
|
29 |
-
options : {
|
30 |
-
spawn : false
|
31 |
-
}
|
32 |
-
}
|
33 |
-
});
|
34 |
-
|
35 |
-
grunt.event.on('watch', function(action, filepath) {
|
36 |
-
// Determine task based on filepath
|
37 |
-
var get_ext = function(path) {
|
38 |
-
var ret = '';
|
39 |
-
var i = path.lastIndexOf('.');
|
40 |
-
if ( -1 !== i && i <= path.length ) {
|
41 |
-
ret = path.substr(i + 1);
|
42 |
-
}
|
43 |
-
return ret;
|
44 |
-
};
|
45 |
-
switch ( get_ext(filepath) ) {
|
46 |
-
// PHP
|
47 |
-
case 'php' :
|
48 |
-
grunt.config('paths.php.files', [filepath]);
|
49 |
-
break;
|
50 |
-
// JavaScript
|
51 |
-
case 'js' :
|
52 |
-
grunt.config('paths.js.files', [filepath]);
|
53 |
-
break;
|
54 |
-
}
|
55 |
-
});
|
56 |
-
|
57 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/class.content_base.php
CHANGED
@@ -1,615 +1,615 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* Content Types - Base Class
|
4 |
-
* Core properties/methods for Content Type derivative classes
|
5 |
-
* @package Cornerstone
|
6 |
-
* @subpackage Content Types
|
7 |
-
* @author Archetyped
|
8 |
-
*/
|
9 |
-
class CNR_Content_Base extends CNR_Base {
|
10 |
-
|
11 |
-
/**
|
12 |
-
* Base class name
|
13 |
-
* @var string
|
14 |
-
*/
|
15 |
-
var $base_class = 'cnr_content_base';
|
16 |
-
|
17 |
-
/**
|
18 |
-
* @var string Unique name
|
19 |
-
*/
|
20 |
-
var $id = '';
|
21 |
-
|
22 |
-
/**
|
23 |
-
* Reference to parent object that current instance inherits from
|
24 |
-
* @var object
|
25 |
-
*/
|
26 |
-
var $parent = null;
|
27 |
-
|
28 |
-
/**
|
29 |
-
* Title
|
30 |
-
* @var string
|
31 |
-
*/
|
32 |
-
var $title = '';
|
33 |
-
|
34 |
-
/**
|
35 |
-
* Plural Title
|
36 |
-
* @var string
|
37 |
-
*/
|
38 |
-
var $title_plural = '';
|
39 |
-
|
40 |
-
/**
|
41 |
-
* @var string Short description
|
42 |
-
*/
|
43 |
-
var $description = '';
|
44 |
-
|
45 |
-
/**
|
46 |
-
* @var array Object Properties
|
47 |
-
*/
|
48 |
-
var $properties = array();
|
49 |
-
|
50 |
-
/**
|
51 |
-
* Data for object
|
52 |
-
* May also contain data for nested objects
|
53 |
-
* @var mixed
|
54 |
-
*/
|
55 |
-
var $data = null;
|
56 |
-
|
57 |
-
/**
|
58 |
-
* @var array Script resources to include for object
|
59 |
-
*/
|
60 |
-
var $scripts = array();
|
61 |
-
|
62 |
-
/**
|
63 |
-
* @var array CSS style resources to include for object
|
64 |
-
*/
|
65 |
-
var $styles = array();
|
66 |
-
|
67 |
-
/**
|
68 |
-
* Hooks (Filters/Actions) for object
|
69 |
-
* @var array
|
70 |
-
*/
|
71 |
-
var $hooks = array();
|
72 |
-
|
73 |
-
/**
|
74 |
-
* Constructor
|
75 |
-
*/
|
76 |
-
function __construct($id = '', $parent = null) {
|
77 |
-
parent::__construct();
|
78 |
-
$id = trim($id);
|
79 |
-
$this->id = $id;
|
80 |
-
if ( is_bool($parent) && $parent )
|
81 |
-
$parent = $id;
|
82 |
-
$this->set_parent($parent);
|
83 |
-
}
|
84 |
-
|
85 |
-
/* Getters/Setters */
|
86 |
-
|
87 |
-
/**
|
88 |
-
* Checks if the specified path exists in the object
|
89 |
-
* @param array $path Path to check for
|
90 |
-
* @return bool TRUE if path exists in object, FALSE otherwise
|
91 |
-
*/
|
92 |
-
function path_isset($path = '') {
|
93 |
-
//Stop execution if no path is supplied
|
94 |
-
if ( empty($path) )
|
95 |
-
return false;
|
96 |
-
$args = func_get_args();
|
97 |
-
$path = $this->util->build_path($args);
|
98 |
-
$item =& $this;
|
99 |
-
//Iterate over path and check if each level exists before moving on to the next
|
100 |
-
for ($x = 0; $x < count($path); $x++) {
|
101 |
-
if ( $this->util->property_exists($item, $path[$x]) ) {
|
102 |
-
//Set $item as reference to next level in path for next iteration
|
103 |
-
$item =& $this->util->get_property($item, $path[$x]);
|
104 |
-
//$item =& $item[ $path[$x] ];
|
105 |
-
} else {
|
106 |
-
return false;
|
107 |
-
}
|
108 |
-
}
|
109 |
-
return true;
|
110 |
-
}
|
111 |
-
|
112 |
-
/**
|
113 |
-
* Retrieves a value from object using a specified path
|
114 |
-
* Checks to make sure path exists in object before retrieving value
|
115 |
-
* @param array $path Path to retrieve value from. Each item in array is a deeper dimension
|
116 |
-
* @return mixed Value at specified path
|
117 |
-
*/
|
118 |
-
function &get_path_value($path = '') {
|
119 |
-
$ret = '';
|
120 |
-
$path = $this->util->build_path(func_get_args());
|
121 |
-
if ( $this->path_isset($path) ) {
|
122 |
-
$ret =& $this;
|
123 |
-
for ($x = 0; $x < count($path); $x++) {
|
124 |
-
if ( 0 == $x )
|
125 |
-
$ret =& $ret->{ $path[$x] };
|
126 |
-
else
|
127 |
-
$ret =& $ret[ $path[$x] ];
|
128 |
-
}
|
129 |
-
}
|
130 |
-
return $ret;
|
131 |
-
}
|
132 |
-
|
133 |
-
/**
|
134 |
-
* Search for specified member value in field type ancestors
|
135 |
-
* @param string $member Name of object member to search (e.g. properties, layout, etc.)
|
136 |
-
* @param string $name Value to retrieve from member
|
137 |
-
* @return mixed Member value if found (Default: empty string)
|
138 |
-
*/
|
139 |
-
function get_parent_value($member, $name = '', $default = '') {
|
140 |
-
$parent =& $this->get_parent();
|
141 |
-
return $this->get_object_value($parent, $member, $name, $default, 'parent');
|
142 |
-
}
|
143 |
-
|
144 |
-
/**
|
145 |
-
* Retrieves specified member value
|
146 |
-
* Handles inherited values
|
147 |
-
* Merging corresponding parents if value is an array (e.g. for property groups)
|
148 |
-
* @param string|array $member Member to search. May also contain a path to the desired member
|
149 |
-
* @param string $name Value to retrieve from member
|
150 |
-
* @param mixed $default Default value if no value found (Default: empty string)
|
151 |
-
* @param string $dir Direction to move through hierarchy to find value
|
152 |
-
* Possible Values:
|
153 |
-
* parent (default) - Search through field parents
|
154 |
-
* current - Do not search through connected objects
|
155 |
-
* container - Search through field containers
|
156 |
-
* caller - Search through field callers
|
157 |
-
* @return mixed Specified member value
|
158 |
-
*/
|
159 |
-
function get_member_value($member, $name = '', $default = '', $dir = 'parent') {
|
160 |
-
//Check if path to member is supplied
|
161 |
-
$path = array();
|
162 |
-
if ( is_array($member) && isset($member['tag']) ) {
|
163 |
-
if ( isset($member['attributes']['ref_base']) ) {
|
164 |
-
if ( 'root' != $member['attributes']['ref_base'] )
|
165 |
-
$path[] = $member['attributes']['ref_base'];
|
166 |
-
} else {
|
167 |
-
$path[] = 'properties';
|
168 |
-
}
|
169 |
-
|
170 |
-
$path[] = $member['tag'];
|
171 |
-
} else {
|
172 |
-
$path = $member;
|
173 |
-
}
|
174 |
-
|
175 |
-
$path = $this->util->build_path($path, $name);
|
176 |
-
//Set defaults and prepare data
|
177 |
-
$val = $default;
|
178 |
-
$inherit = false;
|
179 |
-
$inherit_tag = '{inherit}';
|
180 |
-
|
181 |
-
/* Determine whether the value must be retrieved from a parent/container object
|
182 |
-
* Conditions:
|
183 |
-
* > Path does not exist in current field
|
184 |
-
* > Path exists and is not an object, but at least one of the following is true:
|
185 |
-
* > Value at path is an array (e.g. properties, elements, etc. array)
|
186 |
-
* > Parent/container values should be merged with retrieved array
|
187 |
-
* > Value at path is a string that inherits from another field
|
188 |
-
* > Value from other field will be retrieved and will replace inheritance placeholder in retrieved value
|
189 |
-
*/
|
190 |
-
|
191 |
-
$deeper = false;
|
192 |
-
|
193 |
-
if ( !$this->path_isset($path) )
|
194 |
-
$deeper = true;
|
195 |
-
else {
|
196 |
-
$val = $this->get_path_value($path);
|
197 |
-
if ( !is_object($val) && ( is_array($val) || ($inherit = strpos($val, $inherit_tag)) !== false ) )
|
198 |
-
$deeper = true;
|
199 |
-
else
|
200 |
-
$deeper = false;
|
201 |
-
}
|
202 |
-
if ( $deeper && 'current' != $dir ) {
|
203 |
-
//Get Parent value (recursive)
|
204 |
-
$ex_val = ( 'parent' != $dir ) ? $this->get_container_value($member, $name, $default) : $this->get_parent_value($member, $name, $default);
|
205 |
-
//Handle inheritance
|
206 |
-
if ( is_array($val) ) {
|
207 |
-
//Combine Arrays
|
208 |
-
if ( is_array($ex_val) )
|
209 |
-
$val = array_merge($ex_val, $val);
|
210 |
-
} elseif ( $inherit !== false ) {
|
211 |
-
//Replace placeholder with inherited string
|
212 |
-
$val = str_replace($inherit_tag, $ex_val, $val);
|
213 |
-
} else {
|
214 |
-
//Default: Set parent value as value
|
215 |
-
$val = $ex_val;
|
216 |
-
}
|
217 |
-
}
|
218 |
-
|
219 |
-
return $val;
|
220 |
-
}
|
221 |
-
|
222 |
-
/**
|
223 |
-
* Search for specified member value in an object
|
224 |
-
* @param object $object Reference to object to retrieve value from
|
225 |
-
* @param string $member Name of object member to search (e.g. properties, layout, etc.)
|
226 |
-
* @param string $name (optional) Value to retrieve from member
|
227 |
-
* @param mixed $default (optional) Default value to use if no value found (Default: empty string)
|
228 |
-
* @param string $dir Direction to move through hierarchy to find value @see CNR_Field_Type::get_member_value() for possible values
|
229 |
-
* @return mixed Member value if found (Default: $default)
|
230 |
-
*/
|
231 |
-
function get_object_value(&$object, $member, $name = '', $default = '', $dir = 'parent') {
|
232 |
-
$ret = $default;
|
233 |
-
if ( is_object($object) && method_exists($object, 'get_member_value') )
|
234 |
-
$ret = $object->get_member_value($member, $name, $default, $dir);
|
235 |
-
return $ret;
|
236 |
-
}
|
237 |
-
|
238 |
-
/**
|
239 |
-
* Retrieve value from data member
|
240 |
-
* @param bool $top (optional) Whether to traverse through the field hierarchy to get data for field (Default: TRUE)
|
241 |
-
* @return mixed Value at specified path
|
242 |
-
*/
|
243 |
-
function get_data($top = true) {
|
244 |
-
$top = !!$top;
|
245 |
-
$obj = $this;
|
246 |
-
$obj_path = array($this);
|
247 |
-
$path = array();
|
248 |
-
//Iterate through hiearchy to get top-most object
|
249 |
-
while ( !empty($obj) ) {
|
250 |
-
$new = null;
|
251 |
-
//Try to get caller first
|
252 |
-
if ( method_exists($obj, 'get_caller') ) {
|
253 |
-
$checked = true;
|
254 |
-
$new = $obj->get_caller();
|
255 |
-
}
|
256 |
-
//Try to get container if no caller found
|
257 |
-
if ( empty($new) && method_exists($obj, 'get_container') ) {
|
258 |
-
$checked = true;
|
259 |
-
$new = $obj->get_container();
|
260 |
-
}
|
261 |
-
|
262 |
-
$obj = $new;
|
263 |
-
|
264 |
-
//Stop iteration
|
265 |
-
if ( !empty($obj) ) {
|
266 |
-
//Add object to path if it is valid
|
267 |
-
$obj_path[] = $obj;
|
268 |
-
}
|
269 |
-
}
|
270 |
-
|
271 |
-
//Check each object (starting with top-most) for matching data for current field
|
272 |
-
|
273 |
-
//Reverse array
|
274 |
-
$obj_path = array_reverse($obj_path);
|
275 |
-
//Build path for data location
|
276 |
-
foreach ( $obj_path as $obj ) {
|
277 |
-
if ( $this->util->property_exists($obj, 'id') )
|
278 |
-
$path[] = $obj->id;
|
279 |
-
}
|
280 |
-
|
281 |
-
//Iterate through objects
|
282 |
-
while ( !empty($obj_path) ) {
|
283 |
-
//Get next object
|
284 |
-
$obj = array_shift($obj_path);
|
285 |
-
//Shorten path
|
286 |
-
array_shift($path);
|
287 |
-
//Check for value in object and stop iteration if matching data found
|
288 |
-
if ( ($val = $this->get_object_value($obj, 'data', $path, null, 'current')) && !is_null($val) ) {
|
289 |
-
break;
|
290 |
-
}
|
291 |
-
}
|
292 |
-
|
293 |
-
return $val;
|
294 |
-
}
|
295 |
-
|
296 |
-
/**
|
297 |
-
* Sets value in data member
|
298 |
-
* Sets value to data member itself by default
|
299 |
-
* @param mixed $value Value to set
|
300 |
-
* @param string|array $name Name of value to set (Can also be path to value)
|
301 |
-
*/
|
302 |
-
function set_data($value, $name = '') {
|
303 |
-
$ref =& $this->get_path_value('data', $name);
|
304 |
-
$ref = $value;
|
305 |
-
}
|
306 |
-
|
307 |
-
/**
|
308 |
-
* Retrieve base_class property
|
309 |
-
* @return string base_class property of current class/instance object
|
310 |
-
*/
|
311 |
-
static function get_base_class() {
|
312 |
-
$ret = '';
|
313 |
-
if ( isset($this) )
|
314 |
-
$ret = $this->base_class;
|
315 |
-
else {
|
316 |
-
$ret = CNR_Utilities::get_property(__CLASS__, 'base_class');
|
317 |
-
}
|
318 |
-
|
319 |
-
return $ret;
|
320 |
-
}
|
321 |
-
|
322 |
-
/**
|
323 |
-
* Sets parent object of current instance
|
324 |
-
* Parent objects must be the same object type as current instance
|
325 |
-
* @param string|object $parent Parent ID or reference
|
326 |
-
*/
|
327 |
-
function set_parent($parent) {
|
328 |
-
if ( !empty($parent) ) {
|
329 |
-
//Validate parent object
|
330 |
-
if ( is_array($parent) )
|
331 |
-
$parent =& $parent[0];
|
332 |
-
|
333 |
-
//Retrieve reference object if ID was supplied
|
334 |
-
if ( is_string($parent) ) {
|
335 |
-
$parent = trim($parent);
|
336 |
-
//Check for existence of parent
|
337 |
-
$lookup = $this->base_class . 's';
|
338 |
-
if ( isset($GLOBALS[$lookup][$parent]) ) {
|
339 |
-
//Get reference to parent
|
340 |
-
$parent =& $GLOBALS[$lookup][$parent];
|
341 |
-
}
|
342 |
-
}
|
343 |
-
|
344 |
-
//Set reference to parent field type
|
345 |
-
if ( is_a($parent, $this->base_class) ) {
|
346 |
-
$this->parent =& $parent;
|
347 |
-
}
|
348 |
-
}
|
349 |
-
}
|
350 |
-
|
351 |
-
/**
|
352 |
-
* Retrieve field type parent
|
353 |
-
* @return CNR_Field_Type Reference to parent field
|
354 |
-
*/
|
355 |
-
function &get_parent() {
|
356 |
-
return $this->parent;
|
357 |
-
}
|
358 |
-
|
359 |
-
/**
|
360 |
-
* Retrieves field ID
|
361 |
-
* @param string|CNR_Field|array $field (optional) Field object or ID of field or options array
|
362 |
-
* @return string|bool Field ID, FALSE if $field is invalid
|
363 |
-
*/
|
364 |
-
function get_id($field = null) {
|
365 |
-
$ret = false;
|
366 |
-
|
367 |
-
// Get options.
|
368 |
-
$num_args = func_num_args();
|
369 |
-
$options = ( $num_args > 0 && ( $last_arg = func_get_arg($num_args - 1) ) && is_array($last_arg) ) ? $last_arg : array();
|
370 |
-
|
371 |
-
// Validate field parameter.
|
372 |
-
if ( ( !is_object($field) || !is_a($field, 'cnr_field_type') ) && isset($this) ) {
|
373 |
-
$field =& $this;
|
374 |
-
}
|
375 |
-
|
376 |
-
if ( is_a($field, CNR_Field_Type::get_base_class()) )
|
377 |
-
$id = $field->id;
|
378 |
-
|
379 |
-
if ( is_string($id) )
|
380 |
-
$ret = trim($id);
|
381 |
-
|
382 |
-
// Setup options.
|
383 |
-
$options = wp_parse_args( $options, [ 'format' => null ] );
|
384 |
-
//Check if field should be formatted
|
385 |
-
if ( is_string($ret) && !empty($options['format']) ) {
|
386 |
-
//Clear format option if it is an invalid value
|
387 |
-
if ( is_bool($options['format']) || is_int($options['format']) )
|
388 |
-
$options['format'] = null;
|
389 |
-
//Setup values
|
390 |
-
$wrap = array('open' => '[', 'close' => ']');
|
391 |
-
if ( isset($options['wrap']) && is_array($options['wrap']) )
|
392 |
-
$wrap = wp_parse_args($options['wrap'], $wrap);
|
393 |
-
$wrap_trailing = ( isset($options['wrap_trailing']) ) ? !!$options['wrap_trailing'] : true;
|
394 |
-
switch ( $options['format'] ) {
|
395 |
-
case 'attr_id' :
|
396 |
-
$wrap = (array('open' => '_', 'close' => '_'));
|
397 |
-
$wrap_trailing = false;
|
398 |
-
break;
|
399 |
-
}
|
400 |
-
$c = $field->get_caller();
|
401 |
-
$field_id = array($ret);
|
402 |
-
while ( !!$c ) {
|
403 |
-
//Add ID of current field to array
|
404 |
-
if ( isset($c->id) && is_a($c, $this->base_class) )
|
405 |
-
$field_id[] = $c->id;
|
406 |
-
$c = ( method_exists($c, 'get_caller') ) ? $c->get_caller() : null;
|
407 |
-
}
|
408 |
-
|
409 |
-
//Add prefix to ID value
|
410 |
-
$field_id[] = 'attributes';
|
411 |
-
|
412 |
-
//Convert array to string
|
413 |
-
$ret = $field->prefix . $wrap['open'] . implode($wrap['close'] . $wrap['open'], array_reverse($field_id)) . ( $wrap_trailing ? $wrap['close'] : '');
|
414 |
-
}
|
415 |
-
return $ret;
|
416 |
-
}
|
417 |
-
|
418 |
-
/**
|
419 |
-
* Set object title
|
420 |
-
* @param string $title Title for object
|
421 |
-
* @param string $plural Plural form of title
|
422 |
-
*/
|
423 |
-
function set_title($title = '', $plural = '') {
|
424 |
-
$this->title = strip_tags(trim($title));
|
425 |
-
if ( isset($plural) )
|
426 |
-
$this->title_plural = strip_tags(trim($plural));
|
427 |
-
}
|
428 |
-
|
429 |
-
/**
|
430 |
-
* Retrieve object title
|
431 |
-
* @param bool $plural TRUE if plural title should be retrieved, FALSE otherwise (Default: FALSE)
|
432 |
-
*/
|
433 |
-
function get_title($plural = false) {
|
434 |
-
$dir = 'current';
|
435 |
-
//Singular
|
436 |
-
if ( !$plural )
|
437 |
-
return $this->get_member_value('title', '','', $dir);
|
438 |
-
//Plural
|
439 |
-
$title = $this->get_member_value('title_plural', '', '', $dir);
|
440 |
-
if ( empty($title) ) {
|
441 |
-
//Use singular title for plural base
|
442 |
-
$title = $this->get_member_value('title', '', '', $dir);
|
443 |
-
//Determine technique for making title plural
|
444 |
-
//Get last letter
|
445 |
-
if ( !empty($title) ) {
|
446 |
-
$tail = substr($title, -1);
|
447 |
-
switch ( $tail ) {
|
448 |
-
case 's' :
|
449 |
-
$title .= 'es';
|
450 |
-
break;
|
451 |
-
case 'y' :
|
452 |
-
$title = substr($title, 0, -1) . 'ies';
|
453 |
-
break;
|
454 |
-
default :
|
455 |
-
$title .= 's';
|
456 |
-
}
|
457 |
-
}
|
458 |
-
}
|
459 |
-
return $title;
|
460 |
-
}
|
461 |
-
|
462 |
-
/**
|
463 |
-
* Set object description
|
464 |
-
* @param string $description Description for object
|
465 |
-
*/
|
466 |
-
function set_description($description = '') {
|
467 |
-
$this->description = strip_tags(trim($description));
|
468 |
-
}
|
469 |
-
|
470 |
-
/**
|
471 |
-
* Retrieve object description
|
472 |
-
* @return string Object description
|
473 |
-
*/
|
474 |
-
function get_description() {
|
475 |
-
$dir = 'current';
|
476 |
-
return $this->get_member_value('description', '','', $dir);
|
477 |
-
return $desc;
|
478 |
-
}
|
479 |
-
|
480 |
-
/*-** Hooks **-*/
|
481 |
-
|
482 |
-
/**
|
483 |
-
* Retrieve hooks added to object
|
484 |
-
* @return array Hooks
|
485 |
-
*/
|
486 |
-
function get_hooks() {
|
487 |
-
return $this->get_member_value('hooks', '', array());
|
488 |
-
}
|
489 |
-
|
490 |
-
/**
|
491 |
-
* Add hook for object
|
492 |
-
* @see add_filter() for parameter defaults
|
493 |
-
* @param $tag
|
494 |
-
* @param $function_to_add
|
495 |
-
* @param $priority
|
496 |
-
* @param $accepted_args
|
497 |
-
*/
|
498 |
-
function add_hook($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
|
499 |
-
//Create new array for tag (if not already set)
|
500 |
-
if ( !isset($this->hooks[$tag]) )
|
501 |
-
$this->hooks[$tag] = array();
|
502 |
-
//Build Unique ID
|
503 |
-
if ( is_string($function_to_add) )
|
504 |
-
$id = $function_to_add;
|
505 |
-
elseif ( is_array($function_to_add) && !empty($function_to_add) )
|
506 |
-
$id = strval($function_to_add[count($function_to_add) - 1]);
|
507 |
-
else
|
508 |
-
$id = 'function_' . ( count($this->hooks[$tag]) + 1 );
|
509 |
-
//Add hook
|
510 |
-
$this->hooks[$tag][$id] = func_get_args();
|
511 |
-
}
|
512 |
-
|
513 |
-
/**
|
514 |
-
* Convenience method for adding an action for object
|
515 |
-
* @see add_filter() for parameter defaults
|
516 |
-
* @param $tag
|
517 |
-
* @param $function_to_add
|
518 |
-
* @param $priority
|
519 |
-
* @param $accepted_args
|
520 |
-
*/
|
521 |
-
function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
|
522 |
-
$this->add_hook($tag, $function_to_add, $priority, $accepted_args);
|
523 |
-
}
|
524 |
-
|
525 |
-
/**
|
526 |
-
* Convenience method for adding a filter for object
|
527 |
-
* @see add_filter() for parameter defaults
|
528 |
-
* @param $tag
|
529 |
-
* @param $function_to_add
|
530 |
-
* @param $priority
|
531 |
-
* @param $accepted_args
|
532 |
-
*/
|
533 |
-
function add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
|
534 |
-
$this->add_hook($tag, $function_to_add, $priority, $accepted_args);
|
535 |
-
}
|
536 |
-
|
537 |
-
/*-** Dependencies **-*/
|
538 |
-
|
539 |
-
/**
|
540 |
-
* Adds dependency to object
|
541 |
-
* @param string $type Type of dependency to add (script, style)
|
542 |
-
* @param array|string $context When dependency will be added (@see CNR_Utilities::get_action() for possible contexts)
|
543 |
-
* @see wp_enqueue_script for the following of the parameters
|
544 |
-
* @param $handle
|
545 |
-
* @param $src
|
546 |
-
* @param $deps
|
547 |
-
* @param $ver
|
548 |
-
* @param $ex
|
549 |
-
*/
|
550 |
-
function add_dependency($type, $context, $handle, $src = false, $deps = array(), $ver = false, $ex = false) {
|
551 |
-
$args = func_get_args();
|
552 |
-
//Remove type/context from arguments
|
553 |
-
$args = array_slice($args, 2);
|
554 |
-
|
555 |
-
//Set context
|
556 |
-
if ( !is_array($context) ) {
|
557 |
-
//Wrap single contexts in an array
|
558 |
-
if ( is_string($context) )
|
559 |
-
$context = array($context);
|
560 |
-
else
|
561 |
-
$context = array();
|
562 |
-
}
|
563 |
-
//Add file to instance property
|
564 |
-
$this->{$type}[$handle] = array('context' => $context, 'params' => $args);
|
565 |
-
}
|
566 |
-
|
567 |
-
/**
|
568 |
-
* Add script to object to be added in specified contexts
|
569 |
-
* @param array|string $context Array of contexts to add script to page
|
570 |
-
* @see wp_enqueue_script for the following of the parameters
|
571 |
-
* @param $handle
|
572 |
-
* @param $src
|
573 |
-
* @param $deps
|
574 |
-
* @param $ver
|
575 |
-
* @param $in_footer
|
576 |
-
*/
|
577 |
-
function add_script( $context, $handle, $src = false, $deps = array(), $ver = false, $in_footer = false ) {
|
578 |
-
$args = func_get_args();
|
579 |
-
//Add file type to front of arguments array
|
580 |
-
array_unshift($args, 'scripts');
|
581 |
-
call_user_func_array(array(&$this, 'add_dependency'), $args);
|
582 |
-
}
|
583 |
-
|
584 |
-
/**
|
585 |
-
* Retrieve script dependencies for object
|
586 |
-
* @return array Script dependencies
|
587 |
-
*/
|
588 |
-
function get_scripts() {
|
589 |
-
return $this->get_member_value('scripts', '', array());
|
590 |
-
}
|
591 |
-
|
592 |
-
/**
|
593 |
-
* Add style to object to be added in specified contexts
|
594 |
-
* @param array|string $context Array of contexts to add style to page
|
595 |
-
* @see wp_enqueue_style for the following of the parameters
|
596 |
-
* @param $handle
|
597 |
-
* @param $src
|
598 |
-
* @param $deps
|
599 |
-
* @param $ver
|
600 |
-
* @param $in_footer
|
601 |
-
*/
|
602 |
-
function add_style( $handle, $src = false, $deps = array(), $ver = false, $media = false ) {
|
603 |
-
$args = func_get_args();
|
604 |
-
array_unshift($args, 'styles');
|
605 |
-
call_user_method_array('add_dependency', $this, $args);
|
606 |
-
}
|
607 |
-
|
608 |
-
/**
|
609 |
-
* Retrieve Style dependencies for object
|
610 |
-
* @return array Style dependencies
|
611 |
-
*/
|
612 |
-
function get_styles() {
|
613 |
-
return $this->get_member_value('styles', '', array());
|
614 |
-
}
|
615 |
}
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Content Types - Base Class
|
4 |
+
* Core properties/methods for Content Type derivative classes
|
5 |
+
* @package Cornerstone
|
6 |
+
* @subpackage Content Types
|
7 |
+
* @author Archetyped
|
8 |
+
*/
|
9 |
+
class CNR_Content_Base extends CNR_Base {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Base class name
|
13 |
+
* @var string
|
14 |
+
*/
|
15 |
+
var $base_class = 'cnr_content_base';
|
16 |
+
|
17 |
+
/**
|
18 |
+
* @var string Unique name
|
19 |
+
*/
|
20 |
+
var $id = '';
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Reference to parent object that current instance inherits from
|
24 |
+
* @var object
|
25 |
+
*/
|
26 |
+
var $parent = null;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Title
|
30 |
+
* @var string
|
31 |
+
*/
|
32 |
+
var $title = '';
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Plural Title
|
36 |
+
* @var string
|
37 |
+
*/
|
38 |
+
var $title_plural = '';
|
39 |
+
|
40 |
+
/**
|
41 |
+
* @var string Short description
|
42 |
+
*/
|
43 |
+
var $description = '';
|
44 |
+
|
45 |
+
/**
|
46 |
+
* @var array Object Properties
|
47 |
+
*/
|
48 |
+
var $properties = array();
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Data for object
|
52 |
+
* May also contain data for nested objects
|
53 |
+
* @var mixed
|
54 |
+
*/
|
55 |
+
var $data = null;
|
56 |
+
|
57 |
+
/**
|
58 |
+
* @var array Script resources to include for object
|
59 |
+
*/
|
60 |
+
var $scripts = array();
|
61 |
+
|
62 |
+
/**
|
63 |
+
* @var array CSS style resources to include for object
|
64 |
+
*/
|
65 |
+
var $styles = array();
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Hooks (Filters/Actions) for object
|
69 |
+
* @var array
|
70 |
+
*/
|
71 |
+
var $hooks = array();
|
72 |
+
|
73 |
+
/**
|
74 |
+
* Constructor
|
75 |
+
*/
|
76 |
+
function __construct($id = '', $parent = null) {
|
77 |
+
parent::__construct();
|
78 |
+
$id = trim($id);
|
79 |
+
$this->id = $id;
|
80 |
+
if ( is_bool($parent) && $parent )
|
81 |
+
$parent = $id;
|
82 |
+
$this->set_parent($parent);
|
83 |
+
}
|
84 |
+
|
85 |
+
/* Getters/Setters */
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Checks if the specified path exists in the object
|
89 |
+
* @param array $path Path to check for
|
90 |
+
* @return bool TRUE if path exists in object, FALSE otherwise
|
91 |
+
*/
|
92 |
+
function path_isset($path = '') {
|
93 |
+
//Stop execution if no path is supplied
|
94 |
+
if ( empty($path) )
|
95 |
+
return false;
|
96 |
+
$args = func_get_args();
|
97 |
+
$path = $this->util->build_path($args);
|
98 |
+
$item =& $this;
|
99 |
+
//Iterate over path and check if each level exists before moving on to the next
|
100 |
+
for ($x = 0; $x < count($path); $x++) {
|
101 |
+
if ( $this->util->property_exists($item, $path[$x]) ) {
|
102 |
+
//Set $item as reference to next level in path for next iteration
|
103 |
+
$item =& $this->util->get_property($item, $path[$x]);
|
104 |
+
//$item =& $item[ $path[$x] ];
|
105 |
+
} else {
|
106 |
+
return false;
|
107 |
+
}
|
108 |
+
}
|
109 |
+
return true;
|
110 |
+
}
|
111 |
+
|
112 |
+
/**
|
113 |
+
* Retrieves a value from object using a specified path
|
114 |
+
* Checks to make sure path exists in object before retrieving value
|
115 |
+
* @param array $path Path to retrieve value from. Each item in array is a deeper dimension
|
116 |
+
* @return mixed Value at specified path
|
117 |
+
*/
|
118 |
+
function &get_path_value($path = '') {
|
119 |
+
$ret = '';
|
120 |
+
$path = $this->util->build_path(func_get_args());
|
121 |
+
if ( $this->path_isset($path) ) {
|
122 |
+
$ret =& $this;
|
123 |
+
for ($x = 0; $x < count($path); $x++) {
|
124 |
+
if ( 0 == $x )
|
125 |
+
$ret =& $ret->{ $path[$x] };
|
126 |
+
else
|
127 |
+
$ret =& $ret[ $path[$x] ];
|
128 |
+
}
|
129 |
+
}
|
130 |
+
return $ret;
|
131 |
+
}
|
132 |
+
|
133 |
+
/**
|
134 |
+
* Search for specified member value in field type ancestors
|
135 |
+
* @param string $member Name of object member to search (e.g. properties, layout, etc.)
|
136 |
+
* @param string $name Value to retrieve from member
|
137 |
+
* @return mixed Member value if found (Default: empty string)
|
138 |
+
*/
|
139 |
+
function get_parent_value($member, $name = '', $default = '') {
|
140 |
+
$parent =& $this->get_parent();
|
141 |
+
return $this->get_object_value($parent, $member, $name, $default, 'parent');
|
142 |
+
}
|
143 |
+
|
144 |
+
/**
|
145 |
+
* Retrieves specified member value
|
146 |
+
* Handles inherited values
|
147 |
+
* Merging corresponding parents if value is an array (e.g. for property groups)
|
148 |
+
* @param string|array $member Member to search. May also contain a path to the desired member
|
149 |
+
* @param string $name Value to retrieve from member
|
150 |
+
* @param mixed $default Default value if no value found (Default: empty string)
|
151 |
+
* @param string $dir Direction to move through hierarchy to find value
|
152 |
+
* Possible Values:
|
153 |
+
* parent (default) - Search through field parents
|
154 |
+
* current - Do not search through connected objects
|
155 |
+
* container - Search through field containers
|
156 |
+
* caller - Search through field callers
|
157 |
+
* @return mixed Specified member value
|
158 |
+
*/
|
159 |
+
function get_member_value($member, $name = '', $default = '', $dir = 'parent') {
|
160 |
+
//Check if path to member is supplied
|
161 |
+
$path = array();
|
162 |
+
if ( is_array($member) && isset($member['tag']) ) {
|
163 |
+
if ( isset($member['attributes']['ref_base']) ) {
|
164 |
+
if ( 'root' != $member['attributes']['ref_base'] )
|
165 |
+
$path[] = $member['attributes']['ref_base'];
|
166 |
+
} else {
|
167 |
+
$path[] = 'properties';
|
168 |
+
}
|
169 |
+
|
170 |
+
$path[] = $member['tag'];
|
171 |
+
} else {
|
172 |
+
$path = $member;
|
173 |
+
}
|
174 |
+
|
175 |
+
$path = $this->util->build_path($path, $name);
|
176 |
+
//Set defaults and prepare data
|
177 |
+
$val = $default;
|
178 |
+
$inherit = false;
|
179 |
+
$inherit_tag = '{inherit}';
|
180 |
+
|
181 |
+
/* Determine whether the value must be retrieved from a parent/container object
|
182 |
+
* Conditions:
|
183 |
+
* > Path does not exist in current field
|
184 |
+
* > Path exists and is not an object, but at least one of the following is true:
|
185 |
+
* > Value at path is an array (e.g. properties, elements, etc. array)
|
186 |
+
* > Parent/container values should be merged with retrieved array
|
187 |
+
* > Value at path is a string that inherits from another field
|
188 |
+
* > Value from other field will be retrieved and will replace inheritance placeholder in retrieved value
|
189 |
+
*/
|
190 |
+
|
191 |
+
$deeper = false;
|
192 |
+
|
193 |
+
if ( !$this->path_isset($path) )
|
194 |
+
$deeper = true;
|
195 |
+
else {
|
196 |
+
$val = $this->get_path_value($path);
|
197 |
+
if ( !is_object($val) && ( is_array($val) || ($inherit = strpos($val, $inherit_tag)) !== false ) )
|
198 |
+
$deeper = true;
|
199 |
+
else
|
200 |
+
$deeper = false;
|
201 |
+
}
|
202 |
+
if ( $deeper && 'current' != $dir ) {
|
203 |
+
//Get Parent value (recursive)
|
204 |
+
$ex_val = ( 'parent' != $dir ) ? $this->get_container_value($member, $name, $default) : $this->get_parent_value($member, $name, $default);
|
205 |
+
//Handle inheritance
|
206 |
+
if ( is_array($val) ) {
|
207 |
+
//Combine Arrays
|
208 |
+
if ( is_array($ex_val) )
|
209 |
+
$val = array_merge($ex_val, $val);
|
210 |
+
} elseif ( $inherit !== false ) {
|
211 |
+
//Replace placeholder with inherited string
|
212 |
+
$val = str_replace($inherit_tag, $ex_val, $val);
|
213 |
+
} else {
|
214 |
+
//Default: Set parent value as value
|
215 |
+
$val = $ex_val;
|
216 |
+
}
|
217 |
+
}
|
218 |
+
|
219 |
+
return $val;
|
220 |
+
}
|
221 |
+
|
222 |
+
/**
|
223 |
+
* Search for specified member value in an object
|
224 |
+
* @param object $object Reference to object to retrieve value from
|
225 |
+
* @param string $member Name of object member to search (e.g. properties, layout, etc.)
|
226 |
+
* @param string $name (optional) Value to retrieve from member
|
227 |
+
* @param mixed $default (optional) Default value to use if no value found (Default: empty string)
|
228 |
+
* @param string $dir Direction to move through hierarchy to find value @see CNR_Field_Type::get_member_value() for possible values
|
229 |
+
* @return mixed Member value if found (Default: $default)
|
230 |
+
*/
|
231 |
+
function get_object_value(&$object, $member, $name = '', $default = '', $dir = 'parent') {
|
232 |
+
$ret = $default;
|
233 |
+
if ( is_object($object) && method_exists($object, 'get_member_value') )
|
234 |
+
$ret = $object->get_member_value($member, $name, $default, $dir);
|
235 |
+
return $ret;
|
236 |
+
}
|
237 |
+
|
238 |
+
/**
|
239 |
+
* Retrieve value from data member
|
240 |
+
* @param bool $top (optional) Whether to traverse through the field hierarchy to get data for field (Default: TRUE)
|
241 |
+
* @return mixed Value at specified path
|
242 |
+
*/
|
243 |
+
function get_data($top = true) {
|
244 |
+
$top = !!$top;
|
245 |
+
$obj = $this;
|
246 |
+
$obj_path = array($this);
|
247 |
+
$path = array();
|
248 |
+
//Iterate through hiearchy to get top-most object
|
249 |
+
while ( !empty($obj) ) {
|
250 |
+
$new = null;
|
251 |
+
//Try to get caller first
|
252 |
+
if ( method_exists($obj, 'get_caller') ) {
|
253 |
+
$checked = true;
|
254 |
+
$new = $obj->get_caller();
|
255 |
+
}
|
256 |
+
//Try to get container if no caller found
|
257 |
+
if ( empty($new) && method_exists($obj, 'get_container') ) {
|
258 |
+
$checked = true;
|
259 |
+
$new = $obj->get_container();
|
260 |
+
}
|
261 |
+
|
262 |
+
$obj = $new;
|
263 |
+
|
264 |
+
//Stop iteration
|
265 |
+
if ( !empty($obj) ) {
|
266 |
+
//Add object to path if it is valid
|
267 |
+
$obj_path[] = $obj;
|
268 |
+
}
|
269 |
+
}
|
270 |
+
|
271 |
+
//Check each object (starting with top-most) for matching data for current field
|
272 |
+
|
273 |
+
//Reverse array
|
274 |
+
$obj_path = array_reverse($obj_path);
|
275 |
+
//Build path for data location
|
276 |
+
foreach ( $obj_path as $obj ) {
|
277 |
+
if ( $this->util->property_exists($obj, 'id') )
|
278 |
+
$path[] = $obj->id;
|
279 |
+
}
|
280 |
+
|
281 |
+
//Iterate through objects
|
282 |
+
while ( !empty($obj_path) ) {
|
283 |
+
//Get next object
|
284 |
+
$obj = array_shift($obj_path);
|
285 |
+
//Shorten path
|
286 |
+
array_shift($path);
|
287 |
+
//Check for value in object and stop iteration if matching data found
|
288 |
+
if ( ($val = $this->get_object_value($obj, 'data', $path, null, 'current')) && !is_null($val) ) {
|
289 |
+
break;
|
290 |
+
}
|
291 |
+
}
|
292 |
+
|
293 |
+
return $val;
|
294 |
+
}
|
295 |
+
|
296 |
+
/**
|
297 |
+
* Sets value in data member
|
298 |
+
* Sets value to data member itself by default
|
299 |
+
* @param mixed $value Value to set
|
300 |
+
* @param string|array $name Name of value to set (Can also be path to value)
|
301 |
+
*/
|
302 |
+
function set_data($value, $name = '') {
|
303 |
+
$ref =& $this->get_path_value('data', $name);
|
304 |
+
$ref = $value;
|
305 |
+
}
|
306 |
+
|
307 |
+
/**
|
308 |
+
* Retrieve base_class property
|
309 |
+
* @return string base_class property of current class/instance object
|
310 |
+
*/
|
311 |
+
static function get_base_class() {
|
312 |
+
$ret = '';
|
313 |
+
if ( isset($this) )
|
314 |
+
$ret = $this->base_class;
|
315 |
+
else {
|
316 |
+
$ret = CNR_Utilities::get_property(__CLASS__, 'base_class');
|
317 |
+
}
|
318 |
+
|
319 |
+
return $ret;
|
320 |
+
}
|
321 |
+
|
322 |
+
/**
|
323 |
+
* Sets parent object of current instance
|
324 |
+
* Parent objects must be the same object type as current instance
|
325 |
+
* @param string|object $parent Parent ID or reference
|
326 |
+
*/
|
327 |
+
function set_parent($parent) {
|
328 |
+
if ( !empty($parent) ) {
|
329 |
+
//Validate parent object
|
330 |
+
if ( is_array($parent) )
|
331 |
+
$parent =& $parent[0];
|
332 |
+
|
333 |
+
//Retrieve reference object if ID was supplied
|
334 |
+
if ( is_string($parent) ) {
|
335 |
+
$parent = trim($parent);
|
336 |
+
//Check for existence of parent
|
337 |
+
$lookup = $this->base_class . 's';
|
338 |
+
if ( isset($GLOBALS[$lookup][$parent]) ) {
|
339 |
+
//Get reference to parent
|
340 |
+
$parent =& $GLOBALS[$lookup][$parent];
|
341 |
+
}
|
342 |
+
}
|
343 |
+
|
344 |
+
//Set reference to parent field type
|
345 |
+
if ( is_a($parent, $this->base_class) ) {
|
346 |
+
$this->parent =& $parent;
|
347 |
+
}
|
348 |
+
}
|
349 |
+
}
|
350 |
+
|
351 |
+
/**
|
352 |
+
* Retrieve field type parent
|
353 |
+
* @return CNR_Field_Type Reference to parent field
|
354 |
+
*/
|
355 |
+
function &get_parent() {
|
356 |
+
return $this->parent;
|
357 |
+
}
|
358 |
+
|
359 |
+
/**
|
360 |
+
* Retrieves field ID
|
361 |
+
* @param string|CNR_Field|array $field (optional) Field object or ID of field or options array
|
362 |
+
* @return string|bool Field ID, FALSE if $field is invalid
|
363 |
+
*/
|
364 |
+
function get_id($field = null) {
|
365 |
+
$ret = false;
|
366 |
+
|
367 |
+
// Get options.
|
368 |
+
$num_args = func_num_args();
|
369 |
+
$options = ( $num_args > 0 && ( $last_arg = func_get_arg($num_args - 1) ) && is_array($last_arg) ) ? $last_arg : array();
|
370 |
+
|
371 |
+
// Validate field parameter.
|
372 |
+
if ( ( !is_object($field) || !is_a($field, 'cnr_field_type') ) && isset($this) ) {
|
373 |
+
$field =& $this;
|
374 |
+
}
|
375 |
+
|
376 |
+
if ( is_a($field, CNR_Field_Type::get_base_class()) )
|
377 |
+
$id = $field->id;
|
378 |
+
|
379 |
+
if ( is_string($id) )
|
380 |
+
$ret = trim($id);
|
381 |
+
|
382 |
+
// Setup options.
|
383 |
+
$options = wp_parse_args( $options, [ 'format' => null ] );
|
384 |
+
//Check if field should be formatted
|
385 |
+
if ( is_string($ret) && !empty($options['format']) ) {
|
386 |
+
//Clear format option if it is an invalid value
|
387 |
+
if ( is_bool($options['format']) || is_int($options['format']) )
|
388 |
+
$options['format'] = null;
|
389 |
+
//Setup values
|
390 |
+
$wrap = array('open' => '[', 'close' => ']');
|
391 |
+
if ( isset($options['wrap']) && is_array($options['wrap']) )
|
392 |
+
$wrap = wp_parse_args($options['wrap'], $wrap);
|
393 |
+
$wrap_trailing = ( isset($options['wrap_trailing']) ) ? !!$options['wrap_trailing'] : true;
|
394 |
+
switch ( $options['format'] ) {
|
395 |
+
case 'attr_id' :
|
396 |
+
$wrap = (array('open' => '_', 'close' => '_'));
|
397 |
+
$wrap_trailing = false;
|
398 |
+
break;
|
399 |
+
}
|
400 |
+
$c = $field->get_caller();
|
401 |
+
$field_id = array($ret);
|
402 |
+
while ( !!$c ) {
|
403 |
+
//Add ID of current field to array
|
404 |
+
if ( isset($c->id) && is_a($c, $this->base_class) )
|
405 |
+
$field_id[] = $c->id;
|
406 |
+
$c = ( method_exists($c, 'get_caller') ) ? $c->get_caller() : null;
|
407 |
+
}
|
408 |
+
|
409 |
+
//Add prefix to ID value
|
410 |
+
$field_id[] = 'attributes';
|
411 |
+
|
412 |
+
//Convert array to string
|
413 |
+
$ret = $field->prefix . $wrap['open'] . implode($wrap['close'] . $wrap['open'], array_reverse($field_id)) . ( $wrap_trailing ? $wrap['close'] : '');
|
414 |
+
}
|
415 |
+
return $ret;
|
416 |
+
}
|
417 |
+
|
418 |
+
/**
|
419 |
+
* Set object title
|
420 |
+
* @param string $title Title for object
|
421 |
+
* @param string $plural Plural form of title
|
422 |
+
*/
|
423 |
+
function set_title($title = '', $plural = '') {
|
424 |
+
$this->title = strip_tags(trim($title));
|
425 |
+
if ( isset($plural) )
|
426 |
+
$this->title_plural = strip_tags(trim($plural));
|
427 |
+
}
|
428 |
+
|
429 |
+
/**
|
430 |
+
* Retrieve object title
|
431 |
+
* @param bool $plural TRUE if plural title should be retrieved, FALSE otherwise (Default: FALSE)
|
432 |
+
*/
|
433 |
+
function get_title($plural = false) {
|
434 |
+
$dir = 'current';
|
435 |
+
//Singular
|
436 |
+
if ( !$plural )
|
437 |
+
return $this->get_member_value('title', '','', $dir);
|
438 |
+
//Plural
|
439 |
+
$title = $this->get_member_value('title_plural', '', '', $dir);
|
440 |
+
if ( empty($title) ) {
|
441 |
+
//Use singular title for plural base
|
442 |
+
$title = $this->get_member_value('title', '', '', $dir);
|
443 |
+
//Determine technique for making title plural
|
444 |
+
//Get last letter
|
445 |
+
if ( !empty($title) ) {
|
446 |
+
$tail = substr($title, -1);
|
447 |
+
switch ( $tail ) {
|
448 |
+
case 's' :
|
449 |
+
$title .= 'es';
|
450 |
+
break;
|
451 |
+
case 'y' :
|
452 |
+
$title = substr($title, 0, -1) . 'ies';
|
453 |
+
break;
|
454 |
+
default :
|
455 |
+
$title .= 's';
|
456 |
+
}
|
457 |
+
}
|
458 |
+
}
|
459 |
+
return $title;
|
460 |
+
}
|
461 |
+
|
462 |
+
/**
|
463 |
+
* Set object description
|
464 |
+
* @param string $description Description for object
|
465 |
+
*/
|
466 |
+
function set_description($description = '') {
|
467 |
+
$this->description = strip_tags(trim($description));
|
468 |
+
}
|
469 |
+
|
470 |
+
/**
|
471 |
+
* Retrieve object description
|
472 |
+
* @return string Object description
|
473 |
+
*/
|
474 |
+
function get_description() {
|
475 |
+
$dir = 'current';
|
476 |
+
return $this->get_member_value('description', '','', $dir);
|
477 |
+
return $desc;
|
478 |
+
}
|
479 |
+
|
480 |
+
/*-** Hooks **-*/
|
481 |
+
|
482 |
+
/**
|
483 |
+
* Retrieve hooks added to object
|
484 |
+
* @return array Hooks
|
485 |
+
*/
|
486 |
+
function get_hooks() {
|
487 |
+
return $this->get_member_value('hooks', '', array());
|
488 |
+
}
|
489 |
+
|
490 |
+
/**
|
491 |
+
* Add hook for object
|
492 |
+
* @see add_filter() for parameter defaults
|
493 |
+
* @param $tag
|
494 |
+
* @param $function_to_add
|
495 |
+
* @param $priority
|
496 |
+
* @param $accepted_args
|
497 |
+
*/
|
498 |
+
function add_hook($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
|
499 |
+
//Create new array for tag (if not already set)
|
500 |
+
if ( !isset($this->hooks[$tag]) )
|
501 |
+
$this->hooks[$tag] = array();
|
502 |
+
//Build Unique ID
|
503 |
+
if ( is_string($function_to_add) )
|
504 |
+
$id = $function_to_add;
|
505 |
+
elseif ( is_array($function_to_add) && !empty($function_to_add) )
|
506 |
+
$id = strval($function_to_add[count($function_to_add) - 1]);
|
507 |
+
else
|
508 |
+
$id = 'function_' . ( count($this->hooks[$tag]) + 1 );
|
509 |
+
//Add hook
|
510 |
+
$this->hooks[$tag][$id] = func_get_args();
|
511 |
+
}
|
512 |
+
|
513 |
+
/**
|
514 |
+
* Convenience method for adding an action for object
|
515 |
+
* @see add_filter() for parameter defaults
|
516 |
+
* @param $tag
|
517 |
+
* @param $function_to_add
|
518 |
+
* @param $priority
|
519 |
+
* @param $accepted_args
|
520 |
+
*/
|
521 |
+
function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
|
522 |
+
$this->add_hook($tag, $function_to_add, $priority, $accepted_args);
|
523 |
+
}
|
524 |
+
|
525 |
+
/**
|
526 |
+
* Convenience method for adding a filter for object
|
527 |
+
* @see add_filter() for parameter defaults
|
528 |
+
* @param $tag
|
529 |
+
* @param $function_to_add
|
530 |
+
* @param $priority
|
531 |
+
* @param $accepted_args
|
532 |
+
*/
|
533 |
+
function add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
|
534 |
+
$this->add_hook($tag, $function_to_add, $priority, $accepted_args);
|
535 |
+
}
|
536 |
+
|
537 |
+
/*-** Dependencies **-*/
|
538 |
+
|
539 |
+
/**
|
540 |
+
* Adds dependency to object
|
541 |
+
* @param string $type Type of dependency to add (script, style)
|
542 |
+
* @param array|string $context When dependency will be added (@see CNR_Utilities::get_action() for possible contexts)
|
543 |
+
* @see wp_enqueue_script for the following of the parameters
|
544 |
+
* @param $handle
|
545 |
+
* @param $src
|
546 |
+
* @param $deps
|
547 |
+
* @param $ver
|
548 |
+
* @param $ex
|
549 |
+
*/
|
550 |
+
function add_dependency($type, $context, $handle, $src = false, $deps = array(), $ver = false, $ex = false) {
|
551 |
+
$args = func_get_args();
|
552 |
+
//Remove type/context from arguments
|
553 |
+
$args = array_slice($args, 2);
|
554 |
+
|
555 |
+
//Set context
|
556 |
+
if ( !is_array($context) ) {
|
557 |
+
//Wrap single contexts in an array
|
558 |
+
if ( is_string($context) )
|
559 |
+
$context = array($context);
|
560 |
+
else
|
561 |
+
$context = array();
|
562 |
+
}
|
563 |
+
//Add file to instance property
|
564 |
+
$this->{$type}[$handle] = array('context' => $context, 'params' => $args);
|
565 |
+
}
|
566 |
+
|
567 |
+
/**
|
568 |
+
* Add script to object to be added in specified contexts
|
569 |
+
* @param array|string $context Array of contexts to add script to page
|
570 |
+
* @see wp_enqueue_script for the following of the parameters
|
571 |
+
* @param $handle
|
572 |
+
* @param $src
|
573 |
+
* @param $deps
|
574 |
+
* @param $ver
|
575 |
+
* @param $in_footer
|
576 |
+
*/
|
577 |
+
function add_script( $context, $handle, $src = false, $deps = array(), $ver = false, $in_footer = false ) {
|
578 |
+
$args = func_get_args();
|
579 |
+
//Add file type to front of arguments array
|
580 |
+
array_unshift($args, 'scripts');
|
581 |
+
call_user_func_array(array(&$this, 'add_dependency'), $args);
|
582 |
+
}
|
583 |
+
|
584 |
+
/**
|
585 |
+
* Retrieve script dependencies for object
|
586 |
+
* @return array Script dependencies
|
587 |
+
*/
|
588 |
+
function get_scripts() {
|
589 |
+
return $this->get_member_value('scripts', '', array());
|
590 |
+
}
|
591 |
+
|
592 |
+
/**
|
593 |
+
* Add style to object to be added in specified contexts
|
594 |
+
* @param array|string $context Array of contexts to add style to page
|
595 |
+
* @see wp_enqueue_style for the following of the parameters
|
596 |
+
* @param $handle
|
597 |
+
* @param $src
|
598 |
+
* @param $deps
|
599 |
+
* @param $ver
|
600 |
+
* @param $in_footer
|
601 |
+
*/
|
602 |
+
function add_style( $handle, $src = false, $deps = array(), $ver = false, $media = false ) {
|
603 |
+
$args = func_get_args();
|
604 |
+
array_unshift($args, 'styles');
|
605 |
+
call_user_method_array('add_dependency', $this, $args);
|
606 |
+
}
|
607 |
+
|
608 |
+
/**
|
609 |
+
* Retrieve Style dependencies for object
|
610 |
+
* @return array Style dependencies
|
611 |
+
*/
|
612 |
+
function get_styles() {
|
613 |
+
return $this->get_member_value('styles', '', array());
|
614 |
+
}
|
615 |
}
|
includes/class.content_type.php
CHANGED
@@ -1,402 +1,402 @@
|
|
1 |
-
<?php
|
2 |
-
class CNR_Content_Type extends CNR_Content_Base {
|
3 |
-
|
4 |
-
/**
|
5 |
-
* Base class for instance objects
|
6 |
-
* @var string
|
7 |
-
*/
|
8 |
-
var $base_class = 'cnr_content_type';
|
9 |
-
|
10 |
-
/**
|
11 |
-
* Indexed array of fields in content type
|
12 |
-
* @var array
|
13 |
-
*/
|
14 |
-
var $fields = array();
|
15 |
-
|
16 |
-
/**
|
17 |
-
* Associative array of groups in conten type
|
18 |
-
* Key: Group name
|
19 |
-
* Value: object of group properties
|
20 |
-
* > description string Group description
|
21 |
-
* > location string Location of group on edit form
|
22 |
-
* > fields array Fields in group
|
23 |
-
* @var array
|
24 |
-
*/
|
25 |
-
var $groups = array();
|
26 |
-
|
27 |
-
/* Constructors */
|
28 |
-
|
29 |
-
/**
|
30 |
-
* Class constructor
|
31 |
-
* @param string $id Content type ID
|
32 |
-
* @param string|bool $parent (optional) Parent to inherit properties from (Default: none)
|
33 |
-
* @param array $properties (optional) Properties to set for content type (Default: none)
|
34 |
-
*/
|
35 |
-
function __construct($id = '', $parent = null, $properties = null) {
|
36 |
-
parent::__construct($id, $parent);
|
37 |
-
|
38 |
-
//Set properties
|
39 |
-
//TODO Iterate through additional arguments and set instance properties
|
40 |
-
}
|
41 |
-
|
42 |
-
/* Registration */
|
43 |
-
|
44 |
-
/**
|
45 |
-
* Registers current content type w/CNR
|
46 |
-
*/
|
47 |
-
function register() {
|
48 |
-
global $cnr_content_utilities;
|
49 |
-
$cnr_content_utilities->register_content_type($this);
|
50 |
-
}
|
51 |
-
|
52 |
-
/* Getters/Setters */
|
53 |
-
|
54 |
-
/**
|
55 |
-
* Adds group to content type
|
56 |
-
* Groups are used to display related fields in the UI
|
57 |
-
* @param string $id Unique name for group
|
58 |
-
* @param string $title Group title
|
59 |
-
* @param string $description Short description of group's purpose
|
60 |
-
* @param string $location Where group will be displayed on post edit form (Default: main)
|
61 |
-
* @param array $fields (optional) ID's of existing fields to add to group
|
62 |
-
* @return object Group object
|
63 |
-
*/
|
64 |
-
function &add_group($id, $title = '', $description = '', $location = 'normal', $fields = array()) {
|
65 |
-
//Create new group and set properties
|
66 |
-
$id = trim($id);
|
67 |
-
$this->groups[$id] =& $this->create_group($title, $description, $location);
|
68 |
-
//Add fields to group (if supplied)
|
69 |
-
if ( !empty($fields) && is_array($fields) )
|
70 |
-
$this->add_to_group($id, $fields);
|
71 |
-
return $this->groups[$id];
|
72 |
-
}
|
73 |
-
|
74 |
-
/**
|
75 |
-
* Remove specified group from content type
|
76 |
-
* @param string $id Group ID to remove
|
77 |
-
*/
|
78 |
-
function remove_group($id) {
|
79 |
-
$id = trim($id);
|
80 |
-
if ( $this->group_exists($id) ) {
|
81 |
-
unset($this->groups[$id]);
|
82 |
-
}
|
83 |
-
}
|
84 |
-
|
85 |
-
/**
|
86 |
-
* Standardized method to create a new field group
|
87 |
-
* @param string $title Group title (used in meta boxes, etc.)
|
88 |
-
* @param string $description Short description of group's purpose
|
89 |
-
* @param string $location Where group will be displayed on post edit form (Default: main)
|
90 |
-
* @return object Group object
|
91 |
-
*/
|
92 |
-
function &create_group($title = '', $description = '', $location = 'normal') {
|
93 |
-
$group = new stdClass();
|
94 |
-
$title = ( is_scalar($title) ) ? trim($title) : '';
|
95 |
-
$group->title = $title;
|
96 |
-
$description = ( is_scalar($description) ) ? trim($description) : '';
|
97 |
-
$group->description = $description;
|
98 |
-
$location = ( is_scalar($location) ) ? trim($location) : 'normal';
|
99 |
-
$group->location = $location;
|
100 |
-
$group->fields = array();
|
101 |
-
return $group;
|
102 |
-
}
|
103 |
-
|
104 |
-
/**
|
105 |
-
* Checks if group exists
|
106 |
-
* @param string $id Group name
|
107 |
-
* @return bool TRUE if group exists, FALSE otherwise
|
108 |
-
*/
|
109 |
-
function group_exists($id) {
|
110 |
-
$id = trim($id);
|
111 |
-
//Check if group exists in content type
|
112 |
-
return ( !is_null($this->get_member_value('groups', $id, null)) );
|
113 |
-
}
|
114 |
-
|
115 |
-
/**
|
116 |
-
* Adds field to content type
|
117 |
-
* @param string $id Unique name for field
|
118 |
-
* @param CNR_Field_Type|string $parent Field type that this field is based on
|
119 |
-
* @param array $properties (optional) Field properties
|
120 |
-
* @param string $group (optional) Group ID to add field to
|
121 |
-
* @return CNR_Field Reference to new field
|
122 |
-
*/
|
123 |
-
function &add_field($id, $parent, $properties = array(), $group = null) {
|
124 |
-
//Create new field
|
125 |
-
$id = trim(strval($id));
|
126 |
-
$field = new CNR_Field($id);
|
127 |
-
$field->set_parent($parent);
|
128 |
-
$field->set_container($this);
|
129 |
-
$field->set_properties($properties);
|
130 |
-
|
131 |
-
//Add field to content type
|
132 |
-
$this->fields[$id] =& $field;
|
133 |
-
//Add field to group
|
134 |
-
$this->add_to_group($group, $field->id);
|
135 |
-
return $field;
|
136 |
-
}
|
137 |
-
|
138 |
-
/**
|
139 |
-
* Removes field from content type
|
140 |
-
* @param string|CNR_Field $field Object or Field ID to remove
|
141 |
-
*/
|
142 |
-
function remove_field($field) {
|
143 |
-
if ( $field instanceof CNR_Field_Type ) {
|
144 |
-
$field = $field->get_id();
|
145 |
-
}
|
146 |
-
if ( !is_string($field) || empty($field) )
|
147 |
-
return false;
|
148 |
-
|
149 |
-
//Remove from fields array
|
150 |
-
//$this->fields[$field] = null;
|
151 |
-
unset($this->fields[$field]);
|
152 |
-
|
153 |
-
//Remove field from groups
|
154 |
-
$this->remove_from_group($field);
|
155 |
-
}
|
156 |
-
|
157 |
-
/**
|
158 |
-
* Retrieve specified field in Content Type
|
159 |
-
* @param string $field Field ID
|
160 |
-
* @return CNR_Field Specified field
|
161 |
-
*/
|
162 |
-
function &get_field($field) {
|
163 |
-
if ( $this->has_field($field) ) {
|
164 |
-
$field = trim($field);
|
165 |
-
$field = $this->get_member_value('fields', $field);
|
166 |
-
} else {
|
167 |
-
//Return empty field if no field exists
|
168 |
-
$field = new CNR_Field('');
|
169 |
-
}
|
170 |
-
return $field;
|
171 |
-
}
|
172 |
-
|
173 |
-
/**
|
174 |
-
* Checks if field exists in the content type
|
175 |
-
* @param string $field Field ID
|
176 |
-
* @return bool TRUE if field exists, FALSE otherwise
|
177 |
-
*/
|
178 |
-
function has_field($field) {
|
179 |
-
return ( !is_string($field) || empty($field) || is_null($this->get_member_value('fields', $field, null)) ) ? false : true;
|
180 |
-
}
|
181 |
-
|
182 |
-
/**
|
183 |
-
* Adds field to a group in the content type
|
184 |
-
* Group is created if it does not already exist
|
185 |
-
* @param string|array $group ID of group (or group parameters if new group) to add field to
|
186 |
-
* @param string|array $fields Name or array of field(s) to add to group
|
187 |
-
*/
|
188 |
-
function add_to_group($group, $fields) {
|
189 |
-
//Validate parameters
|
190 |
-
$group_id = '';
|
191 |
-
if ( !empty($group) ) {
|
192 |
-
if ( !is_array($group) ) {
|
193 |
-
$group = array($group, $group);
|
194 |
-
}
|
195 |
-
|
196 |
-
$group[0] = $group_id = trim(sanitize_title_with_dashes($group[0]));
|
197 |
-
}
|
198 |
-
if ( empty($group_id) || empty($fields) )
|
199 |
-
return false;
|
200 |
-
//Create group if it doesn't exist
|
201 |
-
if ( !$this->group_exists($group_id) ) {
|
202 |
-
call_user_func_array($this->m('add_group'), $group);
|
203 |
-
}
|
204 |
-
if ( ! is_array($fields) )
|
205 |
-
$fields = array($fields);
|
206 |
-
foreach ( $fields as $field ) {
|
207 |
-
unset($fref);
|
208 |
-
if ( ! $this->has_field($field) )
|
209 |
-
continue;
|
210 |
-
$fref =& $this->get_field($field);
|
211 |
-
//Remove field from any other group it's in (fields can only be in one group)
|
212 |
-
foreach ( array_keys($this->groups) as $group_name ) {
|
213 |
-
if ( isset($this->groups[$group_name]->fields[$fref->id]) )
|
214 |
-
unset($this->groups[$group_name]->fields[$fref->id]);
|
215 |
-
}
|
216 |
-
//Add reference to field in group
|
217 |
-
$this->groups[$group_id]->fields[$fref->id] =& $fref;
|
218 |
-
}
|
219 |
-
}
|
220 |
-
|
221 |
-
/**
|
222 |
-
* Remove field from a group
|
223 |
-
* If no group is specified, then field is removed from all groups
|
224 |
-
* @param string|CNR_Field $field Field object or ID of field to remove from group
|
225 |
-
* @param string $group (optional) Group ID to remove field from
|
226 |
-
*/
|
227 |
-
function remove_from_group($field, $group = '') {
|
228 |
-
//Get ID of field to remove or stop execution if field invalid
|
229 |
-
if ( $field instanceof CNR_Field_Type ) {
|
230 |
-
$field = $field->get_id();
|
231 |
-
}
|
232 |
-
if ( !is_string($field) || empty($field) )
|
233 |
-
return false;
|
234 |
-
|
235 |
-
//Remove field from group
|
236 |
-
if ( !empty($group) ) {
|
237 |
-
//Remove field from single group
|
238 |
-
if ( ($group =& $this->get_group($group)) && isset($group->fields[$field]) ) {
|
239 |
-
unset($group->fields[$field]);
|
240 |
-
}
|
241 |
-
} else {
|
242 |
-
//Remove field from all groups
|
243 |
-
foreach ( array_keys($this->groups) as $group ) {
|
244 |
-
if ( ($group =& $this->get_group($group)) && isset($group->fields[$field]) ) {
|
245 |
-
unset($group->fields[$field]);
|
246 |
-
}
|
247 |
-
}
|
248 |
-
}
|
249 |
-
}
|
250 |
-
|
251 |
-
/**
|
252 |
-
* Retrieve specified group
|
253 |
-
* @param string $group ID of group to retrieve
|
254 |
-
* @return object Reference to specified group
|
255 |
-
*/
|
256 |
-
function &get_group($group) {
|
257 |
-
$group = trim($group);
|
258 |
-
//Create group if it doesn't already exist
|
259 |
-
if ( ! $this->group_exists($group) )
|
260 |
-
$this->add_group($group);
|
261 |
-
$group = $this->get_member_value('groups', $group);
|
262 |
-
return $group;
|
263 |
-
}
|
264 |
-
|
265 |
-
/**
|
266 |
-
* Retrieve all groups in content type
|
267 |
-
* @return array Reference to group objects
|
268 |
-
*/
|
269 |
-
function &get_groups() {
|
270 |
-
$groups = $this->get_member_value('groups');
|
271 |
-
return $groups;
|
272 |
-
}
|
273 |
-
|
274 |
-
/**
|
275 |
-
* Output fields in a group
|
276 |
-
* @param string $group ID of Group to output
|
277 |
-
* @return string Group output
|
278 |
-
*/
|
279 |
-
function build_group($group) {
|
280 |
-
$out = array();
|
281 |
-
$classnames = (object) array(
|
282 |
-
'multi' => 'multi_field',
|
283 |
-
'single' => 'single_field',
|
284 |
-
'elements' => 'has_elements'
|
285 |
-
);
|
286 |
-
|
287 |
-
//Stop execution if group does not exist
|
288 |
-
if ( $this->group_exists($group) && $group =& $this->get_group($group) ) {
|
289 |
-
$group_fields = ( count($group->fields) > 1 ) ? $classnames->multi : $classnames->single . ( ( ( $fs = array_keys($group->fields) ) && ( $f =& $group->fields[$fs[0]] ) && ( $els = $f->get_member_value('elements', '', null) ) && !empty($els) ) ? '_' . $classnames->elements : '' );
|
290 |
-
$classname = array('cnr_attributes_wrap', $group_fields);
|
291 |
-
$out[] = '<div class="' . implode(' ', $classname) . '">'; //Wrap all fields in group
|
292 |
-
|
293 |
-
//Build layout for each field in group
|
294 |
-
foreach ( array_keys($group->fields) as $field_id ) {
|
295 |
-
/**
|
296 |
-
* CNR_Field_Type
|
297 |
-
*/
|
298 |
-
$field =& $group->fields[$field_id];
|
299 |
-
$field->set_caller($this);
|
300 |
-
//Start field output
|
301 |
-
$id = 'cnr_field_' . $field->get_id();
|
302 |
-
$class = array('cnr_attribute_wrap');
|
303 |
-
//If single field in group, check if field title matches group
|
304 |
-
if ( count($group->fields) == 1 && $group->title == $field->get_property('label') )
|
305 |
-
$class[] = 'group_field_title';
|
306 |
-
//Add flag to indicate that field was loaded on page
|
307 |
-
$inc = 'cnr[fields_loaded][' . $field->get_id() . ']';
|
308 |
-
$out[] = '<input type="hidden" id="' . $inc . '" name="' . $inc . '" value="1" />';
|
309 |
-
$out[] = '<div id="' . $id . '_wrap" class="' . implode(' ', $class) . '">';
|
310 |
-
//Build field layout
|
311 |
-
$out[] = $field->build_layout();
|
312 |
-
//end field output
|
313 |
-
$out[] = '</div>';
|
314 |
-
$field->clear_caller();
|
315 |
-
}
|
316 |
-
$out[] = '</div>'; //Close fields container
|
317 |
-
//Add description if exists
|
318 |
-
if ( !empty($group->description) )
|
319 |
-
$out[] = '<p class="cnr_group_description">' . $group->description . '</p>';
|
320 |
-
}
|
321 |
-
|
322 |
-
//Return group output
|
323 |
-
return implode($out);
|
324 |
-
}
|
325 |
-
|
326 |
-
/**
|
327 |
-
* Set data for a field
|
328 |
-
* @param string|CNR_Field $field Reference or ID of Field to set data for
|
329 |
-
* @param mixed $value Data to set
|
330 |
-
*/
|
331 |
-
function set_data($field, $value = '') {
|
332 |
-
if ( 1 == func_num_args() && is_array($field) )
|
333 |
-
$this->data = $field;
|
334 |
-
else {
|
335 |
-
if ( $field instanceof CNR_Field_Type ) {
|
336 |
-
$field = $field->get_id();
|
337 |
-
}
|
338 |
-
if ( !is_string($field) || empty($field) )
|
339 |
-
return false;
|
340 |
-
$this->data[$field] = $value;
|
341 |
-
}
|
342 |
-
}
|
343 |
-
|
344 |
-
/*-** Admin **-*/
|
345 |
-
|
346 |
-
/**
|
347 |
-
* Adds meta boxes for post's content type
|
348 |
-
* Each group in content type is a separate meta box
|
349 |
-
* @param string $type Type of item meta boxes are being build for (post, page, link)
|
350 |
-
* @param string $context Location of meta box (normal, advanced, side)
|
351 |
-
* @param object $post Post object
|
352 |
-
*/
|
353 |
-
function admin_do_meta_boxes($type, $context, $post) {
|
354 |
-
//Add post data to content type
|
355 |
-
global $cnr_content_utilities;
|
356 |
-
$this->set_data($cnr_content_utilities->get_item_data($post));
|
357 |
-
|
358 |
-
//Get Groups
|
359 |
-
$groups = array_keys($this->get_groups());
|
360 |
-
$priority = 'default';
|
361 |
-
//Iterate through groups and add meta box if it fits the context (location)
|
362 |
-
foreach ( $groups as $group_id ) {
|
363 |
-
$group =& $this->get_group($group_id);
|
364 |
-
if ( $context == $group->location && count($group->fields) ) {
|
365 |
-
//Format ID for meta box
|
366 |
-
$meta_box_id = $this->prefix . '_group_' . $group_id;
|
367 |
-
$group_args = array( 'group' => $group_id );
|
368 |
-
add_meta_box($meta_box_id, $group->title, $this->m('admin_build_meta_box'), $type, $context, $priority, $group_args);
|
369 |
-
}
|
370 |
-
}
|
371 |
-
}
|
372 |
-
|
373 |
-
/**
|
374 |
-
* Outputs group fields for a meta box
|
375 |
-
* @param object $post Post object
|
376 |
-
* @param array $box Meta box properties
|
377 |
-
*/
|
378 |
-
function admin_build_meta_box($post, $box) {
|
379 |
-
//Stop execution if group not specified
|
380 |
-
if ( !isset($box['args']['group']) )
|
381 |
-
return false;
|
382 |
-
|
383 |
-
//Get ID of group to output
|
384 |
-
$group_id =& $box['args']['group'];
|
385 |
-
|
386 |
-
$output = array();
|
387 |
-
$output[] = '<div class="cnr_group_wrap">';
|
388 |
-
$output[] = $this->build_group($group_id);
|
389 |
-
$output[] = '</div>';
|
390 |
-
|
391 |
-
//Output group content to screen
|
392 |
-
echo implode($output);
|
393 |
-
}
|
394 |
-
|
395 |
-
/**
|
396 |
-
* Retrieves type ID formatted as a meta value
|
397 |
-
* @return string
|
398 |
-
*/
|
399 |
-
function get_meta_value() {
|
400 |
-
return serialize(array($this->id));
|
401 |
-
}
|
402 |
}
|
1 |
+
<?php
|
2 |
+
class CNR_Content_Type extends CNR_Content_Base {
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Base class for instance objects
|
6 |
+
* @var string
|
7 |
+
*/
|
8 |
+
var $base_class = 'cnr_content_type';
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Indexed array of fields in content type
|
12 |
+
* @var array
|
13 |
+
*/
|
14 |
+
var $fields = array();
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Associative array of groups in conten type
|
18 |
+
* Key: Group name
|
19 |
+
* Value: object of group properties
|
20 |
+
* > description string Group description
|
21 |
+
* > location string Location of group on edit form
|
22 |
+
* > fields array Fields in group
|
23 |
+
* @var array
|
24 |
+
*/
|
25 |
+
var $groups = array();
|
26 |
+
|
27 |
+
/* Constructors */
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Class constructor
|
31 |
+
* @param string $id Content type ID
|
32 |
+
* @param string|bool $parent (optional) Parent to inherit properties from (Default: none)
|
33 |
+
* @param array $properties (optional) Properties to set for content type (Default: none)
|
34 |
+
*/
|
35 |
+
function __construct($id = '', $parent = null, $properties = null) {
|
36 |
+
parent::__construct($id, $parent);
|
37 |
+
|
38 |
+
//Set properties
|
39 |
+
//TODO Iterate through additional arguments and set instance properties
|
40 |
+
}
|
41 |
+
|
42 |
+
/* Registration */
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Registers current content type w/CNR
|
46 |
+
*/
|
47 |
+
function register() {
|
48 |
+
global $cnr_content_utilities;
|
49 |
+
$cnr_content_utilities->register_content_type($this);
|
50 |
+
}
|
51 |
+
|
52 |
+
/* Getters/Setters */
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Adds group to content type
|
56 |
+
* Groups are used to display related fields in the UI
|
57 |
+
* @param string $id Unique name for group
|
58 |
+
* @param string $title Group title
|
59 |
+
* @param string $description Short description of group's purpose
|
60 |
+
* @param string $location Where group will be displayed on post edit form (Default: main)
|
61 |
+
* @param array $fields (optional) ID's of existing fields to add to group
|
62 |
+
* @return object Group object
|
63 |
+
*/
|
64 |
+
function &add_group($id, $title = '', $description = '', $location = 'normal', $fields = array()) {
|
65 |
+
//Create new group and set properties
|
66 |
+
$id = trim($id);
|
67 |
+
$this->groups[$id] =& $this->create_group($title, $description, $location);
|
68 |
+
//Add fields to group (if supplied)
|
69 |
+
if ( !empty($fields) && is_array($fields) )
|
70 |
+
$this->add_to_group($id, $fields);
|
71 |
+
return $this->groups[$id];
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Remove specified group from content type
|
76 |
+
* @param string $id Group ID to remove
|
77 |
+
*/
|
78 |
+
function remove_group($id) {
|
79 |
+
$id = trim($id);
|
80 |
+
if ( $this->group_exists($id) ) {
|
81 |
+
unset($this->groups[$id]);
|
82 |
+
}
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Standardized method to create a new field group
|
87 |
+
* @param string $title Group title (used in meta boxes, etc.)
|
88 |
+
* @param string $description Short description of group's purpose
|
89 |
+
* @param string $location Where group will be displayed on post edit form (Default: main)
|
90 |
+
* @return object Group object
|
91 |
+
*/
|
92 |
+
function &create_group($title = '', $description = '', $location = 'normal') {
|
93 |
+
$group = new stdClass();
|
94 |
+
$title = ( is_scalar($title) ) ? trim($title) : '';
|
95 |
+
$group->title = $title;
|
96 |
+
$description = ( is_scalar($description) ) ? trim($description) : '';
|
97 |
+
$group->description = $description;
|
98 |
+
$location = ( is_scalar($location) ) ? trim($location) : 'normal';
|
99 |
+
$group->location = $location;
|
100 |
+
$group->fields = array();
|
101 |
+
return $group;
|
102 |
+
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Checks if group exists
|
106 |
+
* @param string $id Group name
|
107 |
+
* @return bool TRUE if group exists, FALSE otherwise
|
108 |
+
*/
|
109 |
+
function group_exists($id) {
|
110 |
+
$id = trim($id);
|
111 |
+
//Check if group exists in content type
|
112 |
+
return ( !is_null($this->get_member_value('groups', $id, null)) );
|
113 |
+
}
|
114 |
+
|
115 |
+
/**
|
116 |
+
* Adds field to content type
|
117 |
+
* @param string $id Unique name for field
|
118 |
+
* @param CNR_Field_Type|string $parent Field type that this field is based on
|
119 |
+
* @param array $properties (optional) Field properties
|
120 |
+
* @param string $group (optional) Group ID to add field to
|
121 |
+
* @return CNR_Field Reference to new field
|
122 |
+
*/
|
123 |
+
function &add_field($id, $parent, $properties = array(), $group = null) {
|
124 |
+
//Create new field
|
125 |
+
$id = trim(strval($id));
|
126 |
+
$field = new CNR_Field($id);
|
127 |
+
$field->set_parent($parent);
|
128 |
+
$field->set_container($this);
|
129 |
+
$field->set_properties($properties);
|
130 |
+
|
131 |
+
//Add field to content type
|
132 |
+
$this->fields[$id] =& $field;
|
133 |
+
//Add field to group
|
134 |
+
$this->add_to_group($group, $field->id);
|
135 |
+
return $field;
|
136 |
+
}
|
137 |
+
|
138 |
+
/**
|
139 |
+
* Removes field from content type
|
140 |
+
* @param string|CNR_Field $field Object or Field ID to remove
|
141 |
+
*/
|
142 |
+
function remove_field($field) {
|
143 |
+
if ( $field instanceof CNR_Field_Type ) {
|
144 |
+
$field = $field->get_id();
|
145 |
+
}
|
146 |
+
if ( !is_string($field) || empty($field) )
|
147 |
+
return false;
|
148 |
+
|
149 |
+
//Remove from fields array
|
150 |
+
//$this->fields[$field] = null;
|
151 |
+
unset($this->fields[$field]);
|
152 |
+
|
153 |
+
//Remove field from groups
|
154 |
+
$this->remove_from_group($field);
|
155 |
+
}
|
156 |
+
|
157 |
+
/**
|
158 |
+
* Retrieve specified field in Content Type
|
159 |
+
* @param string $field Field ID
|
160 |
+
* @return CNR_Field Specified field
|
161 |
+
*/
|
162 |
+
function &get_field($field) {
|
163 |
+
if ( $this->has_field($field) ) {
|
164 |
+
$field = trim($field);
|
165 |
+
$field = $this->get_member_value('fields', $field);
|
166 |
+
} else {
|
167 |
+
//Return empty field if no field exists
|
168 |
+
$field = new CNR_Field('');
|
169 |
+
}
|
170 |
+
return $field;
|
171 |
+
}
|
172 |
+
|
173 |
+
/**
|
174 |
+
* Checks if field exists in the content type
|
175 |
+
* @param string $field Field ID
|
176 |
+
* @return bool TRUE if field exists, FALSE otherwise
|
177 |
+
*/
|
178 |
+
function has_field($field) {
|
179 |
+
return ( !is_string($field) || empty($field) || is_null($this->get_member_value('fields', $field, null)) ) ? false : true;
|
180 |
+
}
|
181 |
+
|
182 |
+
/**
|
183 |
+
* Adds field to a group in the content type
|
184 |
+
* Group is created if it does not already exist
|
185 |
+
* @param string|array $group ID of group (or group parameters if new group) to add field to
|
186 |
+
* @param string|array $fields Name or array of field(s) to add to group
|
187 |
+
*/
|
188 |
+
function add_to_group($group, $fields) {
|
189 |
+
//Validate parameters
|
190 |
+
$group_id = '';
|
191 |
+
if ( !empty($group) ) {
|
192 |
+
if ( !is_array($group) ) {
|
193 |
+
$group = array($group, $group);
|
194 |
+
}
|
195 |
+
|
196 |
+
$group[0] = $group_id = trim(sanitize_title_with_dashes($group[0]));
|
197 |
+
}
|
198 |
+
if ( empty($group_id) || empty($fields) )
|
199 |
+
return false;
|
200 |
+
//Create group if it doesn't exist
|
201 |
+
if ( !$this->group_exists($group_id) ) {
|
202 |
+
call_user_func_array($this->m('add_group'), $group);
|
203 |
+
}
|
204 |
+
if ( ! is_array($fields) )
|
205 |
+
$fields = array($fields);
|
206 |
+
foreach ( $fields as $field ) {
|
207 |
+
unset($fref);
|
208 |
+
if ( ! $this->has_field($field) )
|
209 |
+
continue;
|
210 |
+
$fref =& $this->get_field($field);
|
211 |
+
//Remove field from any other group it's in (fields can only be in one group)
|
212 |
+
foreach ( array_keys($this->groups) as $group_name ) {
|
213 |
+
if ( isset($this->groups[$group_name]->fields[$fref->id]) )
|
214 |
+
unset($this->groups[$group_name]->fields[$fref->id]);
|
215 |
+
}
|
216 |
+
//Add reference to field in group
|
217 |
+
$this->groups[$group_id]->fields[$fref->id] =& $fref;
|
218 |
+
}
|
219 |
+
}
|
220 |
+
|
221 |
+
/**
|
222 |
+
* Remove field from a group
|
223 |
+
* If no group is specified, then field is removed from all groups
|
224 |
+
* @param string|CNR_Field $field Field object or ID of field to remove from group
|
225 |
+
* @param string $group (optional) Group ID to remove field from
|
226 |
+
*/
|
227 |
+
function remove_from_group($field, $group = '') {
|
228 |
+
//Get ID of field to remove or stop execution if field invalid
|
229 |
+
if ( $field instanceof CNR_Field_Type ) {
|
230 |
+
$field = $field->get_id();
|
231 |
+
}
|
232 |
+
if ( !is_string($field) || empty($field) )
|
233 |
+
return false;
|
234 |
+
|
235 |
+
//Remove field from group
|
236 |
+
if ( !empty($group) ) {
|
237 |
+
//Remove field from single group
|
238 |
+
if ( ($group =& $this->get_group($group)) && isset($group->fields[$field]) ) {
|
239 |
+
unset($group->fields[$field]);
|
240 |
+
}
|
241 |
+
} else {
|
242 |
+
//Remove field from all groups
|
243 |
+
foreach ( array_keys($this->groups) as $group ) {
|
244 |
+
if ( ($group =& $this->get_group($group)) && isset($group->fields[$field]) ) {
|
245 |
+
unset($group->fields[$field]);
|
246 |
+
}
|
247 |
+
}
|
248 |
+
}
|
249 |
+
}
|
250 |
+
|
251 |
+
/**
|
252 |
+
* Retrieve specified group
|
253 |
+
* @param string $group ID of group to retrieve
|
254 |
+
* @return object Reference to specified group
|
255 |
+
*/
|
256 |
+
function &get_group($group) {
|
257 |
+
$group = trim($group);
|
258 |
+
//Create group if it doesn't already exist
|
259 |
+
if ( ! $this->group_exists($group) )
|
260 |
+
$this->add_group($group);
|
261 |
+
$group = $this->get_member_value('groups', $group);
|
262 |
+
return $group;
|
263 |
+
}
|
264 |
+
|
265 |
+
/**
|
266 |
+
* Retrieve all groups in content type
|
267 |
+
* @return array Reference to group objects
|
268 |
+
*/
|
269 |
+
function &get_groups() {
|
270 |
+
$groups = $this->get_member_value('groups');
|
271 |
+
return $groups;
|
272 |
+
}
|
273 |
+
|
274 |
+
/**
|
275 |
+
* Output fields in a group
|
276 |
+
* @param string $group ID of Group to output
|
277 |
+
* @return string Group output
|
278 |
+
*/
|
279 |
+
function build_group($group) {
|
280 |
+
$out = array();
|
281 |
+
$classnames = (object) array(
|
282 |
+
'multi' => 'multi_field',
|
283 |
+
'single' => 'single_field',
|
284 |
+
'elements' => 'has_elements'
|
285 |
+
);
|
286 |
+
|
287 |
+
//Stop execution if group does not exist
|
288 |
+
if ( $this->group_exists($group) && $group =& $this->get_group($group) ) {
|
289 |
+
$group_fields = ( count($group->fields) > 1 ) ? $classnames->multi : $classnames->single . ( ( ( $fs = array_keys($group->fields) ) && ( $f =& $group->fields[$fs[0]] ) && ( $els = $f->get_member_value('elements', '', null) ) && !empty($els) ) ? '_' . $classnames->elements : '' );
|
290 |
+
$classname = array('cnr_attributes_wrap', $group_fields);
|
291 |
+
$out[] = '<div class="' . implode(' ', $classname) . '">'; //Wrap all fields in group
|
292 |
+
|
293 |
+
//Build layout for each field in group
|
294 |
+
foreach ( array_keys($group->fields) as $field_id ) {
|
295 |
+
/**
|
296 |
+
* CNR_Field_Type
|
297 |
+
*/
|
298 |
+
$field =& $group->fields[$field_id];
|
299 |
+
$field->set_caller($this);
|
300 |
+
//Start field output
|
301 |
+
$id = 'cnr_field_' . $field->get_id();
|
302 |
+
$class = array('cnr_attribute_wrap');
|
303 |
+
//If single field in group, check if field title matches group
|
304 |
+
if ( count($group->fields) == 1 && $group->title == $field->get_property('label') )
|
305 |
+
$class[] = 'group_field_title';
|
306 |
+
//Add flag to indicate that field was loaded on page
|
307 |
+
$inc = 'cnr[fields_loaded][' . $field->get_id() . ']';
|
308 |
+
$out[] = '<input type="hidden" id="' . $inc . '" name="' . $inc . '" value="1" />';
|
309 |
+
$out[] = '<div id="' . $id . '_wrap" class="' . implode(' ', $class) . '">';
|
310 |
+
//Build field layout
|
311 |
+
$out[] = $field->build_layout();
|
312 |
+
//end field output
|
313 |
+
$out[] = '</div>';
|
314 |
+
$field->clear_caller();
|
315 |
+
}
|
316 |
+
$out[] = '</div>'; //Close fields container
|
317 |
+
//Add description if exists
|
318 |
+
if ( !empty($group->description) )
|
319 |
+
$out[] = '<p class="cnr_group_description">' . $group->description . '</p>';
|
320 |
+
}
|
321 |
+
|
322 |
+
//Return group output
|
323 |
+
return implode($out);
|
324 |
+
}
|
325 |
+
|
326 |
+
/**
|
327 |
+
* Set data for a field
|
328 |
+
* @param string|CNR_Field $field Reference or ID of Field to set data for
|
329 |
+
* @param mixed $value Data to set
|
330 |
+
*/
|
331 |
+
function set_data($field, $value = '') {
|
332 |
+
if ( 1 == func_num_args() && is_array($field) )
|
333 |
+
$this->data = $field;
|
334 |
+
else {
|
335 |
+
if ( $field instanceof CNR_Field_Type ) {
|
336 |
+
$field = $field->get_id();
|
337 |
+
}
|
338 |
+
if ( !is_string($field) || empty($field) )
|
339 |
+
return false;
|
340 |
+
$this->data[$field] = $value;
|
341 |
+
}
|
342 |
+
}
|
343 |
+
|
344 |
+
/*-** Admin **-*/
|
345 |
+
|
346 |
+
/**
|
347 |
+
* Adds meta boxes for post's content type
|
348 |
+
* Each group in content type is a separate meta box
|
349 |
+
* @param string $type Type of item meta boxes are being build for (post, page, link)
|
350 |
+
* @param string $context Location of meta box (normal, advanced, side)
|
351 |
+
* @param object $post Post object
|
352 |
+
*/
|
353 |
+
function admin_do_meta_boxes($type, $context, $post) {
|
354 |
+
//Add post data to content type
|
355 |
+
global $cnr_content_utilities;
|
356 |
+
$this->set_data($cnr_content_utilities->get_item_data($post));
|
357 |
+
|
358 |
+
//Get Groups
|
359 |
+
$groups = array_keys($this->get_groups());
|
360 |
+
$priority = 'default';
|
361 |
+
//Iterate through groups and add meta box if it fits the context (location)
|
362 |
+
foreach ( $groups as $group_id ) {
|
363 |
+
$group =& $this->get_group($group_id);
|
364 |
+
if ( $context == $group->location && count($group->fields) ) {
|
365 |
+
//Format ID for meta box
|
366 |
+
$meta_box_id = $this->prefix . '_group_' . $group_id;
|
367 |
+
$group_args = array( 'group' => $group_id );
|
368 |
+
add_meta_box($meta_box_id, $group->title, $this->m('admin_build_meta_box'), $type, $context, $priority, $group_args);
|
369 |
+
}
|
370 |
+
}
|
371 |
+
}
|
372 |
+
|
373 |
+
/**
|
374 |
+
* Outputs group fields for a meta box
|
375 |
+
* @param object $post Post object
|
376 |
+
* @param array $box Meta box properties
|
377 |
+
*/
|
378 |
+
function admin_build_meta_box($post, $box) {
|
379 |
+
//Stop execution if group not specified
|
380 |
+
if ( !isset($box['args']['group']) )
|
381 |
+
return false;
|
382 |
+
|
383 |
+
//Get ID of group to output
|
384 |
+
$group_id =& $box['args']['group'];
|
385 |
+
|
386 |
+
$output = array();
|
387 |
+
$output[] = '<div class="cnr_group_wrap">';
|
388 |
+
$output[] = $this->build_group($group_id);
|
389 |
+
$output[] = '</div>';
|
390 |
+
|
391 |
+
//Output group content to screen
|
392 |
+
echo implode($output);
|
393 |
+
}
|
394 |
+
|
395 |
+
/**
|
396 |
+
* Retrieves type ID formatted as a meta value
|
397 |
+
* @return string
|
398 |
+
*/
|
399 |
+
function get_meta_value() {
|
400 |
+
return serialize(array($this->id));
|
401 |
+
}
|
402 |
}
|
includes/class.content_utilities.php
CHANGED
@@ -1,1248 +1,1248 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* Utilities for Content Type functionality
|
4 |
-
* @package Cornerstone
|
5 |
-
* @subpackage Content Types
|
6 |
-
* @author Archetyped
|
7 |
-
*/
|
8 |
-
class CNR_Content_Utilities extends CNR_Base {
|
9 |
-
|
10 |
-
/**
|
11 |
-
* Array of hooks called
|
12 |
-
* @var array
|
13 |
-
*/
|
14 |
-
var $hooks_processed = array();
|
15 |
-
|
16 |
-
/**
|
17 |
-
* Initialize content type functionality
|
18 |
-
*/
|
19 |
-
function init() {
|
20 |
-
$this->register_hooks();
|
21 |
-
}
|
22 |
-
|
23 |
-
/**
|
24 |
-
* Registers hooks for content types
|
25 |
-
* @todo 2010-07-30: Check hooks for 3.0 compatibility
|
26 |
-
*/
|
27 |
-
function register_hooks() {
|
28 |
-
//Register types
|
29 |
-
add_action('init', $this->m('register_types'));
|
30 |
-
add_action('init', $this->m('add_hooks'), 11);
|
31 |
-
|
32 |
-
//Enqueue scripts for fields in current post type
|
33 |
-
add_action('admin_enqueue_scripts', $this->m('enqueue_files'));
|
34 |
-
|
35 |
-
//Add menus
|
36 |
-
//add_action('admin_menu', $this->m('admin_menu'));
|
37 |
-
|
38 |
-
//Build UI on post edit form
|
39 |
-
add_action('do_meta_boxes', $this->m('admin_do_meta_boxes'), 10, 3);
|
40 |
-
|
41 |
-
//Get edit link for items
|
42 |
-
//add_filter('get_edit_post_link', $this->m('get_edit_item_url'), 10, 3);
|
43 |
-
|
44 |
-
//add_action('edit_form_advanced', $this->m('admin_page_edit_form'));
|
45 |
-
|
46 |
-
//Save Field data/Content type
|
47 |
-
add_action('save_post', $this->m('save_item_data'), 10, 2);
|
48 |
-
|
49 |
-
//Modify post query for content type compatibility
|
50 |
-
add_action('pre_get_posts', $this->m('pre_get_posts'), 20);
|
51 |
-
}
|
52 |
-
|
53 |
-
/**
|
54 |
-
* Initialize fields and content types
|
55 |
-
*/
|
56 |
-
function register_types() {
|
57 |
-
//Global variables
|
58 |
-
global $cnr_field_types, $cnr_content_types;
|
59 |
-
|
60 |
-
/* Field Types */
|
61 |
-
|
62 |
-
//Base
|
63 |
-
$base = new CNR_Field_Type('base');
|
64 |
-
$base->set_description('Default Element');
|
65 |
-
$base->set_property('tag', 'span');
|
66 |
-
$base->set_property('class', '', 'attr');
|
67 |
-
$base->set_layout('form', '<{tag} name="{field_name}" id="{field_id}" {properties ref_base="root" group="attr"} />');
|
68 |
-
$base->set_layout('label', '<label for="{field_id}">{label}</label>');
|
69 |
-
$base->set_layout('display', '{data format="display"}');
|
70 |
-
$this->register_field($base);
|
71 |
-
|
72 |
-
//Base closed
|
73 |
-
$base_closed = new CNR_Field_Type('base_closed');
|
74 |
-
$base_closed->set_parent('base');
|
75 |
-
$base_closed->set_description('Default Element (Closed Tag)');
|
76 |
-
$base_closed->set_layout('form_start', '<{tag} id="{field_id}" name="{field_name}" {properties ref_base="root" group="attr"}>');
|
77 |
-
$base_closed->set_layout('form_end', '</{tag}>');
|
78 |
-
$base_closed->set_layout('form', '{form_start ref_base="layout"}{data}{form_end ref_base="layout"}');
|
79 |
-
$this->register_field($base_closed);
|
80 |
-
|
81 |
-
//Input
|
82 |
-
$input = new CNR_Field_Type('input');
|
83 |
-
$input->set_parent('base');
|
84 |
-
$input->set_description('Default Input Element');
|
85 |
-
$input->set_property('tag', 'input');
|
86 |
-
$input->set_property('type', 'text', 'attr');
|
87 |
-
$input->set_property('value', CNR_Field::USES_DATA, 'attr');
|
88 |
-
$this->register_field($input);
|
89 |
-
|
90 |
-
//Text input
|
91 |
-
$text = new CNR_Field_Type('text', 'input');
|
92 |
-
$text->set_description('Text Box');
|
93 |
-
$text->set_property('size', 15, 'attr');
|
94 |
-
$text->set_property('label');
|
95 |
-
$text->set_layout('form', '{label ref_base="layout"} {inherit}');
|
96 |
-
$this->register_field($text);
|
97 |
-
|
98 |
-
//Checkbox
|
99 |
-
$checkbox = new CNR_Field_Type('checkbox', 'input');
|
100 |
-
$checkbox->set_description('Checkbox');
|
101 |
-
$checkbox->set_property('type', 'checkbox', 'attr');
|
102 |
-
$checkbox->set_property('label');
|
103 |
-
$checkbox->set_property('checked', '', 'attr');
|
104 |
-
$checkbox->set_layout('form', '{inherit} {label ref_base="layout"}');
|
105 |
-
$this->register_field($checkbox);
|
106 |
-
|
107 |
-
//Textarea
|
108 |
-
$ta = new CNR_Field_Type('textarea', 'base_closed');
|
109 |
-
$ta->set_property('tag', 'textarea');
|
110 |
-
$ta->set_property('cols', 40, 'attr');
|
111 |
-
$ta->set_property('rows', 3, 'attr');
|
112 |
-
$this->register_field($ta);
|
113 |
-
|
114 |
-
//Rich Text
|
115 |
-
$rt = new CNR_Field_Type('richtext', 'textarea');
|
116 |
-
$rt->set_layout('form', '<div class="rt_container">{rich_editor}</div>');
|
117 |
-
$this->register_field($rt);
|
118 |
-
|
119 |
-
//Location
|
120 |
-
$location = new CNR_Field_Type('location');
|
121 |
-
$location->set_description('Geographic Coordinates');
|
122 |
-
$location->set_element('latitude', 'text', array( 'size' => 3, 'label' => 'Latitude' ));
|
123 |
-
$location->set_element('longitude', 'text', array( 'size' => 3, 'label' => 'Longitude' ));
|
124 |
-
$location->set_layout('form', '<span>{latitude ref_base="elements"}</span>, <span>{longitude ref_base="elements"}</span>');
|
125 |
-
$this->register_field($location);
|
126 |
-
|
127 |
-
//Phone
|
128 |
-
$phone = new CNR_Field_Type('phone');
|
129 |
-
$phone->set_description('Phone Number');
|
130 |
-
$phone->set_element('area', 'text', array( 'size' => 3 ));
|
131 |
-
$phone->set_element('prefix', 'text', array( 'size' => 3 ));
|
132 |
-
$phone->set_element('suffix', 'text', array( 'size' => 4 ));
|
133 |
-
$phone->set_layout('form', '({area ref_base="elements"}) {prefix ref_base="elements"} - {suffix ref_base="elements"}');
|
134 |
-
$this->register_field($phone);
|
135 |
-
|
136 |
-
//Hidden
|
137 |
-
$hidden = new CNR_Field_Type('hidden');
|
138 |
-
$hidden->set_parent('input');
|
139 |
-
$hidden->set_description('Hidden Field');
|
140 |
-
$hidden->set_property('type', 'hidden');
|
141 |
-
$this->register_field($hidden);
|
142 |
-
|
143 |
-
//Span
|
144 |
-
$span = new CNR_Field_Type('span');
|
145 |
-
$span->set_description('Inline wrapper');
|
146 |
-
$span->set_parent('base_closed');
|
147 |
-
$span->set_property('tag', 'span');
|
148 |
-
$span->set_property('value', 'Hello there!');
|
149 |
-
$this->register_field($span);
|
150 |
-
|
151 |
-
//Select
|
152 |
-
$select = new CNR_Field_Type('select');
|
153 |
-
$select->set_description('Select tag');
|
154 |
-
$select->set_parent('base_closed');
|
155 |
-
$select->set_property('tag', 'select');
|
156 |
-
$select->set_property('tag_option', 'option');
|
157 |
-
$select->set_property('options', array());
|
158 |
-
$select->set_layout('form', '{label ref_base="layout"} {form_start ref_base="layout"}{loop data="properties.options" layout="option" layout_data="option_data"}{form_end ref_base="layout"}');
|
159 |
-
$select->set_layout('option', '<{tag_option} value="{data_ext id="option_value"}">{data_ext id="option_text"}</{tag_option}>');
|
160 |
-
$select->set_layout('option_data', '<{tag_option} value="{data_ext id="option_value"}" selected="selected">{data_ext id="option_text"}</{tag_option}>');
|
161 |
-
$this->register_field($select);
|
162 |
-
|
163 |
-
//Enable plugins to modify (add, remove, etc.) field types
|
164 |
-
do_action_ref_array('cnr_register_field_types', array(&$cnr_field_types));
|
165 |
-
|
166 |
-
//Content Types
|
167 |
-
|
168 |
-
//Enable plugins to add/remove content types
|
169 |
-
do_action_ref_array('cnr_register_content_types', array(&$cnr_content_types));
|
170 |
-
|
171 |
-
//Enable plugins to modify content types after they have all been registered
|
172 |
-
do_action_ref_array('cnr_content_types_registered', array(&$cnr_content_types));
|
173 |
-
}
|
174 |
-
|
175 |
-
/**
|
176 |
-
* Add content type to global array of content types
|
177 |
-
* @param CNR_Content_Type $ct Content type to register
|
178 |
-
* @global array $cnr_content_types Content types array
|
179 |
-
*/
|
180 |
-
function register_content_type(&$ct) {
|
181 |
-
//Add content type to CNR array
|
182 |
-
if ( $this->is_content_type($ct) && !empty($ct->id) ) {
|
183 |
-
global $cnr_content_types;
|
184 |
-
$cnr_content_types[$ct->id] =& $ct;
|
185 |
-
}
|
186 |
-
//WP Post Type Registration
|
187 |
-
global $wp_post_types;
|
188 |
-
if ( !empty($ct->id) && !isset($wp_post_types[$ct->id]) )
|
189 |
-
register_post_type($ct->id, $this->build_post_type_args($ct));
|
190 |
-
}
|
191 |
-
|
192 |
-
/**
|
193 |
-
* Generates arguments array for WP Post Type Registration
|
194 |
-
* @param CNR_Content_Type $ct Content type being registered
|
195 |
-
* @return array Arguments array
|
196 |
-
* @todo Enable custom taxonomies
|
197 |
-
*/
|
198 |
-
function build_post_type_args(&$ct) {
|
199 |
-
//Setup labels
|
200 |
-
|
201 |
-
//Build labels
|
202 |
-
$labels = array (
|
203 |
-
'name' => _( $ct->get_title(true) ),
|
204 |
-
'singular_name' => _( $ct->get_title(false) ),
|
205 |
-
'all_items' => sprintf( _( 'All %s' ), $ct->get_title(true) ),
|
206 |
-
);
|
207 |
-
|
208 |
-
//Action labels
|
209 |
-
$item_actions = array(
|
210 |
-
'add_new' => 'Add New %s',
|
211 |
-
'edit' => 'Edit %s',
|
212 |
-
'new' => 'New %s',
|
213 |
-
'view' => 'View %s',
|
214 |
-
'search' => array('Search %s', true),
|
215 |
-
'not_found' => array('No %s found', true, false),
|
216 |
-
'not_found_in_trash' => array('No %s found in Trash', true, false)
|
217 |
-
);
|
218 |
-
|
219 |
-
foreach ( $item_actions as $key => $val ) {
|
220 |
-
$excluded = false;
|
221 |
-
$plural = false;
|
222 |
-
if ( is_array($val) ) {
|
223 |
-
if ( count($val) > 1 && true == $val[1] ) {
|
224 |
-
$plural = true;
|
225 |
-
}
|
226 |
-
if ( count($val) > 2 && false == $val[2] )
|
227 |
-
$excluded = true;
|
228 |
-
$val = $val[0];
|
229 |
-
}
|
230 |
-
$title = ( $plural ) ? $labels['name'] : $labels['singular_name'];
|
231 |
-
if ( $excluded )
|
232 |
-
$item = $key;
|
233 |
-
else {
|
234 |
-
$item = $key . '_item' . ( ( $plural ) ? 's' : '' );
|
235 |
-
}
|
236 |
-
$labels[$item] = sprintf($val, $title);
|
237 |
-
}
|
238 |
-
|
239 |
-
//Setup args
|
240 |
-
$args = array (
|
241 |
-
'labels' => $labels,
|
242 |
-
'description' => $ct->get_description(),
|
243 |
-
'public' => true,
|
244 |
-
'capability_type' => 'post',
|
245 |
-
'rewrite' => array( 'slug' => strtolower($labels['name']) ),
|
246 |
-
'has_archive' => true,
|
247 |
-
'hierarchical' => false,
|
248 |
-
'menu_position' => 5,
|
249 |
-
'supports' => array('title', 'editor', 'author', 'thumbnail', 'excerpt', 'trackbacks', 'custom-fields', 'comments', 'revisions'),
|
250 |
-
'taxonomies' => get_object_taxonomies('post'),
|
251 |
-
'show_in_rest' => true,
|
252 |
-
);
|
253 |
-
|
254 |
-
return $args;
|
255 |
-
}
|
256 |
-
|
257 |
-
/**
|
258 |
-
* Add field type to global array of field types
|
259 |
-
* @param CNR_Field_Type $field Field to register
|
260 |
-
*
|
261 |
-
* @global array $cnr_field_types Field types array
|
262 |
-
*/
|
263 |
-
function register_field(&$field) {
|
264 |
-
if ( $this->is_field($field) && !empty($field->id) ) {
|
265 |
-
global $cnr_field_types;
|
266 |
-
$cnr_field_types[$field->id] =& $field;
|
267 |
-
}
|
268 |
-
}
|
269 |
-
|
270 |
-
/*-** Helpers **-*/
|
271 |
-
|
272 |
-
/**
|
273 |
-
* Checks whether an object is a valid content type instance
|
274 |
-
* @param obj $ct Object to evaluate
|
275 |
-
* @return bool TRUE if object is a valid content type instance, FALSE otherwise
|
276 |
-
*/
|
277 |
-
function is_content_type(&$ct) {
|
278 |
-
return is_a($ct, 'cnr_content_type');
|
279 |
-
}
|
280 |
-
|
281 |
-
/**
|
282 |
-
* Checks whether an object is a valid field instance
|
283 |
-
* @param obj $field Object to evaluate
|
284 |
-
* @return bool TRUE if object is a valid field instance, FALSE otherwise
|
285 |
-
*/
|
286 |
-
function is_field(&$field) {
|
287 |
-
return is_a($field, 'cnr_field_type');
|
288 |
-
}
|
289 |
-
|
290 |
-
/*-** Handlers **-*/
|
291 |
-
|
292 |
-
/**
|
293 |
-
* Modifies query parameters to include custom content types
|
294 |
-
* Adds custom content types to default post query so these items are retrieved as well
|
295 |
-
* @param WP_Query $q Reference to WP_Query object being used to perform posts query
|
296 |
-
* @see WP_Query for reference
|
297 |
-
*/
|
298 |
-
function pre_get_posts($q) {
|
299 |
-
$pt =& $q->query_vars['post_type'];
|
300 |
-
/* Do not continue processing if:
|
301 |
-
* > In admin section
|
302 |
-
* > Not main query (or CNR-initiated query)
|
303 |
-
* > Single object requested
|
304 |
-
* > More than one post type is already specified
|
305 |
-
* > Post type other than 'post' is supplied
|
306 |
-
*/
|
307 |
-
if ( is_admin()
|
308 |
-
|| ( !$q->is_main_query() && !isset($q->query_vars[$this->get_prefix()]) )
|
309 |
-
|| $q->is_singular()
|
310 |
-
|| ( is_array($pt)
|
311 |
-
&& ( count($pt) > 1
|
312 |
-
|| 'post' != $pt[0] )
|
313 |
-
)
|
314 |
-
|| !in_array($pt, array('post', null))
|
315 |
-
) {
|
316 |
-
return false;
|
317 |
-
}
|
318 |
-
|
319 |
-
$default_types = $this->get_default_post_types();
|
320 |
-
$custom_types = array_diff(array_keys($this->get_types()), $default_types);
|
321 |
-
if ( !count($custom_types) )
|
322 |
-
return false;
|
323 |
-
//Wrap post type in array
|
324 |
-
if ( empty($pt) || is_null($pt) )
|
325 |
-
$pt = array('post');
|
326 |
-
if ( !is_array($pt) )
|
327 |
-
$pt = array($pt);
|
328 |
-
//Add custom types to query
|
329 |
-
foreach ( $custom_types as $type ) {
|
330 |
-
$pt[] = $type;
|
331 |
-
}
|
332 |
-
}
|
333 |
-
|
334 |
-
/**
|
335 |
-
* Retrieves current context (content type, action)
|
336 |
-
* @return array Content Type and Action of current request
|
337 |
-
*/
|
338 |
-
function get_context() {
|
339 |
-
$post = false;
|
340 |
-
if ( isset($GLOBALS['post']) && !is_null($GLOBALS['post']) )
|
341 |
-
$post = $GLOBALS['post'];
|
342 |
-
elseif ( isset($_REQUEST['post_id']) )
|
343 |
-
$post = $_REQUEST['post_id'];
|
344 |
-
elseif ( isset($_REQUEST['post']) )
|
345 |
-
$post = $_REQUEST['post'];
|
346 |
-
elseif ( isset($_REQUEST['post_type']) )
|
347 |
-
$post = $_REQUEST['post_type'];
|
348 |
-
//Get action
|
349 |
-
$action = $this->util->get_action();
|
350 |
-
if ( empty($post) )
|
351 |
-
$post = $this->get_page_type();
|
352 |
-
//Get post's content type
|
353 |
-
$ct =& $this->get_type($post);
|
354 |
-
|
355 |
-
return array(&$ct, $action);
|
356 |
-
}
|
357 |
-
|
358 |
-
/**
|
359 |
-
* Enqueues files for fields in current content type
|
360 |
-
* @param string $page Current context
|
361 |
-
*/
|
362 |
-
function enqueue_files($page = null) {
|
363 |
-
list($ct, $action) = $this->get_context();
|
364 |
-
$file_types = array('scripts' => 'script', 'styles' => 'style');
|
365 |
-
//Get content type fields
|
366 |
-
foreach ( $ct->fields as $field ) {
|
367 |
-
//Enqueue scripts/styles for each field
|
368 |
-
foreach ( $file_types as $type => $func_base ) {
|
369 |
-
$deps = $field->{"get_$type"}();
|
370 |
-
foreach ( $deps as $handle => $args ) {
|
371 |
-
//Confirm context
|
372 |
-
if ( in_array('all', $args['context']) || in_array($page, $args['context']) || in_array($action, $args['context']) ) {
|
373 |
-
$this->enqueue_file($func_base, $args['params']);
|
374 |
-
}
|
375 |
-
}
|
376 |
-
}
|
377 |
-
}
|
378 |
-
}
|
379 |
-
|
380 |
-
/**
|
381 |
-
* Add plugin hooks for fields used in current request
|
382 |
-
*/
|
383 |
-
function add_hooks() {
|
384 |
-
list($ct, $action) = $this->get_context();
|
385 |
-
//Iterate through content type fields and add hooks from fields
|
386 |
-
foreach ( $ct->fields as $field ) {
|
387 |
-
//Iterate through hooks added to field
|
388 |
-
$hooks = $field->get_hooks();
|
389 |
-
foreach ( $hooks as $tag => $callback ) {
|
390 |
-
//Iterate through function callbacks added to tag
|
391 |
-
foreach ( $callback as $id => $args ) {
|
392 |
-
//Check if hook/function was already processed
|
393 |
-
if ( isset($this->hooks_processed[$tag][$id]) )
|
394 |
-
continue;
|
395 |
-
//Add hook/function to list of processed hooks
|
396 |
-
if ( !isset($this->hooks_processed[$tag]) || !is_array($this->hooks_processed[$tag]) )
|
397 |
-
$this->hooks_processed[$tag] = array($id => true);
|
398 |
-
//Add hook to WP
|
399 |
-
call_user_func_array('add_filter', $args);
|
400 |
-
}
|
401 |
-
}
|
402 |
-
}
|
403 |
-
}
|
404 |
-
|
405 |
-
/**
|
406 |
-
* Enqueues files
|
407 |
-
* @param string $type Type of file to enqueue (script or style)
|
408 |
-
* @param array $args (optional) Arguments to pass to enqueue function
|
409 |
-
*/
|
410 |
-
function enqueue_file($type = 'script', $args = array()) {
|
411 |
-
$func = 'wp_enqueue_' . $type;
|
412 |
-
if ( function_exists($func) ) {
|
413 |
-
call_user_func_array($func, $args);
|
414 |
-
}
|
415 |
-
}
|
416 |
-
|
417 |
-
/**
|
418 |
-
* Add admin menus for content types
|
419 |
-
* @deprecated Not needed for 3.0+
|
420 |
-
*/
|
421 |
-
function admin_menu() {
|
422 |
-
global $cnr_content_types;
|
423 |
-
|
424 |
-
$pos = 21;
|
425 |
-
foreach ( $cnr_content_types as $id => $type ) {
|
426 |
-
if ( $this->is_default_post_type($id) )
|
427 |
-
continue;
|
428 |
-
$page = $this->get_admin_page_file($id);
|
429 |
-
$callback = $this->m('admin_page');
|
430 |
-
$access = 8;
|
431 |
-
$pos += 1;
|
432 |
-
$title = $type->get_title(true);
|
433 |
-
if ( !empty($title) ) {
|
434 |
-
//Main menu
|
435 |
-
add_menu_page($type->get_title(true), $type->get_title(true), $access, $page, $callback, '', $pos);
|
436 |
-
//Edit
|
437 |
-
add_submenu_page($page, __('Edit ' . $type->get_title(true)), __('Edit'), $access, $page, $callback);
|
438 |
-
$hook = get_plugin_page_hookname($page, $page);
|
439 |
-
add_action('load-' . $hook, $this->m('admin_menu_load_plugin'));
|
440 |
-
//Add
|
441 |
-
$page_add = $this->get_admin_page_file($id, 'add');
|
442 |
-
add_submenu_page($page, __('Add New ' . $type->get_title()), __('Add New'), $access, $page_add, $callback);
|
443 |
-
$hook = get_plugin_page_hook($page_add, $page);
|
444 |
-
add_action('load-' . $hook, $this->m('admin_menu_load_plugin'));
|
445 |
-
//Hook for additional menus
|
446 |
-
$menu_hook = 'cnr_admin_menu_type';
|
447 |
-
//Type specific
|
448 |
-
do_action_ref_array($menu_hook . '_' . $id, array(&$type));
|
449 |
-
//General
|
450 |
-
do_action_ref_array($menu_hook, array(&$type));
|
451 |
-
}
|
452 |
-
}
|
453 |
-
}
|
454 |
-
|
455 |
-
/**
|
456 |
-
* Load data for plugin admin page prior to admin-header.php is loaded
|
457 |
-
* Useful for enqueueing scripts/styles, etc.
|
458 |
-
*/
|
459 |
-
function admin_menu_load_plugin() {
|
460 |
-
//Get Action
|
461 |
-
global $editing, $post, $post_ID, $p;
|
462 |
-
$action = $this->util->get_action();
|
463 |
-
if ( isset($_GET['delete_all']) )
|
464 |
-
$action = 'delete_all';
|
465 |
-
if ( isset($_GET['action']) && 'edit' == $_GET['action'] && ! isset($_GET['bulk_edit']))
|
466 |
-
$action = 'manage';
|
467 |
-
switch ( $action ) {
|
468 |
-
case 'delete_all' :
|
469 |
-
case 'edit' :
|
470 |
-
//Handle bulk actions
|
471 |
-
//Redirect to edit.php for processing
|
472 |
-
|
473 |
-
//Build query string
|
474 |
-
$qs = $_GET;
|
475 |
-
unset($qs['page']);
|
476 |
-
$edit_uri = admin_url('edit.php') . '?' . build_query($qs);
|
477 |
-
wp_redirect($edit_uri);
|
478 |
-
break;
|
479 |
-
case 'edit-item' :
|
480 |
-
wp_enqueue_script('admin_comments');
|
481 |
-
enqueue_comment_hotkeys_js();
|
482 |
-
//Get post being edited
|
483 |
-
if ( empty($_GET['post']) ) {
|
484 |
-
wp_redirect("post.php"); //TODO redirect to appropriate manage page
|
485 |
-
exit();
|
486 |
-
}
|
487 |
-
$post_ID = $p = (int) $_GET['post'];
|
488 |
-
$post = get_post($post_ID);
|
489 |
-
if ( !current_user_can('edit_post', $post_ID) )
|
490 |
-
wp_die( __('You are not allowed to edit this item') );
|
491 |
-
|
492 |
-
if ( $last = wp_check_post_lock($post->ID) ) {
|
493 |
-
add_action('admin_notices', '_admin_notice_post_locked');
|
494 |
-
} else {
|
495 |
-
wp_set_post_lock($post->ID);
|
496 |
-
$locked = true;
|
497 |
-
}
|
498 |
-
//Continue on to add case
|
499 |
-
case 'add' :
|
500 |
-
$editing = true;
|
501 |
-
wp_enqueue_script('autosave');
|
502 |
-
wp_enqueue_script('post');
|
503 |
-
if ( user_can_richedit() )
|
504 |
-
wp_enqueue_script('editor');
|
505 |
-
add_thickbox();
|
506 |
-
wp_enqueue_script('media-upload');
|
507 |
-
wp_enqueue_script('word-count');
|
508 |
-
add_action( 'admin_print_footer_scripts', 'wp_tiny_mce', 25 );
|
509 |
-
wp_enqueue_script('quicktags');
|
510 |
-
wp_enqueue_script($this->add_prefix('edit_form'), $this->util->get_file_url('js/lib.admin.edit_form.js'), array('jquery', 'postbox'), false, true);
|
511 |
-
break;
|
512 |
-
default :
|
513 |
-
wp_enqueue_script( $this->add_prefix('inline-edit-post') );
|
514 |
-
}
|
515 |
-
}
|
516 |
-
|
517 |
-
/**
|
518 |
-
* Build admin page file name for the specified post type
|
519 |
-
* @param string|CNR_Content_Type $type Content type ID or object
|
520 |
-
* @param string $action Action to build file name for
|
521 |
-
* @param bool $sep_action Whether action should be a separate query variable (Default: false)
|
522 |
-
* @return string Admin page file name
|
523 |
-
*/
|
524 |
-
function get_admin_page_file($type, $action = '', $sep_action = false) {
|
525 |
-
if ( isset($type->id) )
|
526 |
-
$type = $type->id;
|
527 |
-
$page = $this->add_prefix('post_type_' . $type);
|
528 |
-
if ( !empty($action) ) {
|
529 |
-
if ( $sep_action )
|
530 |
-
$page .= '&action=';
|
531 |
-
else
|
532 |
-
$page .= '-';
|
533 |
-
|
534 |
-
$page .= $action;
|
535 |
-
}
|
536 |
-
return $page;
|
537 |
-
}
|
538 |
-
|
539 |
-
/**
|
540 |
-
* Determine content type based on URL query variables
|
541 |
-
* Uses $_GET['page'] variable to determine content type
|
542 |
-
* @return string Content type of page (NULL if no type defined by page)
|
543 |
-
*/
|
544 |
-
function get_page_type() {
|
545 |
-
$type = null;
|
546 |
-
//Extract type from query variable
|
547 |
-
if ( isset($_GET['page']) ) {
|
548 |
-
$type = $_GET['page'];
|
549 |
-
$prefix = $this->add_prefix('post_type_');
|
550 |
-
//Remove plugin page prefix
|
551 |
-
if ( ($pos = strpos($type, $prefix)) === 0 )
|
552 |
-
$type = substr($type, strlen($prefix));
|
553 |
-
//Remove action (if present)
|
554 |
-
if ( ($pos = strrpos($type, '-')) && $pos !== false )
|
555 |
-
$type = substr($type, 0, $pos);
|
556 |
-
}
|
557 |
-
return $type;
|
558 |
-
}
|
559 |
-
|
560 |
-
/**
|
561 |
-
* Populate administration page for content type
|
562 |
-
*/
|
563 |
-
function admin_page() {
|
564 |
-
$prefix = $this->add_prefix('post_type_');
|
565 |
-
if ( strpos($_GET['page'], $prefix) !== 0 )
|
566 |
-
return false;
|
567 |
-
|
568 |
-
//Get action
|
569 |
-
$action = $this->util->get_action('manage');
|
570 |
-
//Get content type
|
571 |
-
$type =& $this->get_type($this->get_page_type());
|
572 |
-
global $title, $parent_file, $submenu_file;
|
573 |
-
$title = $type->get_title(true);
|
574 |
-
//$parent_file = $prefix . $type->id;
|
575 |
-
//$submenu_file = $parent_file;
|
576 |
-
|
577 |
-
switch ( $action ) {
|
578 |
-
case 'edit-item' :
|
579 |
-
case 'add' :
|
580 |
-
$this->admin_page_edit($type, $action);
|
581 |
-
break;
|
582 |
-
default :
|
583 |
-
$this->admin_page_manage($type, $action);
|
584 |
-
}
|
585 |
-
}
|
586 |
-
|
587 |
-
/**
|
588 |
-
* Queries content items for admin management pages
|
589 |
-
* Also retrieves available post status for specified content type
|
590 |
-
* @see wp_edit_posts_query
|
591 |
-
* @param CNR_Content_Type|string $type Content type instance or ID
|
592 |
-
* @return array All item statuses and Available item status
|
593 |
-
*/
|
594 |
-
function admin_manage_query($type = 'post') {
|
595 |
-
global $wp_query;
|
596 |
-
$q = array();
|
597 |
-
//Get post type
|
598 |
-
if ( ! is_a($type, 'CNR_Content_Type') ) {
|
599 |
-
$type = $this->get_type($type);
|
600 |
-
}
|
601 |
-
$q = array('post_type' => $type->id);
|
602 |
-
$g = $_GET;
|
603 |
-
//Date
|
604 |
-
$q['m'] = isset($g['m']) ? (int) $g['m'] : 0;
|
605 |
-
//Category
|
606 |
-
$q['cat'] = isset($g['cat']) ? (int) $g['cat'] : 0;
|
607 |
-
$post_stati = array( // array( adj, noun )
|
608 |
-
'publish' => array(_x('Published', 'post'), __('Published posts'), _n_noop('Published <span class="count">(%s)</span>', 'Published <span class="count">(%s)</span>')),
|
609 |
-
'future' => array(_x('Scheduled', 'post'), __('Scheduled posts'), _n_noop('Scheduled <span class="count">(%s)</span>', 'Scheduled <span class="count">(%s)</span>')),
|
610 |
-
'pending' => array(_x('Pending Review', 'post'), __('Pending posts'), _n_noop('Pending Review <span class="count">(%s)</span>', 'Pending Review <span class="count">(%s)</span>')),
|
611 |
-
'draft' => array(_x('Draft', 'post'), _x('Drafts', 'manage posts header'), _n_noop('Draft <span class="count">(%s)</span>', 'Drafts <span class="count">(%s)</span>')),
|
612 |
-
'private' => array(_x('Private', 'post'), __('Private posts'), _n_noop('Private <span class="count">(%s)</span>', 'Private <span class="count">(%s)</span>')),
|
613 |
-
'trash' => array(_x('Trash', 'post'), __('Trash posts'), _n_noop('Trash <span class="count">(%s)</span>', 'Trash <span class="count">(%s)</span>')),
|
614 |
-
);
|
615 |
-
|
616 |
-
$post_stati = apply_filters('post_stati', $post_stati);
|
617 |
-
|
618 |
-
$avail_post_stati = get_available_post_statuses('post');
|
619 |
-
|
620 |
-
//Status
|
621 |
-
if ( isset($g['post_status']) && in_array( $g['post_status'], array_keys($post_stati) ) ) {
|
622 |
-
$q['post_status'] = $g['post_status'];
|
623 |
-
$q['perm'] = 'readable';
|
624 |
-
} else {
|
625 |
-
unset($q['post_status']);
|
626 |
-
}
|
627 |
-
|
628 |
-
//Order
|
629 |
-
if ( isset($q['post_status']) && 'pending' === $q['post_status'] ) {
|
630 |
-
$q['order'] = 'ASC';
|
631 |
-
$q['orderby'] = 'modified';
|
632 |
-
} elseif ( isset($q['post_status']) && 'draft' === $q['post_status'] ) {
|
633 |
-
$q['order'] = 'DESC';
|
634 |
-
$q['orderby'] = 'modified';
|
635 |
-
} else {
|
636 |
-
$q['order'] = 'DESC';
|
637 |
-
$q['orderby'] = 'date';
|
638 |
-
}
|
639 |
-
|
640 |
-
//Pagination
|
641 |
-
$posts_per_page = (int) get_user_option( 'edit_per_page', 0, false );
|
642 |
-
if ( empty( $posts_per_page ) || $posts_per_page < 1 )
|
643 |
-
$posts_per_page = 15;
|
644 |
-
if ( isset($g['paged']) && (int) $g['paged'] > 1 )
|
645 |
-
$q['paged'] = (int) $g['paged'];
|
646 |
-
$q['posts_per_page'] = apply_filters( 'edit_posts_per_page', $posts_per_page );
|
647 |
-
//Search
|
648 |
-
$q[s] = ( isset($g['s']) ) ? $g[s] : '';
|
649 |
-
$wp_query->query($q);
|
650 |
-
|
651 |
-
return array($post_stati, $avail_post_stati);
|
652 |
-
}
|
653 |
-
|
654 |
-
/**
|
655 |
-
* Counts the number of items in the specified content type
|
656 |
-
* @see wp_count_posts
|
657 |
-
* @param CNR_Content_Type|string $type Content Type instance or ID
|
658 |
-
* @param string $perm Permission level for items (e.g. readable)
|
659 |
-
* @return array Associative array of item counts by post status (published, draft, etc.)
|
660 |
-
*/
|
661 |
-
function count_posts( $type, $perm = '' ) {
|
662 |
-
global $wpdb;
|
663 |
-
|
664 |
-
$user = wp_get_current_user();
|
665 |
-
|
666 |
-
if ( !is_a($type, 'CNR_Content_Type') )
|
667 |
-
$type = $this->get_type($type);
|
668 |
-
$type_val = $type->get_meta_value();
|
669 |
-
$type = $type->id;
|
670 |
-
$cache_key = $type;
|
671 |
-
|
672 |
-
//$query = "SELECT post_status, COUNT( * ) AS num_posts FROM {$wpdb->posts} WHERE post_type = %s";
|
673 |
-
$query = "SELECT p.post_status, COUNT( * ) as num_posts FROM {$wpdb->posts} p JOIN {$wpdb->postmeta} m ON m.post_id = p.id WHERE m.meta_key = '" . $this->get_type_meta_key() . "' AND m.meta_value = '$type_val'";
|
674 |
-
if ( 'readable' == $perm && is_user_logged_in() ) {
|
675 |
-
//TODO enable check for custom post types "read_private_{$type}s"
|
676 |
-
if ( !current_user_can("read_private_posts") ) {
|
677 |
-
$cache_key .= '_' . $perm . '_' . $user->ID;
|
678 |
-
$query .= " AND (p.post_status != 'private' OR ( p.post_author = '$user->ID' AND p.post_status = 'private' ))";
|
679 |
-
}
|
680 |
-
}
|
681 |
-
$query .= ' GROUP BY p.post_status';
|
682 |
-
|
683 |
-
$count = wp_cache_get($cache_key, 'counts');
|
684 |
-
if ( false !== $count )
|
685 |
-
return $count;
|
686 |
-
|
687 |
-
$count = $wpdb->get_results( $wpdb->prepare( $query, $type ), ARRAY_A );
|
688 |
-
|
689 |
-
$stats = array( 'publish' => 0, 'private' => 0, 'draft' => 0, 'pending' => 0, 'future' => 0, 'trash' => 0 );
|
690 |
-
foreach( (array) $count as $row_num => $row ) {
|
691 |
-
$stats[$row['post_status']] = $row['num_posts'];
|
692 |
-
}
|
693 |
-
|
694 |
-
$stats = (object) $stats;
|
695 |
-
wp_cache_set($cache_key, $stats, 'counts');
|
696 |
-
|
697 |
-
return $stats;
|
698 |
-
}
|
699 |
-
|
700 |
-
/**
|
701 |
-
* Builds management page for items of a specific custom content type
|
702 |
-
* @param CNR_Content_Type $type Content Type to manage
|
703 |
-
* @param string $action Current action
|
704 |
-
*
|
705 |
-
* @global string $title
|
706 |
-
* @global string $parent_file
|
707 |
-
* @global string $plugin_page
|
708 |
-
* @global string $page_hook
|
709 |
-
* @global WP_User $current_user
|
710 |
-
* @global WP_Query $wp_query
|
711 |
-
* @global wpdb $wpdb
|
712 |
-
* @global WP_Locale $wp_locale
|
713 |
-
*/
|
714 |
-
function admin_page_manage($type, $action) {
|
715 |
-
if ( !current_user_can('edit_posts') )
|
716 |
-
wp_die(__('You do not have sufficient permissions to access this page.'));
|
717 |
-
|
718 |
-
global $title, $parent_file, $plugin_page, $page_hook, $current_user, $wp_query, $wpdb, $wp_locale;
|
719 |
-
$title = __('Edit ' . $type->get_title(true));
|
720 |
-
$admin_path = ABSPATH . 'wp-admin/';
|
721 |
-
|
722 |
-
//Pagination
|
723 |
-
if ( ! isset($_GET['paged']) )
|
724 |
-
$_GET['paged'] = 1;
|
725 |
-
|
726 |
-
$add_url = $this->get_admin_page_url($type->id, 'add');
|
727 |
-
$is_trash = isset($_GET['post_status']) && $_GET['post_status'] == 'trash';
|
728 |
-
//User posts
|
729 |
-
$user_posts = false;
|
730 |
-
if ( !current_user_can('edit_others_posts') ) {
|
731 |
-
$user_posts_count = $wpdb->get_var( $wpdb->prepare("SELECT COUNT(1) FROM $wpdb->posts p JOIN $wpdb->postmeta m ON m.post_id = p.id WHERE m.meta_key = '_cnr_post_type' AND m.meta_value = %s AND p.post_status != 'trash' AND p.post_author = %d", $type->get_meta_value(), $current_user->ID) );
|
732 |
-
$user_posts = true;
|
733 |
-
if ( $user_posts_count && empty($_GET['post_status']) && empty($_GET['all_posts']) && empty($_GET['author']) )
|
734 |
-
$_GET['author'] = $current_user->ID;
|
735 |
-
}
|
736 |
-
//Get content type items
|
737 |
-
list($post_stati, $avail_post_stati) = $this->admin_manage_query($type->id);
|
738 |
-
?>
|
739 |
-
<div class="wrap">
|
740 |
-
<?php screen_icon('edit'); ?>
|
741 |
-
<h2><?php echo esc_html( $title ); ?> <a href="<?php echo $add_url; ?>" class="button add-new-h2"><?php echo esc_html_x('Add New', 'post'); ?></a> <?php
|
742 |
-
if ( isset($_GET['s']) && $_GET['s'] )
|
743 |
-
printf( '<span class="subtitle">' . __('Search results for “%s”') . '</span>', esc_html( get_search_query() ) ); ?>
|
744 |
-
</h2>
|
745 |
-
<?php /* Action messages here: saved, trashed, etc. */ ?>
|
746 |
-
<form id="posts-filter" action="<?php echo admin_url('admin.php'); ?>" method="get">
|
747 |
-
<?php if ( isset($_GET['page']) ) { ?>
|
748 |
-
<input type="hidden" name="page" id="page" value="<?php esc_attr_e($_GET['page']); ?>" />
|
749 |
-
<?php } ?>
|
750 |
-
<ul class="subsubsub">
|
751 |
-
<?php
|
752 |
-
/* Status links */
|
753 |
-
if ( empty($locked_post_status) ) :
|
754 |
-
$status_links = array();
|
755 |
-
$num_posts = $this->count_posts($type, 'readable');
|
756 |
-
$class = '';
|
757 |
-
$allposts = '';
|
758 |
-
$curr_page = $_SERVER['PHP_SELF'] . '?page=' . $_GET['page'];
|
759 |
-
if ( $user_posts ) {
|
760 |
-
if ( isset( $_GET['author'] ) && ( $_GET['author'] == $current_user->ID ) )
|
761 |
-
$class = ' class="current"';
|
762 |
-
$status_links[] = "<li><a href='$curr_page&author=$current_user->ID'$class>" . sprintf( _nx( 'My Posts <span class="count">(%s)</span>', 'My Posts <span class="count">(%s)</span>', $user_posts_count, 'posts' ), number_format_i18n( $user_posts_count ) ) . '</a>';
|
763 |
-
$allposts = '?all_posts=1';
|
764 |
-
}
|
765 |
-
|
766 |
-
$total_posts = array_sum( (array) $num_posts ) - $num_posts->trash;
|
767 |
-
$class = empty($class) && empty($_GET['post_status']) ? ' class="current"' : '';
|
768 |
-
$status_links[] = "<li><a href='$curr_page{$allposts}'$class>" . sprintf( _nx( 'All <span class="count">(%s)</span>', 'All <span class="count">(%s)</span>', $total_posts, 'posts' ), number_format_i18n( $total_posts ) ) . '</a>';
|
769 |
-
|
770 |
-
foreach ( $post_stati as $status => $label ) {
|
771 |
-
$class = '';
|
772 |
-
|
773 |
-
if ( !in_array( $status, $avail_post_stati ) )
|
774 |
-
continue;
|
775 |
-
|
776 |
-
if ( empty( $num_posts->$status ) )
|
777 |
-
continue;
|
778 |
-
|
779 |
-
if ( isset($_GET['post_status']) && $status == $_GET['post_status'] )
|
780 |
-
$class = ' class="current"';
|
781 |
-
|
782 |
-
$status_links[] = "<li><a href='$curr_page&post_status=$status'$class>" . sprintf( _n( $label[2][0], $label[2][1], $num_posts->$status ), number_format_i18n( $num_posts->$status ) ) . '</a>';
|
783 |
-
}
|
784 |
-
echo implode( " |</li>\n", $status_links ) . '</li>';
|
785 |
-
unset( $status_links );
|
786 |
-
endif;
|
787 |
-
?>
|
788 |
-
</ul>
|
789 |
-
<p class="search-box">
|
790 |
-
<label class="screen-reader-text" for="post-search-input"><?php _e('Search Posts', 'cornerstone'); ?>:</label>
|
791 |
-
<input type="text" id="post-search-input" name="s" value="<?php the_search_query(); ?>" />
|
792 |
-
<input type="submit" value="<?php esc_attr_e('Search Posts', 'cornerstone'); ?>" class="button" />
|
793 |
-
</p>
|
794 |
-
<?php
|
795 |
-
if ( have_posts() ) {
|
796 |
-
?>
|
797 |
-
<div class="tablenav">
|
798 |
-
<?php
|
799 |
-
$page_links = paginate_links( array(
|
800 |
-
'base' => add_query_arg( 'paged', '%#%' ),
|
801 |
-
'format' => '',
|
802 |
-
'prev_text' => __('«'),
|
803 |
-
'next_text' => __('»'),
|
804 |
-
'total' => $wp_query->max_num_pages,
|
805 |
-
'current' => $_GET['paged']
|
806 |
-
));
|
807 |
-
?>
|
808 |
-
<div class="alignleft actions">
|
809 |
-
<select name="action">
|
810 |
-
<option value="-1" selected="selected"><?php _e('Bulk Actions', 'cornerstone'); ?></option>
|
811 |
-
<?php if ( $is_trash ) { ?>
|
812 |
-
<option value="untrash"><?php _e('Restore', 'cornerstone'); ?></option>
|
813 |
-
<?php } else { ?>
|
814 |
-
<option value="edit"><?php _e('Edit', 'cornerstone'); ?></option>
|
815 |
-
<?php } if ( $is_trash || !EMPTY_TRASH_DAYS ) { ?>
|
816 |
-
<option value="delete"><?php _e('Delete Permanently', 'cornerstone'); ?></option>
|
817 |
-
<?php } else { ?>
|
818 |
-
<option value="trash"><?php _e('Move to Trash', 'cornerstone'); ?></option>
|
819 |
-
<?php } ?>
|
820 |
-
</select>
|
821 |
-
<input type="submit" value="<?php esc_attr_e('Apply', 'cornerstone'); ?>" name="doaction" id="doaction" class="button-secondary action" />
|
822 |
-
<?php wp_nonce_field('bulk-posts'); ?>
|
823 |
-
|
824 |
-
<?php // view filters
|
825 |
-
if ( !is_singular() ) {
|
826 |
-
$arc_query = "SELECT DISTINCT YEAR(post_date) AS yyear, MONTH(post_date) AS mmonth FROM $wpdb->posts p JOIN $wpdb->postmeta m ON m.post_id = p.ID WHERE m.meta_key = '" . $this->get_type_meta_key() . "' AND m.meta_value = '" . $type->get_meta_value() . "' ORDER BY post_date DESC";
|
827 |
-
|
828 |
-
$arc_result = $wpdb->get_results( $arc_query );
|
829 |
-
|
830 |
-
$month_count = count($arc_result);
|
831 |
-
|
832 |
-
if ( $month_count && !( 1 == $month_count && 0 == $arc_result[0]->mmonth ) ) {
|
833 |
-
$m = isset($_GET['m']) ? (int)$_GET['m'] : 0;
|
834 |
-
?>
|
835 |
-
<select name='m'>
|
836 |
-
<option<?php selected( $m, 0 ); ?> value='0'><?php _e('Show all dates', 'cornerstone'); ?></option>
|
837 |
-
<?php
|
838 |
-
foreach ($arc_result as $arc_row) {
|
839 |
-
if ( $arc_row->yyear == 0 )
|
840 |
-
continue;
|
841 |
-
$arc_row->mmonth = zeroise( $arc_row->mmonth, 2 );
|
842 |
-
|
843 |
-
if ( $arc_row->yyear . $arc_row->mmonth == $m )
|
844 |
-
$default = ' selected="selected"';
|
845 |
-
else
|
846 |
-
$default = '';
|
847 |
-
|
848 |
-
echo "<option$default value='" . esc_attr("$arc_row->yyear$arc_row->mmonth") . "'>";
|
849 |
-
echo $wp_locale->get_month($arc_row->mmonth) . " $arc_row->yyear";
|
850 |
-
echo "</option>\n";
|
851 |
-
}
|
852 |
-
?>
|
853 |
-
</select>
|
854 |
-
<?php }
|
855 |
-
|
856 |
-
$dropdown_options = array('show_option_all' => __('View all categories'), 'hide_empty' => 0, 'hierarchical' => 1,
|
857 |
-
'show_count' => 0, 'orderby' => 'name', 'selected' => $cat);
|
858 |
-
wp_dropdown_categories($dropdown_options);
|
859 |
-
do_action('restrict_manage_posts');
|
860 |
-
?>
|
861 |
-
<input type="submit" id="post-query-submit" value="<?php esc_attr_e('Filter', 'cornerstone'); ?>" class="button-secondary" />
|
862 |
-
<?php }
|
863 |
-
|
864 |
-
if ( $is_trash && current_user_can('edit_others_posts') ) { ?>
|
865 |
-
<input type="submit" name="delete_all" id="delete_all" value="<?php esc_attr_e('Empty Trash', 'cornerstone'); ?>" class="button-secondary apply" />
|
866 |
-
<?php } ?>
|
867 |
-
</div>
|
868 |
-
|
869 |
-
<?php if ( $page_links ) { ?>
|
870 |
-
<div class="tablenav-pages"><?php $page_links_text = sprintf( '<span class="displaying-num">' . __( 'Displaying %s–%s of %s' ) . '</span>%s',
|
871 |
-
number_format_i18n( ( $_GET['paged'] - 1 ) * $wp_query->query_vars['posts_per_page'] + 1 ),
|
872 |
-
number_format_i18n( min( $_GET['paged'] * $wp_query->query_vars['posts_per_page'], $wp_query->found_posts ) ),
|
873 |
-
number_format_i18n( $wp_query->found_posts ),
|
874 |
-
$page_links
|
875 |
-
); echo $page_links_text; ?></div>
|
876 |
-
<?php } //page links ?>
|
877 |
-
<div class="clear"></div>
|
878 |
-
</div>
|
879 |
-
<?php
|
880 |
-
include ($admin_path . 'edit-post-rows.php');
|
881 |
-
} else { //have_posts() ?>
|
882 |
-
<div class="clear"></div>
|
883 |
-
<p><?php
|
884 |
-
if ( $is_trash )
|
885 |
-
_e('No posts found in the trash', 'cornerstone');
|
886 |
-
else
|
887 |
-
_e('No posts found', 'cornerstone');
|
888 |
-
?></p>
|
889 |
-
<?php } ?>
|
890 |
-
</form>
|
891 |
-
<?php inline_edit_row('post'); ?>
|
892 |
-
<div id="ajax-response"></div>
|
893 |
-
<br class="clear" />
|
894 |
-
</div>
|
895 |
-
<?php
|
896 |
-
}
|
897 |
-
|
898 |
-
/**
|
899 |
-
* Build admin edit page for custom type item
|
900 |
-
* @param CNR_Content_Type $type Content type being edited
|
901 |
-
* @param string $action Current action (add, edit, manage, etc.)
|
902 |
-
*/
|
903 |
-
function admin_page_edit($type, $action) {
|
904 |
-
global $title, $hook_suffix, $parent_file, $screen_layout_columns, $post, $post_ID, $p;
|
905 |
-
$screen_layout_columns = 2;
|
906 |
-
//TODO Add default icon for content type
|
907 |
-
$parent_file = 'edit.php'; //Makes screen_icon() use edit icon on post edit form
|
908 |
-
switch ( $action ) {
|
909 |
-
case 'edit-item' :
|
910 |
-
$title = 'Edit';
|
911 |
-
$post = get_post_to_edit($post_ID);
|
912 |
-
break;
|
913 |
-
default :
|
914 |
-
$title = 'Add New';
|
915 |
-
$post = get_default_post_to_edit();
|
916 |
-
break;
|
917 |
-
}
|
918 |
-
$title = __($title . ' ' . $type->get_title());
|
919 |
-
$admin_path = ABSPATH . 'wp-admin/';
|
920 |
-
include ($admin_path . 'edit-form-advanced.php');
|
921 |
-
}
|
922 |
-
|
923 |
-
/**
|
924 |
-
* Adds hidden field declaring content type on post edit form
|
925 |
-
* @deprecated no longer needed for WP 3.0+
|
926 |
-
*/
|
927 |
-
function admin_page_edit_form() {
|
928 |
-
global $post, $plugin_page;
|
929 |
-
if ( empty($post) || !$post->ID ) {
|
930 |
-
$type = $this->get_type($post);
|
931 |
-
if ( ! empty($type) && ! empty($type->id) ) {
|
932 |
-
?>
|
933 |
-
<input type="hidden" name="cnr[content_type]" id="cnr[content_type]" value="<?php echo $type->id; ?>" />
|
934 |
-
<?php
|
935 |
-
}
|
936 |
-
}
|
937 |
-
}
|
938 |
-
|
939 |
-
/**
|
940 |
-
* Adds meta boxes for post's content type
|
941 |
-
* Each group in content type is a separate meta box
|
942 |
-
* @param string $type Type of item meta boxes are being build for (post, page, link)
|
943 |
-
* @param string $context Location of meta box (normal, advanced, side)
|
944 |
-
* @param object $post Post object
|
945 |
-
*/
|
946 |
-
function admin_do_meta_boxes($type, $context, $post) {
|
947 |
-
//Validate $type. Should be 'post','page', or a custom post type for our purposes
|
948 |
-
if ( in_array($type, array_merge(array_keys($this->get_types()), array('post', 'page'))) ) {
|
949 |
-
//Get content type definition
|
950 |
-
$ct =& $this->get_type($post);
|
951 |
-
//Pass processing to content type instance
|
952 |
-
$ct->admin_do_meta_boxes($type, $context, $post);
|
953 |
-
}
|
954 |
-
}
|
955 |
-
|
956 |
-
/**
|
957 |
-
* Saves field data submitted for current post
|
958 |
-
* @param int $post_id ID of current post
|
959 |
-
* @param object $post Post object
|
960 |
-
*/
|
961 |
-
function save_item_data($post_id, $post) {
|
962 |
-
if ( empty($post_id) || empty($post) || !isset($_POST['cnr']) || !is_array($_POST['cnr']) )
|
963 |
-
return false;
|
964 |
-
$pdata = $_POST['cnr'];
|
965 |
-
|
966 |
-
if ( isset($pdata['attributes']) && is_array($pdata['attributes']) && isset($pdata['fields_loaded']) && is_array($pdata['fields_loaded']) ) {
|
967 |
-
|
968 |
-
$prev_data = (array) $this->get_item_data($post_id);
|
969 |
-
|
970 |
-
//Remove loaded fields from prev data
|
971 |
-
$prev_data = array_diff_key($prev_data, $pdata['fields_loaded']);
|
972 |
-
|
973 |
-
//Get current field data
|
974 |
-
$curr_data = $pdata['attributes'];
|
975 |
-
|
976 |
-
//Merge arrays together (new data overwrites old data)
|
977 |
-
if ( is_array($prev_data) && is_array($curr_data) ) {
|
978 |
-
$curr_data = array_merge($prev_data, $curr_data);
|
979 |
-
}
|
980 |
-
|
981 |
-
//Save to database
|
982 |
-
update_post_meta($post_id, $this->get_fields_meta_key(), $curr_data);
|
983 |
-
}
|
984 |
-
//Save content type
|
985 |
-
if ( isset($_POST['cnr']['content_type']) ) {
|
986 |
-
$type = $_POST['cnr']['content_type'];
|
987 |
-
$saved_type = get_post_meta($post_id, $this->get_type_meta_key(), true);
|
988 |
-
if ( is_array($saved_type) )
|
989 |
-
$saved_type = implode($saved_type);
|
990 |
-
if ( $type != $saved_type ) {
|
991 |
-
//Continue processing if submitted content type is different from previously-saved content type (or no type was previously set)
|
992 |
-
update_post_meta($post_id, $this->get_type_meta_key(), array($type));
|
993 |
-
}
|
994 |
-
}
|
995 |
-
}
|
996 |
-
|
997 |
-
/*-** Helpers **-*/
|
998 |
-
|
999 |
-
/**
|
1000 |
-
* Get array of default post types
|
1001 |
-
* @return array Default post types
|
1002 |
-
*/
|
1003 |
-
function get_default_post_types() {
|
1004 |
-
return array('post', 'page', 'attachment', 'revision', 'nav_menu');
|
1005 |
-
}
|
1006 |
-
|
1007 |
-
/**
|
1008 |
-
* Checks if post's post type is a standard WP post type
|
1009 |
-
* @param mixed $post_type Post type (default) or post ID/object to evaluate
|
1010 |
-
* @see CNR_Content_Utilities::get_type() for possible parameter values
|
1011 |
-
* @return bool TRUE if post is default type, FALSE if it is a custom type
|
1012 |
-
*/
|
1013 |
-
function is_default_post_type($post_type) {
|
1014 |
-
if ( !is_string($post_type) ) {
|
1015 |
-
$post_type = $this->get_type($post_type);
|
1016 |
-
$post_type = $post_type->id;
|
1017 |
-
}
|
1018 |
-
return in_array($post_type, $this->get_default_post_types());
|
1019 |
-
}
|
1020 |
-
|
1021 |
-
/**
|
1022 |
-
* Checks if specified content type has been defined
|
1023 |
-
* @param string|CNR_Content_Type $type Content type ID or object
|
1024 |
-
* @return bool TRUE if content type exists, FALSE otherwise
|
1025 |
-
*
|
1026 |
-
* @uses array $cnr_content_types
|
1027 |
-
*/
|
1028 |
-
function type_exists($type) {
|
1029 |
-
global $cnr_content_types;
|
1030 |
-
if ( ! is_scalar($type) ) {
|
1031 |
-
if ( is_a($type, 'CNR_Content_Type') )
|
1032 |
-
$type = $type->id;
|
1033 |
-
else
|
1034 |
-
$type = null;
|
1035 |
-
}
|
1036 |
-
return ( isset($cnr_content_types[$type]) );
|
1037 |
-
}
|
1038 |
-
|
1039 |
-
/**
|
1040 |
-
* Retrieves content type definition for specified content item (post, page, etc.)
|
1041 |
-
* If content type does not exist, a new instance object will be created and returned
|
1042 |
-
* > New content types are automatically registered (since we are looking for registered types when using this method)
|
1043 |
-
* @param string|object $item Post object, or item type (string)
|
1044 |
-
* @return CNR_Content_Type Reference to matching content type, empty content type if no matching type exists
|
1045 |
-
*
|
1046 |
-
* @uses array $cnr_content_types
|
1047 |
-
*/
|
1048 |
-
function &get_type($item) {
|
1049 |
-
//Return immediately if $item is a content type instance
|
1050 |
-
if ( is_a($item, 'CNR_Content_Type') )
|
1051 |
-
return $item;
|
1052 |
-
|
1053 |
-
$type = null;
|
1054 |
-
|
1055 |
-
if ( is_string($item) )
|
1056 |
-
$type = $item;
|
1057 |
-
|
1058 |
-
if ( !$this->type_exists($type) ) {
|
1059 |
-
$post = $item;
|
1060 |
-
|
1061 |
-
//Check if $item is a post (object or ID)
|
1062 |
-
if ( $this->util->check_post($post) && isset($post->post_type) ) {
|
1063 |
-
$type = $post->post_type;
|
1064 |
-
}
|
1065 |
-
}
|
1066 |
-
global $cnr_content_types;
|
1067 |
-
if ( $this->type_exists($type) ) {
|
1068 |
-
//Retrieve content type from global array
|
1069 |
-
$type =& $cnr_content_types[$type];
|
1070 |
-
} else {
|
1071 |
-
//Create new empty content type if it does not already exist
|
1072 |
-
$type = new CNR_Content_Type($type);
|
1073 |
-
//Automatically register newly initialized content type if it extends an existing WP post type
|
1074 |
-
if ( $this->is_default_post_type($type->id) )
|
1075 |
-
$type->register();
|
1076 |
-
}
|
1077 |
-
|
1078 |
-
return $type;
|
1079 |
-
}
|
1080 |
-
|
1081 |
-
/**
|
1082 |
-
* Retrieve content types
|
1083 |
-
* @return Reference to content types array
|
1084 |
-
*/
|
1085 |
-
function &get_types() {
|
1086 |
-
return $GLOBALS['cnr_content_types'];
|
1087 |
-
}
|
1088 |
-
|
1089 |
-
/**
|
1090 |
-
* Retrieve meta key for post fields
|
1091 |
-
* @return string Fields meta key
|
1092 |
-
*/
|
1093 |
-
function get_fields_meta_key() {
|
1094 |
-
return $this->util->make_meta_key('fields');
|
1095 |
-
}
|
1096 |
-
|
1097 |
-
/**
|
1098 |
-
* Retrieve meta key for post type
|
1099 |
-
* @return string Post type meta key
|
1100 |
-
*/
|
1101 |
-
function get_type_meta_key() {
|
1102 |
-
return $this->util->make_meta_key('post_type');
|
1103 |
-
}
|
1104 |
-
|
1105 |
-
/**
|
1106 |
-
* Checks if post contains specified field data
|
1107 |
-
* @param Object $post (optional) Post to check data for
|
1108 |
-
* @param string $field (optional) Field ID to check for
|
1109 |
-
* @return bool TRUE if data exists, FALSE otherwise
|
1110 |
-
*/
|
1111 |
-
function has_item_data($item = null, $field = null) {
|
1112 |
-
$ret = $this->get_item_data($item, $field, 'raw', null);
|
1113 |
-
if ( is_scalar($ret) )
|
1114 |
-
return ( !empty($ret) || $ret === 0 );
|
1115 |
-
if ( is_array($ret) ) {
|
1116 |
-
foreach ( $ret as $key => $val ) {
|
1117 |
-
if ( !empty($val) || $val === 0 )
|
1118 |
-
return true;
|
1119 |
-
}
|
1120 |
-
}
|
1121 |
-
return false;
|
1122 |
-
}
|
1123 |
-
|
1124 |
-
/**
|
1125 |
-
* Retrieve specified field data from content item (e.g. post)
|
1126 |
-
* Usage Examples:
|
1127 |
-
* get_item_data($post_id, 'field_id')
|
1128 |
-
* - Retrieves field_id data from global $post object
|
1129 |
-
* - Field data is formatted using 'display' layout of field
|
1130 |
-
*
|
1131 |
-
* get_item_data($post_id, 'field_id', 'raw')
|
1132 |
-
* - Retrieves field_id data from global $post object
|
1133 |
-
* - Raw field data is returned (no formatting)
|
1134 |
-
*
|
1135 |
-
* get_item_data($post_id, 'field_id', 'display', $post_id)
|
1136 |
-
* - Retrieves field_id data from post matching $post_id
|
1137 |
-
* - Field data is formatted using 'display' layout of field
|
1138 |
-
*
|
1139 |
-
* get_item_data($post_id, 'field_id', null)
|
1140 |
-
* - Retrieves field_id data from post matching $post_id
|
1141 |
-
* - Field data is formatted using 'display' layout of field
|
1142 |
-
* - The default layout is used when no valid layout is specified
|
1143 |
-
*
|
1144 |
-
* get_item_data($post_id)
|
1145 |
-
* - Retrieves full data array from post matching $post_id
|
1146 |
-
*
|
1147 |
-
* @param int|object $item(optional) Content item to retrieve field from (Default: null - global $post object will be used)
|
1148 |
-
* @param string $field ID of field to retrieve
|
1149 |
-
* @param string $layout(optional) Layout to use when returning field data (Default: display)
|
1150 |
-
* @param array $attr (optional) Additional attributes to pass along to field object (e.g. for building layout, etc.)
|
1151 |
-
* @see CNR_Field_Type::build_layout for more information on attribute usage
|
1152 |
-
* @return mixed Specified field data
|
1153 |
-
*/
|
1154 |
-
function get_item_data($item = null, $field = null, $layout = null, $default = '', $attr = null) {
|
1155 |
-
$ret = $default;
|
1156 |
-
|
1157 |
-
//Get item
|
1158 |
-
$item = get_post($item);
|
1159 |
-
|
1160 |
-
if ( !isset($item->ID) )
|
1161 |
-
return $ret;
|
1162 |
-
|
1163 |
-
//Get item data
|
1164 |
-
$data = get_post_meta($item->ID, $this->get_fields_meta_key(), true);
|
1165 |
-
|
1166 |
-
//Get field data
|
1167 |
-
|
1168 |
-
//Set return value to data if no field specified
|
1169 |
-
if ( empty($field) || !is_string($field) )
|
1170 |
-
$ret = $data;
|
1171 |
-
//Stop if no valid field specified
|
1172 |
-
if ( !isset($data[$field]) ) {
|
1173 |
-
//TODO Check $item object to see if specified field exists (e.g. title, post_status, etc.)
|
1174 |
-
return $ret;
|
1175 |
-
}
|
1176 |
-
|
1177 |
-
$ret = $data[$field];
|
1178 |
-
|
1179 |
-
//Initialize layout value
|
1180 |
-
$layout_def = 'display';
|
1181 |
-
|
1182 |
-
if ( !is_scalar($layout) || empty($layout) )
|
1183 |
-
$layout = $layout_def;
|
1184 |
-
|
1185 |
-
$layout = strtolower($layout);
|
1186 |
-
|
1187 |
-
//Check if raw data requested
|
1188 |
-
if ( 'raw' == $layout )
|
1189 |
-
return $ret;
|
1190 |
-
|
1191 |
-
/* Build specified layout */
|
1192 |
-
|
1193 |
-
//Get item's content type
|
1194 |
-
$ct =& $this->get_type($item);
|
1195 |
-
$ct->set_data($data);
|
1196 |
-
|
1197 |
-
//Get field definition
|
1198 |
-
$fdef =& $ct->get_field($field);
|
1199 |
-
|
1200 |
-
//Validate layout
|
1201 |
-
if ( !$fdef->has_layout($layout) )
|
1202 |
-
$layout = $layout_def;
|
1203 |
-
|
1204 |
-
//Build layout
|
1205 |
-
$fdef->set_caller($ct);
|
1206 |
-
$ret = $fdef->build_layout($layout, $attr);
|
1207 |
-
$fdef->clear_caller();
|
1208 |
-
|
1209 |
-
//Return formatted value
|
1210 |
-
return $ret;
|
1211 |
-
}
|
1212 |
-
|
1213 |
-
/**
|
1214 |
-
* Prints an item's field data
|
1215 |
-
* @see CNR_Content_Utilities::get_item_data() for more information
|
1216 |
-
* @param int|object $item(optional) Content item to retrieve field from (Default: null - global $post object will be used)
|
1217 |
-
* @param string $field ID of field to retrieve
|
1218 |
-
* @param string $layout(optional) Layout to use when returning field data (Default: display)
|
1219 |
-
* @param mixed $default (optional) Default value to return in case of errors, etc.
|
1220 |
-
* @param array $attr Additional attributes to pass to field
|
1221 |
-
*/
|
1222 |
-
function the_item_data($item = null, $field = null, $layout = null, $default = '', $attr = null) {
|
1223 |
-
echo apply_filters('cnr_the_item_data', $this->get_item_data($item, $field, $layout, $default, $attr), $item, $field, $layout, $default, $attr);
|
1224 |
-
}
|
1225 |
-
|
1226 |
-
/**
|
1227 |
-
* Build Admin URL for specified post type
|
1228 |
-
* @param string|CNR_Content_Type $type Content type ID or object
|
1229 |
-
* @param string $action Action to build URL for
|
1230 |
-
* @param bool $sep_action Whether action should be a separate query variable (Default: false)
|
1231 |
-
* @return string Admin page URL
|
1232 |
-
*/
|
1233 |
-
function get_admin_page_url($type, $action = '', $sep_action = false) {
|
1234 |
-
$url = admin_url('admin.php');
|
1235 |
-
$url .= '?page=' . $this->get_admin_page_file($type, $action, $sep_action);
|
1236 |
-
return $url;
|
1237 |
-
}
|
1238 |
-
|
1239 |
-
function get_edit_item_url($edit_url, $item_id, $context) {
|
1240 |
-
//Get post type
|
1241 |
-
$type = $this->get_type($item_id);
|
1242 |
-
if ( ! $this->is_default_post_type($type->id) && $this->type_exists($type) ) {
|
1243 |
-
$edit_url = $this->get_admin_page_url($type, 'edit-item', true) . '&post=' . $item_id;
|
1244 |
-
}
|
1245 |
-
|
1246 |
-
return $edit_url;
|
1247 |
-
}
|
1248 |
}
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Utilities for Content Type functionality
|
4 |
+
* @package Cornerstone
|
5 |
+
* @subpackage Content Types
|
6 |
+
* @author Archetyped
|
7 |
+
*/
|
8 |
+
class CNR_Content_Utilities extends CNR_Base {
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Array of hooks called
|
12 |
+
* @var array
|
13 |
+
*/
|
14 |
+
var $hooks_processed = array();
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Initialize content type functionality
|
18 |
+
*/
|
19 |
+
function init() {
|
20 |
+
$this->register_hooks();
|
21 |
+
}
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Registers hooks for content types
|
25 |
+
* @todo 2010-07-30: Check hooks for 3.0 compatibility
|
26 |
+
*/
|
27 |
+
function register_hooks() {
|
28 |
+
//Register types
|
29 |
+
add_action('init', $this->m('register_types'));
|
30 |
+
add_action('init', $this->m('add_hooks'), 11);
|
31 |
+
|
32 |
+
//Enqueue scripts for fields in current post type
|
33 |
+
add_action('admin_enqueue_scripts', $this->m('enqueue_files'));
|
34 |
+
|
35 |
+
//Add menus
|
36 |
+
//add_action('admin_menu', $this->m('admin_menu'));
|
37 |
+
|
38 |
+
//Build UI on post edit form
|
39 |
+
add_action('do_meta_boxes', $this->m('admin_do_meta_boxes'), 10, 3);
|
40 |
+
|
41 |
+
//Get edit link for items
|
42 |
+
//add_filter('get_edit_post_link', $this->m('get_edit_item_url'), 10, 3);
|
43 |
+
|
44 |
+
//add_action('edit_form_advanced', $this->m('admin_page_edit_form'));
|
45 |
+
|
46 |
+
//Save Field data/Content type
|
47 |
+
add_action('save_post', $this->m('save_item_data'), 10, 2);
|
48 |
+
|
49 |
+
//Modify post query for content type compatibility
|
50 |
+
add_action('pre_get_posts', $this->m('pre_get_posts'), 20);
|
51 |
+
}
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Initialize fields and content types
|
55 |
+
*/
|
56 |
+
function register_types() {
|
57 |
+
//Global variables
|
58 |
+
global $cnr_field_types, $cnr_content_types;
|
59 |
+
|
60 |
+
/* Field Types */
|
61 |
+
|
62 |
+
//Base
|
63 |
+
$base = new CNR_Field_Type('base');
|
64 |
+
$base->set_description('Default Element');
|
65 |
+
$base->set_property('tag', 'span');
|
66 |
+
$base->set_property('class', '', 'attr');
|
67 |
+
$base->set_layout('form', '<{tag} name="{field_name}" id="{field_id}" {properties ref_base="root" group="attr"} />');
|
68 |
+
$base->set_layout('label', '<label for="{field_id}">{label}</label>');
|
69 |
+
$base->set_layout('display', '{data format="display"}');
|
70 |
+
$this->register_field($base);
|
71 |
+
|
72 |
+
//Base closed
|
73 |
+
$base_closed = new CNR_Field_Type('base_closed');
|
74 |
+
$base_closed->set_parent('base');
|
75 |
+
$base_closed->set_description('Default Element (Closed Tag)');
|
76 |
+
$base_closed->set_layout('form_start', '<{tag} id="{field_id}" name="{field_name}" {properties ref_base="root" group="attr"}>');
|
77 |
+
$base_closed->set_layout('form_end', '</{tag}>');
|
78 |
+
$base_closed->set_layout('form', '{form_start ref_base="layout"}{data}{form_end ref_base="layout"}');
|
79 |
+
$this->register_field($base_closed);
|
80 |
+
|
81 |
+
//Input
|
82 |
+
$input = new CNR_Field_Type('input');
|
83 |
+
$input->set_parent('base');
|
84 |
+
$input->set_description('Default Input Element');
|
85 |
+
$input->set_property('tag', 'input');
|
86 |
+
$input->set_property('type', 'text', 'attr');
|
87 |
+
$input->set_property('value', CNR_Field::USES_DATA, 'attr');
|
88 |
+
$this->register_field($input);
|
89 |
+
|
90 |
+
//Text input
|
91 |
+
$text = new CNR_Field_Type('text', 'input');
|
92 |
+
$text->set_description('Text Box');
|
93 |
+
$text->set_property('size', 15, 'attr');
|
94 |
+
$text->set_property('label');
|
95 |
+
$text->set_layout('form', '{label ref_base="layout"} {inherit}');
|
96 |
+
$this->register_field($text);
|
97 |
+
|
98 |
+
//Checkbox
|
99 |
+
$checkbox = new CNR_Field_Type('checkbox', 'input');
|
100 |
+
$checkbox->set_description('Checkbox');
|
101 |
+
$checkbox->set_property('type', 'checkbox', 'attr');
|
102 |
+
$checkbox->set_property('label');
|
103 |
+
$checkbox->set_property('checked', '', 'attr');
|
104 |
+
$checkbox->set_layout('form', '{inherit} {label ref_base="layout"}');
|
105 |
+
$this->register_field($checkbox);
|
106 |
+
|
107 |
+
//Textarea
|
108 |
+
$ta = new CNR_Field_Type('textarea', 'base_closed');
|
109 |
+
$ta->set_property('tag', 'textarea');
|
110 |
+
$ta->set_property('cols', 40, 'attr');
|
111 |
+
$ta->set_property('rows', 3, 'attr');
|
112 |
+
$this->register_field($ta);
|
113 |
+
|
114 |
+
//Rich Text
|
115 |
+
$rt = new CNR_Field_Type('richtext', 'textarea');
|
116 |
+
$rt->set_layout('form', '<div class="rt_container">{rich_editor}</div>');
|
117 |
+
$this->register_field($rt);
|
118 |
+
|
119 |
+
//Location
|
120 |
+
$location = new CNR_Field_Type('location');
|
121 |
+
$location->set_description('Geographic Coordinates');
|
122 |
+
$location->set_element('latitude', 'text', array( 'size' => 3, 'label' => 'Latitude' ));
|
123 |
+
$location->set_element('longitude', 'text', array( 'size' => 3, 'label' => 'Longitude' ));
|
124 |
+
$location->set_layout('form', '<span>{latitude ref_base="elements"}</span>, <span>{longitude ref_base="elements"}</span>');
|
125 |
+
$this->register_field($location);
|
126 |
+
|
127 |
+
//Phone
|
128 |
+
$phone = new CNR_Field_Type('phone');
|
129 |
+
$phone->set_description('Phone Number');
|
130 |
+
$phone->set_element('area', 'text', array( 'size' => 3 ));
|
131 |
+
$phone->set_element('prefix', 'text', array( 'size' => 3 ));
|
132 |
+
$phone->set_element('suffix', 'text', array( 'size' => 4 ));
|
133 |
+
$phone->set_layout('form', '({area ref_base="elements"}) {prefix ref_base="elements"} - {suffix ref_base="elements"}');
|
134 |
+
$this->register_field($phone);
|
135 |
+
|
136 |
+
//Hidden
|
137 |
+
$hidden = new CNR_Field_Type('hidden');
|
138 |
+
$hidden->set_parent('input');
|
139 |
+
$hidden->set_description('Hidden Field');
|
140 |
+
$hidden->set_property('type', 'hidden');
|
141 |
+
$this->register_field($hidden);
|
142 |
+
|
143 |
+
//Span
|
144 |
+
$span = new CNR_Field_Type('span');
|
145 |
+
$span->set_description('Inline wrapper');
|
146 |
+
$span->set_parent('base_closed');
|
147 |
+
$span->set_property('tag', 'span');
|
148 |
+
$span->set_property('value', 'Hello there!');
|
149 |
+
$this->register_field($span);
|
150 |
+
|
151 |
+
//Select
|
152 |
+
$select = new CNR_Field_Type('select');
|
153 |
+
$select->set_description('Select tag');
|
154 |
+
$select->set_parent('base_closed');
|
155 |
+
$select->set_property('tag', 'select');
|
156 |
+
$select->set_property('tag_option', 'option');
|
157 |
+
$select->set_property('options', array());
|
158 |
+
$select->set_layout('form', '{label ref_base="layout"} {form_start ref_base="layout"}{loop data="properties.options" layout="option" layout_data="option_data"}{form_end ref_base="layout"}');
|
159 |
+
$select->set_layout('option', '<{tag_option} value="{data_ext id="option_value"}">{data_ext id="option_text"}</{tag_option}>');
|
160 |
+
$select->set_layout('option_data', '<{tag_option} value="{data_ext id="option_value"}" selected="selected">{data_ext id="option_text"}</{tag_option}>');
|
161 |
+
$this->register_field($select);
|
162 |
+
|
163 |
+
//Enable plugins to modify (add, remove, etc.) field types
|
164 |
+
do_action_ref_array('cnr_register_field_types', array(&$cnr_field_types));
|
165 |
+
|
166 |
+
//Content Types
|
167 |
+
|
168 |
+
//Enable plugins to add/remove content types
|
169 |
+
do_action_ref_array('cnr_register_content_types', array(&$cnr_content_types));
|
170 |
+
|
171 |
+
//Enable plugins to modify content types after they have all been registered
|
172 |
+
do_action_ref_array('cnr_content_types_registered', array(&$cnr_content_types));
|
173 |
+
}
|
174 |
+
|
175 |
+
/**
|
176 |
+
* Add content type to global array of content types
|
177 |
+
* @param CNR_Content_Type $ct Content type to register
|
178 |
+
* @global array $cnr_content_types Content types array
|
179 |
+
*/
|
180 |
+
function register_content_type(&$ct) {
|
181 |
+
//Add content type to CNR array
|
182 |
+
if ( $this->is_content_type($ct) && !empty($ct->id) ) {
|
183 |
+
global $cnr_content_types;
|
184 |
+
$cnr_content_types[$ct->id] =& $ct;
|
185 |
+
}
|
186 |
+
//WP Post Type Registration
|
187 |
+
global $wp_post_types;
|
188 |
+
if ( !empty($ct->id) && !isset($wp_post_types[$ct->id]) )
|
189 |
+
register_post_type($ct->id, $this->build_post_type_args($ct));
|
190 |
+
}
|
191 |
+
|
192 |
+
/**
|
193 |
+
* Generates arguments array for WP Post Type Registration
|
194 |
+
* @param CNR_Content_Type $ct Content type being registered
|
195 |
+
* @return array Arguments array
|
196 |
+
* @todo Enable custom taxonomies
|
197 |
+
*/
|
198 |
+
function build_post_type_args(&$ct) {
|
199 |
+
//Setup labels
|
200 |
+
|
201 |
+
//Build labels
|
202 |
+
$labels = array (
|
203 |
+
'name' => _( $ct->get_title(true) ),
|
204 |
+
'singular_name' => _( $ct->get_title(false) ),
|
205 |
+
'all_items' => sprintf( _( 'All %s' ), $ct->get_title(true) ),
|
206 |
+
);
|
207 |
+
|
208 |
+
//Action labels
|
209 |
+
$item_actions = array(
|
210 |
+
'add_new' => 'Add New %s',
|
211 |
+
'edit' => 'Edit %s',
|
212 |
+
'new' => 'New %s',
|
213 |
+
'view' => 'View %s',
|
214 |
+
'search' => array('Search %s', true),
|
215 |
+
'not_found' => array('No %s found', true, false),
|
216 |
+
'not_found_in_trash' => array('No %s found in Trash', true, false)
|
217 |
+
);
|
218 |
+
|
219 |
+
foreach ( $item_actions as $key => $val ) {
|
220 |
+
$excluded = false;
|
221 |
+
$plural = false;
|
222 |
+
if ( is_array($val) ) {
|
223 |
+
if ( count($val) > 1 && true == $val[1] ) {
|
224 |
+
$plural = true;
|
225 |
+
}
|
226 |
+
if ( count($val) > 2 && false == $val[2] )
|
227 |
+
$excluded = true;
|
228 |
+
$val = $val[0];
|
229 |
+
}
|
230 |
+
$title = ( $plural ) ? $labels['name'] : $labels['singular_name'];
|
231 |
+
if ( $excluded )
|
232 |
+
$item = $key;
|
233 |
+
else {
|
234 |
+
$item = $key . '_item' . ( ( $plural ) ? 's' : '' );
|
235 |
+
}
|
236 |
+
$labels[$item] = sprintf($val, $title);
|
237 |
+
}
|
238 |
+
|
239 |
+
//Setup args
|
240 |
+
$args = array (
|
241 |
+
'labels' => $labels,
|
242 |
+
'description' => $ct->get_description(),
|
243 |
+
'public' => true,
|
244 |
+
'capability_type' => 'post',
|
245 |
+
'rewrite' => array( 'slug' => strtolower($labels['name']) ),
|
246 |
+
'has_archive' => true,
|
247 |
+
'hierarchical' => false,
|
248 |
+
'menu_position' => 5,
|
249 |
+
'supports' => array('title', 'editor', 'author', 'thumbnail', 'excerpt', 'trackbacks', 'custom-fields', 'comments', 'revisions'),
|
250 |
+
'taxonomies' => get_object_taxonomies('post'),
|
251 |
+
'show_in_rest' => true,
|
252 |
+
);
|
253 |
+
|
254 |
+
return $args;
|
255 |
+
}
|
256 |
+
|
257 |
+
/**
|
258 |
+
* Add field type to global array of field types
|
259 |
+
* @param CNR_Field_Type $field Field to register
|
260 |
+
*
|
261 |
+
* @global array $cnr_field_types Field types array
|
262 |
+
*/
|
263 |
+
function register_field(&$field) {
|
264 |
+
if ( $this->is_field($field) && !empty($field->id) ) {
|
265 |
+
global $cnr_field_types;
|
266 |
+
$cnr_field_types[$field->id] =& $field;
|
267 |
+
}
|
268 |
+
}
|
269 |
+
|
270 |
+
/*-** Helpers **-*/
|
271 |
+
|
272 |
+
/**
|
273 |
+
* Checks whether an object is a valid content type instance
|
274 |
+
* @param obj $ct Object to evaluate
|
275 |
+
* @return bool TRUE if object is a valid content type instance, FALSE otherwise
|
276 |
+
*/
|
277 |
+
function is_content_type(&$ct) {
|
278 |
+
return is_a($ct, 'cnr_content_type');
|
279 |
+
}
|
280 |
+
|
281 |
+
/**
|
282 |
+
* Checks whether an object is a valid field instance
|
283 |
+
* @param obj $field Object to evaluate
|
284 |
+
* @return bool TRUE if object is a valid field instance, FALSE otherwise
|
285 |
+
*/
|
286 |
+
function is_field(&$field) {
|
287 |
+
return is_a($field, 'cnr_field_type');
|
288 |
+
}
|
289 |
+
|
290 |
+
/*-** Handlers **-*/
|
291 |
+
|
292 |
+
/**
|
293 |
+
* Modifies query parameters to include custom content types
|
294 |
+
* Adds custom content types to default post query so these items are retrieved as well
|
295 |
+
* @param WP_Query $q Reference to WP_Query object being used to perform posts query
|
296 |
+
* @see WP_Query for reference
|
297 |
+
*/
|
298 |
+
function pre_get_posts($q) {
|
299 |
+
$pt =& $q->query_vars['post_type'];
|
300 |
+
/* Do not continue processing if:
|
301 |
+
* > In admin section
|
302 |
+
* > Not main query (or CNR-initiated query)
|
303 |
+
* > Single object requested
|
304 |
+
* > More than one post type is already specified
|
305 |
+
* > Post type other than 'post' is supplied
|
306 |
+
*/
|
307 |
+
if ( is_admin()
|
308 |
+
|| ( !$q->is_main_query() && !isset($q->query_vars[$this->get_prefix()]) )
|
309 |
+
|| $q->is_singular()
|
310 |
+
|| ( is_array($pt)
|
311 |
+
&& ( count($pt) > 1
|
312 |
+
|| 'post' != $pt[0] )
|
313 |
+
)
|
314 |
+
|| !in_array($pt, array('post', null))
|
315 |
+
) {
|
316 |
+
return false;
|
317 |
+
}
|
318 |
+
|
319 |
+
$default_types = $this->get_default_post_types();
|
320 |
+
$custom_types = array_diff(array_keys($this->get_types()), $default_types);
|
321 |
+
if ( !count($custom_types) )
|
322 |
+
return false;
|
323 |
+
//Wrap post type in array
|
324 |
+
if ( empty($pt) || is_null($pt) )
|
325 |
+
$pt = array('post');
|
326 |
+
if ( !is_array($pt) )
|
327 |
+
$pt = array($pt);
|
328 |
+
//Add custom types to query
|
329 |
+
foreach ( $custom_types as $type ) {
|
330 |
+
$pt[] = $type;
|
331 |
+
}
|
332 |
+
}
|
333 |
+
|
334 |
+
/**
|
335 |
+
* Retrieves current context (content type, action)
|
336 |
+
* @return array Content Type and Action of current request
|
337 |
+
*/
|
338 |
+
function get_context() {
|
339 |
+
$post = false;
|
340 |
+
if ( isset($GLOBALS['post']) && !is_null($GLOBALS['post']) )
|
341 |
+
$post = $GLOBALS['post'];
|
342 |
+
elseif ( isset($_REQUEST['post_id']) )
|
343 |
+
$post = $_REQUEST['post_id'];
|
344 |
+
elseif ( isset($_REQUEST['post']) )
|
345 |
+
$post = $_REQUEST['post'];
|
346 |
+
elseif ( isset($_REQUEST['post_type']) )
|
347 |
+
$post = $_REQUEST['post_type'];
|
348 |
+
//Get action
|
349 |
+
$action = $this->util->get_action();
|
350 |
+
if ( empty($post) )
|
351 |
+
$post = $this->get_page_type();
|
352 |
+
//Get post's content type
|
353 |
+
$ct =& $this->get_type($post);
|
354 |
+
|
355 |
+
return array(&$ct, $action);
|
356 |
+
}
|
357 |
+
|
358 |
+
/**
|
359 |
+
* Enqueues files for fields in current content type
|
360 |
+
* @param string $page Current context
|
361 |
+
*/
|
362 |
+
function enqueue_files($page = null) {
|
363 |
+
list($ct, $action) = $this->get_context();
|
364 |
+
$file_types = array('scripts' => 'script', 'styles' => 'style');
|
365 |
+
//Get content type fields
|
366 |
+
foreach ( $ct->fields as $field ) {
|
367 |
+
//Enqueue scripts/styles for each field
|
368 |
+
foreach ( $file_types as $type => $func_base ) {
|
369 |
+
$deps = $field->{"get_$type"}();
|
370 |
+
foreach ( $deps as $handle => $args ) {
|
371 |
+
//Confirm context
|
372 |
+
if ( in_array('all', $args['context']) || in_array($page, $args['context']) || in_array($action, $args['context']) ) {
|
373 |
+
$this->enqueue_file($func_base, $args['params']);
|
374 |
+
}
|
375 |
+
}
|
376 |
+
}
|
377 |
+
}
|
378 |
+
}
|
379 |
+
|
380 |
+
/**
|
381 |
+
* Add plugin hooks for fields used in current request
|
382 |
+
*/
|
383 |
+
function add_hooks() {
|
384 |
+
list($ct, $action) = $this->get_context();
|
385 |
+
//Iterate through content type fields and add hooks from fields
|
386 |
+
foreach ( $ct->fields as $field ) {
|
387 |
+
//Iterate through hooks added to field
|
388 |
+
$hooks = $field->get_hooks();
|
389 |
+
foreach ( $hooks as $tag => $callback ) {
|
390 |
+
//Iterate through function callbacks added to tag
|
391 |
+
foreach ( $callback as $id => $args ) {
|
392 |
+
//Check if hook/function was already processed
|
393 |
+
if ( isset($this->hooks_processed[$tag][$id]) )
|
394 |
+
continue;
|
395 |
+
//Add hook/function to list of processed hooks
|
396 |
+
if ( !isset($this->hooks_processed[$tag]) || !is_array($this->hooks_processed[$tag]) )
|
397 |
+
$this->hooks_processed[$tag] = array($id => true);
|
398 |
+
//Add hook to WP
|
399 |
+
call_user_func_array('add_filter', $args);
|
400 |
+
}
|
401 |
+
}
|
402 |
+
}
|
403 |
+
}
|
404 |
+
|
405 |
+
/**
|
406 |
+
* Enqueues files
|
407 |
+
* @param string $type Type of file to enqueue (script or style)
|
408 |
+
* @param array $args (optional) Arguments to pass to enqueue function
|
409 |
+
*/
|
410 |
+
function enqueue_file($type = 'script', $args = array()) {
|
411 |
+
$func = 'wp_enqueue_' . $type;
|
412 |
+
if ( function_exists($func) ) {
|
413 |
+
call_user_func_array($func, $args);
|
414 |
+
}
|
415 |
+
}
|
416 |
+
|
417 |
+
/**
|
418 |
+
* Add admin menus for content types
|
419 |
+
* @deprecated Not needed for 3.0+
|
420 |
+
*/
|
421 |
+
function admin_menu() {
|
422 |
+
global $cnr_content_types;
|
423 |
+
|
424 |
+
$pos = 21;
|
425 |
+
foreach ( $cnr_content_types as $id => $type ) {
|
426 |
+
if ( $this->is_default_post_type($id) )
|
427 |
+
continue;
|
428 |
+
$page = $this->get_admin_page_file($id);
|
429 |
+
$callback = $this->m('admin_page');
|
430 |
+
$access = 8;
|
431 |
+
$pos += 1;
|
432 |
+
$title = $type->get_title(true);
|
433 |
+
if ( !empty($title) ) {
|
434 |
+
//Main menu
|
435 |
+
add_menu_page($type->get_title(true), $type->get_title(true), $access, $page, $callback, '', $pos);
|
436 |
+
//Edit
|
437 |
+
add_submenu_page($page, __('Edit ' . $type->get_title(true)), __('Edit'), $access, $page, $callback);
|
438 |
+
$hook = get_plugin_page_hookname($page, $page);
|
439 |
+
add_action('load-' . $hook, $this->m('admin_menu_load_plugin'));
|
440 |
+
//Add
|
441 |
+
$page_add = $this->get_admin_page_file($id, 'add');
|
442 |
+
add_submenu_page($page, __('Add New ' . $type->get_title()), __('Add New'), $access, $page_add, $callback);
|
443 |
+
$hook = get_plugin_page_hook($page_add, $page);
|
444 |
+
add_action('load-' . $hook, $this->m('admin_menu_load_plugin'));
|
445 |
+
//Hook for additional menus
|
446 |
+
$menu_hook = 'cnr_admin_menu_type';
|
447 |
+
//Type specific
|
448 |
+
do_action_ref_array($menu_hook . '_' . $id, array(&$type));
|
449 |
+
//General
|
450 |
+
do_action_ref_array($menu_hook, array(&$type));
|
451 |
+
}
|
452 |
+
}
|
453 |
+
}
|
454 |
+
|
455 |
+
/**
|
456 |
+
* Load data for plugin admin page prior to admin-header.php is loaded
|
457 |
+
* Useful for enqueueing scripts/styles, etc.
|
458 |
+
*/
|
459 |
+
function admin_menu_load_plugin() {
|
460 |
+
//Get Action
|
461 |
+
global $editing, $post, $post_ID, $p;
|
462 |
+
$action = $this->util->get_action();
|
463 |
+
if ( isset($_GET['delete_all']) )
|
464 |
+
$action = 'delete_all';
|
465 |
+
if ( isset($_GET['action']) && 'edit' == $_GET['action'] && ! isset($_GET['bulk_edit']))
|
466 |
+
$action = 'manage';
|
467 |
+
switch ( $action ) {
|
468 |
+
case 'delete_all' :
|
469 |
+
case 'edit' :
|
470 |
+
//Handle bulk actions
|
471 |
+
//Redirect to edit.php for processing
|
472 |
+
|
473 |
+
//Build query string
|
474 |
+
$qs = $_GET;
|
475 |
+
unset($qs['page']);
|
476 |
+
$edit_uri = admin_url('edit.php') . '?' . build_query($qs);
|
477 |
+
wp_redirect($edit_uri);
|
478 |
+
break;
|
479 |
+
case 'edit-item' :
|
480 |
+
wp_enqueue_script('admin_comments');
|
481 |
+
enqueue_comment_hotkeys_js();
|
482 |
+
//Get post being edited
|
483 |
+
if ( empty($_GET['post']) ) {
|
484 |
+
wp_redirect("post.php"); //TODO redirect to appropriate manage page
|
485 |
+
exit();
|
486 |
+
}
|
487 |
+
$post_ID = $p = (int) $_GET['post'];
|
488 |
+
$post = get_post($post_ID);
|
489 |
+
if ( !current_user_can('edit_post', $post_ID) )
|
490 |
+
wp_die( __('You are not allowed to edit this item') );
|
491 |
+
|
492 |
+
if ( $last = wp_check_post_lock($post->ID) ) {
|
493 |
+
add_action('admin_notices', '_admin_notice_post_locked');
|
494 |
+
} else {
|
495 |
+
wp_set_post_lock($post->ID);
|
496 |
+
$locked = true;
|
497 |
+
}
|
498 |
+
//Continue on to add case
|
499 |
+
case 'add' :
|
500 |
+
$editing = true;
|
501 |
+
wp_enqueue_script('autosave');
|
502 |
+
wp_enqueue_script('post');
|
503 |
+
if ( user_can_richedit() )
|
504 |
+
wp_enqueue_script('editor');
|
505 |
+
add_thickbox();
|
506 |
+
wp_enqueue_script('media-upload');
|
507 |
+
wp_enqueue_script('word-count');
|
508 |
+
add_action( 'admin_print_footer_scripts', 'wp_tiny_mce', 25 );
|
509 |
+
wp_enqueue_script('quicktags');
|
510 |
+
wp_enqueue_script($this->add_prefix('edit_form'), $this->util->get_file_url('js/lib.admin.edit_form.js'), array('jquery', 'postbox'), false, true);
|
511 |
+
break;
|
512 |
+
default :
|
513 |
+
wp_enqueue_script( $this->add_prefix('inline-edit-post') );
|
514 |
+
}
|
515 |
+
}
|
516 |
+
|
517 |
+
/**
|
518 |
+
* Build admin page file name for the specified post type
|
519 |
+
* @param string|CNR_Content_Type $type Content type ID or object
|
520 |
+
* @param string $action Action to build file name for
|
521 |
+
* @param bool $sep_action Whether action should be a separate query variable (Default: false)
|
522 |
+
* @return string Admin page file name
|
523 |
+
*/
|
524 |
+
function get_admin_page_file($type, $action = '', $sep_action = false) {
|
525 |
+
if ( isset($type->id) )
|
526 |
+
$type = $type->id;
|
527 |
+
$page = $this->add_prefix('post_type_' . $type);
|
528 |
+
if ( !empty($action) ) {
|
529 |
+
if ( $sep_action )
|
530 |
+
$page .= '&action=';
|
531 |
+
else
|
532 |
+
$page .= '-';
|
533 |
+
|
534 |
+
$page .= $action;
|
535 |
+
}
|
536 |
+
return $page;
|
537 |
+
}
|
538 |
+
|
539 |
+
/**
|
540 |
+
* Determine content type based on URL query variables
|
541 |
+
* Uses $_GET['page'] variable to determine content type
|
542 |
+
* @return string Content type of page (NULL if no type defined by page)
|
543 |
+
*/
|
544 |
+
function get_page_type() {
|
545 |
+
$type = null;
|
546 |
+
//Extract type from query variable
|
547 |
+
if ( isset($_GET['page']) ) {
|
548 |
+
$type = $_GET['page'];
|
549 |
+
$prefix = $this->add_prefix('post_type_');
|
550 |
+
//Remove plugin page prefix
|
551 |
+
if ( ($pos = strpos($type, $prefix)) === 0 )
|
552 |
+
$type = substr($type, strlen($prefix));
|
553 |
+
//Remove action (if present)
|
554 |
+
if ( ($pos = strrpos($type, '-')) && $pos !== false )
|
555 |
+
$type = substr($type, 0, $pos);
|
556 |
+
}
|
557 |
+
return $type;
|
558 |
+
}
|
559 |
+
|
560 |
+
/**
|
561 |
+
* Populate administration page for content type
|
562 |
+
*/
|
563 |
+
function admin_page() {
|
564 |
+
$prefix = $this->add_prefix('post_type_');
|
565 |
+
if ( strpos($_GET['page'], $prefix) !== 0 )
|
566 |
+
return false;
|
567 |
+
|
568 |
+
//Get action
|
569 |
+
$action = $this->util->get_action('manage');
|
570 |
+
//Get content type
|
571 |
+
$type =& $this->get_type($this->get_page_type());
|
572 |
+
global $title, $parent_file, $submenu_file;
|
573 |
+
$title = $type->get_title(true);
|
574 |
+
//$parent_file = $prefix . $type->id;
|
575 |
+
//$submenu_file = $parent_file;
|
576 |
+
|
577 |
+
switch ( $action ) {
|
578 |
+
case 'edit-item' :
|
579 |
+
case 'add' :
|
580 |
+
$this->admin_page_edit($type, $action);
|
581 |
+
break;
|
582 |
+
default :
|
583 |
+
$this->admin_page_manage($type, $action);
|
584 |
+
}
|
585 |
+
}
|
586 |
+
|
587 |
+
/**
|
588 |
+
* Queries content items for admin management pages
|
589 |
+
* Also retrieves available post status for specified content type
|
590 |
+
* @see wp_edit_posts_query
|
591 |
+
* @param CNR_Content_Type|string $type Content type instance or ID
|
592 |
+
* @return array All item statuses and Available item status
|
593 |
+
*/
|
594 |
+
function admin_manage_query($type = 'post') {
|
595 |
+
global $wp_query;
|
596 |
+
$q = array();
|
597 |
+
//Get post type
|
598 |
+
if ( ! is_a($type, 'CNR_Content_Type') ) {
|
599 |
+
$type = $this->get_type($type);
|
600 |
+
}
|
601 |
+
$q = array('post_type' => $type->id);
|
602 |
+
$g = $_GET;
|
603 |
+
//Date
|
604 |
+
$q['m'] = isset($g['m']) ? (int) $g['m'] : 0;
|
605 |
+
//Category
|
606 |
+
$q['cat'] = isset($g['cat']) ? (int) $g['cat'] : 0;
|
607 |
+
$post_stati = array( // array( adj, noun )
|
608 |
+
'publish' => array(_x('Published', 'post'), __('Published posts'), _n_noop('Published <span class="count">(%s)</span>', 'Published <span class="count">(%s)</span>')),
|
609 |
+
'future' => array(_x('Scheduled', 'post'), __('Scheduled posts'), _n_noop('Scheduled <span class="count">(%s)</span>', 'Scheduled <span class="count">(%s)</span>')),
|
610 |
+
'pending' => array(_x('Pending Review', 'post'), __('Pending posts'), _n_noop('Pending Review <span class="count">(%s)</span>', 'Pending Review <span class="count">(%s)</span>')),
|
611 |
+
'draft' => array(_x('Draft', 'post'), _x('Drafts', 'manage posts header'), _n_noop('Draft <span class="count">(%s)</span>', 'Drafts <span class="count">(%s)</span>')),
|
612 |
+
'private' => array(_x('Private', 'post'), __('Private posts'), _n_noop('Private <span class="count">(%s)</span>', 'Private <span class="count">(%s)</span>')),
|
613 |
+
'trash' => array(_x('Trash', 'post'), __('Trash posts'), _n_noop('Trash <span class="count">(%s)</span>', 'Trash <span class="count">(%s)</span>')),
|
614 |
+
);
|
615 |
+
|
616 |
+
$post_stati = apply_filters('post_stati', $post_stati);
|
617 |
+
|
618 |
+
$avail_post_stati = get_available_post_statuses('post');
|
619 |
+
|
620 |
+
//Status
|
621 |
+
if ( isset($g['post_status']) && in_array( $g['post_status'], array_keys($post_stati) ) ) {
|
622 |
+
$q['post_status'] = $g['post_status'];
|
623 |
+
$q['perm'] = 'readable';
|
624 |
+
} else {
|
625 |
+
unset($q['post_status']);
|
626 |
+
}
|
627 |
+
|
628 |
+
//Order
|
629 |
+
if ( isset($q['post_status']) && 'pending' === $q['post_status'] ) {
|
630 |
+
$q['order'] = 'ASC';
|
631 |
+
$q['orderby'] = 'modified';
|
632 |
+
} elseif ( isset($q['post_status']) && 'draft' === $q['post_status'] ) {
|
633 |
+
$q['order'] = 'DESC';
|
634 |
+
$q['orderby'] = 'modified';
|
635 |
+
} else {
|
636 |
+
$q['order'] = 'DESC';
|
637 |
+
$q['orderby'] = 'date';
|
638 |
+
}
|
639 |
+
|
640 |
+
//Pagination
|
641 |
+
$posts_per_page = (int) get_user_option( 'edit_per_page', 0, false );
|
642 |
+
if ( empty( $posts_per_page ) || $posts_per_page < 1 )
|
643 |
+
$posts_per_page = 15;
|
644 |
+
if ( isset($g['paged']) && (int) $g['paged'] > 1 )
|
645 |
+
$q['paged'] = (int) $g['paged'];
|
646 |
+
$q['posts_per_page'] = apply_filters( 'edit_posts_per_page', $posts_per_page );
|
647 |
+
//Search
|
648 |
+
$q[s] = ( isset($g['s']) ) ? $g[s] : '';
|
649 |
+
$wp_query->query($q);
|
650 |
+
|
651 |
+
return array($post_stati, $avail_post_stati);
|
652 |
+
}
|
653 |
+
|
654 |
+
/**
|
655 |
+
* Counts the number of items in the specified content type
|
656 |
+
* @see wp_count_posts
|
657 |
+
* @param CNR_Content_Type|string $type Content Type instance or ID
|
658 |
+
* @param string $perm Permission level for items (e.g. readable)
|
659 |
+
* @return array Associative array of item counts by post status (published, draft, etc.)
|
660 |
+
*/
|
661 |
+
function count_posts( $type, $perm = '' ) {
|
662 |
+
global $wpdb;
|
663 |
+
|
664 |
+
$user = wp_get_current_user();
|
665 |
+
|
666 |
+
if ( !is_a($type, 'CNR_Content_Type') )
|
667 |
+
$type = $this->get_type($type);
|
668 |
+
$type_val = $type->get_meta_value();
|
669 |
+
$type = $type->id;
|
670 |
+
$cache_key = $type;
|
671 |
+
|
672 |
+
//$query = "SELECT post_status, COUNT( * ) AS num_posts FROM {$wpdb->posts} WHERE post_type = %s";
|
673 |
+
$query = "SELECT p.post_status, COUNT( * ) as num_posts FROM {$wpdb->posts} p JOIN {$wpdb->postmeta} m ON m.post_id = p.id WHERE m.meta_key = '" . $this->get_type_meta_key() . "' AND m.meta_value = '$type_val'";
|
674 |
+
if ( 'readable' == $perm && is_user_logged_in() ) {
|
675 |
+
//TODO enable check for custom post types "read_private_{$type}s"
|
676 |
+
if ( !current_user_can("read_private_posts") ) {
|
677 |
+
$cache_key .= '_' . $perm . '_' . $user->ID;
|
678 |
+
$query .= " AND (p.post_status != 'private' OR ( p.post_author = '$user->ID' AND p.post_status = 'private' ))";
|
679 |
+
}
|
680 |
+
}
|
681 |
+
$query .= ' GROUP BY p.post_status';
|
682 |
+
|
683 |
+
$count = wp_cache_get($cache_key, 'counts');
|
684 |
+
if ( false !== $count )
|
685 |
+
return $count;
|
686 |
+
|
687 |
+
$count = $wpdb->get_results( $wpdb->prepare( $query, $type ), ARRAY_A );
|
688 |
+
|
689 |
+
$stats = array( 'publish' => 0, 'private' => 0, 'draft' => 0, 'pending' => 0, 'future' => 0, 'trash' => 0 );
|
690 |
+
foreach( (array) $count as $row_num => $row ) {
|
691 |
+
$stats[$row['post_status']] = $row['num_posts'];
|
692 |
+
}
|
693 |
+
|
694 |
+
$stats = (object) $stats;
|
695 |
+
wp_cache_set($cache_key, $stats, 'counts');
|
696 |
+
|
697 |
+
return $stats;
|
698 |
+
}
|
699 |
+
|
700 |
+
/**
|
701 |
+
* Builds management page for items of a specific custom content type
|
702 |
+
* @param CNR_Content_Type $type Content Type to manage
|
703 |
+
* @param string $action Current action
|
704 |
+
*
|
705 |
+
* @global string $title
|
706 |
+
* @global string $parent_file
|
707 |
+
* @global string $plugin_page
|
708 |
+
* @global string $page_hook
|
709 |
+
* @global WP_User $current_user
|
710 |
+
* @global WP_Query $wp_query
|
711 |
+
* @global wpdb $wpdb
|
712 |
+
* @global WP_Locale $wp_locale
|
713 |
+
*/
|
714 |
+
function admin_page_manage($type, $action) {
|
715 |
+
if ( !current_user_can('edit_posts') )
|
716 |
+
wp_die(__('You do not have sufficient permissions to access this page.'));
|
717 |
+
|
718 |
+
global $title, $parent_file, $plugin_page, $page_hook, $current_user, $wp_query, $wpdb, $wp_locale;
|
719 |
+
$title = __('Edit ' . $type->get_title(true));
|
720 |
+
$admin_path = ABSPATH . 'wp-admin/';
|
721 |
+
|
722 |
+
//Pagination
|
723 |
+
if ( ! isset($_GET['paged']) )
|
724 |
+
$_GET['paged'] = 1;
|
725 |
+
|
726 |
+
$add_url = $this->get_admin_page_url($type->id, 'add');
|
727 |
+
$is_trash = isset($_GET['post_status']) && $_GET['post_status'] == 'trash';
|
728 |
+
//User posts
|
729 |
+
$user_posts = false;
|
730 |
+
if ( !current_user_can('edit_others_posts') ) {
|
731 |
+
$user_posts_count = $wpdb->get_var( $wpdb->prepare("SELECT COUNT(1) FROM $wpdb->posts p JOIN $wpdb->postmeta m ON m.post_id = p.id WHERE m.meta_key = '_cnr_post_type' AND m.meta_value = %s AND p.post_status != 'trash' AND p.post_author = %d", $type->get_meta_value(), $current_user->ID) );
|
732 |
+
$user_posts = true;
|
733 |
+
if ( $user_posts_count && empty($_GET['post_status']) && empty($_GET['all_posts']) && empty($_GET['author']) )
|
734 |
+
$_GET['author'] = $current_user->ID;
|
735 |
+
}
|
736 |
+
//Get content type items
|
737 |
+
list($post_stati, $avail_post_stati) = $this->admin_manage_query($type->id);
|
738 |
+
?>
|
739 |
+
<div class="wrap">
|
740 |
+
<?php screen_icon('edit'); ?>
|
741 |
+
<h2><?php echo esc_html( $title ); ?> <a href="<?php echo $add_url; ?>" class="button add-new-h2"><?php echo esc_html_x('Add New', 'post'); ?></a> <?php
|
742 |
+
if ( isset($_GET['s']) && $_GET['s'] )
|
743 |
+
printf( '<span class="subtitle">' . __('Search results for “%s”') . '</span>', esc_html( get_search_query() ) ); ?>
|
744 |
+
</h2>
|
745 |
+
<?php /* Action messages here: saved, trashed, etc. */ ?>
|
746 |
+
<form id="posts-filter" action="<?php echo admin_url('admin.php'); ?>" method="get">
|
747 |
+
<?php if ( isset($_GET['page']) ) { ?>
|
748 |
+
<input type="hidden" name="page" id="page" value="<?php esc_attr_e($_GET['page']); ?>" />
|
749 |
+
<?php } ?>
|
750 |
+
<ul class="subsubsub">
|
751 |
+
<?php
|
752 |
+
/* Status links */
|
753 |
+
if ( empty($locked_post_status) ) :
|
754 |
+
$status_links = array();
|
755 |
+
$num_posts = $this->count_posts($type, 'readable');
|
756 |
+
$class = '';
|
757 |
+
$allposts = '';
|
758 |
+
$curr_page = $_SERVER['PHP_SELF'] . '?page=' . $_GET['page'];
|
759 |
+
if ( $user_posts ) {
|
760 |
+
if ( isset( $_GET['author'] ) && ( $_GET['author'] == $current_user->ID ) )
|
761 |
+
$class = ' class="current"';
|
762 |
+
$status_links[] = "<li><a href='$curr_page&author=$current_user->ID'$class>" . sprintf( _nx( 'My Posts <span class="count">(%s)</span>', 'My Posts <span class="count">(%s)</span>', $user_posts_count, 'posts' ), number_format_i18n( $user_posts_count ) ) . '</a>';
|
763 |
+
$allposts = '?all_posts=1';
|
764 |
+
}
|
765 |
+
|
766 |
+
$total_posts = array_sum( (array) $num_posts ) - $num_posts->trash;
|
767 |
+
$class = empty($class) && empty($_GET['post_status']) ? ' class="current"' : '';
|
768 |
+
$status_links[] = "<li><a href='$curr_page{$allposts}'$class>" . sprintf( _nx( 'All <span class="count">(%s)</span>', 'All <span class="count">(%s)</span>', $total_posts, 'posts' ), number_format_i18n( $total_posts ) ) . '</a>';
|
769 |
+
|
770 |
+
foreach ( $post_stati as $status => $label ) {
|
771 |
+
$class = '';
|
772 |
+
|
773 |
+
if ( !in_array( $status, $avail_post_stati ) )
|
774 |
+
continue;
|
775 |
+
|
776 |
+
if ( empty( $num_posts->$status ) )
|
777 |
+
continue;
|
778 |
+
|
779 |
+
if ( isset($_GET['post_status']) && $status == $_GET['post_status'] )
|
780 |
+
$class = ' class="current"';
|
781 |
+
|
782 |
+
$status_links[] = "<li><a href='$curr_page&post_status=$status'$class>" . sprintf( _n( $label[2][0], $label[2][1], $num_posts->$status ), number_format_i18n( $num_posts->$status ) ) . '</a>';
|
783 |
+
}
|
784 |
+
echo implode( " |</li>\n", $status_links ) . '</li>';
|
785 |
+
unset( $status_links );
|
786 |
+
endif;
|
787 |
+
?>
|
788 |
+
</ul>
|
789 |
+
<p class="search-box">
|
790 |
+
<label class="screen-reader-text" for="post-search-input"><?php _e('Search Posts', 'cornerstone'); ?>:</label>
|
791 |
+
<input type="text" id="post-search-input" name="s" value="<?php the_search_query(); ?>" />
|
792 |
+
<input type="submit" value="<?php esc_attr_e('Search Posts', 'cornerstone'); ?>" class="button" />
|
793 |
+
</p>
|
794 |
+
<?php
|
795 |
+
if ( have_posts() ) {
|
796 |
+
?>
|
797 |
+
<div class="tablenav">
|
798 |
+
<?php
|
799 |
+
$page_links = paginate_links( array(
|
800 |
+
'base' => add_query_arg( 'paged', '%#%' ),
|
801 |
+
'format' => '',
|
802 |
+
'prev_text' => __('«'),
|
803 |
+
'next_text' => __('»'),
|
804 |
+
'total' => $wp_query->max_num_pages,
|
805 |
+
'current' => $_GET['paged']
|
806 |
+
));
|
807 |
+
?>
|
808 |
+
<div class="alignleft actions">
|
809 |
+
<select name="action">
|
810 |
+
<option value="-1" selected="selected"><?php _e('Bulk Actions', 'cornerstone'); ?></option>
|
811 |
+
<?php if ( $is_trash ) { ?>
|
812 |
+
<option value="untrash"><?php _e('Restore', 'cornerstone'); ?></option>
|
813 |
+
<?php } else { ?>
|
814 |
+
<option value="edit"><?php _e('Edit', 'cornerstone'); ?></option>
|
815 |
+
<?php } if ( $is_trash || !EMPTY_TRASH_DAYS ) { ?>
|
816 |
+
<option value="delete"><?php _e('Delete Permanently', 'cornerstone'); ?></option>
|
817 |
+
<?php } else { ?>
|
818 |
+
<option value="trash"><?php _e('Move to Trash', 'cornerstone'); ?></option>
|
819 |
+
<?php } ?>
|
820 |
+
</select>
|
821 |
+
<input type="submit" value="<?php esc_attr_e('Apply', 'cornerstone'); ?>" name="doaction" id="doaction" class="button-secondary action" />
|
822 |
+
<?php wp_nonce_field('bulk-posts'); ?>
|
823 |
+
|
824 |
+
<?php // view filters
|
825 |
+
if ( !is_singular() ) {
|
826 |
+
$arc_query = "SELECT DISTINCT YEAR(post_date) AS yyear, MONTH(post_date) AS mmonth FROM $wpdb->posts p JOIN $wpdb->postmeta m ON m.post_id = p.ID WHERE m.meta_key = '" . $this->get_type_meta_key() . "' AND m.meta_value = '" . $type->get_meta_value() . "' ORDER BY post_date DESC";
|
827 |
+
|
828 |
+
$arc_result = $wpdb->get_results( $arc_query );
|
829 |
+
|
830 |
+
$month_count = count($arc_result);
|
831 |
+
|
832 |
+
if ( $month_count && !( 1 == $month_count && 0 == $arc_result[0]->mmonth ) ) {
|
833 |
+
$m = isset($_GET['m']) ? (int)$_GET['m'] : 0;
|
834 |
+
?>
|
835 |
+
<select name='m'>
|
836 |
+
<option<?php selected( $m, 0 ); ?> value='0'><?php _e('Show all dates', 'cornerstone'); ?></option>
|
837 |
+
<?php
|
838 |
+
foreach ($arc_result as $arc_row) {
|
839 |
+
if ( $arc_row->yyear == 0 )
|
840 |
+
continue;
|
841 |
+
$arc_row->mmonth = zeroise( $arc_row->mmonth, 2 );
|
842 |
+
|
843 |
+
if ( $arc_row->yyear . $arc_row->mmonth == $m )
|
844 |
+
$default = ' selected="selected"';
|
845 |
+
else
|
846 |
+
$default = '';
|
847 |
+
|
848 |
+
echo "<option$default value='" . esc_attr("$arc_row->yyear$arc_row->mmonth") . "'>";
|
849 |
+
echo $wp_locale->get_month($arc_row->mmonth) . " $arc_row->yyear";
|
850 |
+
echo "</option>\n";
|
851 |
+
}
|
852 |
+
?>
|
853 |
+
</select>
|
854 |
+
<?php }
|
855 |
+
|
856 |
+
$dropdown_options = array('show_option_all' => __('View all categories'), 'hide_empty' => 0, 'hierarchical' => 1,
|
857 |
+
'show_count' => 0, 'orderby' => 'name', 'selected' => $cat);
|
858 |
+
wp_dropdown_categories($dropdown_options);
|
859 |
+
do_action('restrict_manage_posts');
|
860 |
+
?>
|
861 |
+
<input type="submit" id="post-query-submit" value="<?php esc_attr_e('Filter', 'cornerstone'); ?>" class="button-secondary" />
|
862 |
+
<?php }
|
863 |
+
|
864 |
+
if ( $is_trash && current_user_can('edit_others_posts') ) { ?>
|
865 |
+
<input type="submit" name="delete_all" id="delete_all" value="<?php esc_attr_e('Empty Trash', 'cornerstone'); ?>" class="button-secondary apply" />
|
866 |
+
<?php } ?>
|
867 |
+
</div>
|
868 |
+
|
869 |
+
<?php if ( $page_links ) { ?>
|
870 |
+
<div class="tablenav-pages"><?php $page_links_text = sprintf( '<span class="displaying-num">' . __( 'Displaying %s–%s of %s' ) . '</span>%s',
|
871 |
+
number_format_i18n( ( $_GET['paged'] - 1 ) * $wp_query->query_vars['posts_per_page'] + 1 ),
|
872 |
+
number_format_i18n( min( $_GET['paged'] * $wp_query->query_vars['posts_per_page'], $wp_query->found_posts ) ),
|
873 |
+
number_format_i18n( $wp_query->found_posts ),
|
874 |
+
$page_links
|
875 |
+
); echo $page_links_text; ?></div>
|
876 |
+
<?php } //page links ?>
|
877 |
+
<div class="clear"></div>
|
878 |
+
</div>
|
879 |
+
<?php
|
880 |
+
include ($admin_path . 'edit-post-rows.php');
|
881 |
+
} else { //have_posts() ?>
|
882 |
+
<div class="clear"></div>
|
883 |
+
<p><?php
|
884 |
+
if ( $is_trash )
|
885 |
+
_e('No posts found in the trash', 'cornerstone');
|
886 |
+
else
|
887 |
+
_e('No posts found', 'cornerstone');
|
888 |
+
?></p>
|
889 |
+
<?php } ?>
|
890 |
+
</form>
|
891 |
+
<?php inline_edit_row('post'); ?>
|
892 |
+
<div id="ajax-response"></div>
|
893 |
+
<br class="clear" />
|
894 |
+
</div>
|
895 |
+
<?php
|
896 |
+
}
|
897 |
+
|
898 |
+
/**
|
899 |
+
* Build admin edit page for custom type item
|
900 |
+
* @param CNR_Content_Type $type Content type being edited
|
901 |
+
* @param string $action Current action (add, edit, manage, etc.)
|
902 |
+
*/
|
903 |
+
function admin_page_edit($type, $action) {
|
904 |
+
global $title, $hook_suffix, $parent_file, $screen_layout_columns, $post, $post_ID, $p;
|
905 |
+
$screen_layout_columns = 2;
|
906 |
+
//TODO Add default icon for content type
|
907 |
+
$parent_file = 'edit.php'; //Makes screen_icon() use edit icon on post edit form
|
908 |
+
switch ( $action ) {
|
909 |
+
case 'edit-item' :
|
910 |
+
$title = 'Edit';
|
911 |
+
$post = get_post_to_edit($post_ID);
|
912 |
+
break;
|
913 |
+
default :
|
914 |
+
$title = 'Add New';
|
915 |
+
$post = get_default_post_to_edit();
|
916 |
+
break;
|
917 |
+
}
|
918 |
+
$title = __($title . ' ' . $type->get_title());
|
919 |
+
$admin_path = ABSPATH . 'wp-admin/';
|
920 |
+
include ($admin_path . 'edit-form-advanced.php');
|
921 |
+
}
|
922 |
+
|
923 |
+
/**
|
924 |
+
* Adds hidden field declaring content type on post edit form
|
925 |
+
* @deprecated no longer needed for WP 3.0+
|
926 |
+
*/
|
927 |
+
function admin_page_edit_form() {
|
928 |
+
global $post, $plugin_page;
|
929 |
+
if ( empty($post) || !$post->ID ) {
|
930 |
+
$type = $this->get_type($post);
|
931 |
+
if ( ! empty($type) && ! empty($type->id) ) {
|
932 |
+
?>
|
933 |
+
<input type="hidden" name="cnr[content_type]" id="cnr[content_type]" value="<?php echo $type->id; ?>" />
|
934 |
+
<?php
|
935 |
+
}
|
936 |
+
}
|
937 |
+
}
|
938 |
+
|
939 |
+
/**
|
940 |
+
* Adds meta boxes for post's content type
|
941 |
+
* Each group in content type is a separate meta box
|
942 |
+
* @param string $type Type of item meta boxes are being build for (post, page, link)
|
943 |
+
* @param string $context Location of meta box (normal, advanced, side)
|
944 |
+
* @param object $post Post object
|
945 |
+
*/
|
946 |
+
function admin_do_meta_boxes($type, $context, $post) {
|
947 |
+
//Validate $type. Should be 'post','page', or a custom post type for our purposes
|
948 |
+
if ( in_array($type, array_merge(array_keys($this->get_types()), array('post', 'page'))) ) {
|
949 |
+
//Get content type definition
|
950 |
+
$ct =& $this->get_type($post);
|
951 |
+
//Pass processing to content type instance
|
952 |
+
$ct->admin_do_meta_boxes($type, $context, $post);
|
953 |
+
}
|
954 |
+
}
|
955 |
+
|
956 |
+
/**
|
957 |
+
* Saves field data submitted for current post
|
958 |
+
* @param int $post_id ID of current post
|
959 |
+
* @param object $post Post object
|
960 |
+
*/
|
961 |
+
function save_item_data($post_id, $post) {
|
962 |
+
if ( empty($post_id) || empty($post) || !isset($_POST['cnr']) || !is_array($_POST['cnr']) )
|
963 |
+
return false;
|
964 |
+
$pdata = $_POST['cnr'];
|
965 |
+
|
966 |
+
if ( isset($pdata['attributes']) && is_array($pdata['attributes']) && isset($pdata['fields_loaded']) && is_array($pdata['fields_loaded']) ) {
|
967 |
+
|
968 |
+
$prev_data = (array) $this->get_item_data($post_id);
|
969 |
+
|
970 |
+
//Remove loaded fields from prev data
|
971 |
+
$prev_data = array_diff_key($prev_data, $pdata['fields_loaded']);
|
972 |
+
|
973 |
+
//Get current field data
|
974 |
+
$curr_data = $pdata['attributes'];
|
975 |
+
|
976 |
+
//Merge arrays together (new data overwrites old data)
|
977 |
+
if ( is_array($prev_data) && is_array($curr_data) ) {
|
978 |
+
$curr_data = array_merge($prev_data, $curr_data);
|
979 |
+
}
|
980 |
+
|
981 |
+
//Save to database
|
982 |
+
update_post_meta($post_id, $this->get_fields_meta_key(), $curr_data);
|
983 |
+
}
|
984 |
+
//Save content type
|
985 |
+
if ( isset($_POST['cnr']['content_type']) ) {
|
986 |
+
$type = $_POST['cnr']['content_type'];
|
987 |
+
$saved_type = get_post_meta($post_id, $this->get_type_meta_key(), true);
|
988 |
+
if ( is_array($saved_type) )
|
989 |
+
$saved_type = implode($saved_type);
|
990 |
+
if ( $type != $saved_type ) {
|
991 |
+
//Continue processing if submitted content type is different from previously-saved content type (or no type was previously set)
|
992 |
+
update_post_meta($post_id, $this->get_type_meta_key(), array($type));
|
993 |
+
}
|
994 |
+
}
|
995 |
+
}
|
996 |
+
|
997 |
+
/*-** Helpers **-*/
|
998 |
+
|
999 |
+
/**
|
1000 |
+
* Get array of default post types
|
1001 |
+
* @return array Default post types
|
1002 |
+
*/
|
1003 |
+
function get_default_post_types() {
|
1004 |
+
return array('post', 'page', 'attachment', 'revision', 'nav_menu');
|
1005 |
+
}
|
1006 |
+
|
1007 |
+
/**
|
1008 |
+
* Checks if post's post type is a standard WP post type
|
1009 |
+
* @param mixed $post_type Post type (default) or post ID/object to evaluate
|
1010 |
+
* @see CNR_Content_Utilities::get_type() for possible parameter values
|
1011 |
+
* @return bool TRUE if post is default type, FALSE if it is a custom type
|
1012 |
+
*/
|
1013 |
+
function is_default_post_type($post_type) {
|
1014 |
+
if ( !is_string($post_type) ) {
|
1015 |
+
$post_type = $this->get_type($post_type);
|
1016 |
+
$post_type = $post_type->id;
|
1017 |
+
}
|
1018 |
+
return in_array($post_type, $this->get_default_post_types());
|
1019 |
+
}
|
1020 |
+
|
1021 |
+
/**
|
1022 |
+
* Checks if specified content type has been defined
|
1023 |
+
* @param string|CNR_Content_Type $type Content type ID or object
|
1024 |
+
* @return bool TRUE if content type exists, FALSE otherwise
|
1025 |
+
*
|
1026 |
+
* @uses array $cnr_content_types
|
1027 |
+
*/
|
1028 |
+
function type_exists($type) {
|
1029 |
+
global $cnr_content_types;
|
1030 |
+
if ( ! is_scalar($type) ) {
|
1031 |
+
if ( is_a($type, 'CNR_Content_Type') )
|
1032 |
+
$type = $type->id;
|
1033 |
+
else
|
1034 |
+
$type = null;
|
1035 |
+
}
|
1036 |
+
return ( isset($cnr_content_types[$type]) );
|
1037 |
+
}
|
1038 |
+
|
1039 |
+
/**
|
1040 |
+
* Retrieves content type definition for specified content item (post, page, etc.)
|
1041 |
+
* If content type does not exist, a new instance object will be created and returned
|
1042 |
+
* > New content types are automatically registered (since we are looking for registered types when using this method)
|
1043 |
+
* @param string|object $item Post object, or item type (string)
|
1044 |
+
* @return CNR_Content_Type Reference to matching content type, empty content type if no matching type exists
|
1045 |
+
*
|
1046 |
+
* @uses array $cnr_content_types
|
1047 |
+
*/
|
1048 |
+
function &get_type($item) {
|
1049 |
+
//Return immediately if $item is a content type instance
|
1050 |
+
if ( is_a($item, 'CNR_Content_Type') )
|
1051 |
+
return $item;
|
1052 |
+
|
1053 |
+
$type = null;
|
1054 |
+
|
1055 |
+
if ( is_string($item) )
|
1056 |
+
$type = $item;
|
1057 |
+
|
1058 |
+
if ( !$this->type_exists($type) ) {
|
1059 |
+
$post = $item;
|
1060 |
+
|
1061 |
+
//Check if $item is a post (object or ID)
|
1062 |
+
if ( $this->util->check_post($post) && isset($post->post_type) ) {
|
1063 |
+
$type = $post->post_type;
|
1064 |
+
}
|
1065 |
+
}
|
1066 |
+
global $cnr_content_types;
|
1067 |
+
if ( $this->type_exists($type) ) {
|
1068 |
+
//Retrieve content type from global array
|
1069 |
+
$type =& $cnr_content_types[$type];
|
1070 |
+
} else {
|
1071 |
+
//Create new empty content type if it does not already exist
|
1072 |
+
$type = new CNR_Content_Type($type);
|
1073 |
+
//Automatically register newly initialized content type if it extends an existing WP post type
|
1074 |
+
if ( $this->is_default_post_type($type->id) )
|
1075 |
+
$type->register();
|
1076 |
+
}
|
1077 |
+
|
1078 |
+
return $type;
|
1079 |
+
}
|
1080 |
+
|
1081 |
+
/**
|
1082 |
+
* Retrieve content types
|
1083 |
+
* @return Reference to content types array
|
1084 |
+
*/
|
1085 |
+
function &get_types() {
|
1086 |
+
return $GLOBALS['cnr_content_types'];
|
1087 |
+
}
|
1088 |
+
|
1089 |
+
/**
|
1090 |
+
* Retrieve meta key for post fields
|
1091 |
+
* @return string Fields meta key
|
1092 |
+
*/
|
1093 |
+
function get_fields_meta_key() {
|
1094 |
+
return $this->util->make_meta_key('fields');
|
1095 |
+
}
|
1096 |
+
|
1097 |
+
/**
|
1098 |
+
* Retrieve meta key for post type
|
1099 |
+
* @return string Post type meta key
|
1100 |
+
*/
|
1101 |
+
function get_type_meta_key() {
|
1102 |
+
return $this->util->make_meta_key('post_type');
|
1103 |
+
}
|
1104 |
+
|
1105 |
+
/**
|
1106 |
+
* Checks if post contains specified field data
|
1107 |
+
* @param Object $post (optional) Post to check data for
|
1108 |
+
* @param string $field (optional) Field ID to check for
|
1109 |
+
* @return bool TRUE if data exists, FALSE otherwise
|
1110 |
+
*/
|
1111 |
+
function has_item_data($item = null, $field = null) {
|
1112 |
+
$ret = $this->get_item_data($item, $field, 'raw', null);
|
1113 |
+
if ( is_scalar($ret) )
|
1114 |
+
return ( !empty($ret) || $ret === 0 );
|
1115 |
+
if ( is_array($ret) ) {
|
1116 |
+
foreach ( $ret as $key => $val ) {
|
1117 |
+
if ( !empty($val) || $val === 0 )
|
1118 |
+
return true;
|
1119 |
+
}
|
1120 |
+
}
|
1121 |
+
return false;
|
1122 |
+
}
|
1123 |
+
|
1124 |
+
/**
|
1125 |
+
* Retrieve specified field data from content item (e.g. post)
|
1126 |
+
* Usage Examples:
|
1127 |
+
* get_item_data($post_id, 'field_id')
|
1128 |
+
* - Retrieves field_id data from global $post object
|
1129 |
+
* - Field data is formatted using 'display' layout of field
|
1130 |
+
*
|
1131 |
+
* get_item_data($post_id, 'field_id', 'raw')
|
1132 |
+
* - Retrieves field_id data from global $post object
|
1133 |
+
* - Raw field data is returned (no formatting)
|
1134 |
+
*
|
1135 |
+
* get_item_data($post_id, 'field_id', 'display', $post_id)
|
1136 |
+
* - Retrieves field_id data from post matching $post_id
|
1137 |
+
* - Field data is formatted using 'display' layout of field
|
1138 |
+
*
|
1139 |
+
* get_item_data($post_id, 'field_id', null)
|
1140 |
+
* - Retrieves field_id data from post matching $post_id
|
1141 |
+
* - Field data is formatted using 'display' layout of field
|
1142 |
+
* - The default layout is used when no valid layout is specified
|
1143 |
+
*
|
1144 |
+
* get_item_data($post_id)
|
1145 |
+
* - Retrieves full data array from post matching $post_id
|
1146 |
+
*
|
1147 |
+
* @param int|object $item(optional) Content item to retrieve field from (Default: null - global $post object will be used)
|
1148 |
+
* @param string $field ID of field to retrieve
|
1149 |
+
* @param string $layout(optional) Layout to use when returning field data (Default: display)
|
1150 |
+
* @param array $attr (optional) Additional attributes to pass along to field object (e.g. for building layout, etc.)
|
1151 |
+
* @see CNR_Field_Type::build_layout for more information on attribute usage
|
1152 |
+
* @return mixed Specified field data
|
1153 |
+
*/
|
1154 |
+
function get_item_data($item = null, $field = null, $layout = null, $default = '', $attr = null) {
|
1155 |
+
$ret = $default;
|
1156 |
+
|
1157 |
+
//Get item
|
1158 |
+
$item = get_post($item);
|
1159 |
+
|
1160 |
+
if ( !isset($item->ID) )
|
1161 |
+
return $ret;
|
1162 |
+
|
1163 |
+
//Get item data
|
1164 |
+
$data = get_post_meta($item->ID, $this->get_fields_meta_key(), true);
|
1165 |
+
|
1166 |
+
//Get field data
|
1167 |
+
|
1168 |
+
//Set return value to data if no field specified
|
1169 |
+
if ( empty($field) || !is_string($field) )
|
1170 |
+
$ret = $data;
|
1171 |
+
//Stop if no valid field specified
|
1172 |
+
if ( !isset($data[$field]) ) {
|
1173 |
+
//TODO Check $item object to see if specified field exists (e.g. title, post_status, etc.)
|
1174 |
+
return $ret;
|
1175 |
+
}
|
1176 |
+
|
1177 |
+
$ret = $data[$field];
|
1178 |
+
|
1179 |
+
//Initialize layout value
|
1180 |
+
$layout_def = 'display';
|
1181 |
+
|
1182 |
+
if ( !is_scalar($layout) || empty($layout) )
|
1183 |
+
$layout = $layout_def;
|
1184 |
+
|
1185 |
+
$layout = strtolower($layout);
|
1186 |
+
|
1187 |
+
//Check if raw data requested
|
1188 |
+
if ( 'raw' == $layout )
|
1189 |
+
return $ret;
|
1190 |
+
|
1191 |
+
/* Build specified layout */
|
1192 |
+
|
1193 |
+
//Get item's content type
|
1194 |
+
$ct =& $this->get_type($item);
|
1195 |
+
$ct->set_data($data);
|
1196 |
+
|
1197 |
+
//Get field definition
|
1198 |
+
$fdef =& $ct->get_field($field);
|
1199 |
+
|
1200 |
+
//Validate layout
|
1201 |
+
if ( !$fdef->has_layout($layout) )
|
1202 |
+
$layout = $layout_def;
|
1203 |
+
|
1204 |
+
//Build layout
|
1205 |
+
$fdef->set_caller($ct);
|
1206 |
+
$ret = $fdef->build_layout($layout, $attr);
|
1207 |
+
$fdef->clear_caller();
|
1208 |
+
|
1209 |
+
//Return formatted value
|
1210 |
+
return $ret;
|
1211 |
+
}
|
1212 |
+
|
1213 |
+
/**
|
1214 |
+
* Prints an item's field data
|
1215 |
+
* @see CNR_Content_Utilities::get_item_data() for more information
|
1216 |
+
* @param int|object $item(optional) Content item to retrieve field from (Default: null - global $post object will be used)
|
1217 |
+
* @param string $field ID of field to retrieve
|
1218 |
+
* @param string $layout(optional) Layout to use when returning field data (Default: display)
|
1219 |
+
* @param mixed $default (optional) Default value to return in case of errors, etc.
|
1220 |
+
* @param array $attr Additional attributes to pass to field
|
1221 |
+
*/
|
1222 |
+
function the_item_data($item = null, $field = null, $layout = null, $default = '', $attr = null) {
|
1223 |
+
echo apply_filters('cnr_the_item_data', $this->get_item_data($item, $field, $layout, $default, $attr), $item, $field, $layout, $default, $attr);
|
1224 |
+
}
|
1225 |
+
|
1226 |
+
/**
|
1227 |
+
* Build Admin URL for specified post type
|
1228 |
+
* @param string|CNR_Content_Type $type Content type ID or object
|
1229 |
+
* @param string $action Action to build URL for
|
1230 |
+
* @param bool $sep_action Whether action should be a separate query variable (Default: false)
|
1231 |
+
* @return string Admin page URL
|
1232 |
+
*/
|
1233 |
+
function get_admin_page_url($type, $action = '', $sep_action = false) {
|
1234 |
+
$url = admin_url('admin.php');
|
1235 |
+
$url .= '?page=' . $this->get_admin_page_file($type, $action, $sep_action);
|
1236 |
+
return $url;
|
1237 |
+
}
|
1238 |
+
|
1239 |
+
function get_edit_item_url($edit_url, $item_id, $context) {
|
1240 |
+
//Get post type
|
1241 |
+
$type = $this->get_type($item_id);
|
1242 |
+
if ( ! $this->is_default_post_type($type->id) && $this->type_exists($type) ) {
|
1243 |
+
$edit_url = $this->get_admin_page_url($type, 'edit-item', true) . '&post=' . $item_id;
|
1244 |
+
}
|
1245 |
+
|
1246 |
+
return $edit_url;
|
1247 |
+
}
|
1248 |
}
|
includes/class.field.php
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
<?php
|
2 |
-
class CNR_Field extends CNR_Field_Type {
|
3 |
-
|
4 |
}
|
1 |
+
<?php
|
2 |
+
class CNR_Field extends CNR_Field_Type {
|
3 |
+
|
4 |
}
|
includes/class.field_type.php
CHANGED
@@ -1,698 +1,698 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* Content Type - Field Types
|
4 |
-
* Stores properties for a specific field
|
5 |
-
* @package Cornerstone
|
6 |
-
* @subpackage Content Types
|
7 |
-
* @author Archetyped
|
8 |
-
*/
|
9 |
-
class CNR_Field_Type extends CNR_Content_Base {
|
10 |
-
/* Properties */
|
11 |
-
|
12 |
-
const USES_DATA = '{data}';
|
13 |
-
|
14 |
-
/**
|
15 |
-
* Base class name
|
16 |
-
* @var string
|
17 |
-
*/
|
18 |
-
var $base_class = 'cnr_field_type';
|
19 |
-
|
20 |
-
/**
|
21 |
-
* @var array Array of Field types that make up current Field type
|
22 |
-
*/
|
23 |
-
var $elements = array();
|
24 |
-
|
25 |
-
/**
|
26 |
-
* Structure: Property names stored as keys in group
|
27 |
-
* Root
|
28 |
-
* -> Group Name
|
29 |
-
* -> Property Name => Null
|
30 |
-
* Reason: Faster searching over large arrays
|
31 |
-
* @var array Groupings of Properties
|
32 |
-
*/
|
33 |
-
var $property_groups = array();
|
34 |
-
|
35 |
-
/**
|
36 |
-
* @var array Field type layouts
|
37 |
-
*/
|
38 |
-
var $layout = array();
|
39 |
-
|
40 |
-
/**
|
41 |
-
* @var CNR_Field_Type Parent field type (reference)
|
42 |
-
*/
|
43 |
-
var $parent = null;
|
44 |
-
|
45 |
-
/**
|
46 |
-
* Object that field is in
|
47 |
-
* @var CNR_Field|CNR_Field_Type|CNR_Content_Type
|
48 |
-
*/
|
49 |
-
var $container = null;
|
50 |
-
|
51 |
-
/**
|
52 |
-
* Object that called field
|
53 |
-
* Used to determine field hierarchy/nesting
|
54 |
-
* @var CNR_Field|CNR_Field_Type|CNR_Content_Type
|
55 |
-
*/
|
56 |
-
var $caller = null;
|
57 |
-
|
58 |
-
/**
|
59 |
-
* Constructor
|
60 |
-
*/
|
61 |
-
function __construct($id = '', $parent = null) {
|
62 |
-
parent::__construct($id);
|
63 |
-
|
64 |
-
$this->id = $id;
|
65 |
-
$this->set_parent($parent);
|
66 |
-
}
|
67 |
-
|
68 |
-
/* Getters/Setters */
|
69 |
-
|
70 |
-
/**
|
71 |
-
* Search for specified member value in field's container object (if exists)
|
72 |
-
* @param string $member Name of object member to search (e.g. properties, layout, etc.)
|
73 |
-
* @param string $name Value to retrieve from member
|
74 |
-
* @return mixed Member value if found (Default: empty string)
|
75 |
-
*/
|
76 |
-
function get_container_value($member, $name = '', $default = '') {
|
77 |
-
$container =& $this->get_container();
|
78 |
-
return $this->get_object_value($container, $member, $name, $default, 'container');
|
79 |
-
}
|
80 |
-
|
81 |
-
/**
|
82 |
-
* Search for specified member value in field's container object (if exists)
|
83 |
-
* @param string $member Name of object member to search (e.g. properties, layout, etc.)
|
84 |
-
* @param string $name Value to retrieve from member
|
85 |
-
* @return mixed Member value if found (Default: empty string)
|
86 |
-
*/
|
87 |
-
function get_caller_value($member, $name = '', $default = '') {
|
88 |
-
$caller =& $this->get_caller();
|
89 |
-
return $this->get_object_value($caller, $member, $name, $default, 'caller');
|
90 |
-
}
|
91 |
-
|
92 |
-
/**
|
93 |
-
* Sets reference to container object of current field
|
94 |
-
* Reference is cleared if no valid object is passed to method
|
95 |
-
* @param object $container
|
96 |
-
*/
|
97 |
-
function set_container(&$container) {
|
98 |
-
if ( !empty($container) && is_object($container) ) {
|
99 |
-
//Set as param as container for current field
|
100 |
-
$this->container =& $container;
|
101 |
-
} else {
|
102 |
-
//Clear container member if argument is invalid
|
103 |
-
$this->clear_container();
|
104 |
-
}
|
105 |
-
}
|
106 |
-
|
107 |
-
/**
|
108 |
-
* Clears reference to container object of current field
|
109 |
-
*/
|
110 |
-
function clear_container() {
|
111 |
-
$this->container = null;
|
112 |
-
}
|
113 |
-
|
114 |
-
/**
|
115 |
-
* Retrieves reference to container object of current field
|
116 |
-
* @return object Reference to container object
|
117 |
-
*/
|
118 |
-
function &get_container() {
|
119 |
-
$ret = null;
|
120 |
-
if ( $this->has_container() )
|
121 |
-
$ret =& $this->container;
|
122 |
-
return $ret;
|
123 |
-
}
|
124 |
-
|
125 |
-
/**
|
126 |
-
* Checks if field has a container reference
|
127 |
-
* @return bool TRUE if field is contained, FALSE otherwise
|
128 |
-
*/
|
129 |
-
function has_container() {
|
130 |
-
return !empty($this->container);
|
131 |
-
}
|
132 |
-
|
133 |
-
/**
|
134 |
-
* Sets reference to calling object of current field
|
135 |
-
* Any existing reference is cleared if no valid object is passed to method
|
136 |
-
* @param object $caller Calling object
|
137 |
-
*/
|
138 |
-
function set_caller(&$caller) {
|
139 |
-
if ( !empty($caller) && is_object($caller) )
|
140 |
-
$this->caller =& $caller;
|
141 |
-
else
|
142 |
-
$this->clear_caller();
|
143 |
-
}
|
144 |
-
|
145 |
-
/**
|
146 |
-
* Clears reference to calling object of current field
|
147 |
-
*/
|
148 |
-
function clear_caller() {
|
149 |
-
unset($this->caller);
|
150 |
-
}
|
151 |
-
|
152 |
-
/**
|
153 |
-
* Retrieves reference to caller object of current field
|
154 |
-
* @return object Reference to caller object
|
155 |
-
*/
|
156 |
-
function &get_caller() {
|
157 |
-
$ret = null;
|
158 |
-
if ( $this->has_caller() )
|
159 |
-
$ret =& $this->caller;
|
160 |
-
return $ret;
|
161 |
-
}
|
162 |
-
|
163 |
-
/**
|
164 |
-
* Checks if field has a caller reference
|
165 |
-
* @return bool TRUE if field is called by another field, FALSE otherwise
|
166 |
-
*/
|
167 |
-
function has_caller() {
|
168 |
-
return !empty($this->caller);
|
169 |
-
}
|
170 |
-
|
171 |
-
/**
|
172 |
-
* Add/Set a property on the field definition
|
173 |
-
* @param string $name Name of property
|
174 |
-
* @param mixed $value Default value for property
|
175 |
-
* @param string|array $group Group(s) property belongs to
|
176 |
-
* @return boolean TRUE if property is successfully added to field type, FALSE otherwise
|
177 |
-
*/
|
178 |
-
function set_property($name, $value = '', $group = null) {
|
179 |
-
//Do not add if property name is not a string
|
180 |
-
if ( !is_string($name) )
|
181 |
-
return false;
|
182 |
-
//Create property array
|
183 |
-
$prop_arr = array();
|
184 |
-
$prop_arr['value'] = $value;
|
185 |
-
//Add to properties array
|
186 |
-
$this->properties[$name] = $value;
|
187 |
-
//Add property to specified groups
|
188 |
-
if ( !empty($group) ) {
|
189 |
-
$this->set_group_property($group, $name);
|
190 |
-
}
|
191 |
-
return true;
|
192 |
-
}
|
193 |
-
|
194 |
-
/**
|
195 |
-
* Sets multiple properties on field type at once
|
196 |
-
* @param array $properties Properties. Each element is an array containing the arguments to set a new property
|
197 |
-
* @return boolean TRUE if successful, FALSE otherwise
|
198 |
-
*/
|
199 |
-
function set_properties($properties) {
|
200 |
-
if ( !is_array($properties) )
|
201 |
-
return false;
|
202 |
-
foreach ( $properties as $name => $val) {
|
203 |
-
$this->set_property($name, $val);
|
204 |
-
}
|
205 |
-
}
|
206 |
-
|
207 |
-
/**
|
208 |
-
* Retreives property from field type
|
209 |
-
* @param string $name Name of property to retrieve
|
210 |
-
* @return mixed Specified Property if exists (Default: Empty string)
|
211 |
-
*/
|
212 |
-
function get_property($name) {
|
213 |
-
$val = $this->get_member_value('properties', $name);
|
214 |
-
return $val;
|
215 |
-
}
|
216 |
-
|
217 |
-
/**
|
218 |
-
* Adds Specified Property to a Group
|
219 |
-
* @param string|array $group Group(s) to add property to
|
220 |
-
* @param string $property Property to add to group
|
221 |
-
*/
|
222 |
-
function set_group_property($group, $property) {
|
223 |
-
if ( is_string($group) && isset($this->property_groups[$group][$property]) )
|
224 |
-
return;
|
225 |
-
if ( !is_array($group) ) {
|
226 |
-
$group = array($group);
|
227 |
-
}
|
228 |
-
|
229 |
-
foreach ($group as $g) {
|
230 |
-
$g = trim($g);
|
231 |
-
//Initialize group if it doesn't already exist
|
232 |
-
if ( !isset($this->property_groups[$g]) )
|
233 |
-
$this->property_groups[$g] = array();
|
234 |
-
|
235 |
-
//Add property to group
|
236 |
-
$this->property_groups[$g][$property] = null;
|
237 |
-
}
|
238 |
-
}
|
239 |
-
|
240 |
-
/**
|
241 |
-
* Retrieve property group
|
242 |
-
* @param string $group Group to retrieve
|
243 |
-
* @return array Array of properties in specified group
|
244 |
-
*/
|
245 |
-
function get_group($group) {
|
246 |
-
return $this->get_member_value('property_groups', $group, array());
|
247 |
-
}
|
248 |
-
|
249 |
-
/**
|
250 |
-
* Sets an element for the field type
|
251 |
-
* @param string $name Name of element
|
252 |
-
* @param CNR_Field_Type $type Reference of field type to use for element
|
253 |
-
* @param array $properties Properties for element (passed as keyed associative array)
|
254 |
-
* @param string $id_prop Name of property to set $name to (e.g. ID, etc.)
|
255 |
-
*/
|
256 |
-
function set_element($name, $type, $properties = array(), $id_prop = 'id') {
|
257 |
-
$name = trim(strval($name));
|
258 |
-
if ( empty($name) )
|
259 |
-
return false;
|
260 |
-
//Create new field for element
|
261 |
-
$el = new CNR_Field($name, $type);
|
262 |
-
//Set container to current field instance
|
263 |
-
$el->set_container($this);
|
264 |
-
//Add properties to element
|
265 |
-
$el->set_properties($properties);
|
266 |
-
//Save element to current instance
|
267 |
-
$this->elements[$name] =& $el;
|
268 |
-
}
|
269 |
-
|
270 |
-
/**
|
271 |
-
* Add a layout to the field
|
272 |
-
* @param string $name Name of layout
|
273 |
-
* @param string $value Layout text
|
274 |
-
*/
|
275 |
-
function set_layout($name, $value = '') {
|
276 |
-
if ( !is_string($name) )
|
277 |
-
return false;
|
278 |
-
$name = trim($name);
|
279 |
-
$this->layout[$name] = $value;
|
280 |
-
return true;
|
281 |
-
}
|
282 |
-
|
283 |
-
/**
|
284 |
-
* Retrieve specified layout
|
285 |
-
* @param string $name Layout name
|
286 |
-
* @param bool $parse_nested (optional) Whether nested layouts should be expanded in retreived layout or not (Default: TRUE)
|
287 |
-
* @return string Specified layout text
|
288 |
-
*/
|
289 |
-
function get_layout($name = 'form', $parse_nested = true) {
|
290 |
-
//Retrieve specified layout (use $name value if no layout by that name exists)
|
291 |
-
$layout = $this->get_member_value('layout', $name, $name);
|
292 |
-
|
293 |
-
//Find all nested layouts in current layout
|
294 |
-
if ( !empty($layout) && !!$parse_nested ) {
|
295 |
-
$ph = $this->get_placeholder_defaults();
|
296 |
-
|
297 |
-
while ($ph->match = $this->parse_layout($layout, $ph->pattern_layout)) {
|
298 |
-
//Iterate through the different types of layout placeholders
|
299 |
-
foreach ($ph->match as $tag => $instances) {
|
300 |
-
//Iterate through instances of a specific type of layout placeholder
|
301 |
-
foreach ($instances as $instance) {
|
302 |
-
//Get nested layout
|
303 |
-
$nested_layout = $this->get_member_value($instance);
|
304 |
-
|
305 |
-
//Replace layout placeholder with retrieved item data
|
306 |
-
if ( !empty($nested_layout) )
|
307 |
-
$layout = str_replace($ph->start . $instance['match'] . $ph->end, $nested_layout, $layout);
|
308 |
-
}
|
309 |
-
}
|
310 |
-
}
|
311 |
-
}
|
312 |
-
|
313 |
-
return $layout;
|
314 |
-
}
|
315 |
-
|
316 |
-
/**
|
317 |
-
* Checks if specified layout exists
|
318 |
-
* Finds layout if it exists in current object or any of its parents
|
319 |
-
* @param string $layout Name of layout to check for
|
320 |
-
* @return bool TRUE if layout exists, FALSE otherwise
|
321 |
-
*/
|
322 |
-
function has_layout($layout) {
|
323 |
-
$ret = false;
|
324 |
-
if ( is_string($layout) && ($layout = trim($layout)) && !empty($layout) ) {
|
325 |
-
$layout = $this->get_member_value('layout', $layout, false);
|
326 |
-
if ( $layout !== false )
|
327 |
-
$ret = true;
|
328 |
-
}
|
329 |
-
|
330 |
-
return $ret;
|
331 |
-
}
|
332 |
-
|
333 |
-
/**
|
334 |
-
* Checks if layout content is valid
|
335 |
-
* Layouts need to have placeholders to be valid
|
336 |
-
* @param string $layout_content Layout content (markup)
|
337 |
-
* @return bool TRUE if layout is valid, FALSE otherwise
|
338 |
-
*/
|
339 |
-
function is_valid_layout($layout_content) {
|
340 |
-
$ph = $this->get_placeholder_defaults();
|
341 |
-
return preg_match($ph->pattern_general, $layout_content);
|
342 |
-
}
|
343 |
-
|
344 |
-
/**
|
345 |
-
* Parse field layout with a regular expression
|
346 |
-
* @param string $layout Layout data
|
347 |
-
* @param string $search Regular expression pattern to search layout for
|
348 |
-
* @return array Associative array containing all of the regular expression matches in the layout data
|
349 |
-
* Array Structure:
|
350 |
-
* root => placeholder tags
|
351 |
-
* => Tag instances (array)
|
352 |
-
* 'tag' => (string) tag name
|
353 |
-
* 'match' => (string) placeholder match
|
354 |
-
* 'attributes' => (array) attributes
|
355 |
-
*/
|
356 |
-
function parse_layout($layout, $search) {
|
357 |
-
$ph_xml = '';
|
358 |
-
$parse_match = '';
|
359 |
-
$ph_root_tag = 'ph_root_element';
|
360 |
-
$ph_start_xml = '<';
|
361 |
-
$ph_end_xml = ' />';
|
362 |
-
$ph_wrap_start = '<' . $ph_root_tag . '>';
|
363 |
-
$ph_wrap_end = '</' . $ph_root_tag . '>';
|
364 |
-
$parse_result = false;
|
365 |
-
|
366 |
-
//Find all nested layouts in layout
|
367 |
-
$match_value = preg_match_all($search, $layout, $parse_match, PREG_PATTERN_ORDER);
|
368 |
-
|
369 |
-
if ($match_value !== false && $match_value > 0) {
|
370 |
-
$parse_result = array();
|
371 |
-
//Get all matched elements
|
372 |
-
$parse_match = $parse_match[1];
|
373 |
-
|
374 |
-
//Build XML string from placeholders
|
375 |
-
foreach ($parse_match as $ph) {
|
376 |
-
$ph_xml .= $ph_start_xml . $ph . $ph_end_xml . ' ';
|
377 |
-
}
|
378 |
-
$ph_xml = $ph_wrap_start . $ph_xml . $ph_wrap_end;
|
379 |
-
//Parse XML data
|
380 |
-
$ph_prs = xml_parser_create();
|
381 |
-
xml_parser_set_option($ph_prs, XML_OPTION_SKIP_WHITE, 1);
|
382 |
-
xml_parser_set_option($ph_prs, XML_OPTION_CASE_FOLDING, 0);
|
383 |
-
$ret = xml_parse_into_struct($ph_prs, $ph_xml, $parse_result['values'], $parse_result['index']);
|
384 |
-
xml_parser_free($ph_prs);
|
385 |
-
|
386 |
-
//Build structured array with all parsed data
|
387 |
-
|
388 |
-
unset($parse_result['index'][$ph_root_tag]);
|
389 |
-
|
390 |
-
//Build structured array
|
391 |
-
$result = array();
|
392 |
-
foreach ($parse_result['index'] as $tag => $instances) {
|
393 |
-
$result[$tag] = array();
|
394 |
-
//Instances
|
395 |
-
foreach ($instances as $instance) {
|
396 |
-
//Skip instance if it doesn't exist in parse results
|
397 |
-
if (!isset($parse_result['values'][$instance]))
|
398 |
-
continue;
|
399 |
-
|
400 |
-
//Stop processing instance if a previously-saved instance with the same options already exists
|
401 |
-
foreach ($result[$tag] as $tag_match) {
|
402 |
-
if ($tag_match['match'] == $parse_match[$instance - 1])
|
403 |
-
continue 2;
|
404 |
-
}
|
405 |
-
|
406 |
-
//Init instance data array
|
407 |
-
$inst_data = array();
|
408 |
-
|
409 |
-
//Add Tag to array
|
410 |
-
$inst_data['tag'] = $parse_result['values'][$instance]['tag'];
|
411 |
-
|
412 |
-
//Add instance data to array
|
413 |
-
$inst_data['attributes'] = (isset($parse_result['values'][$instance]['attributes'])) ? $inst_data['attributes'] = $parse_result['values'][$instance]['attributes'] : '';
|
414 |
-
|
415 |
-
//Add match to array
|
416 |
-
$inst_data['match'] = $parse_match[$instance - 1];
|
417 |
-
|
418 |
-
//Add to result array
|
419 |
-
$result[$tag][] = $inst_data;
|
420 |
-
}
|
421 |
-
}
|
422 |
-
$parse_result = $result;
|
423 |
-
}
|
424 |
-
|
425 |
-
return $parse_result;
|
426 |
-
}
|
427 |
-
|
428 |
-
/**
|
429 |
-
* Retrieves default properties to use when evaluating layout placeholders
|
430 |
-
* @return object Object with properties for evaluating layout placeholders
|
431 |
-
*/
|
432 |
-
function get_placeholder_defaults() {
|
433 |
-
$ph = new stdClass();
|
434 |
-
$ph->start = '{';
|
435 |
-
$ph->end = '}';
|
436 |
-
$ph->reserved = array('ref' => 'ref_base');
|
437 |
-
$ph->pattern_general = '/' . $ph->start . '([a-zA-Z0-9_].*?)' . $ph->end . '/i';
|
438 |
-
$ph->pattern_layout = '/' . $ph->start . '([a-zA-Z0-9].*?\s+' . $ph->reserved['ref'] . '="layout.*?".*?)' . $ph->end . '/i';
|
439 |
-
return $ph;
|
440 |
-
}
|
441 |
-
|
442 |
-
/**
|
443 |
-
* Builds HTML for a field based on its properties
|
444 |
-
* @param array $field Field properties (id, field, etc.)
|
445 |
-
* @param array data Additional data for current field
|
446 |
-
*/
|
447 |
-
function build_layout($layout = 'form', $data = null) {
|
448 |
-
$out_default = '';
|
449 |
-
|
450 |
-
/* Layout */
|
451 |
-
|
452 |
-
//Get base layout
|
453 |
-
$out = $this->get_layout($layout);
|
454 |
-
|
455 |
-
//Only parse valid layouts
|
456 |
-
if ( $this->is_valid_layout($out) ) {
|
457 |
-
//Parse Layout
|
458 |
-
$ph = $this->get_placeholder_defaults();
|
459 |
-
|
460 |
-
//Search layout for placeholders
|
461 |
-
while ( $ph->match = $this->parse_layout($out, $ph->pattern_general) ) {
|
462 |
-
//Iterate through placeholders (tag, id, etc.)
|
463 |
-
foreach ( $ph->match as $tag => $instances ) {
|
464 |
-
//Iterate through instances of current placeholder
|
465 |
-
foreach ( $instances as $instance ) {
|
466 |
-
//Process value based on placeholder name
|
467 |
-
$target_property = apply_filters('cnr_process_placeholder_' . $tag, '', $this, $instance, $layout, $data);
|
468 |
-
|
469 |
-
//Process value using default processors (if necessary)
|
470 |
-
if ( '' == $target_property ) {
|
471 |
-
$target_property = apply_filters('cnr_process_placeholder', $target_property, $this, $instance, $layout, $data);
|
472 |
-
}
|
473 |
-
|
474 |
-
//Clear value if value not a string
|
475 |
-
if ( !is_scalar($target_property) ) {
|
476 |
-
$target_property = '';
|
477 |
-
}
|
478 |
-
//Replace layout placeholder with retrieved item data
|
479 |
-
$out = str_replace($ph->start . $instance['match'] . $ph->end, $target_property, $out);
|
480 |
-
}
|
481 |
-
}
|
482 |
-
}
|
483 |
-
} else {
|
484 |
-
$out = $out_default;
|
485 |
-
}
|
486 |
-
|
487 |
-
/* Return generated value */
|
488 |
-
|
489 |
-
return $out;
|
490 |
-
}
|
491 |
-
|
492 |
-
/*-** Static Methods **-*/
|
493 |
-
|
494 |
-
/**
|
495 |
-
* Returns indacator to use field data (in layouts, property values, etc.)
|
496 |
-
*/
|
497 |
-
function uses_data() {
|
498 |
-
return self::USES_DATA;
|
499 |
-
}
|
500 |
-
|
501 |
-
/**
|
502 |
-
* Register a function to handle a placeholder
|
503 |
-
* Multiple handlers may be registered for a single placeholder
|
504 |
-
* Basically a wrapper function to facilitate adding hooks for placeholder processing
|
505 |
-
* @uses add_filter()
|
506 |
-
* @param string $placeholder Name of placeholder to add handler for (Using 'all' will set the function as a handler for all placeholders
|
507 |
-
* @param callback $handler Function to set as a handler
|
508 |
-
* @param int $priority (optional) Priority of handler
|
509 |
-
*/
|
510 |
-
static function register_placeholder_handler($placeholder, $handler, $priority = 10) {
|
511 |
-
if ( 'all' == $placeholder )
|
512 |
-
$placeholder = '';
|
513 |
-
else
|
514 |
-
$placeholder = '_' . $placeholder;
|
515 |
-
|
516 |
-
add_filter('cnr_process_placeholder' . $placeholder, $handler, $priority, 5);
|
517 |
-
}
|
518 |
-
|
519 |
-
/**
|
520 |
-
* Default placeholder processing
|
521 |
-
* To be executed when current placeholder has not been handled by another handler
|
522 |
-
* @param string $ph_output Value to be used in place of placeholder
|
523 |
-
* @param CNR_Field $field Field containing placeholder
|
524 |
-
* @param array $placeholder Current placeholder
|
525 |
-
* @see CNR_Field::parse_layout for structure of $placeholder array
|
526 |
-
* @param string $layout Layout to build
|
527 |
-
* @param array $data Extended data for field
|
528 |
-
* @return string Value to use in place of current placeholder
|
529 |
-
*/
|
530 |
-
static function process_placeholder_default($ph_output, $field, $placeholder, $layout, $data) {
|
531 |
-
//Validate parameters before processing
|
532 |
-
if ( empty($ph_output) && is_a($field, 'CNR_Field_Type') && is_array($placeholder) ) {
|
533 |
-
//Build path to replacement data
|
534 |
-
$ph_output = $field->get_member_value($placeholder);
|
535 |
-
|
536 |
-
//Check if value is group (properties, etc.)
|
537 |
-
//All groups must have additional attributes (beyond reserved attributes) that define how items in group are used
|
538 |
-
if (is_array($ph_output)
|
539 |
-
&& !empty($placeholder['attributes'])
|
540 |
-
&& is_array($placeholder['attributes'])
|
541 |
-
&& ($ph = $field->get_placeholder_defaults())
|
542 |
-
&& $attribs = array_diff(array_keys($placeholder['attributes']), array_values($ph->reserved))
|
543 |
-
) {
|
544 |
-
/* Targeted property is an array, but the placeholder contains additional options on how property is to be used */
|
545 |
-
|
546 |
-
//Find items matching criteria in $ph_output
|
547 |
-
//Check for group criteria
|
548 |
-
//TODO: Implement more robust/flexible criteria handling (2010-03-11: Currently only processes property groups)
|
549 |
-
if ( 'properties' == $placeholder['tag'] && ($prop_group = $field->get_group($placeholder['attributes']['group'])) && !empty($prop_group) ) {
|
550 |
-
/* Process group */
|
551 |
-
$group_out = array();
|
552 |
-
//Iterate through properties in group and build string
|
553 |
-
foreach ( $prop_group as $prop_key => $prop_val ) {
|
554 |
-
$group_out[] = $prop_key . '="' . $field->get_property($prop_key) . '"';
|
555 |
-
}
|
556 |
-
$ph_output = implode(' ', $group_out);
|
557 |
-
}
|
558 |
-
} elseif ( is_object($ph_output) && is_a($ph_output, $field->base_class) ) {
|
559 |
-
/* Targeted property is actually a nested field */
|
560 |
-
//Set caller to current field
|
561 |
-
$ph_output->set_caller($field);
|
562 |
-
//Build layout for nested element
|
563 |
-
$ph_output = $ph_output->build_layout($layout);
|
564 |
-
}
|
565 |
-
}
|
566 |
-
|
567 |
-
return $ph_output;
|
568 |
-
}
|
569 |
-
|
570 |
-
/**
|
571 |
-
* Build Field ID attribute
|
572 |
-
* @see CNR_Field_Type::process_placeholder_default for parameter descriptions
|
573 |
-
* @return string Placeholder output
|
574 |
-
*/
|
575 |
-
static function process_placeholder_id($ph_output, $field, $placeholder, $layout, $data) {
|
576 |
-
//Get attributes
|
577 |
-
$args = wp_parse_args($placeholder['attributes'], array('format' => 'attr_id'));
|
578 |
-
return $field->get_id($args);
|
579 |
-
}
|
580 |
-
|
581 |
-
/**
|
582 |
-
* Build Field name attribute
|
583 |
-
* Name is formatted as an associative array for processing by PHP after submission
|
584 |
-
* @see CNR_Field_Type::process_placeholder_default for parameter descriptions
|
585 |
-
* @return string Placeholder output
|
586 |
-
*/
|
587 |
-
static function process_placeholder_name($ph_output, $field, $placeholder, $layout, $data) {
|
588 |
-
//Get attributes
|
589 |
-
$args = wp_parse_args($placeholder['attributes'], array('format' => 'default'));
|
590 |
-
return $field->get_id($args);
|
591 |
-
}
|
592 |
-
|
593 |
-
/**
|
594 |
-
* Retrieve data for field
|
595 |
-
* @see CNR_Field_Type::process_placeholder_default for parameter descriptions
|
596 |
-
* @return string Placeholder output
|
597 |
-
*/
|
598 |
-
static function process_placeholder_data($ph_output, $field, $placeholder, $layout) {
|
599 |
-
$val = $field->get_data();
|
600 |
-
if ( !is_null($val) ) {
|
601 |
-
$ph_output = $val;
|
602 |
-
$attr =& $placeholder['attributes'];
|
603 |
-
//Get specific member in value (e.g. value from a specific field element)
|
604 |
-
if ( isset($attr['element']) && is_array($ph_output) && ( $el = $attr['element'] ) && isset($ph_output[$el]) )
|
605 |
-
$ph_output = $ph_output[$el];
|
606 |
-
if ( isset($attr['format']) && 'display' == $attr['format'] )
|
607 |
-
$ph_output = nl2br($ph_output);
|
608 |
-
}
|
609 |
-
|
610 |
-
//Return data
|
611 |
-
return $ph_output;
|
612 |
-
}
|
613 |
-
|
614 |
-
/**
|
615 |
-
* Loops over data to build field output
|
616 |
-
* Options:
|
617 |
-
* data - Dot-delimited path in field that contains data to loop through
|
618 |
-
* layout - Name of layout to use for each data item in loop
|
619 |
-
* layout_data - Name of layout to use for data item that matches previously-saved field data
|
620 |
-
* @see CNR_Field_Type::process_placeholder_default for parameter descriptions
|
621 |
-
* @return string Placeholder output
|
622 |
-
*/
|
623 |
-
static function process_placeholder_loop($ph_output, $field, $placeholder, $layout, $data) {
|
624 |
-
//Setup loop options
|
625 |
-
$attr_defaults = array (
|
626 |
-
'layout' => '',
|
627 |
-
'layout_data' => null,
|
628 |
-
'data' => ''
|
629 |
-
);
|
630 |
-
|
631 |
-
$attr = wp_parse_args($placeholder['attributes'], $attr_defaults);
|
632 |
-
|
633 |
-
if ( is_null($attr['layout_data']) ) {
|
634 |
-
$attr['layout_data'] =& $attr['layout'];
|
635 |
-
}
|
636 |
-
|
637 |
-
//Get data for loop
|
638 |
-
$path = explode('.', $attr['data']);
|
639 |
-
$loop_data = $field->get_member_value($path);
|
640 |
-
/*if ( isset($loop_data['value']) )
|
641 |
-
$loop_data = $loop_data['value'];
|
642 |
-
*/
|
643 |
-
$out = array();
|
644 |
-
|
645 |
-
//Get field data
|
646 |
-
$data = $field->get_data();
|
647 |
-
|
648 |
-
//Iterate over data and build output
|
649 |
-
if ( is_array($loop_data) && !empty($loop_data) ) {
|
650 |
-
foreach ( $loop_data as $value => $label ) {
|
651 |
-
//Load appropriate layout based on field value
|
652 |
-
$layout = ( ($data === 0 && $value === $data) xor $data == $value ) ? $attr['layout_data'] : $attr['layout'];
|
653 |
-
//Stop processing if no valid layout is returned
|
654 |
-
if ( empty($layout) )
|
655 |
-
continue;
|
656 |
-
//Prep extended field data
|
657 |
-
$data_ext = array('option_value' => $value, 'option_text' => $label);
|
658 |
-
$out[] = $field->build_layout($layout, $data_ext);
|
659 |
-
}
|
660 |
-
}
|
661 |
-
|
662 |
-
//Return output
|
663 |
-
return implode($out);
|
664 |
-
}
|
665 |
-
|
666 |
-
/**
|
667 |
-
* Returns specified value from extended data array for field
|
668 |
-
* @see CNR_Field_Type::process_placeholder_default for parameter descriptions
|
669 |
-
* @return string Placeholder output
|
670 |
-
*/
|
671 |
-
static function process_placeholder_data_ext($ph_output, $field, $placeholder, $layout, $data) {
|
672 |
-
if ( isset($placeholder['attributes']['id']) && ($key = $placeholder['attributes']['id']) && isset($data[$key]) ) {
|
673 |
-
$ph_output = strval($data[$key]);
|
674 |
-
}
|
675 |
-
|
676 |
-
return $ph_output;
|
677 |
-
}
|
678 |
-
|
679 |
-
/**
|
680 |
-
* WP Editor
|
681 |
-
* @see CNR_Field_Type::process_placeholder_default for parameter descriptions
|
682 |
-
* @return string Placeholder output
|
683 |
-
*/
|
684 |
-
static function process_placeholder_rich_editor($ph_output, $field, $placeholder, $layout, $data) {
|
685 |
-
$id = $field->get_id( array (
|
686 |
-
'format' => 'attr_id'
|
687 |
-
));
|
688 |
-
$settings = array (
|
689 |
-
'textarea_name' => $field->get_id( array (
|
690 |
-
'format' => 'default'
|
691 |
-
))
|
692 |
-
);
|
693 |
-
ob_start();
|
694 |
-
wp_editor($field->get_data(), $id, $settings);
|
695 |
-
$out = ob_get_clean();
|
696 |
-
return $out;
|
697 |
-
}
|
698 |
}
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Content Type - Field Types
|
4 |
+
* Stores properties for a specific field
|
5 |
+
* @package Cornerstone
|
6 |
+
* @subpackage Content Types
|
7 |
+
* @author Archetyped
|
8 |
+
*/
|
9 |
+
class CNR_Field_Type extends CNR_Content_Base {
|
10 |
+
/* Properties */
|
11 |
+
|
12 |
+
const USES_DATA = '{data}';
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Base class name
|
16 |
+
* @var string
|
17 |
+
*/
|
18 |
+
var $base_class = 'cnr_field_type';
|
19 |
+
|
20 |
+
/**
|
21 |
+
* @var array Array of Field types that make up current Field type
|
22 |
+
*/
|
23 |
+
var $elements = array();
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Structure: Property names stored as keys in group
|
27 |
+
* Root
|
28 |
+
* -> Group Name
|
29 |
+
* -> Property Name => Null
|
30 |
+
* Reason: Faster searching over large arrays
|
31 |
+
* @var array Groupings of Properties
|
32 |
+
*/
|
33 |
+
var $property_groups = array();
|
34 |
+
|
35 |
+
/**
|
36 |
+
* @var array Field type layouts
|
37 |
+
*/
|
38 |
+
var $layout = array();
|
39 |
+
|
40 |
+
/**
|
41 |
+
* @var CNR_Field_Type Parent field type (reference)
|
42 |
+
*/
|
43 |
+
var $parent = null;
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Object that field is in
|
47 |
+
* @var CNR_Field|CNR_Field_Type|CNR_Content_Type
|
48 |
+
*/
|
49 |
+
var $container = null;
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Object that called field
|
53 |
+
* Used to determine field hierarchy/nesting
|
54 |
+
* @var CNR_Field|CNR_Field_Type|CNR_Content_Type
|
55 |
+
*/
|
56 |
+
var $caller = null;
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Constructor
|
60 |
+
*/
|
61 |
+
function __construct($id = '', $parent = null) {
|
62 |
+
parent::__construct($id);
|
63 |
+
|
64 |
+
$this->id = $id;
|
65 |
+
$this->set_parent($parent);
|
66 |
+
}
|
67 |
+
|
68 |
+
/* Getters/Setters */
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Search for specified member value in field's container object (if exists)
|
72 |
+
* @param string $member Name of object member to search (e.g. properties, layout, etc.)
|
73 |
+
* @param string $name Value to retrieve from member
|
74 |
+
* @return mixed Member value if found (Default: empty string)
|
75 |
+
*/
|
76 |
+
function get_container_value($member, $name = '', $default = '') {
|
77 |
+
$container =& $this->get_container();
|
78 |
+
return $this->get_object_value($container, $member, $name, $default, 'container');
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Search for specified member value in field's container object (if exists)
|
83 |
+
* @param string $member Name of object member to search (e.g. properties, layout, etc.)
|
84 |
+
* @param string $name Value to retrieve from member
|
85 |
+
* @return mixed Member value if found (Default: empty string)
|
86 |
+
*/
|
87 |
+
function get_caller_value($member, $name = '', $default = '') {
|
88 |
+
$caller =& $this->get_caller();
|
89 |
+
return $this->get_object_value($caller, $member, $name, $default, 'caller');
|
90 |
+
}
|
91 |
+
|
92 |
+
/**
|
93 |
+
* Sets reference to container object of current field
|
94 |
+
* Reference is cleared if no valid object is passed to method
|
95 |
+
* @param object $container
|
96 |
+
*/
|
97 |
+
function set_container(&$container) {
|
98 |
+
if ( !empty($container) && is_object($container) ) {
|
99 |
+
//Set as param as container for current field
|
100 |
+
$this->container =& $container;
|
101 |
+
} else {
|
102 |
+
//Clear container member if argument is invalid
|
103 |
+
$this->clear_container();
|
104 |
+
}
|
105 |
+
}
|
106 |
+
|
107 |
+
/**
|
108 |
+
* Clears reference to container object of current field
|
109 |
+
*/
|
110 |
+
function clear_container() {
|
111 |
+
$this->container = null;
|
112 |
+
}
|
113 |
+
|
114 |
+
/**
|
115 |
+
* Retrieves reference to container object of current field
|
116 |
+
* @return object Reference to container object
|
117 |
+
*/
|
118 |
+
function &get_container() {
|
119 |
+
$ret = null;
|
120 |
+
if ( $this->has_container() )
|
121 |
+
$ret =& $this->container;
|
122 |
+
return $ret;
|
123 |
+
}
|
124 |
+
|
125 |
+
/**
|
126 |
+
* Checks if field has a container reference
|
127 |
+
* @return bool TRUE if field is contained, FALSE otherwise
|
128 |
+
*/
|
129 |
+
function has_container() {
|
130 |
+
return !empty($this->container);
|
131 |
+
}
|
132 |
+
|
133 |
+
/**
|
134 |
+
* Sets reference to calling object of current field
|
135 |
+
* Any existing reference is cleared if no valid object is passed to method
|
136 |
+
* @param object $caller Calling object
|
137 |
+
*/
|
138 |
+
function set_caller(&$caller) {
|
139 |
+
if ( !empty($caller) && is_object($caller) )
|
140 |
+
$this->caller =& $caller;
|
141 |
+
else
|
142 |
+
$this->clear_caller();
|
143 |
+
}
|
144 |
+
|
145 |
+
/**
|
146 |
+
* Clears reference to calling object of current field
|
147 |
+
*/
|
148 |
+
function clear_caller() {
|
149 |
+
unset($this->caller);
|
150 |
+
}
|
151 |
+
|
152 |
+
/**
|
153 |
+
* Retrieves reference to caller object of current field
|
154 |
+
* @return object Reference to caller object
|
155 |
+
*/
|
156 |
+
function &get_caller() {
|
157 |
+
$ret = null;
|
158 |
+
if ( $this->has_caller() )
|
159 |
+
$ret =& $this->caller;
|
160 |
+
return $ret;
|
161 |
+
}
|
162 |
+
|
163 |
+
/**
|
164 |
+
* Checks if field has a caller reference
|
165 |
+
* @return bool TRUE if field is called by another field, FALSE otherwise
|
166 |
+
*/
|
167 |
+
function has_caller() {
|
168 |
+
return !empty($this->caller);
|
169 |
+
}
|
170 |
+
|
171 |
+
/**
|
172 |
+
* Add/Set a property on the field definition
|
173 |
+
* @param string $name Name of property
|
174 |
+
* @param mixed $value Default value for property
|
175 |
+
* @param string|array $group Group(s) property belongs to
|
176 |
+
* @return boolean TRUE if property is successfully added to field type, FALSE otherwise
|
177 |
+
*/
|
178 |
+
function set_property($name, $value = '', $group = null) {
|
179 |
+
//Do not add if property name is not a string
|
180 |
+
if ( !is_string($name) )
|
181 |
+
return false;
|
182 |
+
//Create property array
|
183 |
+
$prop_arr = array();
|
184 |
+
$prop_arr['value'] = $value;
|
185 |
+
//Add to properties array
|
186 |
+
$this->properties[$name] = $value;
|
187 |
+
//Add property to specified groups
|
188 |
+
if ( !empty($group) ) {
|
189 |
+
$this->set_group_property($group, $name);
|
190 |
+
}
|
191 |
+
return true;
|
192 |
+
}
|
193 |
+
|
194 |
+
/**
|
195 |
+
* Sets multiple properties on field type at once
|
196 |
+
* @param array $properties Properties. Each element is an array containing the arguments to set a new property
|
197 |
+
* @return boolean TRUE if successful, FALSE otherwise
|
198 |
+
*/
|
199 |
+
function set_properties($properties) {
|
200 |
+
if ( !is_array($properties) )
|
201 |
+
return false;
|
202 |
+
foreach ( $properties as $name => $val) {
|
203 |
+
$this->set_property($name, $val);
|
204 |
+
}
|
205 |
+
}
|
206 |
+
|
207 |
+
/**
|
208 |
+
* Retreives property from field type
|
209 |
+
* @param string $name Name of property to retrieve
|
210 |
+
* @return mixed Specified Property if exists (Default: Empty string)
|
211 |
+
*/
|
212 |
+
function get_property($name) {
|
213 |
+
$val = $this->get_member_value('properties', $name);
|
214 |
+
return $val;
|
215 |
+
}
|
216 |
+
|
217 |
+
/**
|
218 |
+
* Adds Specified Property to a Group
|
219 |
+
* @param string|array $group Group(s) to add property to
|
220 |
+
* @param string $property Property to add to group
|
221 |
+
*/
|
222 |
+
function set_group_property($group, $property) {
|
223 |
+
if ( is_string($group) && isset($this->property_groups[$group][$property]) )
|
224 |
+
return;
|
225 |
+
if ( !is_array($group) ) {
|
226 |
+
$group = array($group);
|
227 |
+
}
|
228 |
+
|
229 |
+
foreach ($group as $g) {
|
230 |
+
$g = trim($g);
|
231 |
+
//Initialize group if it doesn't already exist
|
232 |
+
if ( !isset($this->property_groups[$g]) )
|
233 |
+
$this->property_groups[$g] = array();
|
234 |
+
|
235 |
+
//Add property to group
|
236 |
+
$this->property_groups[$g][$property] = null;
|
237 |
+
}
|
238 |
+
}
|
239 |
+
|
240 |
+
/**
|
241 |
+
* Retrieve property group
|
242 |
+
* @param string $group Group to retrieve
|
243 |
+
* @return array Array of properties in specified group
|
244 |
+
*/
|
245 |
+
function get_group($group) {
|
246 |
+
return $this->get_member_value('property_groups', $group, array());
|
247 |
+
}
|
248 |
+
|
249 |
+
/**
|
250 |
+
* Sets an element for the field type
|
251 |
+
* @param string $name Name of element
|
252 |
+
* @param CNR_Field_Type $type Reference of field type to use for element
|
253 |
+
* @param array $properties Properties for element (passed as keyed associative array)
|
254 |
+
* @param string $id_prop Name of property to set $name to (e.g. ID, etc.)
|
255 |
+
*/
|
256 |
+
function set_element($name, $type, $properties = array(), $id_prop = 'id') {
|
257 |
+
$name = trim(strval($name));
|
258 |
+
if ( empty($name) )
|
259 |
+
return false;
|
260 |
+
//Create new field for element
|
261 |
+
$el = new CNR_Field($name, $type);
|
262 |
+
//Set container to current field instance
|
263 |
+
$el->set_container($this);
|
264 |
+
//Add properties to element
|
265 |
+
$el->set_properties($properties);
|
266 |
+
//Save element to current instance
|
267 |
+
$this->elements[$name] =& $el;
|
268 |
+
}
|
269 |
+
|
270 |
+
/**
|
271 |
+
* Add a layout to the field
|
272 |
+
* @param string $name Name of layout
|
273 |
+
* @param string $value Layout text
|
274 |
+
*/
|
275 |
+
function set_layout($name, $value = '') {
|
276 |
+
if ( !is_string($name) )
|
277 |
+
return false;
|
278 |
+
$name = trim($name);
|
279 |
+
$this->layout[$name] = $value;
|
280 |
+
return true;
|
281 |
+
}
|
282 |
+
|
283 |
+
/**
|
284 |
+
* Retrieve specified layout
|
285 |
+
* @param string $name Layout name
|
286 |
+
* @param bool $parse_nested (optional) Whether nested layouts should be expanded in retreived layout or not (Default: TRUE)
|
287 |
+
* @return string Specified layout text
|
288 |
+
*/
|
289 |
+
function get_layout($name = 'form', $parse_nested = true) {
|
290 |
+
//Retrieve specified layout (use $name value if no layout by that name exists)
|
291 |
+
$layout = $this->get_member_value('layout', $name, $name);
|
292 |
+
|
293 |
+
//Find all nested layouts in current layout
|
294 |
+
if ( !empty($layout) && !!$parse_nested ) {
|
295 |
+
$ph = $this->get_placeholder_defaults();
|
296 |
+
|
297 |
+
while ($ph->match = $this->parse_layout($layout, $ph->pattern_layout)) {
|
298 |
+
//Iterate through the different types of layout placeholders
|
299 |
+
foreach ($ph->match as $tag => $instances) {
|
300 |
+
//Iterate through instances of a specific type of layout placeholder
|
301 |
+
foreach ($instances as $instance) {
|
302 |
+
//Get nested layout
|
303 |
+
$nested_layout = $this->get_member_value($instance);
|
304 |
+
|
305 |
+
//Replace layout placeholder with retrieved item data
|
306 |
+
if ( !empty($nested_layout) )
|
307 |
+
$layout = str_replace($ph->start . $instance['match'] . $ph->end, $nested_layout, $layout);
|
308 |
+
}
|
309 |
+
}
|
310 |
+
}
|
311 |
+
}
|
312 |
+
|
313 |
+
return $layout;
|
314 |
+
}
|
315 |
+
|
316 |
+
/**
|
317 |
+
* Checks if specified layout exists
|
318 |
+
* Finds layout if it exists in current object or any of its parents
|
319 |
+
* @param string $layout Name of layout to check for
|
320 |
+
* @return bool TRUE if layout exists, FALSE otherwise
|
321 |
+
*/
|
322 |
+
function has_layout($layout) {
|
323 |
+
$ret = false;
|
324 |
+
if ( is_string($layout) && ($layout = trim($layout)) && !empty($layout) ) {
|
325 |
+
$layout = $this->get_member_value('layout', $layout, false);
|
326 |
+
if ( $layout !== false )
|
327 |
+
$ret = true;
|
328 |
+
}
|
329 |
+
|
330 |
+
return $ret;
|
331 |
+
}
|
332 |
+
|
333 |
+
/**
|
334 |
+
* Checks if layout content is valid
|
335 |
+
* Layouts need to have placeholders to be valid
|
336 |
+
* @param string $layout_content Layout content (markup)
|
337 |
+
* @return bool TRUE if layout is valid, FALSE otherwise
|
338 |
+
*/
|
339 |
+
function is_valid_layout($layout_content) {
|
340 |
+
$ph = $this->get_placeholder_defaults();
|
341 |
+
return preg_match($ph->pattern_general, $layout_content);
|
342 |
+
}
|
343 |
+
|
344 |
+
/**
|
345 |
+
* Parse field layout with a regular expression
|
346 |
+
* @param string $layout Layout data
|
347 |
+
* @param string $search Regular expression pattern to search layout for
|
348 |
+
* @return array Associative array containing all of the regular expression matches in the layout data
|
349 |
+
* Array Structure:
|
350 |
+
* root => placeholder tags
|
351 |
+
* => Tag instances (array)
|
352 |
+
* 'tag' => (string) tag name
|
353 |
+
* 'match' => (string) placeholder match
|
354 |
+
* 'attributes' => (array) attributes
|
355 |
+
*/
|
356 |
+
function parse_layout($layout, $search) {
|
357 |
+
$ph_xml = '';
|
358 |
+
$parse_match = '';
|
359 |
+
$ph_root_tag = 'ph_root_element';
|
360 |
+
$ph_start_xml = '<';
|
361 |
+
$ph_end_xml = ' />';
|
362 |
+
$ph_wrap_start = '<' . $ph_root_tag . '>';
|
363 |
+
$ph_wrap_end = '</' . $ph_root_tag . '>';
|
364 |
+
$parse_result = false;
|
365 |
+
|
366 |
+
//Find all nested layouts in layout
|
367 |
+
$match_value = preg_match_all($search, $layout, $parse_match, PREG_PATTERN_ORDER);
|
368 |
+
|
369 |
+
if ($match_value !== false && $match_value > 0) {
|
370 |
+
$parse_result = array();
|
371 |
+
//Get all matched elements
|
372 |
+
$parse_match = $parse_match[1];
|
373 |
+
|
374 |
+
//Build XML string from placeholders
|
375 |
+
foreach ($parse_match as $ph) {
|
376 |
+
$ph_xml .= $ph_start_xml . $ph . $ph_end_xml . ' ';
|
377 |
+
}
|
378 |
+
$ph_xml = $ph_wrap_start . $ph_xml . $ph_wrap_end;
|
379 |
+
//Parse XML data
|
380 |
+
$ph_prs = xml_parser_create();
|
381 |
+
xml_parser_set_option($ph_prs, XML_OPTION_SKIP_WHITE, 1);
|
382 |
+
xml_parser_set_option($ph_prs, XML_OPTION_CASE_FOLDING, 0);
|
383 |
+
$ret = xml_parse_into_struct($ph_prs, $ph_xml, $parse_result['values'], $parse_result['index']);
|
384 |
+
xml_parser_free($ph_prs);
|
385 |
+
|
386 |
+
//Build structured array with all parsed data
|
387 |
+
|
388 |
+
unset($parse_result['index'][$ph_root_tag]);
|
389 |
+
|
390 |
+
//Build structured array
|
391 |
+
$result = array();
|
392 |
+
foreach ($parse_result['index'] as $tag => $instances) {
|
393 |
+
$result[$tag] = array();
|
394 |
+
//Instances
|
395 |
+
foreach ($instances as $instance) {
|
396 |
+
//Skip instance if it doesn't exist in parse results
|
397 |
+
if (!isset($parse_result['values'][$instance]))
|
398 |
+
continue;
|
399 |
+
|
400 |
+
//Stop processing instance if a previously-saved instance with the same options already exists
|
401 |
+
foreach ($result[$tag] as $tag_match) {
|
402 |
+
if ($tag_match['match'] == $parse_match[$instance - 1])
|
403 |
+
continue 2;
|
404 |
+
}
|
405 |
+
|
406 |
+
//Init instance data array
|
407 |
+
$inst_data = array();
|
408 |
+
|
409 |
+
//Add Tag to array
|
410 |
+
$inst_data['tag'] = $parse_result['values'][$instance]['tag'];
|
411 |
+
|
412 |
+
//Add instance data to array
|
413 |
+
$inst_data['attributes'] = (isset($parse_result['values'][$instance]['attributes'])) ? $inst_data['attributes'] = $parse_result['values'][$instance]['attributes'] : '';
|
414 |
+
|
415 |
+
//Add match to array
|
416 |
+
$inst_data['match'] = $parse_match[$instance - 1];
|
417 |
+
|
418 |
+
//Add to result array
|
419 |
+
$result[$tag][] = $inst_data;
|
420 |
+
}
|
421 |
+
}
|
422 |
+
$parse_result = $result;
|
423 |
+
}
|
424 |
+
|
425 |
+
return $parse_result;
|
426 |
+
}
|
427 |
+
|
428 |
+
/**
|
429 |
+
* Retrieves default properties to use when evaluating layout placeholders
|
430 |
+
* @return object Object with properties for evaluating layout placeholders
|
431 |
+
*/
|
432 |
+
function get_placeholder_defaults() {
|
433 |
+
$ph = new stdClass();
|
434 |
+
$ph->start = '{';
|
435 |
+
$ph->end = '}';
|
436 |
+
$ph->reserved = array('ref' => 'ref_base');
|
437 |
+
$ph->pattern_general = '/' . $ph->start . '([a-zA-Z0-9_].*?)' . $ph->end . '/i';
|
438 |
+
$ph->pattern_layout = '/' . $ph->start . '([a-zA-Z0-9].*?\s+' . $ph->reserved['ref'] . '="layout.*?".*?)' . $ph->end . '/i';
|
439 |
+
return $ph;
|
440 |
+
}
|
441 |
+
|
442 |
+
/**
|
443 |
+
* Builds HTML for a field based on its properties
|
444 |
+
* @param array $field Field properties (id, field, etc.)
|
445 |
+
* @param array data Additional data for current field
|
446 |
+
*/
|
447 |
+
function build_layout($layout = 'form', $data = null) {
|
448 |
+
$out_default = '';
|
449 |
+
|
450 |
+
/* Layout */
|
451 |
+
|
452 |
+
//Get base layout
|
453 |
+
$out = $this->get_layout($layout);
|
454 |
+
|
455 |
+
//Only parse valid layouts
|
456 |
+
if ( $this->is_valid_layout($out) ) {
|
457 |
+
//Parse Layout
|
458 |
+
$ph = $this->get_placeholder_defaults();
|
459 |
+
|
460 |
+
//Search layout for placeholders
|
461 |
+
while ( $ph->match = $this->parse_layout($out, $ph->pattern_general) ) {
|
462 |
+
//Iterate through placeholders (tag, id, etc.)
|
463 |
+
foreach ( $ph->match as $tag => $instances ) {
|
464 |
+
//Iterate through instances of current placeholder
|
465 |
+
foreach ( $instances as $instance ) {
|
466 |
+
//Process value based on placeholder name
|
467 |
+
$target_property = apply_filters('cnr_process_placeholder_' . $tag, '', $this, $instance, $layout, $data);
|
468 |
+
|
469 |
+
//Process value using default processors (if necessary)
|
470 |
+
if ( '' == $target_property ) {
|
471 |
+
$target_property = apply_filters('cnr_process_placeholder', $target_property, $this, $instance, $layout, $data);
|
472 |
+
}
|
473 |
+
|
474 |
+
//Clear value if value not a string
|
475 |
+
if ( !is_scalar($target_property) ) {
|
476 |
+
$target_property = '';
|
477 |
+
}
|
478 |
+
//Replace layout placeholder with retrieved item data
|
479 |
+
$out = str_replace($ph->start . $instance['match'] . $ph->end, $target_property, $out);
|
480 |
+
}
|
481 |
+
}
|
482 |
+
}
|
483 |
+
} else {
|
484 |
+
$out = $out_default;
|
485 |
+
}
|
486 |
+
|
487 |
+
/* Return generated value */
|
488 |
+
|
489 |
+
return $out;
|
490 |
+
}
|
491 |
+
|
492 |
+
/*-** Static Methods **-*/
|
493 |
+
|
494 |
+
/**
|
495 |
+
* Returns indacator to use field data (in layouts, property values, etc.)
|
496 |
+
*/
|
497 |
+
function uses_data() {
|
498 |
+
return self::USES_DATA;
|
499 |
+
}
|
500 |
+
|
501 |
+
/**
|
502 |
+
* Register a function to handle a placeholder
|
503 |
+
* Multiple handlers may be registered for a single placeholder
|
504 |
+
* Basically a wrapper function to facilitate adding hooks for placeholder processing
|
505 |
+
* @uses add_filter()
|
506 |
+
* @param string $placeholder Name of placeholder to add handler for (Using 'all' will set the function as a handler for all placeholders
|
507 |
+
* @param callback $handler Function to set as a handler
|
508 |
+
* @param int $priority (optional) Priority of handler
|
509 |
+
*/
|
510 |
+
static function register_placeholder_handler($placeholder, $handler, $priority = 10) {
|
511 |
+
if ( 'all' == $placeholder )
|
512 |
+
$placeholder = '';
|
513 |
+
else
|
514 |
+
$placeholder = '_' . $placeholder;
|
515 |
+
|
516 |
+
add_filter('cnr_process_placeholder' . $placeholder, $handler, $priority, 5);
|
517 |
+
}
|
518 |
+
|
519 |
+
/**
|
520 |
+
* Default placeholder processing
|
521 |
+
* To be executed when current placeholder has not been handled by another handler
|
522 |
+
* @param string $ph_output Value to be used in place of placeholder
|
523 |
+
* @param CNR_Field $field Field containing placeholder
|
524 |
+
* @param array $placeholder Current placeholder
|
525 |
+
* @see CNR_Field::parse_layout for structure of $placeholder array
|
526 |
+
* @param string $layout Layout to build
|
527 |
+
* @param array $data Extended data for field
|
528 |
+
* @return string Value to use in place of current placeholder
|
529 |
+
*/
|
530 |
+
static function process_placeholder_default($ph_output, $field, $placeholder, $layout, $data) {
|
531 |
+
//Validate parameters before processing
|
532 |
+
if ( empty($ph_output) && is_a($field, 'CNR_Field_Type') && is_array($placeholder) ) {
|
533 |
+
//Build path to replacement data
|
534 |
+
$ph_output = $field->get_member_value($placeholder);
|
535 |
+
|
536 |
+
//Check if value is group (properties, etc.)
|
537 |
+
//All groups must have additional attributes (beyond reserved attributes) that define how items in group are used
|
538 |
+
if (is_array($ph_output)
|
539 |
+
&& !empty($placeholder['attributes'])
|
540 |
+
&& is_array($placeholder['attributes'])
|
541 |
+
&& ($ph = $field->get_placeholder_defaults())
|
542 |
+
&& $attribs = array_diff(array_keys($placeholder['attributes']), array_values($ph->reserved))
|
543 |
+
) {
|
544 |
+
/* Targeted property is an array, but the placeholder contains additional options on how property is to be used */
|
545 |
+
|
546 |
+
//Find items matching criteria in $ph_output
|
547 |
+
//Check for group criteria
|
548 |
+
//TODO: Implement more robust/flexible criteria handling (2010-03-11: Currently only processes property groups)
|
549 |
+
if ( 'properties' == $placeholder['tag'] && ($prop_group = $field->get_group($placeholder['attributes']['group'])) && !empty($prop_group) ) {
|
550 |
+
/* Process group */
|
551 |
+
$group_out = array();
|
552 |
+
//Iterate through properties in group and build string
|
553 |
+
foreach ( $prop_group as $prop_key => $prop_val ) {
|
554 |
+
$group_out[] = $prop_key . '="' . $field->get_property($prop_key) . '"';
|
555 |
+
}
|
556 |
+
$ph_output = implode(' ', $group_out);
|
557 |
+
}
|
558 |
+
} elseif ( is_object($ph_output) && is_a($ph_output, $field->base_class) ) {
|
559 |
+
/* Targeted property is actually a nested field */
|
560 |
+
//Set caller to current field
|
561 |
+
$ph_output->set_caller($field);
|
562 |
+
//Build layout for nested element
|
563 |
+
$ph_output = $ph_output->build_layout($layout);
|
564 |
+
}
|
565 |
+
}
|
566 |
+
|
567 |
+
return $ph_output;
|
568 |
+
}
|
569 |
+
|
570 |
+
/**
|
571 |
+
* Build Field ID attribute
|
572 |
+
* @see CNR_Field_Type::process_placeholder_default for parameter descriptions
|
573 |
+
* @return string Placeholder output
|
574 |
+
*/
|
575 |
+
static function process_placeholder_id($ph_output, $field, $placeholder, $layout, $data) {
|
576 |
+
//Get attributes
|
577 |
+
$args = wp_parse_args($placeholder['attributes'], array('format' => 'attr_id'));
|
578 |
+
return $field->get_id($args);
|
579 |
+
}
|
580 |
+
|
581 |
+
/**
|
582 |
+
* Build Field name attribute
|
583 |
+
* Name is formatted as an associative array for processing by PHP after submission
|
584 |
+
* @see CNR_Field_Type::process_placeholder_default for parameter descriptions
|
585 |
+
* @return string Placeholder output
|
586 |
+
*/
|
587 |
+
static function process_placeholder_name($ph_output, $field, $placeholder, $layout, $data) {
|
588 |
+
//Get attributes
|
589 |
+
$args = wp_parse_args($placeholder['attributes'], array('format' => 'default'));
|
590 |
+
return $field->get_id($args);
|
591 |
+
}
|
592 |
+
|
593 |
+
/**
|
594 |
+
* Retrieve data for field
|
595 |
+
* @see CNR_Field_Type::process_placeholder_default for parameter descriptions
|
596 |
+
* @return string Placeholder output
|
597 |
+
*/
|
598 |
+
static function process_placeholder_data($ph_output, $field, $placeholder, $layout) {
|
599 |
+
$val = $field->get_data();
|
600 |
+
if ( !is_null($val) ) {
|
601 |
+
$ph_output = $val;
|
602 |
+
$attr =& $placeholder['attributes'];
|
603 |
+
//Get specific member in value (e.g. value from a specific field element)
|
604 |
+
if ( isset($attr['element']) && is_array($ph_output) && ( $el = $attr['element'] ) && isset($ph_output[$el]) )
|
605 |
+
$ph_output = $ph_output[$el];
|
606 |
+
if ( isset($attr['format']) && 'display' == $attr['format'] )
|
607 |
+
$ph_output = nl2br($ph_output);
|
608 |
+
}
|
609 |
+
|
610 |
+
//Return data
|
611 |
+
return $ph_output;
|
612 |
+
}
|
613 |
+
|
614 |
+
/**
|
615 |
+
* Loops over data to build field output
|
616 |
+
* Options:
|
617 |
+
* data - Dot-delimited path in field that contains data to loop through
|
618 |
+
* layout - Name of layout to use for each data item in loop
|
619 |
+
* layout_data - Name of layout to use for data item that matches previously-saved field data
|
620 |
+
* @see CNR_Field_Type::process_placeholder_default for parameter descriptions
|
621 |
+
* @return string Placeholder output
|
622 |
+
*/
|
623 |
+
static function process_placeholder_loop($ph_output, $field, $placeholder, $layout, $data) {
|
624 |
+
//Setup loop options
|
625 |
+
$attr_defaults = array (
|
626 |
+
'layout' => '',
|
627 |
+
'layout_data' => null,
|
628 |
+
'data' => ''
|
629 |
+
);
|
630 |
+
|
631 |
+
$attr = wp_parse_args($placeholder['attributes'], $attr_defaults);
|
632 |
+
|
633 |
+
if ( is_null($attr['layout_data']) ) {
|
634 |
+
$attr['layout_data'] =& $attr['layout'];
|
635 |
+
}
|
636 |
+
|
637 |
+
//Get data for loop
|
638 |
+
$path = explode('.', $attr['data']);
|
639 |
+
$loop_data = $field->get_member_value($path);
|
640 |
+
/*if ( isset($loop_data['value']) )
|
641 |
+
$loop_data = $loop_data['value'];
|
642 |
+
*/
|
643 |
+
$out = array();
|
644 |
+
|
645 |
+
//Get field data
|
646 |
+
$data = $field->get_data();
|
647 |
+
|
648 |
+
//Iterate over data and build output
|
649 |
+
if ( is_array($loop_data) && !empty($loop_data) ) {
|
650 |
+
foreach ( $loop_data as $value => $label ) {
|
651 |
+
//Load appropriate layout based on field value
|
652 |
+
$layout = ( ($data === 0 && $value === $data) xor $data == $value ) ? $attr['layout_data'] : $attr['layout'];
|
653 |
+
//Stop processing if no valid layout is returned
|
654 |
+
if ( empty($layout) )
|
655 |
+
continue;
|
656 |
+
//Prep extended field data
|
657 |
+
$data_ext = array('option_value' => $value, 'option_text' => $label);
|
658 |
+
$out[] = $field->build_layout($layout, $data_ext);
|
659 |
+
}
|
660 |
+
}
|
661 |
+
|
662 |
+
//Return output
|
663 |
+
return implode($out);
|
664 |
+
}
|
665 |
+
|
666 |
+
/**
|
667 |
+
* Returns specified value from extended data array for field
|
668 |
+
* @see CNR_Field_Type::process_placeholder_default for parameter descriptions
|
669 |
+
* @return string Placeholder output
|
670 |
+
*/
|
671 |
+
static function process_placeholder_data_ext($ph_output, $field, $placeholder, $layout, $data) {
|
672 |
+
if ( isset($placeholder['attributes']['id']) && ($key = $placeholder['attributes']['id']) && isset($data[$key]) ) {
|
673 |
+
$ph_output = strval($data[$key]);
|
674 |
+
}
|
675 |
+
|
676 |
+
return $ph_output;
|
677 |
+
}
|
678 |
+
|
679 |
+
/**
|
680 |
+
* WP Editor
|
681 |
+
* @see CNR_Field_Type::process_placeholder_default for parameter descriptions
|
682 |
+
* @return string Placeholder output
|
683 |
+
*/
|
684 |
+
static function process_placeholder_rich_editor($ph_output, $field, $placeholder, $layout, $data) {
|
685 |
+
$id = $field->get_id( array (
|
686 |
+
'format' => 'attr_id'
|
687 |
+
));
|
688 |
+
$settings = array (
|
689 |
+
'textarea_name' => $field->get_id( array (
|
690 |
+
'format' => 'default'
|
691 |
+
))
|
692 |
+
);
|
693 |
+
ob_start();
|
694 |
+
wp_editor($field->get_data(), $id, $settings);
|
695 |
+
$out = ob_get_clean();
|
696 |
+
return $out;
|
697 |
+
}
|
698 |
}
|
includes/class.post.php
CHANGED
@@ -1,248 +1,248 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* @package Cornerstone
|
4 |
-
* @subpackage Posts
|
5 |
-
* @author Archetyped
|
6 |
-
*
|
7 |
-
*/
|
8 |
-
class CNR_Post extends CNR_Base {
|
9 |
-
|
10 |
-
/*-** Properties **-*/
|
11 |
-
|
12 |
-
/**
|
13 |
-
* Script files
|
14 |
-
* @see CNR_Base::client_files
|
15 |
-
* @var array
|
16 |
-
*/
|
17 |
-
var $scripts = array (
|
18 |
-
'posts' => array (
|
19 |
-
'file' => 'js/lib.posts.js',
|
20 |
-
'deps' => '[core]',
|
21 |
-
'context' => 'admin'
|
22 |
-
),
|
23 |
-
);
|
24 |
-
|
25 |
-
/**
|
26 |
-
* Default title separator
|
27 |
-
* @var string
|
28 |
-
*/
|
29 |
-
var $title_sep = '‹';
|
30 |
-
|
31 |
-
/*-** Initialization **-*/
|
32 |
-
|
33 |
-
function register_hooks() {
|
34 |
-
parent::register_hooks();
|
35 |
-
|
36 |
-
//Template
|
37 |
-
// add_filter('wp_title', $this->m('page_title'), 11, 3);
|
38 |
-
|
39 |
-
//Admin
|
40 |
-
add_action('admin_head', $this->m('admin_set_title'), 11);
|
41 |
-
}
|
42 |
-
|
43 |
-
/**
|
44 |
-
* Gets entire parent tree of post as an array
|
45 |
-
*
|
46 |
-
* Array order is from top level to immediate post parent
|
47 |
-
* @static
|
48 |
-
* @param object $post Post to get path for
|
49 |
-
* @param string $prop Property to retrieve from parents. If specified, array will contain only this property from parents
|
50 |
-
* @param $depth Unused
|
51 |
-
* @return array of Post Objects/Properties
|
52 |
-
*/
|
53 |
-
static function get_parents($post, $prop = '', $depth = '') {
|
54 |
-
$parents = get_post_ancestors($post = get_post($post, OBJECT, ''));
|
55 |
-
if ( is_object($post) && !empty($parents) && ('id' != strtolower(trim($prop))) ) {
|
56 |
-
//Retrieve post data for parents if full data or property other than post ID is required
|
57 |
-
$args = array(
|
58 |
-
'include' => $parents,
|
59 |
-
'post_type' => 'any',
|
60 |
-
);
|
61 |
-
$ancestors = get_posts($args);
|
62 |
-
|
63 |
-
//Sort array in ancestor order
|
64 |
-
$temp_parents = array();
|
65 |
-
foreach ($ancestors as $ancestor) {
|
66 |
-
//Get index of ancestor
|
67 |
-
$i = array_search($ancestor->ID, $parents);
|
68 |
-
if ( false === $i )
|
69 |
-
continue;
|
70 |
-
//Insert post at index
|
71 |
-
$temp_parents[$i] = $ancestor;
|
72 |
-
}
|
73 |
-
|
74 |
-
if ( !empty($temp_parents) )
|
75 |
-
$parents = $temp_parents;
|
76 |
-
}
|
77 |
-
//Reverse Array (to put top level parent at beginning of array)
|
78 |
-
$parents = array_reverse($parents);
|
79 |
-
return $parents;
|
80 |
-
}
|
81 |
-
|
82 |
-
/**
|
83 |
-
* Get the IDs of a collection of posts
|
84 |
-
* @return array IDs of Posts passed to function
|
85 |
-
* @param array $posts Array of Post objects
|
86 |
-
*/
|
87 |
-
function get_ids($posts) {
|
88 |
-
$callback = create_function('$post', 'return $post->ID;');
|
89 |
-
$arr_ids = array_map($callback, $posts);
|
90 |
-
return $arr_ids;
|
91 |
-
}
|
92 |
-
|
93 |
-
/*-** Children **-*/
|
94 |
-
|
95 |
-
/**
|
96 |
-
* Gets children posts of specified page and stores them for later use
|
97 |
-
* This method hooks into 'the_posts' filter to retrieve child posts for any single page retrieved by WP
|
98 |
-
* @param int|object $post ID or Object of Post to get children for
|
99 |
-
* @return CNR_Post_Query $posts Posts array (required by 'the_posts' filter)
|
100 |
-
*
|
101 |
-
* @global WP_Query $wp_query
|
102 |
-
*/
|
103 |
-
function &get_children($post = null) {
|
104 |
-
//Global variables
|
105 |
-
global $wp_query;
|
106 |
-
$children = new CNR_Post_Query();
|
107 |
-
if ( empty($post) && !empty($wp_query->posts) )
|
108 |
-
$post = $wp_query->posts[0];
|
109 |
-
|
110 |
-
if ( is_object($post) )
|
111 |
-
$post = $post->ID;
|
112 |
-
if ( is_numeric($post) )
|
113 |
-
$post = (int) $post;
|
114 |
-
else
|
115 |
-
return $children;
|
116 |
-
|
117 |
-
//Get children posts of page
|
118 |
-
if ( $post ) {
|
119 |
-
//Set arguments to retrieve children posts of current page
|
120 |
-
$limit = ( is_feed() ) ? get_option('posts_per_rss') : get_option('posts_per_page');
|
121 |
-
$offset = ( is_paged() ) ? ( (get_query_var('paged') - 1) * $limit ) : 0;
|
122 |
-
$c_args = array(
|
123 |
-
'post_parent' => $post,
|
124 |
-
'numberposts' => $limit,
|
125 |
-
'offset' => $offset
|
126 |
-
);
|
127 |
-
|
128 |
-
//Create post query object
|
129 |
-
$children->set_arg($c_args);
|
130 |
-
|
131 |
-
//Get children posts
|
132 |
-
$children->get();
|
133 |
-
}
|
134 |
-
|
135 |
-
return $children;
|
136 |
-
}
|
137 |
-
|
138 |
-
/*-** Post Metadata **-*/
|
139 |
-
|
140 |
-
/**
|
141 |
-
* Retrieves the post's section data
|
142 |
-
* @param string $data (optional) Section data to return (Default: full section object)
|
143 |
-
* Possible values:
|
144 |
-
* NULL Full section post object
|
145 |
-
* Column name Post column data (if exists)
|
146 |
-
*
|
147 |
-
* @return mixed post's section data (Default: ID value)
|
148 |
-
*/
|
149 |
-
static function get_section($post = null, $data = null) {
|
150 |
-
$p = get_post($post);
|
151 |
-
$retval = 0;
|
152 |
-
if ( is_object($p) && isset($p->post_parent) )
|
153 |
-
$retval = intval($p->post_parent);
|
154 |
-
|
155 |
-
//Get specified section data for posts with valid parents
|
156 |
-
if ( $retval > 0 ) {
|
157 |
-
if ( !empty($data) ) {
|
158 |
-
$retval = get_post_field($data, $retval);
|
159 |
-
} else {
|
160 |
-
$retval = get_post($retval);
|
161 |
-
}
|
162 |
-
}
|
163 |
-
|
164 |
-
return $retval;
|
165 |
-
}
|
166 |
-
|
167 |
-
/**
|
168 |
-
* Prints the post's section data
|
169 |
-
* @uses CNR_Post::get_section()
|
170 |
-
* @param string $type (optional) Type of data to return (Default: ID)
|
171 |
-
*/
|
172 |
-
static function the_section($post = null, $data = 'ID') {
|
173 |
-
if ( empty($data) )
|
174 |
-
$data = 'ID';
|
175 |
-
echo CNR_Post::get_section($post, $data);
|
176 |
-
}
|
177 |
-
|
178 |
-
/*-** Admin **-*/
|
179 |
-
|
180 |
-
function admin_set_title() {
|
181 |
-
global $post;
|
182 |
-
|
183 |
-
if ( !$post )
|
184 |
-
return false;
|
185 |
-
|
186 |
-
$obj = new stdClass();
|
187 |
-
//Section title
|
188 |
-
$sec = $this->get_section($post);
|
189 |
-
if ( $sec )
|
190 |
-
$obj->item_section = get_the_title($sec);
|
191 |
-
//Separator
|
192 |
-
$obj->title_sep = $this->page_title_get_sep();
|
193 |
-
$this->util->extend_client_object('posts', $obj, true);
|
194 |
-
}
|
195 |
-
|
196 |
-
function page_title_get_sep($pad = true) {
|
197 |
-
$sep = $this->title_sep;
|
198 |
-
if ( $pad )
|
199 |
-
$sep = ' ' . trim($sep) . ' ';
|
200 |
-
return $sep;
|
201 |
-
}
|
202 |
-
|
203 |
-
/*-** Template **-*/
|
204 |
-
|
205 |
-
/**
|
206 |
-
* Builds page title for current request
|
207 |
-
* Adds subtitle to title
|
208 |
-
* Filter called by `wp_title` hook
|
209 |
-
* @param $title
|
210 |
-
* @param $sep
|
211 |
-
* @param $seplocation
|
212 |
-
* @return string Title text
|
213 |
-
*/
|
214 |
-
function page_title_get($title, $sep = '', $seplocation = '') {
|
215 |
-
global $post;
|
216 |
-
|
217 |
-
$sep = $this->page_title_get_sep();
|
218 |
-
|
219 |
-
if ( is_single() ) {
|
220 |
-
//Append section name to post title
|
221 |
-
$ptitle = get_the_title();
|
222 |
-
$ptitle_pos = ( $ptitle ) ? strpos($title, $ptitle) : false;
|
223 |
-
if ( $ptitle_pos !== false ) {
|
224 |
-
//Get section
|
225 |
-
if ( ( $sec = $this->get_section($post) ) ) {
|
226 |
-
//Append section name to post title only once
|
227 |
-
$title = substr_replace($ptitle, $ptitle . $sep . get_the_title($sec), $ptitle_pos, strlen($ptitle)) . substr($title, strlen($ptitle));
|
228 |
-
}
|
229 |
-
}
|
230 |
-
}
|
231 |
-
|
232 |
-
//Return new title
|
233 |
-
return $title;
|
234 |
-
}
|
235 |
-
|
236 |
-
/**
|
237 |
-
* Builds page title for current request
|
238 |
-
* Filter called by `wp_title` hook
|
239 |
-
* @param $title
|
240 |
-
* @param $sep
|
241 |
-
* @param $seplocation
|
242 |
-
* @return string Title text
|
243 |
-
* @uses CNR::page_title_get()
|
244 |
-
*/
|
245 |
-
function page_title($title, $sep = '', $seplocation = '') {
|
246 |
-
return $this->page_title_get($title, $sep, $seplocation);
|
247 |
-
}
|
248 |
}
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package Cornerstone
|
4 |
+
* @subpackage Posts
|
5 |
+
* @author Archetyped
|
6 |
+
*
|
7 |
+
*/
|
8 |
+
class CNR_Post extends CNR_Base {
|
9 |
+
|
10 |
+
/*-** Properties **-*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Script files
|
14 |
+
* @see CNR_Base::client_files
|
15 |
+
* @var array
|
16 |
+
*/
|
17 |
+
var $scripts = array (
|
18 |
+
'posts' => array (
|
19 |
+
'file' => 'js/lib.posts.js',
|
20 |
+
'deps' => '[core]',
|
21 |
+
'context' => 'admin'
|
22 |
+
),
|
23 |
+
);
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Default title separator
|
27 |
+
* @var string
|
28 |
+
*/
|
29 |
+
var $title_sep = '‹';
|
30 |
+
|
31 |
+
/*-** Initialization **-*/
|
32 |
+
|
33 |
+
function register_hooks() {
|
34 |
+
parent::register_hooks();
|
35 |
+
|
36 |
+
//Template
|
37 |
+
// add_filter('wp_title', $this->m('page_title'), 11, 3);
|
38 |
+
|
39 |
+
//Admin
|
40 |
+
add_action('admin_head', $this->m('admin_set_title'), 11);
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Gets entire parent tree of post as an array
|
45 |
+
*
|
46 |
+
* Array order is from top level to immediate post parent
|
47 |
+
* @static
|
48 |
+
* @param object $post Post to get path for
|
49 |
+
* @param string $prop Property to retrieve from parents. If specified, array will contain only this property from parents
|
50 |
+
* @param $depth Unused
|
51 |
+
* @return array of Post Objects/Properties
|
52 |
+
*/
|
53 |
+
static function get_parents($post, $prop = '', $depth = '') {
|
54 |
+
$parents = get_post_ancestors($post = get_post($post, OBJECT, ''));
|
55 |
+
if ( is_object($post) && !empty($parents) && ('id' != strtolower(trim($prop))) ) {
|
56 |
+
//Retrieve post data for parents if full data or property other than post ID is required
|
57 |
+
$args = array(
|
58 |
+
'include' => $parents,
|
59 |
+
'post_type' => 'any',
|
60 |
+
);
|
61 |
+
$ancestors = get_posts($args);
|
62 |
+
|
63 |
+
//Sort array in ancestor order
|
64 |
+
$temp_parents = array();
|
65 |
+
foreach ($ancestors as $ancestor) {
|
66 |
+
//Get index of ancestor
|
67 |
+
$i = array_search($ancestor->ID, $parents);
|
68 |
+
if ( false === $i )
|
69 |
+
continue;
|
70 |
+
//Insert post at index
|
71 |
+
$temp_parents[$i] = $ancestor;
|
72 |
+
}
|
73 |
+
|
74 |
+
if ( !empty($temp_parents) )
|
75 |
+
$parents = $temp_parents;
|
76 |
+
}
|
77 |
+
//Reverse Array (to put top level parent at beginning of array)
|
78 |
+
$parents = array_reverse($parents);
|
79 |
+
return $parents;
|
80 |
+
}
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Get the IDs of a collection of posts
|
84 |
+
* @return array IDs of Posts passed to function
|
85 |
+
* @param array $posts Array of Post objects
|
86 |
+
*/
|
87 |
+
function get_ids($posts) {
|
88 |
+
$callback = create_function('$post', 'return $post->ID;');
|
89 |
+
$arr_ids = array_map($callback, $posts);
|
90 |
+
return $arr_ids;
|
91 |
+
}
|
92 |
+
|
93 |
+
/*-** Children **-*/
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Gets children posts of specified page and stores them for later use
|
97 |
+
* This method hooks into 'the_posts' filter to retrieve child posts for any single page retrieved by WP
|
98 |
+
* @param int|object $post ID or Object of Post to get children for
|
99 |
+
* @return CNR_Post_Query $posts Posts array (required by 'the_posts' filter)
|
100 |
+
*
|
101 |
+
* @global WP_Query $wp_query
|
102 |
+
*/
|
103 |
+
function &get_children($post = null) {
|
104 |
+
//Global variables
|
105 |
+
global $wp_query;
|
106 |
+
$children = new CNR_Post_Query();
|
107 |
+
if ( empty($post) && !empty($wp_query->posts) )
|
108 |
+
$post = $wp_query->posts[0];
|
109 |
+
|
110 |
+
if ( is_object($post) )
|
111 |
+
$post = $post->ID;
|
112 |
+
if ( is_numeric($post) )
|
113 |
+
$post = (int) $post;
|
114 |
+
else
|
115 |
+
return $children;
|
116 |
+
|
117 |
+
//Get children posts of page
|
118 |
+
if ( $post ) {
|
119 |
+
//Set arguments to retrieve children posts of current page
|
120 |
+
$limit = ( is_feed() ) ? get_option('posts_per_rss') : get_option('posts_per_page');
|
121 |
+
$offset = ( is_paged() ) ? ( (get_query_var('paged') - 1) * $limit ) : 0;
|
122 |
+
$c_args = array(
|
123 |
+
'post_parent' => $post,
|
124 |
+
'numberposts' => $limit,
|
125 |
+
'offset' => $offset
|
126 |
+
);
|
127 |
+
|
128 |
+
//Create post query object
|
129 |
+
$children->set_arg($c_args);
|
130 |
+
|
131 |
+
//Get children posts
|
132 |
+
$children->get();
|
133 |
+
}
|
134 |
+
|
135 |
+
return $children;
|
136 |
+
}
|
137 |
+
|
138 |
+
/*-** Post Metadata **-*/
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Retrieves the post's section data
|
142 |
+
* @param string $data (optional) Section data to return (Default: full section object)
|
143 |
+
* Possible values:
|
144 |
+
* NULL Full section post object
|
145 |
+
* Column name Post column data (if exists)
|
146 |
+
*
|
147 |
+
* @return mixed post's section data (Default: ID value)
|
148 |
+
*/
|
149 |
+
static function get_section($post = null, $data = null) {
|
150 |
+
$p = get_post($post);
|
151 |
+
$retval = 0;
|
152 |
+
if ( is_object($p) && isset($p->post_parent) )
|
153 |
+
$retval = intval($p->post_parent);
|
154 |
+
|
155 |
+
//Get specified section data for posts with valid parents
|
156 |
+
if ( $retval > 0 ) {
|
157 |
+
if ( !empty($data) ) {
|
158 |
+
$retval = get_post_field($data, $retval);
|
159 |
+
} else {
|
160 |
+
$retval = get_post($retval);
|
161 |
+
}
|
162 |
+
}
|
163 |
+
|
164 |
+
return $retval;
|
165 |
+
}
|
166 |
+
|
167 |
+
/**
|
168 |
+
* Prints the post's section data
|
169 |
+
* @uses CNR_Post::get_section()
|
170 |
+
* @param string $type (optional) Type of data to return (Default: ID)
|
171 |
+
*/
|
172 |
+
static function the_section($post = null, $data = 'ID') {
|
173 |
+
if ( empty($data) )
|
174 |
+
$data = 'ID';
|
175 |
+
echo CNR_Post::get_section($post, $data);
|
176 |
+
}
|
177 |
+
|
178 |
+
/*-** Admin **-*/
|
179 |
+
|
180 |
+
function admin_set_title() {
|
181 |
+
global $post;
|
182 |
+
|
183 |
+
if ( !$post )
|
184 |
+
return false;
|
185 |
+
|
186 |
+
$obj = new stdClass();
|
187 |
+
//Section title
|
188 |
+
$sec = $this->get_section($post);
|
189 |
+
if ( $sec )
|
190 |
+
$obj->item_section = get_the_title($sec);
|
191 |
+
//Separator
|
192 |
+
$obj->title_sep = $this->page_title_get_sep();
|
193 |
+
$this->util->extend_client_object('posts', $obj, true);
|
194 |
+
}
|
195 |
+
|
196 |
+
function page_title_get_sep($pad = true) {
|
197 |
+
$sep = $this->title_sep;
|
198 |
+
if ( $pad )
|
199 |
+
$sep = ' ' . trim($sep) . ' ';
|
200 |
+
return $sep;
|
201 |
+
}
|
202 |
+
|
203 |
+
/*-** Template **-*/
|
204 |
+
|
205 |
+
/**
|
206 |
+
* Builds page title for current request
|
207 |
+
* Adds subtitle to title
|
208 |
+
* Filter called by `wp_title` hook
|
209 |
+
* @param $title
|
210 |
+
* @param $sep
|
211 |
+
* @param $seplocation
|
212 |
+
* @return string Title text
|
213 |
+
*/
|
214 |
+
function page_title_get($title, $sep = '', $seplocation = '') {
|
215 |
+
global $post;
|
216 |
+
|
217 |
+
$sep = $this->page_title_get_sep();
|
218 |
+
|
219 |
+
if ( is_single() ) {
|
220 |
+
//Append section name to post title
|
221 |
+
$ptitle = get_the_title();
|
222 |
+
$ptitle_pos = ( $ptitle ) ? strpos($title, $ptitle) : false;
|
223 |
+
if ( $ptitle_pos !== false ) {
|
224 |
+
//Get section
|
225 |
+
if ( ( $sec = $this->get_section($post) ) ) {
|
226 |
+
//Append section name to post title only once
|
227 |
+
$title = substr_replace($ptitle, $ptitle . $sep . get_the_title($sec), $ptitle_pos, strlen($ptitle)) . substr($title, strlen($ptitle));
|
228 |
+
}
|
229 |
+
}
|
230 |
+
}
|
231 |
+
|
232 |
+
//Return new title
|
233 |
+
return $title;
|
234 |
+
}
|
235 |
+
|
236 |
+
/**
|
237 |
+
* Builds page title for current request
|
238 |
+
* Filter called by `wp_title` hook
|
239 |
+
* @param $title
|
240 |
+
* @param $sep
|
241 |
+
* @param $seplocation
|
242 |
+
* @return string Title text
|
243 |
+
* @uses CNR::page_title_get()
|
244 |
+
*/
|
245 |
+
function page_title($title, $sep = '', $seplocation = '') {
|
246 |
+
return $this->page_title_get($title, $sep, $seplocation);
|
247 |
+
}
|
248 |
}
|
includes/class.post_query.php
CHANGED
@@ -1,439 +1,439 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* @package Cornerstone
|
4 |
-
* @subpackage Posts
|
5 |
-
* @author Archetyped
|
6 |
-
*
|
7 |
-
* Represents a collection of posts in a query
|
8 |
-
* Handles navigating through post collection, reporting status, etc.
|
9 |
-
*
|
10 |
-
*/
|
11 |
-
class CNR_Post_Query extends CNR_Base {
|
12 |
-
|
13 |
-
/*-** Variables **-*/
|
14 |
-
|
15 |
-
var $scripts = array (
|
16 |
-
'posts' => array (
|
17 |
-
'file' => 'js/lib.posts.js',
|
18 |
-
'deps' => '[core]',
|
19 |
-
'context' => 'admin'
|
20 |
-
),
|
21 |
-
);
|
22 |
-
|
23 |
-
/**
|
24 |
-
* Holds posts
|
25 |
-
* @var array
|
26 |
-
*/
|
27 |
-
var $posts;
|
28 |
-
|
29 |
-
/**
|
30 |
-
* IDs of posts in $posts
|
31 |
-
* @var array
|
32 |
-
*/
|
33 |
-
var $post_ids;
|
34 |
-
|
35 |
-
/**
|
36 |
-
* whether or not object contains posts
|
37 |
-
* @var bool
|
38 |
-
*/
|
39 |
-
var $has;
|
40 |
-
|
41 |
-
/**
|
42 |
-
* Index of post in current iteration
|
43 |
-
* @var int
|
44 |
-
*/
|
45 |
-
var $current;
|
46 |
-
|
47 |
-
/**
|
48 |
-
* Total number of posts in object
|
49 |
-
* @var int
|
50 |
-
*/
|
51 |
-
var $count;
|
52 |
-
|
53 |
-
/**
|
54 |
-
* Total number of matching posts found in DB
|
55 |
-
* All found posts may not have been returned in current query though (paging, etc.)
|
56 |
-
* @var int
|
57 |
-
*/
|
58 |
-
var $found = 0;
|
59 |
-
|
60 |
-
/**
|
61 |
-
* Query arguments
|
62 |
-
* @var array
|
63 |
-
*/
|
64 |
-
var $args;
|
65 |
-
|
66 |
-
/**
|
67 |
-
* Argument to be used during query to identify request
|
68 |
-
* Prefix added during init
|
69 |
-
* @see init()
|
70 |
-
* @var string
|
71 |
-
*/
|
72 |
-
var $arg_fetch = 'fetch';
|
73 |
-
|
74 |
-
/**
|
75 |
-
* TRUE if posts have been fetched, FALSE otherwise
|
76 |
-
* @var bool
|
77 |
-
*/
|
78 |
-
var $fetched;
|
79 |
-
|
80 |
-
function __construct( $args = null ) {
|
81 |
-
parent::__construct();
|
82 |
-
|
83 |
-
parent::init();
|
84 |
-
//Init properties
|
85 |
-
$this->init();
|
86 |
-
|
87 |
-
//Set arguments
|
88 |
-
if ( !empty($args) && is_array($args) ) {
|
89 |
-
$this->args = wp_parse_args($args, $this->args);
|
90 |
-
}
|
91 |
-
}
|
92 |
-
|
93 |
-
/**
|
94 |
-
* Initializes object properties with default values
|
95 |
-
* @return void
|
96 |
-
*/
|
97 |
-
function init() {
|
98 |
-
$this->posts = array();
|
99 |
-
$this->post_ids = array();
|
100 |
-
$this->has = false;
|
101 |
-
$this->current = -1;
|
102 |
-
$this->count = 0;
|
103 |
-
$this->found = 0;
|
104 |
-
$this->arg_fetch = $this->add_prefix($this->arg_fetch);
|
105 |
-
$this->args = array($this->arg_fetch => true, $this->get_prefix() => true);
|
106 |
-
$this->fetched = false;
|
107 |
-
}
|
108 |
-
|
109 |
-
/**
|
110 |
-
* Set argument value
|
111 |
-
* @param string $arg Argument name
|
112 |
-
* @param mixed $value Argument value
|
113 |
-
*/
|
114 |
-
function set_arg($arg, $value = null) {
|
115 |
-
if ( is_scalar($arg) ) { //Single argument (key/value) pair
|
116 |
-
$this->args[$arg] = $value;
|
117 |
-
} elseif ( is_array($arg) ) { //Multiple arguments
|
118 |
-
$this->args = wp_parse_args($arg, $this->args);
|
119 |
-
}
|
120 |
-
}
|
121 |
-
|
122 |
-
/**
|
123 |
-
* Retrieve argument value
|
124 |
-
* @param string $arg Argument name
|
125 |
-
* @return mixed Argument value
|
126 |
-
*/
|
127 |
-
function get_arg($arg) {
|
128 |
-
return ( $this->arg_isset($arg) ) ? $this->args[$arg] : null;
|
129 |
-
}
|
130 |
-
|
131 |
-
/**
|
132 |
-
* Checks if an argument is set in the object
|
133 |
-
* @param string $arg Argument name
|
134 |
-
* @return bool TRUE if argument is set, FALSE otherwise
|
135 |
-
*/
|
136 |
-
function arg_isset($arg) {
|
137 |
-
return ( isset($this->args[$arg]) );
|
138 |
-
}
|
139 |
-
|
140 |
-
/**
|
141 |
-
* Gets posts matching parameters and stores them in object
|
142 |
-
*
|
143 |
-
* @param int $limit (optional) Maximum number of posts to retrieve (Default: -1 = All matching posts)
|
144 |
-
* @param array $args (optional) Additional arguments to use in post query
|
145 |
-
* @return array Retrieved posts
|
146 |
-
*/
|
147 |
-
function get( $limit = null, $args = null ) {
|
148 |
-
//Global variables
|
149 |
-
global $wp_query;
|
150 |
-
|
151 |
-
//Clear previously retrieved post data
|
152 |
-
$this->unload();
|
153 |
-
|
154 |
-
//Determine section
|
155 |
-
$p_arg = 'post_parent';
|
156 |
-
if ( ! $this->arg_isset($p_arg) ) {
|
157 |
-
$parent = null;
|
158 |
-
if ( is_page() ) {
|
159 |
-
$parent = $wp_query->get_queried_object_id();
|
160 |
-
}
|
161 |
-
if ( !! $parent )
|
162 |
-
$this->set_arg($p_arg, $parent);
|
163 |
-
}
|
164 |
-
|
165 |
-
//Check if parent is valid post ID
|
166 |
-
if ((int)$parent < 1) {
|
167 |
-
//Get featured posts from all sections if no valid parent is set
|
168 |
-
$parent = null;
|
169 |
-
}
|
170 |
-
|
171 |
-
//Set query args
|
172 |
-
if ( !empty($args) )
|
173 |
-
$this->args = wp_parse_args($args, $this->args);
|
174 |
-
//Set post limit
|
175 |
-
if ( is_numeric($limit) )
|
176 |
-
$limit = intval($limit);
|
177 |
-
if ( ! $limit && !$this->arg_isset('numberposts') )
|
178 |
-
$limit = ( is_feed() ) ? get_option('posts_per_rss') : get_option('posts_per_page');
|
179 |
-
if ( $limit > 0 )
|
180 |
-
$this->set_arg('numberposts', $limit);
|
181 |
-
|
182 |
-
//Set offset (pagination)
|
183 |
-
if ( !is_feed() ) {
|
184 |
-
$c_page = $wp_query->get('paged');
|
185 |
-
$offset = ( $c_page > 0 ) ? $c_page - 1 : 0;
|
186 |
-
$offset = $limit * $offset;
|
187 |
-
$this->set_arg('offset', $offset);
|
188 |
-
}
|
189 |
-
|
190 |
-
//Retrieve posts
|
191 |
-
$filter = 'found_posts';
|
192 |
-
$f_callback = $this->m('set_found');
|
193 |
-
$action = 'parse_query';
|
194 |
-
$a_callback = $this->m('set_found_flag');
|
195 |
-
|
196 |
-
//Add filter to populate found property during query
|
197 |
-
add_filter($filter, $f_callback);
|
198 |
-
add_action($action, $a_callback);
|
199 |
-
//Get posts
|
200 |
-
$posts = get_posts($this->args);
|
201 |
-
//Remove filter after query has completed
|
202 |
-
remove_action($action, $a_callback);
|
203 |
-
remove_filter($filter, $f_callback);
|
204 |
-
//Save retrieved posts to array
|
205 |
-
$this->load($posts);
|
206 |
-
//Return retrieved posts so that array may be manipulated further if desired
|
207 |
-
return $this->posts;
|
208 |
-
}
|
209 |
-
|
210 |
-
/**
|
211 |
-
* Sets object properties with post data
|
212 |
-
*
|
213 |
-
* @param array $posts Array of post objects
|
214 |
-
* @return void
|
215 |
-
*/
|
216 |
-
function load( $posts = null ) {
|
217 |
-
$this->fetched = true;
|
218 |
-
if ( !empty($posts) ) {
|
219 |
-
$this->posts = $posts;
|
220 |
-
$this->has = true;
|
221 |
-
$this->count = count($this->posts);
|
222 |
-
}
|
223 |
-
}
|
224 |
-
|
225 |
-
/**
|
226 |
-
* Resets object properties to allow for new data to be saved
|
227 |
-
* @return void
|
228 |
-
*/
|
229 |
-
function unload() {
|
230 |
-
//Temporarily save properties that should persist
|
231 |
-
$_args = $this->args;
|
232 |
-
|
233 |
-
//Initialize object properties
|
234 |
-
$this->init();
|
235 |
-
|
236 |
-
//Restore persistent properties
|
237 |
-
$this->args = $_args;
|
238 |
-
}
|
239 |
-
|
240 |
-
/**
|
241 |
-
* Sets number of found posts in object's query
|
242 |
-
* @link `found_posts` hook
|
243 |
-
* @see WP_Query::get_posts()
|
244 |
-
* @param int $num_found
|
245 |
-
*/
|
246 |
-
function set_found($num_found) {
|
247 |
-
$this->found = $num_found;
|
248 |
-
}
|
249 |
-
|
250 |
-
/**
|
251 |
-
* Modifies query parameters to allow `found_posts` hook to be called
|
252 |
-
* Unsets `no_found_rows` query parameter set in WP 3.1
|
253 |
-
* @see WP_Query::parse_query()
|
254 |
-
* @link `parse_query` action hook
|
255 |
-
* @param WP_Query $q Query instance object
|
256 |
-
*/
|
257 |
-
function set_found_flag(&$q) {
|
258 |
-
if ( isset($q->query_vars[$this->arg_fetch]) ) {
|
259 |
-
$q->query_vars['no_found_rows'] = false;
|
260 |
-
}
|
261 |
-
}
|
262 |
-
|
263 |
-
/**
|
264 |
-
* Makes sure query was run prior
|
265 |
-
* @return void
|
266 |
-
*/
|
267 |
-
function confirm_fetched() {
|
268 |
-
if ( !$this->fetched )
|
269 |
-
$this->get();
|
270 |
-
}
|
271 |
-
|
272 |
-
/**
|
273 |
-
* Returns number of matching posts found in DB
|
274 |
-
* May not necessarily match number of posts contained in object (due to post limits, pagination, etc.)
|
275 |
-
* @return int Number of posts found
|
276 |
-
*/
|
277 |
-
function found() {
|
278 |
-
$this->confirm_fetched();
|
279 |
-
return $this->found;
|
280 |
-
}
|
281 |
-
|
282 |
-
/**
|
283 |
-
* Checks whether posts related to this object are available in the current context
|
284 |
-
*
|
285 |
-
* If no accessible posts are found, current post (section) is set as global post variable
|
286 |
-
*
|
287 |
-
* @see 'the_posts' filter
|
288 |
-
* @see get_children()
|
289 |
-
*
|
290 |
-
* @param bool $fetch Whether posts should be fetched if they have not yet been retrieved
|
291 |
-
* @return boolean TRUE if section contains children, FALSE otherwise
|
292 |
-
* Note: Will also return FALSE if section contains children, but all children have been previously accessed
|
293 |
-
*
|
294 |
-
* @global WP_Query $wp_query
|
295 |
-
* @global obj $post
|
296 |
-
*/
|
297 |
-
function has( $fetch = true ) {
|
298 |
-
global $wp_query, $post, $more;
|
299 |
-
|
300 |
-
$this->confirm_fetched();
|
301 |
-
|
302 |
-
//Check if any posts on current page were retrieved
|
303 |
-
//If posts are found, make sure there are more posts
|
304 |
-
if ( $this->count > 0 && ( $this->current < $this->count - 1 ) ) {
|
305 |
-
return true;
|
306 |
-
}
|
307 |
-
|
308 |
-
//Reset current post position if all posts have been processed
|
309 |
-
$this->rewind();
|
310 |
-
$wp_query->in_the_loop = false;
|
311 |
-
//If no posts were found (or the last post has been previously loaded),
|
312 |
-
//load previous post back into global post variable
|
313 |
-
$i = ( $wp_query->current_post >= 0 ) ? $wp_query->current_post : 0;
|
314 |
-
if ( count($wp_query->posts) ) {
|
315 |
-
$post = $wp_query->posts[ $i ];
|
316 |
-
setup_postdata($post);
|
317 |
-
}
|
318 |
-
|
319 |
-
if ( is_single() || is_page() )
|
320 |
-
$more = 1;
|
321 |
-
return false;
|
322 |
-
}
|
323 |
-
|
324 |
-
/**
|
325 |
-
* Loads next post into global $post variable for use in the loop
|
326 |
-
* Allows use of WP template tags
|
327 |
-
* @return void
|
328 |
-
*
|
329 |
-
* @global obj $post Post object
|
330 |
-
*/
|
331 |
-
function next() {
|
332 |
-
global $post, $more, $wp_query;
|
333 |
-
|
334 |
-
if ( $this->has() ) {
|
335 |
-
$wp_query->in_the_loop = true;
|
336 |
-
//Increment post position
|
337 |
-
$this->current++;
|
338 |
-
|
339 |
-
//Load post into global post variable
|
340 |
-
$post = $this->posts[ $this->current ];
|
341 |
-
|
342 |
-
setup_postdata($post);
|
343 |
-
$more = 0;
|
344 |
-
}
|
345 |
-
}
|
346 |
-
|
347 |
-
/**
|
348 |
-
* Resets position of current post
|
349 |
-
* Allows for multiple loops over $posts array
|
350 |
-
* @return void
|
351 |
-
*/
|
352 |
-
function rewind() {
|
353 |
-
$this->current = -1;
|
354 |
-
}
|
355 |
-
|
356 |
-
/**
|
357 |
-
* Gets index of current post
|
358 |
-
* @return int Index position of current post
|
359 |
-
*/
|
360 |
-
function current() {
|
361 |
-
return $this->current;
|
362 |
-
}
|
363 |
-
|
364 |
-
/**
|
365 |
-
* Returns number of posts in object
|
366 |
-
* @return int number of posts
|
367 |
-
*/
|
368 |
-
function count() {
|
369 |
-
$this->confirm_fetched();
|
370 |
-
return $this->count;
|
371 |
-
}
|
372 |
-
|
373 |
-
/**
|
374 |
-
* Gets the number of pages needed to list all found posts
|
375 |
-
* @return int Total number of pages
|
376 |
-
*/
|
377 |
-
function max_num_pages() {
|
378 |
-
$this->confirm_fetched();
|
379 |
-
$posts_per_page = $this->get_arg('numberposts');
|
380 |
-
if ( ! $posts_per_page )
|
381 |
-
$posts_per_page = get_option('posts_per_page');
|
382 |
-
return ceil( $this->found / $posts_per_page );
|
383 |
-
}
|
384 |
-
|
385 |
-
/**
|
386 |
-
* Checks if current post is the first post
|
387 |
-
* @return bool TRUE if current post is the first post in array, FALSE otherwise
|
388 |
-
*/
|
389 |
-
function is_first() {
|
390 |
-
$this->confirm_fetched();
|
391 |
-
return ( 0 == $this->current() );
|
392 |
-
}
|
393 |
-
|
394 |
-
/**
|
395 |
-
* Checks if current featured post is the last item in the post array
|
396 |
-
* @return bool TRUE if item is the last featured item, FALSE otherwise
|
397 |
-
*/
|
398 |
-
function is_last() {
|
399 |
-
$this->confirm_fetched();
|
400 |
-
return ($this->current == $this->count - 1) ? true : false;
|
401 |
-
}
|
402 |
-
|
403 |
-
/**
|
404 |
-
* @param int $post (optional) ID of post to check for existence in the object's posts array (uses global $post object if no value passed)
|
405 |
-
* @return bool TRUE if post is in posts array
|
406 |
-
*/
|
407 |
-
function contains( $post = null ) {
|
408 |
-
$this->confirm_fetched();
|
409 |
-
//Use argument value if it is an integer
|
410 |
-
if ( is_numeric($post) && intval($post) > 0 ) {
|
411 |
-
//Cast to object and set ID property (for later use)
|
412 |
-
$post = (object) $post;
|
413 |
-
$post->ID = $post->scalar;
|
414 |
-
}
|
415 |
-
//Otherwise check if argument is valid post
|
416 |
-
elseif ( !$this->util->check_post($post) ) {
|
417 |
-
return false;
|
418 |
-
}
|
419 |
-
|
420 |
-
//Check for existence of post ID in posts array
|
421 |
-
return in_array($post->ID, $this->get_ids());
|
422 |
-
}
|
423 |
-
|
424 |
-
/**
|
425 |
-
* Retrieve IDs of all retrieved posts
|
426 |
-
*/
|
427 |
-
function get_ids() {
|
428 |
-
$this->confirm_fetched();
|
429 |
-
|
430 |
-
if ( $this->has && empty($this->post_ids) ) {
|
431 |
-
//Build array of post ids in array
|
432 |
-
foreach ($this->posts as $post) {
|
433 |
-
$this->post_ids[] = $post->ID;
|
434 |
-
}
|
435 |
-
}
|
436 |
-
|
437 |
-
return $this->post_ids;
|
438 |
-
}
|
439 |
}
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package Cornerstone
|
4 |
+
* @subpackage Posts
|
5 |
+
* @author Archetyped
|
6 |
+
*
|
7 |
+
* Represents a collection of posts in a query
|
8 |
+
* Handles navigating through post collection, reporting status, etc.
|
9 |
+
*
|
10 |
+
*/
|
11 |
+
class CNR_Post_Query extends CNR_Base {
|
12 |
+
|
13 |
+
/*-** Variables **-*/
|
14 |
+
|
15 |
+
var $scripts = array (
|
16 |
+
'posts' => array (
|
17 |
+
'file' => 'js/lib.posts.js',
|
18 |
+
'deps' => '[core]',
|
19 |
+
'context' => 'admin'
|
20 |
+
),
|
21 |
+
);
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Holds posts
|
25 |
+
* @var array
|
26 |
+
*/
|
27 |
+
var $posts;
|
28 |
+
|
29 |
+
/**
|
30 |
+
* IDs of posts in $posts
|
31 |
+
* @var array
|
32 |
+
*/
|
33 |
+
var $post_ids;
|
34 |
+
|
35 |
+
/**
|
36 |
+
* whether or not object contains posts
|
37 |
+
* @var bool
|
38 |
+
*/
|
39 |
+
var $has;
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Index of post in current iteration
|
43 |
+
* @var int
|
44 |
+
*/
|
45 |
+
var $current;
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Total number of posts in object
|
49 |
+
* @var int
|
50 |
+
*/
|
51 |
+
var $count;
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Total number of matching posts found in DB
|
55 |
+
* All found posts may not have been returned in current query though (paging, etc.)
|
56 |
+
* @var int
|
57 |
+
*/
|
58 |
+
var $found = 0;
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Query arguments
|
62 |
+
* @var array
|
63 |
+
*/
|
64 |
+
var $args;
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Argument to be used during query to identify request
|
68 |
+
* Prefix added during init
|
69 |
+
* @see init()
|
70 |
+
* @var string
|
71 |
+
*/
|
72 |
+
var $arg_fetch = 'fetch';
|
73 |
+
|
74 |
+
/**
|
75 |
+
* TRUE if posts have been fetched, FALSE otherwise
|
76 |
+
* @var bool
|
77 |
+
*/
|
78 |
+
var $fetched;
|
79 |
+
|
80 |
+
function __construct( $args = null ) {
|
81 |
+
parent::__construct();
|
82 |
+
|
83 |
+
parent::init();
|
84 |
+
//Init properties
|
85 |
+
$this->init();
|
86 |
+
|
87 |
+
//Set arguments
|
88 |
+
if ( !empty($args) && is_array($args) ) {
|
89 |
+
$this->args = wp_parse_args($args, $this->args);
|
90 |
+
}
|
91 |
+
}
|
92 |
+
|
93 |
+
/**
|
94 |
+
* Initializes object properties with default values
|
95 |
+
* @return void
|
96 |
+
*/
|
97 |
+
function init() {
|
98 |
+
$this->posts = array();
|
99 |
+
$this->post_ids = array();
|
100 |
+
$this->has = false;
|
101 |
+
$this->current = -1;
|
102 |
+
$this->count = 0;
|
103 |
+
$this->found = 0;
|
104 |
+
$this->arg_fetch = $this->add_prefix($this->arg_fetch);
|
105 |
+
$this->args = array($this->arg_fetch => true, $this->get_prefix() => true);
|
106 |
+
$this->fetched = false;
|
107 |
+
}
|
108 |
+
|
109 |
+
/**
|
110 |
+
* Set argument value
|
111 |
+
* @param string $arg Argument name
|
112 |
+
* @param mixed $value Argument value
|
113 |
+
*/
|
114 |
+
function set_arg($arg, $value = null) {
|
115 |
+
if ( is_scalar($arg) ) { //Single argument (key/value) pair
|
116 |
+
$this->args[$arg] = $value;
|
117 |
+
} elseif ( is_array($arg) ) { //Multiple arguments
|
118 |
+
$this->args = wp_parse_args($arg, $this->args);
|
119 |
+
}
|
120 |
+
}
|
121 |
+
|
122 |
+
/**
|
123 |
+
* Retrieve argument value
|
124 |
+
* @param string $arg Argument name
|
125 |
+
* @return mixed Argument value
|
126 |
+
*/
|
127 |
+
function get_arg($arg) {
|
128 |
+
return ( $this->arg_isset($arg) ) ? $this->args[$arg] : null;
|
129 |
+
}
|
130 |
+
|
131 |
+
/**
|
132 |
+
* Checks if an argument is set in the object
|
133 |
+
* @param string $arg Argument name
|
134 |
+
* @return bool TRUE if argument is set, FALSE otherwise
|
135 |
+
*/
|
136 |
+
function arg_isset($arg) {
|
137 |
+
return ( isset($this->args[$arg]) );
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Gets posts matching parameters and stores them in object
|
142 |
+
*
|
143 |
+
* @param int $limit (optional) Maximum number of posts to retrieve (Default: -1 = All matching posts)
|
144 |
+
* @param array $args (optional) Additional arguments to use in post query
|
145 |
+
* @return array Retrieved posts
|
146 |
+
*/
|
147 |
+
function get( $limit = null, $args = null ) {
|
148 |
+
//Global variables
|
149 |
+
global $wp_query;
|
150 |
+
|
151 |
+
//Clear previously retrieved post data
|
152 |
+
$this->unload();
|
153 |
+
|
154 |
+
//Determine section
|
155 |
+
$p_arg = 'post_parent';
|
156 |
+
if ( ! $this->arg_isset($p_arg) ) {
|
157 |
+
$parent = null;
|
158 |
+
if ( is_page() ) {
|
159 |
+
$parent = $wp_query->get_queried_object_id();
|
160 |
+
}
|
161 |
+
if ( !! $parent )
|
162 |
+
$this->set_arg($p_arg, $parent);
|
163 |
+
}
|
164 |
+
|
165 |
+
//Check if parent is valid post ID
|
166 |
+
if ((int)$parent < 1) {
|
167 |
+
//Get featured posts from all sections if no valid parent is set
|
168 |
+
$parent = null;
|
169 |
+
}
|
170 |
+
|
171 |
+
//Set query args
|
172 |
+
if ( !empty($args) )
|
173 |
+
$this->args = wp_parse_args($args, $this->args);
|
174 |
+
//Set post limit
|
175 |
+
if ( is_numeric($limit) )
|
176 |
+
$limit = intval($limit);
|
177 |
+
if ( ! $limit && !$this->arg_isset('numberposts') )
|
178 |
+
$limit = ( is_feed() ) ? get_option('posts_per_rss') : get_option('posts_per_page');
|
179 |
+
if ( $limit > 0 )
|
180 |
+
$this->set_arg('numberposts', $limit);
|
181 |
+
|
182 |
+
//Set offset (pagination)
|
183 |
+
if ( !is_feed() ) {
|
184 |
+
$c_page = $wp_query->get('paged');
|
185 |
+
$offset = ( $c_page > 0 ) ? $c_page - 1 : 0;
|
186 |
+
$offset = $limit * $offset;
|
187 |
+
$this->set_arg('offset', $offset);
|
188 |
+
}
|
189 |
+
|
190 |
+
//Retrieve posts
|
191 |
+
$filter = 'found_posts';
|
192 |
+
$f_callback = $this->m('set_found');
|
193 |
+
$action = 'parse_query';
|
194 |
+
$a_callback = $this->m('set_found_flag');
|
195 |
+
|
196 |
+
//Add filter to populate found property during query
|
197 |
+
add_filter($filter, $f_callback);
|
198 |
+
add_action($action, $a_callback);
|
199 |
+
//Get posts
|
200 |
+
$posts = get_posts($this->args);
|
201 |
+
//Remove filter after query has completed
|
202 |
+
remove_action($action, $a_callback);
|
203 |
+
remove_filter($filter, $f_callback);
|
204 |
+
//Save retrieved posts to array
|
205 |
+
$this->load($posts);
|
206 |
+
//Return retrieved posts so that array may be manipulated further if desired
|
207 |
+
return $this->posts;
|
208 |
+
}
|
209 |
+
|
210 |
+
/**
|
211 |
+
* Sets object properties with post data
|
212 |
+
*
|
213 |
+
* @param array $posts Array of post objects
|
214 |
+
* @return void
|
215 |
+
*/
|
216 |
+
function load( $posts = null ) {
|
217 |
+
$this->fetched = true;
|
218 |
+
if ( !empty($posts) ) {
|
219 |
+
$this->posts = $posts;
|
220 |
+
$this->has = true;
|
221 |
+
$this->count = count($this->posts);
|
222 |
+
}
|
223 |
+
}
|
224 |
+
|
225 |
+
/**
|
226 |
+
* Resets object properties to allow for new data to be saved
|
227 |
+
* @return void
|
228 |
+
*/
|
229 |
+
function unload() {
|
230 |
+
//Temporarily save properties that should persist
|
231 |
+
$_args = $this->args;
|
232 |
+
|
233 |
+
//Initialize object properties
|
234 |
+
$this->init();
|
235 |
+
|
236 |
+
//Restore persistent properties
|
237 |
+
$this->args = $_args;
|
238 |
+
}
|
239 |
+
|
240 |
+
/**
|
241 |
+
* Sets number of found posts in object's query
|
242 |
+
* @link `found_posts` hook
|
243 |
+
* @see WP_Query::get_posts()
|
244 |
+
* @param int $num_found
|
245 |
+
*/
|
246 |
+
function set_found($num_found) {
|
247 |
+
$this->found = $num_found;
|
248 |
+
}
|
249 |
+
|
250 |
+
/**
|
251 |
+
* Modifies query parameters to allow `found_posts` hook to be called
|
252 |
+
* Unsets `no_found_rows` query parameter set in WP 3.1
|
253 |
+
* @see WP_Query::parse_query()
|
254 |
+
* @link `parse_query` action hook
|
255 |
+
* @param WP_Query $q Query instance object
|
256 |
+
*/
|
257 |
+
function set_found_flag(&$q) {
|
258 |
+
if ( isset($q->query_vars[$this->arg_fetch]) ) {
|
259 |
+
$q->query_vars['no_found_rows'] = false;
|
260 |
+
}
|
261 |
+
}
|
262 |
+
|
263 |
+
/**
|
264 |
+
* Makes sure query was run prior
|
265 |
+
* @return void
|
266 |
+
*/
|
267 |
+
function confirm_fetched() {
|
268 |
+
if ( !$this->fetched )
|
269 |
+
$this->get();
|
270 |
+
}
|
271 |
+
|
272 |
+
/**
|
273 |
+
* Returns number of matching posts found in DB
|
274 |
+
* May not necessarily match number of posts contained in object (due to post limits, pagination, etc.)
|
275 |
+
* @return int Number of posts found
|
276 |
+
*/
|
277 |
+
function found() {
|
278 |
+
$this->confirm_fetched();
|
279 |
+
return $this->found;
|
280 |
+
}
|
281 |
+
|
282 |
+
/**
|
283 |
+
* Checks whether posts related to this object are available in the current context
|
284 |
+
*
|
285 |
+
* If no accessible posts are found, current post (section) is set as global post variable
|
286 |
+
*
|
287 |
+
* @see 'the_posts' filter
|
288 |
+
* @see get_children()
|
289 |
+
*
|
290 |
+
* @param bool $fetch Whether posts should be fetched if they have not yet been retrieved
|
291 |
+
* @return boolean TRUE if section contains children, FALSE otherwise
|
292 |
+
* Note: Will also return FALSE if section contains children, but all children have been previously accessed
|
293 |
+
*
|
294 |
+
* @global WP_Query $wp_query
|
295 |
+
* @global obj $post
|
296 |
+
*/
|
297 |
+
function has( $fetch = true ) {
|
298 |
+
global $wp_query, $post, $more;
|
299 |
+
|
300 |
+
$this->confirm_fetched();
|
301 |
+
|
302 |
+
//Check if any posts on current page were retrieved
|
303 |
+
//If posts are found, make sure there are more posts
|
304 |
+
if ( $this->count > 0 && ( $this->current < $this->count - 1 ) ) {
|
305 |
+
return true;
|
306 |
+
}
|
307 |
+
|
308 |
+
//Reset current post position if all posts have been processed
|
309 |
+
$this->rewind();
|
310 |
+
$wp_query->in_the_loop = false;
|
311 |
+
//If no posts were found (or the last post has been previously loaded),
|
312 |
+
//load previous post back into global post variable
|
313 |
+
$i = ( $wp_query->current_post >= 0 ) ? $wp_query->current_post : 0;
|
314 |
+
if ( count($wp_query->posts) ) {
|
315 |
+
$post = $wp_query->posts[ $i ];
|
316 |
+
setup_postdata($post);
|
317 |
+
}
|
318 |
+
|
319 |
+
if ( is_single() || is_page() )
|
320 |
+
$more = 1;
|
321 |
+
return false;
|
322 |
+
}
|
323 |
+
|
324 |
+
/**
|
325 |
+
* Loads next post into global $post variable for use in the loop
|
326 |
+
* Allows use of WP template tags
|
327 |
+
* @return void
|
328 |
+
*
|
329 |
+
* @global obj $post Post object
|
330 |
+
*/
|
331 |
+
function next() {
|
332 |
+
global $post, $more, $wp_query;
|
333 |
+
|
334 |
+
if ( $this->has() ) {
|
335 |
+
$wp_query->in_the_loop = true;
|
336 |
+
//Increment post position
|
337 |
+
$this->current++;
|
338 |
+
|
339 |
+
//Load post into global post variable
|
340 |
+
$post = $this->posts[ $this->current ];
|
341 |
+
|
342 |
+
setup_postdata($post);
|
343 |
+
$more = 0;
|
344 |
+
}
|
345 |
+
}
|
346 |
+
|
347 |
+
/**
|
348 |
+
* Resets position of current post
|
349 |
+
* Allows for multiple loops over $posts array
|
350 |
+
* @return void
|
351 |
+
*/
|
352 |
+
function rewind() {
|
353 |
+
$this->current = -1;
|
354 |
+
}
|
355 |
+
|
356 |
+
/**
|
357 |
+
* Gets index of current post
|
358 |
+
* @return int Index position of current post
|
359 |
+
*/
|
360 |
+
function current() {
|
361 |
+
return $this->current;
|
362 |
+
}
|
363 |
+
|
364 |
+
/**
|
365 |
+
* Returns number of posts in object
|
366 |
+
* @return int number of posts
|
367 |
+
*/
|
368 |
+
function count() {
|
369 |
+
$this->confirm_fetched();
|
370 |
+
return $this->count;
|
371 |
+
}
|
372 |
+
|
373 |
+
/**
|
374 |
+
* Gets the number of pages needed to list all found posts
|
375 |
+
* @return int Total number of pages
|
376 |
+
*/
|
377 |
+
function max_num_pages() {
|
378 |
+
$this->confirm_fetched();
|
379 |
+
$posts_per_page = $this->get_arg('numberposts');
|
380 |
+
if ( ! $posts_per_page )
|
381 |
+
$posts_per_page = get_option('posts_per_page');
|
382 |
+
return ceil( $this->found / $posts_per_page );
|
383 |
+
}
|
384 |
+
|
385 |
+
/**
|
386 |
+
* Checks if current post is the first post
|
387 |
+
* @return bool TRUE if current post is the first post in array, FALSE otherwise
|
388 |
+
*/
|
389 |
+
function is_first() {
|
390 |
+
$this->confirm_fetched();
|
391 |
+
return ( 0 == $this->current() );
|
392 |
+
}
|
393 |
+
|
394 |
+
/**
|
395 |
+
* Checks if current featured post is the last item in the post array
|
396 |
+
* @return bool TRUE if item is the last featured item, FALSE otherwise
|
397 |
+
*/
|
398 |
+
function is_last() {
|
399 |
+
$this->confirm_fetched();
|
400 |
+
return ($this->current == $this->count - 1) ? true : false;
|
401 |
+
}
|
402 |
+
|
403 |
+
/**
|
404 |
+
* @param int $post (optional) ID of post to check for existence in the object's posts array (uses global $post object if no value passed)
|
405 |
+
* @return bool TRUE if post is in posts array
|
406 |
+
*/
|
407 |
+
function contains( $post = null ) {
|
408 |
+
$this->confirm_fetched();
|
409 |
+
//Use argument value if it is an integer
|
410 |
+
if ( is_numeric($post) && intval($post) > 0 ) {
|
411 |
+
//Cast to object and set ID property (for later use)
|
412 |
+
$post = (object) $post;
|
413 |
+
$post->ID = $post->scalar;
|
414 |
+
}
|
415 |
+
//Otherwise check if argument is valid post
|
416 |
+
elseif ( !$this->util->check_post($post) ) {
|
417 |
+
return false;
|
418 |
+
}
|
419 |
+
|
420 |
+
//Check for existence of post ID in posts array
|
421 |
+
return in_array($post->ID, $this->get_ids());
|
422 |
+
}
|
423 |
+
|
424 |
+
/**
|
425 |
+
* Retrieve IDs of all retrieved posts
|
426 |
+
*/
|
427 |
+
function get_ids() {
|
428 |
+
$this->confirm_fetched();
|
429 |
+
|
430 |
+
if ( $this->has && empty($this->post_ids) ) {
|
431 |
+
//Build array of post ids in array
|
432 |
+
foreach ($this->posts as $post) {
|
433 |
+
$this->post_ids[] = $post->ID;
|
434 |
+
}
|
435 |
+
}
|
436 |
+
|
437 |
+
return $this->post_ids;
|
438 |
+
}
|
439 |
}
|
js/lib.structure.admin.js
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
/**
|
2 |
* Structure Administration
|
3 |
* @package Cornerstone
|
4 |
-
* @subpackage Structure
|
5 |
*/
|
6 |
|
7 |
(function($) {
|
@@ -9,38 +9,112 @@
|
|
9 |
if ( CNR && CNR.structure && CNR.structure.extend ) CNR.structure.extend('admin', {
|
10 |
parent: CNR.structure,
|
11 |
options: {},
|
12 |
-
|
13 |
manage_option: function() {
|
14 |
//Continue processing if permalink option has been defined
|
15 |
if ( this.options && this.options.label && this.options.example && this.parent.options.permalink_structure ) {
|
|
|
|
|
16 |
var o = this.options;
|
17 |
var po = this.parent.options;
|
18 |
-
//
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
30 |
} else {
|
31 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
32 |
}
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
$(last).before($(option));
|
41 |
}
|
|
|
|
|
42 |
}
|
43 |
-
}
|
44 |
});
|
45 |
|
46 |
if ( CNR.structure.admin.manage_option )
|
1 |
/**
|
2 |
* Structure Administration
|
3 |
* @package Cornerstone
|
4 |
+
* @subpackage Structure
|
5 |
*/
|
6 |
|
7 |
(function($) {
|
9 |
if ( CNR && CNR.structure && CNR.structure.extend ) CNR.structure.extend('admin', {
|
10 |
parent: CNR.structure,
|
11 |
options: {},
|
12 |
+
|
13 |
manage_option: function() {
|
14 |
//Continue processing if permalink option has been defined
|
15 |
if ( this.options && this.options.label && this.options.example && this.parent.options.permalink_structure ) {
|
16 |
+
const opts = this.options;
|
17 |
+
opts.parent = this.parent.options;
|
18 |
var o = this.options;
|
19 |
var po = this.parent.options;
|
20 |
+
// Options handling (WP 6.1+)
|
21 |
+
const cfg = {
|
22 |
+
selectors: {
|
23 |
+
match: '.structure-selection',
|
24 |
+
template: '.structure-selection > .row',
|
25 |
+
input: 'input[type="radio"]',
|
26 |
+
label: 'label',
|
27 |
+
example: 'p > code',
|
28 |
+
},
|
29 |
+
elements: {
|
30 |
+
template: null,
|
31 |
+
base: null,
|
32 |
+
input: null,
|
33 |
+
label: null,
|
34 |
+
example: null,
|
35 |
+
custom: document.getElementById( 'permalink_structure' ),
|
36 |
+
},
|
37 |
+
values: {
|
38 |
+
id_base: 'cnr-structured',
|
39 |
+
},
|
40 |
+
legacy: {
|
41 |
+
selectors: {
|
42 |
+
template: '.permalink-structure tr',
|
43 |
+
input: 'input[type="radio"]',
|
44 |
+
label: 'label',
|
45 |
+
example: 'td > code',
|
46 |
+
},
|
47 |
+
},
|
48 |
+
};
|
49 |
+
// Set up values
|
50 |
+
cfg.values.input_id = 'permalink-input-' + cfg.values.id_base;
|
51 |
+
cfg.values.example_id = 'permalink-' + cfg.values.id_base;
|
52 |
+
// Determine if legacy configuration should be used.
|
53 |
+
if ( !document.querySelector(cfg.selectors.match) ) {
|
54 |
+
let prop;
|
55 |
+
for ( prop in cfg.legacy ) {
|
56 |
+
cfg[ prop ] = cfg.legacy[ prop ];
|
57 |
+
}
|
58 |
+
}
|
59 |
+
// Reference elements object.
|
60 |
+
const els = cfg.elements;
|
61 |
+
// Retrieve single structure option to use as template for custom option.
|
62 |
+
els.template = document.querySelector( cfg.selectors.template );
|
63 |
+
// Stop if option not found.
|
64 |
+
if ( !els.template ) {
|
65 |
+
return false;
|
66 |
+
}
|
67 |
+
// Clone option element.
|
68 |
+
els.base = els.template.cloneNode( true );
|
69 |
+
// Input element.
|
70 |
+
els.input = els.base.querySelector( cfg.selectors.input );
|
71 |
+
if ( !els.input ) {
|
72 |
+
return false;
|
73 |
+
}
|
74 |
+
els.input.setAttribute( 'id', cfg.values.input_id );
|
75 |
+
els.input.setAttribute( 'value', opts.parent.permalink_structure );
|
76 |
+
if ( els.custom ) {
|
77 |
+
if ( els.custom.value === opts.parent.permalink_structure ) {
|
78 |
+
els.input.setAttribute( 'checked', 'checked' );
|
79 |
} else {
|
80 |
+
els.input.removeAttribute( 'checked' );
|
81 |
+
}
|
82 |
+
// Event handler (option selected).
|
83 |
+
els.input.addEventListener('change', function() {
|
84 |
+
if ( els.custom.value === this.value ) {
|
85 |
+
return false;
|
86 |
+
}
|
87 |
+
els.custom.value = this.value;
|
88 |
+
});
|
89 |
+
}
|
90 |
+
// Label element.
|
91 |
+
els.label = els.base.querySelector( cfg.selectors.label );
|
92 |
+
if ( els.label ) {
|
93 |
+
// Attributes.
|
94 |
+
els.label.setAttribute( 'for', els.input.getAttribute( 'id' ) );
|
95 |
+
// Text.
|
96 |
+
// Fallback: Set text for label element directly.
|
97 |
+
let labelNode = els.label;
|
98 |
+
// Update text node within label element.
|
99 |
+
if ( els.label.childNodes.length > 1 ) {
|
100 |
+
// Get first text node.
|
101 |
+
let childNode = Array.from( els.label.childNodes ).find( cnode => cnode.nodeType === Node.TEXT_NODE );
|
102 |
+
if ( childNode ) {
|
103 |
+
labelNode = childNode;
|
104 |
+
}
|
105 |
}
|
106 |
+
labelNode.textContent = labelNode.textContent.replace(/^(\s*)(\S.+?)(\s*)$/i, `\$1${opts.label}\$3`);
|
107 |
+
}
|
108 |
+
// Example permalink element.
|
109 |
+
els.example = els.base.querySelector( cfg.selectors.example );
|
110 |
+
if ( els.example ) {
|
111 |
+
els.example.setAttribute( 'id', cfg.values.example_id );
|
112 |
+
els.example.textContent = opts.example;
|
|
|
113 |
}
|
114 |
+
// Insert option into list.
|
115 |
+
els.template.parentNode.insertBefore( els.base, els.template );
|
116 |
}
|
117 |
+
}
|
118 |
});
|
119 |
|
120 |
if ( CNR.structure.admin.manage_option )
|
load.php
CHANGED
@@ -1,65 +1,65 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/* Constants */
|
4 |
-
|
5 |
-
if ( !defined('CNR_DEV') ) {
|
6 |
-
define('CNR_DEV', ( isset( $_REQUEST['cnr_dev'] ) && !!$_REQUEST['cnr_dev'] ) );
|
7 |
-
}
|
8 |
-
|
9 |
-
/* Class Management */
|
10 |
-
|
11 |
-
/**
|
12 |
-
* Class loading handler
|
13 |
-
* @param string $classname Class to load
|
14 |
-
*/
|
15 |
-
function cnr_autoload($classname) {
|
16 |
-
$prefix = 'cnr_';
|
17 |
-
$cls = strtolower($classname);
|
18 |
-
//Remove prefix
|
19 |
-
if ( 0 !== strpos($cls, $prefix) ) {
|
20 |
-
return false;
|
21 |
-
}
|
22 |
-
//Format class for filename
|
23 |
-
$fn = 'class.' . substr($cls, strlen($prefix)) . '.php';
|
24 |
-
//Build path
|
25 |
-
$path = dirname(__FILE__) . '/' . "includes/" . $fn;
|
26 |
-
//Load file
|
27 |
-
if ( is_readable($path) ) {
|
28 |
-
require $path;
|
29 |
-
}
|
30 |
-
}
|
31 |
-
|
32 |
-
// Register autoloader
|
33 |
-
spl_autoload_register('cnr_autoload');
|
34 |
-
|
35 |
-
/* Load Assets */
|
36 |
-
$path = dirname(__FILE__) . '/';
|
37 |
-
require_once $path . 'controller.php';
|
38 |
-
require_once $path . 'functions.php';
|
39 |
-
|
40 |
-
/* Variables */
|
41 |
-
|
42 |
-
//Global content type variables
|
43 |
-
if ( !isset($GLOBALS['cnr_content_types']) )
|
44 |
-
$GLOBALS['cnr_content_types'] = array();
|
45 |
-
if ( !isset($GLOBALS['cnr_field_types']) )
|
46 |
-
$GLOBALS['cnr_field_types'] = array();
|
47 |
-
|
48 |
-
/* Init */
|
49 |
-
$GLOBALS['cnr_content_utilities'] = new CNR_Content_Utilities();
|
50 |
-
$GLOBALS['cnr_content_utilities']->init();
|
51 |
-
|
52 |
-
/* Hooks */
|
53 |
-
|
54 |
-
// Register Default placeholder handlers
|
55 |
-
cnr_register_placeholder_handler('all', array('CNR_Field_Type', 'process_placeholder_default'), 11);
|
56 |
-
cnr_register_placeholder_handler('field_id', array('CNR_Field_Type', 'process_placeholder_id'));
|
57 |
-
cnr_register_placeholder_handler('field_name', array('CNR_Field_Type', 'process_placeholder_name'));
|
58 |
-
cnr_register_placeholder_handler('data', array('CNR_Field_Type', 'process_placeholder_data'));
|
59 |
-
cnr_register_placeholder_handler('loop', array('CNR_Field_Type', 'process_placeholder_loop'));
|
60 |
-
cnr_register_placeholder_handler('data_ext', array('CNR_Field_Type', 'process_placeholder_data_ext'));
|
61 |
-
cnr_register_placeholder_handler('rich_editor', array('CNR_Field_Type', 'process_placeholder_rich_editor'));
|
62 |
-
|
63 |
-
/* Start */
|
64 |
-
|
65 |
$GLOBALS['cnr'] = new Cornerstone();
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/* Constants */
|
4 |
+
|
5 |
+
if ( !defined('CNR_DEV') ) {
|
6 |
+
define('CNR_DEV', ( isset( $_REQUEST['cnr_dev'] ) && !!$_REQUEST['cnr_dev'] ) );
|
7 |
+
}
|
8 |
+
|
9 |
+
/* Class Management */
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Class loading handler
|
13 |
+
* @param string $classname Class to load
|
14 |
+
*/
|
15 |
+
function cnr_autoload($classname) {
|
16 |
+
$prefix = 'cnr_';
|
17 |
+
$cls = strtolower($classname);
|
18 |
+
//Remove prefix
|
19 |
+
if ( 0 !== strpos($cls, $prefix) ) {
|
20 |
+
return false;
|
21 |
+
}
|
22 |
+
//Format class for filename
|
23 |
+
$fn = 'class.' . substr($cls, strlen($prefix)) . '.php';
|
24 |
+
//Build path
|
25 |
+
$path = dirname(__FILE__) . '/' . "includes/" . $fn;
|
26 |
+
//Load file
|
27 |
+
if ( is_readable($path) ) {
|
28 |
+
require $path;
|
29 |
+
}
|
30 |
+
}
|
31 |
+
|
32 |
+
// Register autoloader
|
33 |
+
spl_autoload_register('cnr_autoload');
|
34 |
+
|
35 |
+
/* Load Assets */
|
36 |
+
$path = dirname(__FILE__) . '/';
|
37 |
+
require_once $path . 'controller.php';
|
38 |
+
require_once $path . 'functions.php';
|
39 |
+
|
40 |
+
/* Variables */
|
41 |
+
|
42 |
+
//Global content type variables
|
43 |
+
if ( !isset($GLOBALS['cnr_content_types']) )
|
44 |
+
$GLOBALS['cnr_content_types'] = array();
|
45 |
+
if ( !isset($GLOBALS['cnr_field_types']) )
|
46 |
+
$GLOBALS['cnr_field_types'] = array();
|
47 |
+
|
48 |
+
/* Init */
|
49 |
+
$GLOBALS['cnr_content_utilities'] = new CNR_Content_Utilities();
|
50 |
+
$GLOBALS['cnr_content_utilities']->init();
|
51 |
+
|
52 |
+
/* Hooks */
|
53 |
+
|
54 |
+
// Register Default placeholder handlers
|
55 |
+
cnr_register_placeholder_handler('all', array('CNR_Field_Type', 'process_placeholder_default'), 11);
|
56 |
+
cnr_register_placeholder_handler('field_id', array('CNR_Field_Type', 'process_placeholder_id'));
|
57 |
+
cnr_register_placeholder_handler('field_name', array('CNR_Field_Type', 'process_placeholder_name'));
|
58 |
+
cnr_register_placeholder_handler('data', array('CNR_Field_Type', 'process_placeholder_data'));
|
59 |
+
cnr_register_placeholder_handler('loop', array('CNR_Field_Type', 'process_placeholder_loop'));
|
60 |
+
cnr_register_placeholder_handler('data_ext', array('CNR_Field_Type', 'process_placeholder_data_ext'));
|
61 |
+
cnr_register_placeholder_handler('rich_editor', array('CNR_Field_Type', 'process_placeholder_rich_editor'));
|
62 |
+
|
63 |
+
/* Start */
|
64 |
+
|
65 |
$GLOBALS['cnr'] = new Cornerstone();
|
main.php
CHANGED
@@ -4,17 +4,17 @@
|
|
4 |
*
|
5 |
* @package Cornerstone
|
6 |
* @author Archetyped <support@archetyped.com>
|
7 |
-
* @copyright
|
8 |
*
|
9 |
* Plugin Name: Cornerstone
|
10 |
* Plugin URI: http://archetyped.com/tools/cornerstone/
|
11 |
* Description: Enhanced content management for WordPress
|
12 |
-
* Version: 0.
|
13 |
* Requires at least: 5.3
|
14 |
* Text Domain: cornerstone
|
15 |
* Author: Archetyped
|
16 |
* Author URI: http://archetyped.com
|
17 |
-
* Support URI: https://github.com/archetyped/cornerstone/wiki/
|
18 |
*/
|
19 |
|
20 |
$cnr = null;
|
4 |
*
|
5 |
* @package Cornerstone
|
6 |
* @author Archetyped <support@archetyped.com>
|
7 |
+
* @copyright 2022 Archetyped
|
8 |
*
|
9 |
* Plugin Name: Cornerstone
|
10 |
* Plugin URI: http://archetyped.com/tools/cornerstone/
|
11 |
* Description: Enhanced content management for WordPress
|
12 |
+
* Version: 0.8.0
|
13 |
* Requires at least: 5.3
|
14 |
* Text Domain: cornerstone
|
15 |
* Author: Archetyped
|
16 |
* Author URI: http://archetyped.com
|
17 |
+
* Support URI: https://github.com/archetyped/cornerstone/wiki/Support-&-Feedback
|
18 |
*/
|
19 |
|
20 |
$cnr = null;
|
package.json
CHANGED
@@ -1,20 +1,20 @@
|
|
1 |
-
{
|
2 |
-
"name": "cornerstone",
|
3 |
-
"version": "0.
|
4 |
-
"title": "Cornerstone",
|
5 |
-
"description": "Enhanced content management for WordPress",
|
6 |
-
"author": "Archetyped <support@archetyped.com>",
|
7 |
-
"license": "GPLv2",
|
8 |
-
"private": true,
|
9 |
-
"devDependencies": {
|
10 |
-
"grunt": "^0.4.5",
|
11 |
-
"grunt-contrib-jshint": "^0.11.2",
|
12 |
-
"grunt-contrib-uglify": "^0.9.1",
|
13 |
-
"grunt-contrib-watch": "^0.6.1",
|
14 |
-
"grunt-phplint": "0.0.5",
|
15 |
-
"grunt-sass": "^1.0.0",
|
16 |
-
"jshint-stylish": "^2.0.1",
|
17 |
-
"load-grunt-tasks": "^3.2.0",
|
18 |
-
"time-grunt": "^1.2.1"
|
19 |
-
}
|
20 |
-
}
|
1 |
+
{
|
2 |
+
"name": "cornerstone",
|
3 |
+
"version": "0.8.0",
|
4 |
+
"title": "Cornerstone",
|
5 |
+
"description": "Enhanced content management for WordPress",
|
6 |
+
"author": "Archetyped <support@archetyped.com>",
|
7 |
+
"license": "GPLv2",
|
8 |
+
"private": true,
|
9 |
+
"devDependencies": {
|
10 |
+
"grunt": "^0.4.5",
|
11 |
+
"grunt-contrib-jshint": "^0.11.2",
|
12 |
+
"grunt-contrib-uglify": "^0.9.1",
|
13 |
+
"grunt-contrib-watch": "^0.6.1",
|
14 |
+
"grunt-phplint": "0.0.5",
|
15 |
+
"grunt-sass": "^1.0.0",
|
16 |
+
"jshint-stylish": "^2.0.1",
|
17 |
+
"load-grunt-tasks": "^3.2.0",
|
18 |
+
"time-grunt": "^1.2.1"
|
19 |
+
}
|
20 |
+
}
|
readme.txt
CHANGED
@@ -1,137 +1,141 @@
|
|
1 |
-
=== Cornerstone ===
|
2 |
-
Contributors: Archetyped
|
3 |
-
Donate: https://gum.co/cnr-donate
|
4 |
-
Tags: cornerstone, cms, content, management, system, structure, organization, sections
|
5 |
-
Plugin Link: http://archetyped.com/tools/cornerstone/
|
6 |
-
Requires at least: 5.3
|
7 |
-
Tested up to:
|
8 |
-
Stable tag: trunk
|
9 |
-
|
10 |
-
Enhanced content management for WordPress
|
11 |
-
|
12 |
-
== Description ==
|
13 |
-
Cornerstone makes WordPress practical for **any type of site** by enhancing its content management capabilities. Too long have we had to resort to hacks like using categories in menus to build a pseudo site structure (you know what I'm talking about).
|
14 |
-
|
15 |
-
Cornerstone enhances your WordPress site in several ways. One of the most useful features is one that allows WordPress to be used for sites that go beyond just blogging-- **Posts in Sections**. Create a section, add posts to it, they show up when visitors navigate to that section. Simple as that. It really is, but because you're awesome [here's a tutorial on how to do it](http://archetyped.com/know/how-to-organize-posts-in-sections-in-wordpress-with-cornerstone/).
|
16 |
-
|
17 |
-
### Thanks for the Support!
|
18 |
-
|
19 |
-
The support from the users that love Cornerstone is huge. You can support Cornerstone's future development and help to make it even better by donating or even just by sending me a nice message :)
|
20 |
-
|
21 |
-
[Donate to Cornerstone](https://gum.co/cnr-donate)
|
22 |
-
|
23 |
-
### Features
|
24 |
-
|
25 |
-
* ''NEW: Content Types'' - Add custom fields to default (posts, pages, etc.) and custom (events, properties, etc.) post types
|
26 |
-
* Posts in Sections (see above for more info. Why are you reading from the bottom up?)
|
27 |
-
* Structured permalinks - post permalinks are based on the section they are in (e.g. ``/section-name/post-name/``)
|
28 |
-
* RSS for Sections - Let users subscribe to and receive updates for specific sections on the site.
|
29 |
-
|
30 |
-
### Next Up
|
31 |
-
|
32 |
-
* Template functionality - enhanced page titles, featured content, etc.
|
33 |
-
* And more, which is where your feedback comes in.
|
34 |
-
|
35 |
-
[Plugin home page](http://archetyped.com/tools/cornerstone/)
|
36 |
-
|
37 |
-
== Installation ==
|
38 |
-
|
39 |
-
Install and Activate the plugin from the WordPress.org Plugins Repository
|
40 |
-
|
41 |
-
#### Tutorials
|
42 |
-
|
43 |
-
* [How to Organize Posts in Sections with Cornerstone](http://archetyped.com/know/how-to-organize-posts-in-sections-in-wordpress-with-cornerstone/)
|
44 |
-
* [Cornerstone: Creating Custom Content Types in WordPress](http://archetyped.com/know/cornerstone-creating-custom-content-types-in-wordpress/)
|
45 |
-
* [Cornerstone: Using Custom Field Data in WordPress](http://archetyped.com/know/cornerstone-using-custom-field-data-in-wordpress/)
|
46 |
-
|
47 |
-
== Upgrade Notice ==
|
48 |
-
|
49 |
-
No upgrade notices
|
50 |
-
|
51 |
-
== Frequently Asked Questions ==
|
52 |
-
|
53 |
-
Post your questions/comments at [Cornerstone's official issue tracker](https://github.com/archetyped/cornerstone/wiki/
|
54 |
-
|
55 |
-
== Screenshots ==
|
56 |
-
|
57 |
-
1. Set a post's section on the post edit form
|
58 |
-
1. Manage posts by section on post management page
|
59 |
-
1. Quickly modify a post's section
|
60 |
-
|
61 |
-
== Changelog ==
|
62 |
-
|
63 |
-
= 0.
|
64 |
-
|
65 |
-
*
|
66 |
-
|
67 |
-
= 0.7.
|
68 |
-
|
69 |
-
* Fix:
|
70 |
-
|
71 |
-
= 0.7.
|
72 |
-
|
73 |
-
*
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
*
|
78 |
-
*
|
79 |
-
*
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
*
|
84 |
-
|
85 |
-
= 0.7.
|
86 |
-
|
87 |
-
*
|
88 |
-
|
89 |
-
= 0.7.
|
90 |
-
|
91 |
-
*
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
* Update: WordPress
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
*
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
* Update:
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
*
|
112 |
-
*
|
113 |
-
*
|
114 |
-
* Optimize:
|
115 |
-
* Optimize:
|
116 |
-
*
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
*
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
*
|
125 |
-
*
|
126 |
-
*
|
127 |
-
*
|
128 |
-
*
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
*
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
*
|
|
|
|
|
|
|
|
1 |
+
=== Cornerstone ===
|
2 |
+
Contributors: Archetyped
|
3 |
+
Donate: https://gum.co/cnr-donate
|
4 |
+
Tags: cornerstone, cms, content, management, system, structure, organization, sections
|
5 |
+
Plugin Link: http://archetyped.com/tools/cornerstone/
|
6 |
+
Requires at least: 5.3
|
7 |
+
Tested up to: 6.1
|
8 |
+
Stable tag: trunk
|
9 |
+
|
10 |
+
Enhanced content management for WordPress
|
11 |
+
|
12 |
+
== Description ==
|
13 |
+
Cornerstone makes WordPress practical for **any type of site** by enhancing its content management capabilities. Too long have we had to resort to hacks like using categories in menus to build a pseudo site structure (you know what I'm talking about).
|
14 |
+
|
15 |
+
Cornerstone enhances your WordPress site in several ways. One of the most useful features is one that allows WordPress to be used for sites that go beyond just blogging-- **Posts in Sections**. Create a section, add posts to it, they show up when visitors navigate to that section. Simple as that. It really is, but because you're awesome [here's a tutorial on how to do it](http://archetyped.com/know/how-to-organize-posts-in-sections-in-wordpress-with-cornerstone/).
|
16 |
+
|
17 |
+
### Thanks for the Support!
|
18 |
+
|
19 |
+
The support from the users that love Cornerstone is huge. You can support Cornerstone's future development and help to make it even better by donating or even just by sending me a nice message :)
|
20 |
+
|
21 |
+
[Donate to Cornerstone](https://gum.co/cnr-donate)
|
22 |
+
|
23 |
+
### Features
|
24 |
+
|
25 |
+
* ''NEW: Content Types'' - Add custom fields to default (posts, pages, etc.) and custom (events, properties, etc.) post types
|
26 |
+
* Posts in Sections (see above for more info. Why are you reading from the bottom up?)
|
27 |
+
* Structured permalinks - post permalinks are based on the section they are in (e.g. ``/section-name/post-name/``)
|
28 |
+
* RSS for Sections - Let users subscribe to and receive updates for specific sections on the site.
|
29 |
+
|
30 |
+
### Next Up
|
31 |
+
|
32 |
+
* Template functionality - enhanced page titles, featured content, etc.
|
33 |
+
* And more, which is where your feedback comes in.
|
34 |
+
|
35 |
+
[Plugin home page](http://archetyped.com/tools/cornerstone/)
|
36 |
+
|
37 |
+
== Installation ==
|
38 |
+
|
39 |
+
Install and Activate the plugin from the WordPress.org Plugins Repository
|
40 |
+
|
41 |
+
#### Tutorials
|
42 |
+
|
43 |
+
* [How to Organize Posts in Sections with Cornerstone](http://archetyped.com/know/how-to-organize-posts-in-sections-in-wordpress-with-cornerstone/)
|
44 |
+
* [Cornerstone: Creating Custom Content Types in WordPress](http://archetyped.com/know/cornerstone-creating-custom-content-types-in-wordpress/)
|
45 |
+
* [Cornerstone: Using Custom Field Data in WordPress](http://archetyped.com/know/cornerstone-using-custom-field-data-in-wordpress/)
|
46 |
+
|
47 |
+
== Upgrade Notice ==
|
48 |
+
|
49 |
+
No upgrade notices
|
50 |
+
|
51 |
+
== Frequently Asked Questions ==
|
52 |
+
|
53 |
+
Post your questions/comments at [Cornerstone's official issue tracker](https://github.com/archetyped/cornerstone/wiki/Support-&-Feedback).
|
54 |
+
|
55 |
+
== Screenshots ==
|
56 |
+
|
57 |
+
1. Set a post's section on the post edit form
|
58 |
+
1. Manage posts by section on post management page
|
59 |
+
1. Quickly modify a post's section
|
60 |
+
|
61 |
+
== Changelog ==
|
62 |
+
|
63 |
+
= 0.8.0 =
|
64 |
+
|
65 |
+
* Update: Custom permalink structure option integration for WordPress 6.1+.
|
66 |
+
|
67 |
+
= 0.7.8 =
|
68 |
+
|
69 |
+
* Fix: Utilities: `count()` used on non-Countable value. (props @nikelaos)
|
70 |
+
|
71 |
+
= 0.7.7 =
|
72 |
+
|
73 |
+
* Fix: Spelling.
|
74 |
+
|
75 |
+
= 0.7.6 =
|
76 |
+
|
77 |
+
* Add: Block editor support for Custom Post Types (CPTs).
|
78 |
+
* Update: WordPress 5.3 Compatibility confirmed.
|
79 |
+
* Update: WordPress minimum version requirement (5.3).
|
80 |
+
* Fix: Plugin initialization.
|
81 |
+
* Fix: Display/Save custom field data in post/page editor.
|
82 |
+
* Fix: Display/Save section in Quick Editor.
|
83 |
+
* Optimize: Code cleanup.
|
84 |
+
|
85 |
+
= 0.7.5 =
|
86 |
+
|
87 |
+
* Update: Use plugin-specific text domain for localized strings
|
88 |
+
|
89 |
+
= 0.7.4 =
|
90 |
+
|
91 |
+
* Add: Text Domain header for translations
|
92 |
+
|
93 |
+
= 0.7.3 =
|
94 |
+
|
95 |
+
* Fix: Registration of Media field types
|
96 |
+
* Update: Confirm compatibility with WordPress v4.3.1
|
97 |
+
|
98 |
+
= 0.7.2 =
|
99 |
+
|
100 |
+
* Update: WordPress 4.2.3 support
|
101 |
+
* Optimize: Autoload classes
|
102 |
+
* Optimize: Remove legacy PHP code
|
103 |
+
|
104 |
+
= 0.7.1 =
|
105 |
+
|
106 |
+
* Update: WordPress 3.8 support
|
107 |
+
* Update: Readme content (description, features, links)
|
108 |
+
|
109 |
+
= 0.7 =
|
110 |
+
|
111 |
+
* Update: WordPress 3.6 support
|
112 |
+
* Update: Remove deprecated jQuery code
|
113 |
+
* Update: Remove legacy shortcode code
|
114 |
+
* Optimize: Custom content types only added to main and CNR-initiated queries (Props Andy Owen)
|
115 |
+
* Optimize: Remove legacy files/code
|
116 |
+
* Optimize: Permalink structure reset when plugin activated/deactivated
|
117 |
+
* Optimize: Post Editor: Update permalink preview when section changed
|
118 |
+
* Optimize: Improved client-side file loading
|
119 |
+
* Optimize: Encapsulate client-side code
|
120 |
+
* Fix: Custom content types not displayed in sections (Unflappable Andy)
|
121 |
+
|
122 |
+
= 0.6 =
|
123 |
+
|
124 |
+
* Add: Content type support
|
125 |
+
* Add: Media support
|
126 |
+
* Add: Display tagline in home page title
|
127 |
+
* Optimize: Internal performance
|
128 |
+
* Optimize: Code cleanup
|
129 |
+
* Fix: Section not always saved
|
130 |
+
* Fix: Section posts fetched prior to checking posts' properties
|
131 |
+
* Fix: Pagination compatibility for WordPress 3.1+
|
132 |
+
* Fix: Various bug fixes
|
133 |
+
|
134 |
+
= 0.5.1b =
|
135 |
+
|
136 |
+
* Structure
|
137 |
+
* Fix: Section column duplication bug
|
138 |
+
|
139 |
+
= 0.5b =
|
140 |
+
|
141 |
+
* Public beta
|