Version Description
- Fix: Spelling.
Download this release
Release Info
Developer | Archetyped |
Plugin | Cornerstone |
Version | 0.7.7 |
Comparing to | |
See all releases |
Version 0.7.7
- COPYING +339 -0
- Gruntfile.js +49 -0
- README.md +12 -0
- controller.php +235 -0
- css/admin.css +159 -0
- functions.php +256 -0
- grunt/jshint.js +38 -0
- grunt/phplint.js +14 -0
- grunt/sass.js +35 -0
- grunt/uglify.js +21 -0
- grunt/watch.js +57 -0
- images/page_delete.gif +0 -0
- includes/class.base.php +364 -0
- includes/class.content_base.php +615 -0
- includes/class.content_type.php +402 -0
- includes/class.content_utilities.php +1248 -0
- includes/class.feeds.php +214 -0
- includes/class.field.php +4 -0
- includes/class.field_type.php +698 -0
- includes/class.media.php +636 -0
- includes/class.post.php +248 -0
- includes/class.post_query.php +439 -0
- includes/class.structure.php +815 -0
- includes/class.utilities.php +1416 -0
- js/lib.admin.edit_form.js +5 -0
- js/lib.admin.js +11 -0
- js/lib.core.js +190 -0
- js/lib.media.js +125 -0
- js/lib.posts.inline_edit.js +84 -0
- js/lib.posts.js +63 -0
- js/lib.structure.admin.js +49 -0
- js/lib.structure.js +71 -0
- load.php +65 -0
- main.php +29 -0
- package.json +20 -0
- readme.txt +133 -0
- screenshot-1.png +0 -0
- screenshot-2.png +0 -0
- screenshot-3.png +0 -0
COPYING
ADDED
@@ -0,0 +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.
|
Gruntfile.js
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
ADDED
@@ -0,0 +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/Reporting-Issues "Report an issue"
|
12 |
+
[home]: http://archetyped.com/tools/cornerstone/ "Cornerstone home page"
|
controller.php
ADDED
@@ -0,0 +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 |
+
}
|
css/admin.css
ADDED
@@ -0,0 +1,159 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.site-pages .actions {
|
2 |
+
clear: both;
|
3 |
+
}
|
4 |
+
|
5 |
+
.group-pages, .list-pages {
|
6 |
+
padding-left: 1em;
|
7 |
+
margin-top: 1em;
|
8 |
+
width: 20em;
|
9 |
+
float: left;
|
10 |
+
margin-right: 5em;
|
11 |
+
}
|
12 |
+
|
13 |
+
.ph {
|
14 |
+
background: #FFE1E2;
|
15 |
+
}
|
16 |
+
|
17 |
+
.item-page, .page_item a {
|
18 |
+
border: 1px solid #bbb;
|
19 |
+
background: #eee;
|
20 |
+
padding: .5em;
|
21 |
+
width: 15em;
|
22 |
+
display: block;
|
23 |
+
margin: .5em 0;
|
24 |
+
}
|
25 |
+
|
26 |
+
.site-pages li ul {
|
27 |
+
padding: 0 1.5em;
|
28 |
+
}
|
29 |
+
|
30 |
+
.group-pages {
|
31 |
+
min-height: 5em;
|
32 |
+
}
|
33 |
+
|
34 |
+
ul.empty {
|
35 |
+
border: 1px dashed #ccc;
|
36 |
+
}
|
37 |
+
|
38 |
+
.group-pages .empty {
|
39 |
+
border: 0;
|
40 |
+
background: none;
|
41 |
+
|
42 |
+
}
|
43 |
+
|
44 |
+
.item-page,
|
45 |
+
.item-page a {
|
46 |
+
color: #333;
|
47 |
+
}
|
48 |
+
|
49 |
+
.item-page {
|
50 |
+
position: relative;
|
51 |
+
}
|
52 |
+
|
53 |
+
.js .page-group .actions-commit {
|
54 |
+
display: none;
|
55 |
+
}
|
56 |
+
|
57 |
+
.item-page .page-delete {
|
58 |
+
position: absolute;
|
59 |
+
right: 10px;
|
60 |
+
top: 30%;
|
61 |
+
text-indent: -2000em;
|
62 |
+
cursor: pointer;
|
63 |
+
height: 11px;
|
64 |
+
width: 11px;
|
65 |
+
background: url("images/page_delete.gif") top left no-repeat;
|
66 |
+
opacity: .5;
|
67 |
+
}
|
68 |
+
|
69 |
+
.item-page .page-delete.on {
|
70 |
+
opacity: 1;
|
71 |
+
}
|
72 |
+
|
73 |
+
.page-group .sorting {
|
74 |
+
|
75 |
+
}
|
76 |
+
|
77 |
+
.checking {
|
78 |
+
border: 1px solid green;
|
79 |
+
background-color: #E6FFE6;
|
80 |
+
}
|
81 |
+
|
82 |
+
.removing {
|
83 |
+
background-color: #FFCECE;
|
84 |
+
opacity: 0;
|
85 |
+
}
|
86 |
+
|
87 |
+
/* Post/Page Edit Form */
|
88 |
+
|
89 |
+
#quicktags #cnr_inturl {
|
90 |
+
color: #00f;
|
91 |
+
text-decoration: underline;
|
92 |
+
}
|
93 |
+
|
94 |
+
.buttons {
|
95 |
+
padding: 6px;
|
96 |
+
}
|
97 |
+
|
98 |
+
.options-default,
|
99 |
+
.confirmation-default {
|
100 |
+
visibility: hidden;
|
101 |
+
}
|
102 |
+
|
103 |
+
/* Fields */
|
104 |
+
|
105 |
+
.rt_container {
|
106 |
+
border: 1px solid #dfdfdf;
|
107 |
+
}
|
108 |
+
|
109 |
+
.rt_container textarea {
|
110 |
+
border: 0;
|
111 |
+
}
|
112 |
+
|
113 |
+
/* Management Page */
|
114 |
+
|
115 |
+
.tablenav #cnr_section {
|
116 |
+
width: 150px;
|
117 |
+
}
|
118 |
+
|
119 |
+
/* Content Types */
|
120 |
+
|
121 |
+
.cnr_group_wrap {
|
122 |
+
overflow: hidden;
|
123 |
+
}
|
124 |
+
|
125 |
+
.cnr_attribute_wrap {
|
126 |
+
margin: 1em;
|
127 |
+
display: block;
|
128 |
+
}
|
129 |
+
|
130 |
+
.cnr_attribute_wrap .actions {
|
131 |
+
margin-top: 1em;
|
132 |
+
}
|
133 |
+
|
134 |
+
.cnr_attribute_wrap .media_frame,
|
135 |
+
.cnr_attribute_wrap .media_link {
|
136 |
+
clear: both;
|
137 |
+
display: block;
|
138 |
+
}
|
139 |
+
|
140 |
+
.cnr_attribute_wrap .media_frame {
|
141 |
+
margin-bottom: 1em;
|
142 |
+
border: 1px solid #F5F5F5;
|
143 |
+
}
|
144 |
+
|
145 |
+
/* Single Field Groups */
|
146 |
+
|
147 |
+
.cnr_group_wrap .single_field textarea,
|
148 |
+
.cnr_group_wrap .single_field input[type=text] {
|
149 |
+
width: 98%;
|
150 |
+
}
|
151 |
+
|
152 |
+
.cnr_group_wrap .single_field .group_field_title label,
|
153 |
+
.cnr_group_wrap .single_field_has_elements legend {
|
154 |
+
display: none;
|
155 |
+
}
|
156 |
+
|
157 |
+
.cnr_pages_dropdown select {
|
158 |
+
width: 100%;
|
159 |
+
}
|
functions.php
ADDED
@@ -0,0 +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 |
+
}
|
grunt/jshint.js
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
};
|
images/page_delete.gif
ADDED
Binary file
|
includes/class.base.php
ADDED
@@ -0,0 +1,364 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package Cornerstone
|
5 |
+
* @subpackage Base
|
6 |
+
* @author Archetyped
|
7 |
+
*
|
8 |
+
*/
|
9 |
+
class CNR_Base {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Variable name of base object in global scope
|
13 |
+
* @var string
|
14 |
+
*/
|
15 |
+
var $base = 'cnr';
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Prefix for plugin-related data (attributes, DB tables, etc.)
|
19 |
+
* @var string
|
20 |
+
*/
|
21 |
+
var $prefix = 'cnr';
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Client files
|
25 |
+
* @var array
|
26 |
+
* Structure
|
27 |
+
* > Key: unique file ID
|
28 |
+
* > Properties
|
29 |
+
* > file (string) File path (Relative to plugin base)
|
30 |
+
* > deps (array) Script dependencies
|
31 |
+
* > Internal dependencies are wrapped in square brackets ([])
|
32 |
+
* > context (string|array)
|
33 |
+
* > Context in which the script should be included
|
34 |
+
* > in_footer (bool) optional [Default: FALSE]
|
35 |
+
* > If TRUE, file will be included in footer of page, otherwise it will be included in the header
|
36 |
+
*
|
37 |
+
* Array is processed and converted to an object on init
|
38 |
+
*/
|
39 |
+
var $client_files = array(
|
40 |
+
'scripts' => array(),
|
41 |
+
'styles' => array()
|
42 |
+
);
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Utilities
|
46 |
+
* @var CNR_Utilities
|
47 |
+
*/
|
48 |
+
var $util = null;
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Options
|
52 |
+
* @var CNR_Options
|
53 |
+
*/
|
54 |
+
var $options = null;
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Constructor
|
58 |
+
*/
|
59 |
+
function __construct() {
|
60 |
+
$this->util = new CNR_Utilities($this);
|
61 |
+
}
|
62 |
+
|
63 |
+
/*-** Init **-*/
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Default initialization method
|
67 |
+
* To be overridden by child classes
|
68 |
+
* @uses this::init_options()
|
69 |
+
* @uses this::init_client_files()
|
70 |
+
* @uses this::register_hooks()
|
71 |
+
* @uses this::init_env()
|
72 |
+
* @uses add_action()
|
73 |
+
*/
|
74 |
+
function init() {
|
75 |
+
if ( !isset($this) )
|
76 |
+
return false;
|
77 |
+
|
78 |
+
//Options
|
79 |
+
$this->init_options();
|
80 |
+
add_action('admin_init', $this->m('init_options_text'));
|
81 |
+
|
82 |
+
/* Client files */
|
83 |
+
$this->init_client_files();
|
84 |
+
|
85 |
+
/* Hooks */
|
86 |
+
$this->register_hooks();
|
87 |
+
|
88 |
+
/* Environment */
|
89 |
+
$hook = 'init';
|
90 |
+
if ( did_action( $hook ) ) {
|
91 |
+
$this->init_env();
|
92 |
+
} else {
|
93 |
+
add_action( $hook, $this->m('init_env'), 1 );
|
94 |
+
}
|
95 |
+
}
|
96 |
+
|
97 |
+
function register_hooks() {
|
98 |
+
//Activation
|
99 |
+
$func_activate = 'activate';
|
100 |
+
if ( method_exists($this, $func_activate) )
|
101 |
+
register_activation_hook($this->util->get_plugin_base_file(), $this->m($func_activate));
|
102 |
+
//Deactivation
|
103 |
+
$func_deactivate = 'deactivate';
|
104 |
+
if ( method_exists($this, $func_deactivate) )
|
105 |
+
register_deactivation_hook($this->util->get_plugin_base_file(), $this->m($func_deactivate));
|
106 |
+
}
|
107 |
+
|
108 |
+
/**
|
109 |
+
* Checks if options are valid
|
110 |
+
* > Validates:
|
111 |
+
* > Option data
|
112 |
+
* > Option class existence
|
113 |
+
* > Optional: Options instance variable (Default: yes)
|
114 |
+
* @param array $data Data to be used on options
|
115 |
+
* @return bool TRUE if options are valid, FALSE otherwise
|
116 |
+
*/
|
117 |
+
function is_options_valid($data, $check_var = true) {
|
118 |
+
$class = $this->get_options_class();
|
119 |
+
$ret = ( empty($data) || !is_array($data) || !class_exists($class) ) ? false : true;
|
120 |
+
if ( $ret && $check_var && !is_a($this->options, $class) )
|
121 |
+
$ret = false;
|
122 |
+
return $ret;
|
123 |
+
}
|
124 |
+
|
125 |
+
/**
|
126 |
+
* Retrieves options class name
|
127 |
+
* @return string
|
128 |
+
*/
|
129 |
+
function get_options_class() {
|
130 |
+
static $class = null;
|
131 |
+
if ( is_null($class) )
|
132 |
+
$class = $this->add_prefix_uc('Options');
|
133 |
+
return $class;
|
134 |
+
}
|
135 |
+
|
136 |
+
/**
|
137 |
+
* Initialize options
|
138 |
+
* To be called by child class
|
139 |
+
*/
|
140 |
+
function init_options($options_config = null) {
|
141 |
+
if ( !$this->is_options_valid($options_config, false) )
|
142 |
+
return false;
|
143 |
+
$class = $this->get_options_class();
|
144 |
+
$this->options = new $class($options_config);
|
145 |
+
}
|
146 |
+
|
147 |
+
/**
|
148 |
+
* Initialize options text
|
149 |
+
* Must be called separately from standard options init because textdomain is not available until later
|
150 |
+
* To be called by method in child class
|
151 |
+
* @param array $opts_text Options passed by method in child class
|
152 |
+
* @return void
|
153 |
+
*/
|
154 |
+
function init_options_text($options_text = null) {
|
155 |
+
if ( !$this->is_options_valid($options_text) )
|
156 |
+
return false;
|
157 |
+
|
158 |
+
//Groups
|
159 |
+
if ( isset($options_text['groups']) ) {
|
160 |
+
foreach ( $options_text['groups'] as $id => $title) {
|
161 |
+
$g_temp =& $this->options->get_group($id);
|
162 |
+
$g_temp->title = $title;
|
163 |
+
}
|
164 |
+
}
|
165 |
+
|
166 |
+
//Options
|
167 |
+
if ( isset($options_text['items']) ) {
|
168 |
+
foreach ( $options_text['items'] as $opt => $title ) {
|
169 |
+
$option_temp =& $this->options->get($opt);
|
170 |
+
$option_temp->set_title($title);
|
171 |
+
}
|
172 |
+
}
|
173 |
+
}
|
174 |
+
|
175 |
+
/**
|
176 |
+
* Initialize environment (Localization, etc.)
|
177 |
+
* To be overriden by child class
|
178 |
+
* @uses `init` Action hook as trigger
|
179 |
+
*/
|
180 |
+
function init_env() {}
|
181 |
+
|
182 |
+
function init_client_files() {
|
183 |
+
foreach ( $this->client_files as $key => $val ) {
|
184 |
+
if ( empty($val) && isset($this->{$key}) )
|
185 |
+
$this->client_files[$key] =& $this->{$key};
|
186 |
+
$g =& $this->client_files[$key];
|
187 |
+
if ( is_array($g) && !empty($g) ) {
|
188 |
+
$g = $this->util->parse_client_files($g, $key);
|
189 |
+
}
|
190 |
+
}
|
191 |
+
|
192 |
+
//Register
|
193 |
+
add_action('init', $this->m('register_client_files'));
|
194 |
+
|
195 |
+
//Enqueue
|
196 |
+
$hook_enqueue = ( ( is_admin() ) ? 'admin' : 'wp' ) . '_enqueue_scripts' ;
|
197 |
+
add_action($hook_enqueue, $this->m('enqueue_client_files'));
|
198 |
+
}
|
199 |
+
|
200 |
+
/**
|
201 |
+
* Register client files
|
202 |
+
* @uses `init` Action hook for execution
|
203 |
+
* @return void
|
204 |
+
*/
|
205 |
+
function register_client_files() {
|
206 |
+
$v = $this->util->get_plugin_version();
|
207 |
+
foreach ( $this->client_files as $type => $files ) {
|
208 |
+
if ( !empty($files) ) {
|
209 |
+
$func = $this->get_client_files_handler($type, 'register');
|
210 |
+
if ( !$func )
|
211 |
+
continue;
|
212 |
+
foreach ( $files as $f ) {
|
213 |
+
$f->file = ( is_array($f->file) && is_callable($f->file) ) ? call_user_func($f->file) : $this->util->get_file_url($f->file);
|
214 |
+
$params = array($f->id, $f->file, $f->deps, $v);
|
215 |
+
switch ( $type ) {
|
216 |
+
case 'scripts':
|
217 |
+
$params[] = $f->in_footer;
|
218 |
+
break;
|
219 |
+
case 'styles':
|
220 |
+
$params[] = $f->media;
|
221 |
+
break;
|
222 |
+
}
|
223 |
+
call_user_func_array($func, $params);
|
224 |
+
}
|
225 |
+
}
|
226 |
+
}
|
227 |
+
}
|
228 |
+
|
229 |
+
/**
|
230 |
+
* Enqueues files for client output (scripts/styles)
|
231 |
+
* @uses `admin_enqueue_scripts` Action hook depending on context
|
232 |
+
* @uses `wp_enqueue_scripts` Action hook depending on context
|
233 |
+
* @return void
|
234 |
+
*/
|
235 |
+
function enqueue_client_files() {
|
236 |
+
//Enqueue files
|
237 |
+
foreach ( $this->client_files as $type => $files ) {
|
238 |
+
if ( !empty($files) ) {
|
239 |
+
$func = $this->get_client_files_handler($type, 'enqueue');
|
240 |
+
if ( !$func )
|
241 |
+
continue;
|
242 |
+
foreach ( $files as $f ) {
|
243 |
+
$load = true;
|
244 |
+
//Callback
|
245 |
+
if ( is_callable($f->callback) && !call_user_func($f->callback) )
|
246 |
+
$load = false;
|
247 |
+
|
248 |
+
//Context
|
249 |
+
if ( $load && !empty($f->context) && !$this->util->is_context($f->context) )
|
250 |
+
$load = false;
|
251 |
+
|
252 |
+
//Load valid file
|
253 |
+
if ( $load )
|
254 |
+
$func($f->id);
|
255 |
+
}
|
256 |
+
}
|
257 |
+
}
|
258 |
+
}
|
259 |
+
|
260 |
+
/**
|
261 |
+
* Build function name for handling client operations
|
262 |
+
*/
|
263 |
+
function get_client_files_handler($type, $action) {
|
264 |
+
$func = 'wp_' . $action . '_' . substr($type, 0, -1);
|
265 |
+
if ( !function_exists($func) )
|
266 |
+
$func = false;
|
267 |
+
return $func;
|
268 |
+
}
|
269 |
+
|
270 |
+
/*-** Reflection **-*/
|
271 |
+
|
272 |
+
/**
|
273 |
+
* Retrieve base object
|
274 |
+
* @return object|bool Base object (FALSE if object does not exist)
|
275 |
+
*/
|
276 |
+
function &get_base() {
|
277 |
+
$base = false;
|
278 |
+
if ( isset($GLOBALS[$this->base]) )
|
279 |
+
$base =& $GLOBALS[$this->base];
|
280 |
+
return $base;
|
281 |
+
}
|
282 |
+
|
283 |
+
/*-** Method/Function calling **-*/
|
284 |
+
|
285 |
+
/**
|
286 |
+
* Returns callback to instance method
|
287 |
+
* @param string $method Method name
|
288 |
+
* @return array Callback array
|
289 |
+
*/
|
290 |
+
function &m($method) {
|
291 |
+
return $this->util->m($this, $method);
|
292 |
+
}
|
293 |
+
|
294 |
+
/*-** Prefix **-*/
|
295 |
+
|
296 |
+
/**
|
297 |
+
* Retrieve class prefix (with separator if set)
|
298 |
+
* @param bool|string $sep Separator to append to class prefix (Default: no separator)
|
299 |
+
* @return string Class prefix
|
300 |
+
*/
|
301 |
+
function get_prefix($sep = null) {
|
302 |
+
$args = func_get_args();
|
303 |
+
return call_user_func_array($this->util->m($this->util, 'get_prefix'), $args);
|
304 |
+
}
|
305 |
+
|
306 |
+
/**
|
307 |
+
* Check if a string is prefixed
|
308 |
+
* @param string $text Text to check for prefix
|
309 |
+
* @param string $sep (optional) Separator used
|
310 |
+
*/
|
311 |
+
function has_prefix($text, $sep = null) {
|
312 |
+
$args = func_get_args();
|
313 |
+
return call_user_func_array($this->util->m($this->util, 'has_prefix'), $args);
|
314 |
+
}
|
315 |
+
|
316 |
+
/**
|
317 |
+
* Prepend plugin prefix to some text
|
318 |
+
* @param string $text Text to add to prefix
|
319 |
+
* @param string $sep (optional) Text used to separate prefix and text
|
320 |
+
* @param bool $once (optional) Whether to add prefix to text that already contains a prefix or not
|
321 |
+
* @return string Text with prefix prepended
|
322 |
+
*/
|
323 |
+
function add_prefix($text, $sep = null, $once = true) {
|
324 |
+
$args = func_get_args();
|
325 |
+
return call_user_func_array($this->util->m($this->util, 'add_prefix'), $args);
|
326 |
+
}
|
327 |
+
|
328 |
+
/**
|
329 |
+
* Prepend uppercased plugin prefix to some text
|
330 |
+
* @param string $text Text to add to prefix
|
331 |
+
* @param string $sep (optional) Text used to separate prefix and text
|
332 |
+
* @param bool $once (optional) Whether to add prefix to text that already contains a prefix or not
|
333 |
+
* @return string Text with prefix prepended
|
334 |
+
*/
|
335 |
+
function add_prefix_uc($text, $sep = null, $once = true) {
|
336 |
+
$args = func_get_args();
|
337 |
+
return call_user_func_array($this->util->m($this->util, 'add_prefix_uc'), $args);
|
338 |
+
}
|
339 |
+
|
340 |
+
/**
|
341 |
+
* Add prefix to variable reference
|
342 |
+
* Updates actual variable rather than return value
|
343 |
+
* @uses CNR_Utilities::add_prefix_ref();
|
344 |
+
* @param string $var Variable to add prefix to
|
345 |
+
* @param string $sep (optional) Separator text
|
346 |
+
* @param bool $once (optional) Add prefix only once
|
347 |
+
* @return void
|
348 |
+
*/
|
349 |
+
function add_prefix_ref(&$var, $sep = null, $once = true) {
|
350 |
+
$args = func_get_args();
|
351 |
+
$args[0] =& $var;
|
352 |
+
call_user_func_array($this->util->m($this->util, 'add_prefix_ref'), $args);
|
353 |
+
}
|
354 |
+
|
355 |
+
/**
|
356 |
+
* Remove prefix from specified string
|
357 |
+
* @param string $text String to remove prefix from
|
358 |
+
* @param string $sep (optional) Separator used with prefix
|
359 |
+
*/
|
360 |
+
function remove_prefix($text, $sep = null) {
|
361 |
+
$args = func_get_args();
|
362 |
+
return call_user_func_array($this->util->m($this->util, 'remove_prefix'), $args);
|
363 |
+
}
|
364 |
+
}
|
includes/class.content_base.php
ADDED
@@ -0,0 +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 |
+
}
|
includes/class.content_type.php
ADDED
@@ -0,0 +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 |
+
}
|
includes/class.content_utilities.php
ADDED
@@ -0,0 +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 |
+
}
|
includes/class.feeds.php
ADDED
@@ -0,0 +1,214 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Core properties/methods for feed management
|
5 |
+
* @package Cornerstone
|
6 |
+
* @subpackage Feeds
|
7 |
+
* @author Archetyped
|
8 |
+
* @uses CNR_Post
|
9 |
+
*/
|
10 |
+
class CNR_Feeds extends CNR_Base {
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Constructor
|
14 |
+
*/
|
15 |
+
function __construct() {
|
16 |
+
parent::__construct();
|
17 |
+
}
|
18 |
+
|
19 |
+
/* Methods */
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Registers plugin hooks
|
23 |
+
*/
|
24 |
+
function register_hooks() {
|
25 |
+
// add_action('template_redirect', $this->m('feed_redirect'));
|
26 |
+
// add_filter('get_wp_title_rss', $this->m('get_title'));
|
27 |
+
// add_filter('get_bloginfo_rss', $this->m('get_description'), 10, 2);
|
28 |
+
// add_filter('the_title_rss', $this->m('get_item_title'), 9);
|
29 |
+
add_filter('the_content_feed', $this->m('get_item_content'));
|
30 |
+
add_filter('the_excerpt_rss', $this->m('get_item_content'));
|
31 |
+
|
32 |
+
//Feed links (header)
|
33 |
+
/*
|
34 |
+
if ( $p = has_action('wp_head', 'feed_links_extra') )
|
35 |
+
remove_action('wp_head', 'feed_links_extra', $p);
|
36 |
+
add_action('wp_head', $this->m('feed_links_extra'));
|
37 |
+
*/
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Customize feed links (header) for sections
|
42 |
+
* @uses feed_links_extra() To generate additional feed links
|
43 |
+
*/
|
44 |
+
function feed_links_extra() {
|
45 |
+
$args = array();
|
46 |
+
if ( is_page() ) {
|
47 |
+
//Add custom feed title for section
|
48 |
+
$args['singletitle'] = __('%1$s %2$s %3$s Feed');
|
49 |
+
//Make sure feed is processed
|
50 |
+
$cb = create_function('', 'return true;');
|
51 |
+
$tag = 'pings_open';
|
52 |
+
$priority = 99;
|
53 |
+
add_filter($tag, $cb, $priority);
|
54 |
+
}
|
55 |
+
feed_links_extra($args);
|
56 |
+
|
57 |
+
//Remove filter after section feed link has been generated
|
58 |
+
if ( is_page() ) {
|
59 |
+
remove_filter($tag, $cb, $priority);
|
60 |
+
}
|
61 |
+
}
|
62 |
+
|
63 |
+
/**
|
64 |
+
* Customizes feed to use for certain requests
|
65 |
+
* Example: Pages (Sections) should load the normal feed template (instead of the comments feed template)
|
66 |
+
*/
|
67 |
+
function feed_redirect() {
|
68 |
+
if ( is_page() && is_feed() ) {
|
69 |
+
//Turn off comments feed for sections
|
70 |
+
global $wp_query;
|
71 |
+
$wp_query->is_comment_feed = false;
|
72 |
+
//Enable child content retrieval for section feeds
|
73 |
+
$m = $this->m('get_children');
|
74 |
+
$feed = get_query_var('feed');
|
75 |
+
if ('feed' == $feed)
|
76 |
+
$feed = get_default_feed();
|
77 |
+
$hook = ('rdf' == $feed) ? 'rdf_header' : $feed . "_head";
|
78 |
+
add_action($hook, $m);
|
79 |
+
}
|
80 |
+
}
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Retrieves a section's child content for output in a feed
|
84 |
+
*/
|
85 |
+
static function get_children() {
|
86 |
+
if ( is_page() && is_feed() ) {
|
87 |
+
global $wp_query;
|
88 |
+
//Get children of current page
|
89 |
+
$children =& CNR_Post::get_children();
|
90 |
+
|
91 |
+
//Set retrieved posts to global $wp_query object
|
92 |
+
$wp_query->posts = $children->posts;
|
93 |
+
$wp_query->current_post = $children->current;
|
94 |
+
$wp_query->post_count = $children->count;
|
95 |
+
}
|
96 |
+
}
|
97 |
+
|
98 |
+
/**
|
99 |
+
* Sets feed title for sections
|
100 |
+
* @param string $title Title passed from 'get_wp_title_rss' hook
|
101 |
+
* @return string Updated title
|
102 |
+
*/
|
103 |
+
function get_title($title) {
|
104 |
+
$sep = '›';
|
105 |
+
remove_filter('get_wp_title_rss', $this->m('get_title'));
|
106 |
+
$title = get_wp_title_rss($sep);
|
107 |
+
add_filter('get_wp_title_rss', $this->m('get_title'));
|
108 |
+
return $title;
|
109 |
+
}
|
110 |
+
|
111 |
+
/**
|
112 |
+
* Adds description to feed
|
113 |
+
* Specifically, adds feed descriptions for sections
|
114 |
+
*
|
115 |
+
* @param string $description Description passed from 'get_bloginfo_rss' hook
|
116 |
+
* @param string $show Specifies data to retrieve
|
117 |
+
* @see get_bloginfo() For the list of possible values to display.
|
118 |
+
* @return string Modified feed description
|
119 |
+
*/
|
120 |
+
function get_description($description, $show = '') {
|
121 |
+
global $post;
|
122 |
+
if ( is_feed() && is_page() && 'description' == $show && strlen($post->post_content) > 0 ) {
|
123 |
+
//Get section's own description (if exists)
|
124 |
+
$description = convert_chars($post->post_content);
|
125 |
+
}
|
126 |
+
return $description;
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* Sets title for feed items
|
131 |
+
* Specifies what section an item is in if the feed is not specifically for that section
|
132 |
+
*
|
133 |
+
* @param string $title Post title passed from 'the_title_rss' hook
|
134 |
+
* @return string Updated post title
|
135 |
+
*/
|
136 |
+
function get_item_title($title) {
|
137 |
+
if ( is_feed() && !is_page() ) {
|
138 |
+
//Get item's section
|
139 |
+
$section = CNR_Post::get_section(null, 'post_title');
|
140 |
+
$title = "$section › $title"; //Section precedes post title
|
141 |
+
}
|
142 |
+
return $title;
|
143 |
+
}
|
144 |
+
|
145 |
+
/**
|
146 |
+
* Modifies post content/excerpt for feed items
|
147 |
+
* @param string $content Post content
|
148 |
+
* @return string Updated post content
|
149 |
+
*/
|
150 |
+
function get_item_content($content = '') {
|
151 |
+
global $post;
|
152 |
+
|
153 |
+
//Skip processing in the following scenarios
|
154 |
+
// > Request is not feed
|
155 |
+
// > Current post requires a password
|
156 |
+
if ( !is_feed() || post_password_required() ) {
|
157 |
+
return $content;
|
158 |
+
}
|
159 |
+
|
160 |
+
//Add post thumbnail
|
161 |
+
if ( has_post_thumbnail() ) {
|
162 |
+
$content = get_the_post_thumbnail(null, 'large') . $content;
|
163 |
+
}
|
164 |
+
|
165 |
+
return $content;
|
166 |
+
}
|
167 |
+
|
168 |
+
/**
|
169 |
+
* Generates feed links based on current request
|
170 |
+
* @return string Feed links (HTML)
|
171 |
+
*/
|
172 |
+
function get_links() {
|
173 |
+
$text = array();
|
174 |
+
$links = array();
|
175 |
+
$link_template = '<a class="link_feed" rel="alternate" href="%1$s" title="%3$s">%2$s</a>';
|
176 |
+
//Page specific feeds
|
177 |
+
if ( is_page() || is_single() ) {
|
178 |
+
global $post, $wpdb;
|
179 |
+
$is_p = $wpdb->get_var("SELECT COUNT(*) FROM $wpdb->posts WHERE ID = $post->ID");
|
180 |
+
if ( $is_p ) {
|
181 |
+
$href = get_post_comments_feed_link($post->ID);
|
182 |
+
if ( is_page() )
|
183 |
+
$title = __('Subscribe to this Section');
|
184 |
+
else
|
185 |
+
$title = __('Subscribe to Comments');
|
186 |
+
$links[$href] = $title;
|
187 |
+
}
|
188 |
+
} elseif ( is_search() ) {
|
189 |
+
$links[get_search_feed_link()] = __('Subscribe to this Search');
|
190 |
+
} elseif ( is_tag() ) {
|
191 |
+
$links[get_tag_feed_link( get_query_var('tag_id') )] = __('Subscribe to this Tag');
|
192 |
+
} elseif ( is_category() ) {
|
193 |
+
$links[get_category_feed_link( get_query_var('cat') )] = __('Subscribe to this Topic');
|
194 |
+
}
|
195 |
+
|
196 |
+
//Sitewide feed
|
197 |
+
$title = ( !empty($links) ) ? __('Subscribe to All updates') : __('Subscribe to Updates');
|
198 |
+
$links[get_feed_link()] = $title;
|
199 |
+
foreach ($links as $href => $title) {
|
200 |
+
$text[] = sprintf($link_template, $href, $title, esc_attr($title));
|
201 |
+
}
|
202 |
+
$text = implode(' or ', $text);
|
203 |
+
return $text;
|
204 |
+
}
|
205 |
+
|
206 |
+
/**
|
207 |
+
* Outputs feed links based on current page
|
208 |
+
* @see get_links()
|
209 |
+
* @return void
|
210 |
+
*/
|
211 |
+
function the_links() {
|
212 |
+
echo $this->get_links();
|
213 |
+
}
|
214 |
+
}
|
includes/class.field.php
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
class CNR_Field extends CNR_Field_Type {
|
3 |
+
|
4 |
+
}
|
includes/class.field_type.php
ADDED
@@ -0,0 +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 |
+
}
|
includes/class.media.php
ADDED
@@ -0,0 +1,636 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Core properties/methods for Media management
|
5 |
+
* @package Cornerstone
|
6 |
+
* @subpackage Media
|
7 |
+
* @author Archetyped
|
8 |
+
*/
|
9 |
+
class CNR_Media extends CNR_Base {
|
10 |
+
|
11 |
+
var $var_field = 'field';
|
12 |
+
|
13 |
+
var $var_action = 'action';
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Constructor
|
17 |
+
*/
|
18 |
+
function __construct() {
|
19 |
+
parent::__construct();
|
20 |
+
$this->var_field = $this->add_prefix($this->var_field);
|
21 |
+
$this->var_action = $this->add_prefix($this->var_action);
|
22 |
+
}
|
23 |
+
|
24 |
+
/* Methods */
|
25 |
+
|
26 |
+
function register_hooks() {
|
27 |
+
parent::register_hooks();
|
28 |
+
|
29 |
+
//Register media placeholder handler
|
30 |
+
cnr_register_placeholder_handler('media', $this->m('content_type_process_placeholder_media'));
|
31 |
+
|
32 |
+
//Register field types
|
33 |
+
add_action('cnr_register_field_types', $this->m('register_field_types'));
|
34 |
+
|
35 |
+
//Register/Modify content types
|
36 |
+
// add_action('cnr_register_content_types', $this->m('register_content_types'));
|
37 |
+
|
38 |
+
//Register handler for custom media requests
|
39 |
+
add_action('media_upload_cnr_field_media', $this->m('field_upload_media'));
|
40 |
+
|
41 |
+
//Display 'Set as...' button in media item box
|
42 |
+
add_filter('attachment_fields_to_edit', $this->m('attachment_fields_to_edit'), 11, 2);
|
43 |
+
|
44 |
+
//Add form fields to upload form (to pass query vars along with form submission)
|
45 |
+
add_action('pre-html-upload-ui', $this->m('attachment_html_upload_ui'));
|
46 |
+
|
47 |
+
//Display additional meta data for media item (dimensions, etc.)
|
48 |
+
//add_filter('media_meta', $this->m('media_meta'), 10, 2);
|
49 |
+
|
50 |
+
//Modifies media upload query vars so that request is routed through plugin
|
51 |
+
add_filter('admin_url', $this->m('media_upload_url'), 10, 2);
|
52 |
+
|
53 |
+
//Adds admin menus for content types
|
54 |
+
add_action('cnr_admin_menu_type', $this->m('type_admin_menu'));
|
55 |
+
|
56 |
+
//Modify tabs in upload popup for fields
|
57 |
+
add_filter('media_upload_tabs', $this->m('field_upload_tabs'));
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Register media-specific field types
|
62 |
+
*/
|
63 |
+
function register_field_types($field_types) {
|
64 |
+
/**
|
65 |
+
* @var CNR_Content_Utilities
|
66 |
+
*/
|
67 |
+
global $cnr_content_utilities;
|
68 |
+
|
69 |
+
// Media (Base)
|
70 |
+
$media = new CNR_Field_Type('media', 'base_closed');
|
71 |
+
$media->set_description('Media Item');
|
72 |
+
$media->set_property('title', 'Select Media');
|
73 |
+
$media->set_property('button','Select Media');
|
74 |
+
$media->set_property('remove', 'Remove Media');
|
75 |
+
$media->set_property('set_as', 'media');
|
76 |
+
$media->set_layout('form', '{media}');
|
77 |
+
$media->set_layout('display', '{media format="display"}');
|
78 |
+
$media->set_layout('display_url', '{media format="display" type="url"}');
|
79 |
+
$media->add_script( array('add', 'edit-item', 'post-new.php', 'post.php', 'media-upload-popup'), $this->add_prefix('script_media'), $this->util->get_file_url('js/lib.media.js'), array($this->add_prefix('core'), $this->add_prefix('admin')));
|
80 |
+
$cnr_content_utilities->register_field($media);
|
81 |
+
|
82 |
+
// Image
|
83 |
+
$image = new CNR_Field_Type('image', 'media');
|
84 |
+
$image->set_description('Image');
|
85 |
+
$image->set_parent('media');
|
86 |
+
$image->set_property('title', 'Select Image');
|
87 |
+
$image->set_property('button', 'Select Image');
|
88 |
+
$image->set_property('remove', 'Remove Image');
|
89 |
+
$image->set_property('set_as', 'image');
|
90 |
+
$cnr_content_utilities->register_field($image);
|
91 |
+
}
|
92 |
+
|
93 |
+
/**
|
94 |
+
* Media placeholder handler
|
95 |
+
* @param string $ph_output Value to be used in place of placeholder
|
96 |
+
* @param CNR_Field $field Field containing placeholder
|
97 |
+
* @param array $placeholder Current placeholder @see CNR_Field::parse_layout for structure of $placeholder array
|
98 |
+
* @param string $layout Layout to build
|
99 |
+
* @param array $data Extended data for field (Default: null)
|
100 |
+
* @return string Value to use in place of current placeholder
|
101 |
+
*/
|
102 |
+
function content_type_process_placeholder_media($ph_output, $field, $placeholder, $layout, $data) {
|
103 |
+
global $post_ID, $temp_ID;
|
104 |
+
$attr_default = array('format' => 'form', 'type' => 'html', 'id' => '', 'class' => '');
|
105 |
+
$attr = wp_parse_args($placeholder['attributes'], $attr_default);
|
106 |
+
//Get media ID
|
107 |
+
$post_media = $field->get_data();
|
108 |
+
|
109 |
+
//Format output based on placeholder attribute
|
110 |
+
switch ( strtolower($attr['format']) ) {
|
111 |
+
case 'form':
|
112 |
+
$uploading_iframe_ID = (int) (0 == $post_ID ? $temp_ID : $post_ID);
|
113 |
+
$media_upload_iframe_src = "media-upload.php";
|
114 |
+
$media_id = $field->get_id(array('format' => true));
|
115 |
+
$media_name = $media_id;
|
116 |
+
$query = array (
|
117 |
+
'post_id' => $uploading_iframe_ID,
|
118 |
+
'type' => $this->add_prefix('field_media'),
|
119 |
+
$this->var_action => 'true',
|
120 |
+
$this->var_field => $media_id,
|
121 |
+
'cnr_set_as' => $field->get_property('set_as'),
|
122 |
+
'TB_iframe' => 'true'
|
123 |
+
);
|
124 |
+
$media_upload_iframe_src = apply_filters('image_upload_iframe_src', $media_upload_iframe_src . '?' . http_build_query($query));
|
125 |
+
|
126 |
+
//Get Attachment media URL
|
127 |
+
$post_media_valid = get_post($post_media);
|
128 |
+
$post_media_valid = ( isset($post_media_valid->post_type) && 'attachment' == $post_media_valid->post_type ) ? true : false;
|
129 |
+
|
130 |
+
//Start output
|
131 |
+
ob_start();
|
132 |
+
?>
|
133 |
+
<div id="<?php echo "$media_name-data"; ?>" class="media_data">
|
134 |
+
<?php
|
135 |
+
if ($post_media_valid) {
|
136 |
+
//Add media preview
|
137 |
+
?>
|
138 |
+
{media format="display" id="<?php echo "$media_name-frame"; ?>" class="media_frame"}
|
139 |
+
{media format="display" type="link" target="_blank" id="<?php echo "$media_name-link"; ?>" class="media_link"}
|
140 |
+
<input type="hidden" name="<?php echo "$media_name"?>" id="<?php echo "$media_name"?>" value="<?php echo $post_media ?>" />
|
141 |
+
<?php
|
142 |
+
}
|
143 |
+
?>
|
144 |
+
</div>
|
145 |
+
<?php
|
146 |
+
//Add media action options (upload, remove, etc.)
|
147 |
+
?>
|
148 |
+
<div class="actions buttons">
|
149 |
+
<a href="<?php echo "$media_upload_iframe_src" ?>" id="<?php echo "$media_name-lnk"?>" class="thickbox button" title="{title}" onclick="return false;">{button}</a>
|
150 |
+
<span id="<?php echo "$media_name-options"?>" class="options <?php if (!$post_media_valid) : ?> options-default <?php endif; ?>">
|
151 |
+
or <a href="#" title="Remove media" class="del-link" id="<?php echo "$media_name-option_remove"?>" onclick="CNR.media.doAction(this); return false;">{remove}</a>
|
152 |
+
<span id="<?php echo "$media_name-remove_confirmation"?>" class="confirmation remove-confirmation confirmation-default">Are you sure? <a href="#" id="<?php echo "$media_name-remove"?>" class="delete" onclick="return CNR.media.doAction(this);">Remove</a> or <a href="#" id="<?php echo "$media_name-remove_cancel"?>" onclick="return CNR.media.doAction(this);">Cancel</a></span>
|
153 |
+
</span>
|
154 |
+
</div>
|
155 |
+
<?php
|
156 |
+
//Output content
|
157 |
+
$ph_output = ob_get_clean();
|
158 |
+
break;
|
159 |
+
case 'display':
|
160 |
+
//Add placeholder attributes to attributes from function call
|
161 |
+
|
162 |
+
//Remove attributes used by system
|
163 |
+
$type = $attr['type'];
|
164 |
+
$attr_system = array('format', 'type');
|
165 |
+
foreach ( $attr_system as $key ) {
|
166 |
+
unset($attr[$key]);
|
167 |
+
}
|
168 |
+
$data = wp_parse_args($data, $attr);
|
169 |
+
$ph_output = $this->get_media_output($post_media, $type, $data);
|
170 |
+
break;
|
171 |
+
}
|
172 |
+
return $ph_output;
|
173 |
+
}
|
174 |
+
|
175 |
+
/**
|
176 |
+
* Handles upload of Post media on post edit form
|
177 |
+
* @return void
|
178 |
+
*/
|
179 |
+
function field_upload_media() {
|
180 |
+
$errors = array();
|
181 |
+
$id = 0;
|
182 |
+
|
183 |
+
//Process media selection
|
184 |
+
if ( isset($_POST['setmedia']) ) {
|
185 |
+
/* Send image data to main post edit form and close popup */
|
186 |
+
//Get Attachment ID
|
187 |
+
$field_var = $this->add_prefix('field');
|
188 |
+
$args = new stdClass();
|
189 |
+
$args->id = array_shift( array_keys($_POST['setmedia']) );
|
190 |
+
$args->field = '';
|
191 |
+
if ( isset($_REQUEST['attachments'][$args->id][$this->var_field]) )
|
192 |
+
$args->field = $_REQUEST['attachments'][$args->id][$this->var_field];
|
193 |
+
elseif ( isset($_REQUEST[$this->var_field]) )
|
194 |
+
$args->field = $_REQUEST[$this->var_field];
|
195 |
+
$a =& get_post($args->id);
|
196 |
+
if ( ! empty($a) ) {
|
197 |
+
$args->url = wp_get_attachment_url($a->ID);
|
198 |
+
$args->type = get_post_mime_type($a->ID);
|
199 |
+
$icon = !wp_attachment_is_image($a->ID);
|
200 |
+
$args->preview = wp_get_attachment_image_src($a->ID, '', $icon);
|
201 |
+
$args->preview = ( ! $args->preview ) ? '' : $args->preview[0];
|
202 |
+
}
|
203 |
+
//Update parent window (JS)
|
204 |
+
$js_out = "var win = window.dialogArguments || opener || parent || top; win.CNR.media.setPostMedia(" . json_encode($args) . ");";
|
205 |
+
echo $this->util->build_script_element($js_out, 'media_upload', false);
|
206 |
+
exit;
|
207 |
+
}
|
208 |
+
|
209 |
+
//Handle HTML upload
|
210 |
+
if ( isset($_POST['html-upload']) && !empty($_FILES) ) {
|
211 |
+
$id = media_handle_upload('async-upload', $_REQUEST['post_id']);
|
212 |
+
//Clear uploaded files
|
213 |
+
unset($_FILES);
|
214 |
+
if ( is_wp_error($id) ) {
|
215 |
+
$errors['upload_error'] = $id;
|
216 |
+
$id = false;
|
217 |
+
}
|
218 |
+
}
|
219 |
+
|
220 |
+
//Display default UI
|
221 |
+
|
222 |
+
//Determine media type
|
223 |
+
$type = ( isset($_REQUEST['type']) ) ? $_REQUEST['type'] : 'cnr_field_media';
|
224 |
+
//Determine UI to use (disk or URL upload)
|
225 |
+
$upload_form = ( isset($_GET['tab']) && 'type_url' == $_GET['tab'] ) ? 'media_upload_type_url_form' : 'media_upload_type_form';
|
226 |
+
//Load UI
|
227 |
+
return wp_iframe( $upload_form, $type, $errors, $id );
|
228 |
+
}
|
229 |
+
|
230 |
+
/**
|
231 |
+
* Modifies array of form fields to display on Attachment edit form
|
232 |
+
* Array items are in the form:
|
233 |
+
* 'key' => array(
|
234 |
+
* 'label' => "Label Text",
|
235 |
+
* 'value' => Value
|
236 |
+
* )
|
237 |
+
*
|
238 |
+
* @return array Form fields to display on Attachment edit form
|
239 |
+
* @param array $form_fields Associative array of Fields to display on form (@see get_attachment_fields_to_edit())
|
240 |
+
* @param object $attachment Attachment post object
|
241 |
+
*/
|
242 |
+
function attachment_fields_to_edit($form_fields, $attachment) {
|
243 |
+
|
244 |
+
if ( $this->is_custom_media() ) {
|
245 |
+
$post =& get_post($attachment);
|
246 |
+
//Clear all form fields
|
247 |
+
$form_fields = array();
|
248 |
+
//TODO Display custom buttons based on mime type defined in content type's field
|
249 |
+
$set_as = 'media';
|
250 |
+
$qvar = 'cnr_set_as';
|
251 |
+
//Get set as text from request
|
252 |
+
if ( isset($_REQUEST[$qvar]) && !empty($_REQUEST[$qvar]) )
|
253 |
+
$set_as = $_REQUEST[$qvar];
|
254 |
+
elseif ( ( strpos($_SERVER['PHP_SELF'], 'async-upload.php') !== false || isset($_POST['html-upload']) ) && ($ref = wp_get_referer()) && strpos($ref, $qvar) !== false && ($ref = parse_url($ref)) && isset($ref['query']) ) {
|
255 |
+
//Get set as text from referer (for async uploads)
|
256 |
+
$qs = array();
|
257 |
+
parse_str($ref['query'], $qs);
|
258 |
+
if ( isset($qs[$qvar]) )
|
259 |
+
$set_as = $qs[$qvar];
|
260 |
+
}
|
261 |
+
//Add "Set as Image" button to form fields array
|
262 |
+
$set_as = 'Set as ' . $set_as;
|
263 |
+
$field = array(
|
264 |
+
'label' => '',
|
265 |
+
'input' => 'html',
|
266 |
+
'html' => '<input type="submit" class="button" value="' . $set_as . '" name="setmedia[' . $post->ID . ']" />'
|
267 |
+
);
|
268 |
+
$form_fields['buttons'] = $field;
|
269 |
+
//Add field ID value as hidden field (if set)
|
270 |
+
if ( isset($_REQUEST[$this->var_field]) ) {
|
271 |
+
$field = array(
|
272 |
+
'input' => 'hidden',
|
273 |
+
'value' => $_REQUEST[$this->var_field]
|
274 |
+
);
|
275 |
+
$form_fields[$this->var_field] = $field;
|
276 |
+
}
|
277 |
+
}
|
278 |
+
return $form_fields;
|
279 |
+
}
|
280 |
+
|
281 |
+
/**
|
282 |
+
* Checks if value represents a valid media item
|
283 |
+
* @param int|object $media Attachment ID or Object to check
|
284 |
+
* @return bool TRUE if item is media, FALSE otherwise
|
285 |
+
*/
|
286 |
+
function is_media($media) {
|
287 |
+
$media =& get_post($media);
|
288 |
+
return ( ! empty($media) && 'attachment' == $media->post_type );
|
289 |
+
}
|
290 |
+
|
291 |
+
/**
|
292 |
+
* Checks whether current media upload/selection request is initiated by the plugin
|
293 |
+
*/
|
294 |
+
function is_custom_media() {
|
295 |
+
$ret = false;
|
296 |
+
$action = $this->var_action;
|
297 |
+
$upload = false;
|
298 |
+
if (isset($_REQUEST[$action]))
|
299 |
+
$ret = true;
|
300 |
+
elseif (isset($_SERVER['HTTP_REFERER']) ) {
|
301 |
+
$qs = array();
|
302 |
+
$ref = parse_url($_SERVER['HTTP_REFERER']);
|
303 |
+
if ( isset($ref['query']) )
|
304 |
+
parse_str($ref['query'], $qs);
|
305 |
+
if (array_key_exists($action, $qs))
|
306 |
+
$ret = true;
|
307 |
+
}
|
308 |
+
|
309 |
+
return $ret;
|
310 |
+
}
|
311 |
+
|
312 |
+
/**
|
313 |
+
* Add HTML Media upload form
|
314 |
+
* @return void
|
315 |
+
*/
|
316 |
+
function attachment_html_upload_ui() {
|
317 |
+
$vars = array ($this->var_action, $this->var_field);
|
318 |
+
foreach ( $vars as $var ) {
|
319 |
+
if ( isset($_REQUEST[$var]) )
|
320 |
+
echo '<input type="hidden" name="' . $var . '" id="' . $var . '" value="' . esc_attr($_REQUEST[$var]) . '" />';
|
321 |
+
}
|
322 |
+
}
|
323 |
+
|
324 |
+
/**
|
325 |
+
* Adds additional media meta data to media item display
|
326 |
+
* @param object $meta Meta data to display
|
327 |
+
* @param object $post Attachment post object
|
328 |
+
* @return string Meta data to display
|
329 |
+
*/
|
330 |
+
function media_meta($meta, $post) {
|
331 |
+
if ($this->is_custom_media() && wp_attachment_is_image($post->ID)) {
|
332 |
+
//Get attachment image info
|
333 |
+
$img = wp_get_attachment_image_src($post->ID, '');
|
334 |
+
if (is_array($img) && count($img) > 2) {
|
335 |
+
//Add image dimensions to output
|
336 |
+
$meta .= sprintf('<div>%dpx × %dpx</div>', $img[1], $img[2]);
|
337 |
+
}
|
338 |
+
}
|
339 |
+
return $meta;
|
340 |
+
}
|
341 |
+
|
342 |
+
/**
|
343 |
+
* Modifies media upload URL to work with CNR attachments
|
344 |
+
* @param string $url Full admin URL
|
345 |
+
* @param string $path Path part of URL
|
346 |
+
* @return string Modified media upload URL
|
347 |
+
*/
|
348 |
+
function media_upload_url($url, $path) {
|
349 |
+
if (strpos($path, 'media-upload.php') === 0) {
|
350 |
+
//Get query vars
|
351 |
+
$qs = parse_url($url);
|
352 |
+
$qs = ( isset($qs['query']) ) ? $qs['query'] : '';
|
353 |
+
$q = array();
|
354 |
+
parse_str($qs, $q);
|
355 |
+
//Check for tab variable
|
356 |
+
if (isset($q['tab'])) {
|
357 |
+
//Replace tab value
|
358 |
+
$q['cnr_tab'] = $q['tab'];
|
359 |
+
$q['tab'] = 'type';
|
360 |
+
}
|
361 |
+
//Rebuild query string
|
362 |
+
$qs_upd = build_query($q);
|
363 |
+
//Update query string on URL
|
364 |
+
$url = str_replace($qs, $qs_upd, $url);
|
365 |
+
}
|
366 |
+
return $url;
|
367 |
+
}
|
368 |
+
|
369 |
+
/*-** Field-Specific **-*/
|
370 |
+
|
371 |
+
/**
|
372 |
+
* Removes URL tab from media upload popup for fields
|
373 |
+
* Fields currently only support media stored @ website
|
374 |
+
* @param array $default_tabs Media upload tabs
|
375 |
+
* @see media_upload_tabs() for full $default_tabs array
|
376 |
+
* @return array Modified tabs array
|
377 |
+
*/
|
378 |
+
function field_upload_tabs($default_tabs) {
|
379 |
+
if ( $this->is_custom_media() )
|
380 |
+
unset($default_tabs['type_url']);
|
381 |
+
return $default_tabs;
|
382 |
+
}
|
383 |
+
|
384 |
+
/*-** Post Attachments **-*/
|
385 |
+
|
386 |
+
/**
|
387 |
+
* Retrieves matching attachments for post
|
388 |
+
* @param object|int $post Post object or Post ID (Default: current global post)
|
389 |
+
* @param array $args (optional) Associative array of query arguments
|
390 |
+
* @see get_posts() for query arguments
|
391 |
+
* @return array|bool Array of post attachments (FALSE on failure)
|
392 |
+
*/
|
393 |
+
function post_get_attachments($post = null, $args = '', $filter_special = true) {
|
394 |
+
if (!$this->util->check_post($post))
|
395 |
+
return false;
|
396 |
+
global $wpdb;
|
397 |
+
|
398 |
+
//Default arguments
|
399 |
+
$defaults = array(
|
400 |
+
'post_type' => 'attachment',
|
401 |
+
'post_parent' => (int) $post->ID,
|
402 |
+
'numberposts' => -1
|
403 |
+
);
|
404 |
+
|
405 |
+
$args = wp_parse_args($args, $defaults);
|
406 |
+
|
407 |
+
//Get attachments
|
408 |
+
$attachments = get_children($args);
|
409 |
+
|
410 |
+
//Filter special attachments
|
411 |
+
if ( $filter_special ) {
|
412 |
+
$start = '[';
|
413 |
+
$end = ']';
|
414 |
+
$removed = false;
|
415 |
+
foreach ( $attachments as $i => $a ) {
|
416 |
+
if ( $start == substr($a->post_title, 0, 1) && $end == substr($a->post_title, -1) ) {
|
417 |
+
unset($attachments[$i]);
|
418 |
+
$removed = true;
|
419 |
+
}
|
420 |
+
}
|
421 |
+
if ( $removed )
|
422 |
+
$attachments = array_values($attachments);
|
423 |
+
}
|
424 |
+
|
425 |
+
//Return attachments
|
426 |
+
return $attachments;
|
427 |
+
}
|
428 |
+
|
429 |
+
/**
|
430 |
+
* Retrieve the attachment's path
|
431 |
+
* Path = Full URL to attachment - site's base URL
|
432 |
+
* Useful for filesystem operations (e.g. file_exists(), etc.)
|
433 |
+
* @param object|id $post Attachment object or ID
|
434 |
+
* @return string Attachment path
|
435 |
+
*/
|
436 |
+
function get_attachment_path($post = null) {
|
437 |
+
if (!$this->util->check_post($post))
|
438 |
+
return '';
|
439 |
+
//Get Attachment URL
|
440 |
+
$url = wp_get_attachment_url($post->ID);
|
441 |
+
//Replace with absolute path
|
442 |
+
$path = str_replace(get_bloginfo('wpurl') . '/', ABSPATH, $url);
|
443 |
+
return $path;
|
444 |
+
}
|
445 |
+
|
446 |
+
/**
|
447 |
+
* Retrieves filesize of an attachment
|
448 |
+
* @param obj|int $post (optional) Attachment object or ID (uses global $post object if parameter not provided)
|
449 |
+
* @param bool $formatted (optional) Whether or not filesize should be formatted (kb/mb, etc.) (Default: TRUE)
|
450 |
+
* @return int|string Filesize in bytes (@see filesize()) or as formatted string based on parameters
|
451 |
+
*/
|
452 |
+
function get_attachment_filesize($post = null, $formatted = true) {
|
453 |
+
$size = 0;
|
454 |
+
if (!$this->util->check_post($post))
|
455 |
+
return $size;
|
456 |
+
//Get path to attachment
|
457 |
+
$path = $this->get_attachment_path($post);
|
458 |
+
//Get file size
|
459 |
+
if (file_exists($path))
|
460 |
+
$size = filesize($path);
|
461 |
+
if ($size > 0 && $formatted) {
|
462 |
+
$size = (int) $size;
|
463 |
+
$label = 'b';
|
464 |
+
$format = "%s%s";
|
465 |
+
//Format file size
|
466 |
+
if ($size >= 1024 && $size < 102400) {
|
467 |
+
$label = 'kb';
|
468 |
+
$size = intval($size/1024);
|
469 |
+
}
|
470 |
+
elseif ($size >= 102400) {
|
471 |
+
$label = 'mb';
|
472 |
+
$size = round(($size/1024)/1024, 1);
|
473 |
+
}
|
474 |
+
$size = sprintf($format, $size, $label);
|
475 |
+
}
|
476 |
+
|
477 |
+
return $size;
|
478 |
+
}
|
479 |
+
|
480 |
+
/**
|
481 |
+
* Prints the attachment's filesize
|
482 |
+
* @param obj|int $post (optional) Attachment object or ID (uses global $post object if parameter not provided)
|
483 |
+
* @param bool $formatted (optional) Whether or not filesize should be formatted (kb/mb, etc.) (Default: TRUE)
|
484 |
+
*/
|
485 |
+
function the_attachment_filesize($post = null, $formatted = true) {
|
486 |
+
echo $this->get_attachment_filesize($post, $formatted);
|
487 |
+
}
|
488 |
+
|
489 |
+
/**
|
490 |
+
* Build output for media item
|
491 |
+
* Based on media type and output type parameter
|
492 |
+
* @param int|obj $media Media object or ID
|
493 |
+
* @param string $type (optional) Output type (Default: source URL)
|
494 |
+
* @return string Media output
|
495 |
+
*/
|
496 |
+
function get_media_output($media, $type = 'url', $attr = array()) {
|
497 |
+
$ret = '';
|
498 |
+
$media =& get_post($media);
|
499 |
+
//Continue processing valid media items
|
500 |
+
if ( $this->is_media($media) ) {
|
501 |
+
//URL - Same for all attachments
|
502 |
+
if ( 'url' == $type ) {
|
503 |
+
$ret = wp_get_attachment_url($media->ID);
|
504 |
+
} elseif ( 'link' == $type ) {
|
505 |
+
$ret = $this->get_link($media, $attr);
|
506 |
+
} else {
|
507 |
+
//Determine media type
|
508 |
+
$mime = get_post_mime_type($media);
|
509 |
+
$mime_main = substr($mime, 0, strpos($mime, '/'));
|
510 |
+
|
511 |
+
//Pass to handler for media type + output type
|
512 |
+
$handler = implode('_', array('get', $mime_main, 'output'));
|
513 |
+
if ( method_exists($this, $handler))
|
514 |
+
$ret = $this->{$handler}($media, $type, $attr);
|
515 |
+
else {
|
516 |
+
//Default output if no handler exists
|
517 |
+
$ret = $this->get_image_output($media, $type, $attr);
|
518 |
+
}
|
519 |
+
}
|
520 |
+
}
|
521 |
+
|
522 |
+
|
523 |
+
return apply_filters($this->add_prefix('get_media_output'), $ret, $media, $type);
|
524 |
+
}
|
525 |
+
|
526 |
+
/**
|
527 |
+
* Build HTML for displaying media
|
528 |
+
* Output based on media type (image, video, etc.)
|
529 |
+
* @param int|obj $media (Media object or ID)
|
530 |
+
* @return string HTML for media
|
531 |
+
*/
|
532 |
+
function get_media_html($media) {
|
533 |
+
$out = '';
|
534 |
+
return $out;
|
535 |
+
}
|
536 |
+
|
537 |
+
function get_link($media, $attr = array()) {
|
538 |
+
$ret = '';
|
539 |
+
$media =& get_post($media);
|
540 |
+
if ( $this->is_media($media) ) {
|
541 |
+
$attr['href'] = wp_get_attachment_url($media->ID);
|
542 |
+
$text = ( isset($attr['text']) ) ? $attr['text'] : basename($attr['href']);
|
543 |
+
unset($attr['text']);
|
544 |
+
//Build attribute string
|
545 |
+
$attr = wp_parse_args($attr, array('href' => ''));
|
546 |
+
$attr_string = $this->util->build_attribute_string($attr);
|
547 |
+
$ret = '<a ' . $attr_string . '>' . $text . '</a>';
|
548 |
+
}
|
549 |
+
return $ret;
|
550 |
+
}
|
551 |
+
|
552 |
+
/**
|
553 |
+
* Builds output for image attachments
|
554 |
+
* @param int|obj $media Media object or ID
|
555 |
+
* @param string $type Output type
|
556 |
+
* @return string Image output
|
557 |
+
*/
|
558 |
+
function get_image_output($media, $type = 'html', $attr = array()) {
|
559 |
+
$ret = '';
|
560 |
+
$icon = !wp_attachment_is_image($media->ID);
|
561 |
+
|
562 |
+
//Get image properties
|
563 |
+
$attr = wp_parse_args($attr, array('alt' => trim(strip_tags( $media->post_excerpt ))));
|
564 |
+
list($attr['src'], $attribs['width'], $attribs['height']) = wp_get_attachment_image_src($media->ID, '', $icon);
|
565 |
+
|
566 |
+
switch ( $type ) {
|
567 |
+
case 'html' :
|
568 |
+
//Remove attributes that must not be empty
|
569 |
+
$attr_nonempty = array('id');
|
570 |
+
foreach ( $attr_nonempty as $key ) {
|
571 |
+
if ( !isset($attr[$key]) || empty($attr[$key]) )
|
572 |
+
unset($attr[$key]);
|
573 |
+
}
|
574 |
+
$attr_str = $this->util->build_attribute_string($attr);
|
575 |
+
$ret = '<img ' . $attr_str . ' />';
|
576 |
+
break;
|
577 |
+
}
|
578 |
+
|
579 |
+
return $ret;
|
580 |
+
}
|
581 |
+
|
582 |
+
/**
|
583 |
+
* Build HTML IMG element of an Image
|
584 |
+
* @param array $image Array of image properties
|
585 |
+
* 0: Source URI
|
586 |
+
* 1: Width
|
587 |
+
* 2: Height
|
588 |
+
* @return string HTML IMG element of specified image
|
589 |
+
*/
|
590 |
+
function get_image_html($image, $attributes = '') {
|
591 |
+
$ret = '';
|
592 |
+
if (is_array($image) && count($image) >= 3) {
|
593 |
+
//Build attribute string
|
594 |
+
if (is_array($attributes)) {
|
595 |
+
$attribs = '';
|
596 |
+
$attr_format = '%s="%s"';
|
597 |
+
foreach ($attributes as $attr => $val) {
|
598 |
+
$attribs .= sprintf($attr_format, $attr, esc_attr($val));
|
599 |
+
}
|
600 |
+
$attributes = $attribs;
|
601 |
+
}
|
602 |
+
$format = '<img src="%1$s" width="%2$d" height="%3$d" ' . $attributes . ' />';
|
603 |
+
$ret = sprintf($format, $image[0], $image[1], $image[2]);
|
604 |
+
}
|
605 |
+
return $ret;
|
606 |
+
}
|
607 |
+
|
608 |
+
/**
|
609 |
+
* Registers admin menus for content types
|
610 |
+
* @param CNR_Content_Type $type Content Type Instance
|
611 |
+
*
|
612 |
+
* @global CNR_Content_Utilities $cnr_content_utilities
|
613 |
+
*/
|
614 |
+
function type_admin_menu($type) {
|
615 |
+
global $cnr_content_utilities;
|
616 |
+
$u =& $cnr_content_utilities;
|
617 |
+
|
618 |
+
//Add Menu
|
619 |
+
$parent_page = $u->get_admin_page_file($type->id);
|
620 |
+
$menu_page = $u->get_admin_page_file($type->id, 'extra');
|
621 |
+
$this->util->add_submenu_page($parent_page, __('Extra Menu'), __('Extra Menu'), 8, $menu_page, $this->m('type_admin_page'));
|
622 |
+
}
|
623 |
+
|
624 |
+
function type_admin_page() {
|
625 |
+
global $title;
|
626 |
+
?>
|
627 |
+
<div class="wrap">
|
628 |
+
<?php screen_icon('edit'); ?>
|
629 |
+
<h2><?php esc_html_e($title); ?></h2>
|
630 |
+
<p>
|
631 |
+
This is an extra menu for a specific content type added via a plugin hook
|
632 |
+
</p>
|
633 |
+
</div>
|
634 |
+
<?php
|
635 |
+
}
|
636 |
+
}
|
includes/class.post.php
ADDED
@@ -0,0 +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 |
+
}
|
includes/class.post_query.php
ADDED
@@ -0,0 +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 |
+
}
|
includes/class.structure.php
ADDED
@@ -0,0 +1,815 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Core properties/methods for Media management
|
5 |
+
* @package Cornerstone
|
6 |
+
* @subpackage Media
|
7 |
+
* @author Archetyped
|
8 |
+
* @uses CNR_Post
|
9 |
+
*/
|
10 |
+
class CNR_Structure extends CNR_Base {
|
11 |
+
|
12 |
+
/* Properties */
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Postname token
|
16 |
+
* @var string
|
17 |
+
*/
|
18 |
+
var $tok_post = '%postname%';
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Post Path token
|
22 |
+
* @var string
|
23 |
+
*/
|
24 |
+
var $tok_path = '%postpath%';
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Custom post permalink structure
|
28 |
+
* @var string
|
29 |
+
*/
|
30 |
+
var $permalink_structure = null;
|
31 |
+
|
32 |
+
/**
|
33 |
+
* ID of old permalink structure
|
34 |
+
* Used as base of meta key
|
35 |
+
* @see __construct() Value prefixed
|
36 |
+
* @var string
|
37 |
+
*/
|
38 |
+
var $permalink_structure_old = 'permastruct_old';
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Name of field used for setting post parent
|
42 |
+
* Prefix added in constructor
|
43 |
+
* @var string
|
44 |
+
*/
|
45 |
+
var $field_parent = 'post_parent';
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Name of post being created/edited
|
49 |
+
* @var string
|
50 |
+
*/
|
51 |
+
var $data_insert_post_name = null;
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Indicates whether process is currently occurring
|
55 |
+
* Used to determine whether to skip hook handlers, etc.
|
56 |
+
* @var bool
|
57 |
+
*/
|
58 |
+
var $status_processing = false;
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Column name on posts management page
|
62 |
+
* @var string
|
63 |
+
*/
|
64 |
+
var $management_column = array( 'name' => 'section', 'title' => 'Section' );
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Client scripts
|
68 |
+
* @var array
|
69 |
+
*/
|
70 |
+
var $scripts = array(
|
71 |
+
'structure' => array (
|
72 |
+
'file' => 'js/lib.structure.js',
|
73 |
+
'deps' => array('jquery', '[core]'),
|
74 |
+
'context' => 'admin'
|
75 |
+
),
|
76 |
+
'structure_permalink' => array (
|
77 |
+
'file' => 'js/lib.structure.admin.js',
|
78 |
+
'deps' => array('jquery', '[admin]','[structure]'),
|
79 |
+
'context' => 'admin_page_options-permalink'
|
80 |
+
)
|
81 |
+
);
|
82 |
+
|
83 |
+
/* Constructor */
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Constructor
|
87 |
+
*/
|
88 |
+
function __construct() {
|
89 |
+
parent::__construct();
|
90 |
+
$this->add_prefix_ref($this->field_parent);
|
91 |
+
$this->add_prefix_ref($this->permalink_structure_old);
|
92 |
+
$this->permalink_structure = $this->util->normalize_path($this->tok_path, $this->tok_post, array(true, true));
|
93 |
+
$this->add_prefix_ref($this->management_column['name']);
|
94 |
+
}
|
95 |
+
|
96 |
+
/* Methods */
|
97 |
+
|
98 |
+
function register_hooks() {
|
99 |
+
parent::register_hooks();
|
100 |
+
|
101 |
+
//Rewrite Rules
|
102 |
+
add_filter('rewrite_rules_array', $this->m('rewrite_rules_array'));
|
103 |
+
|
104 |
+
//Request
|
105 |
+
add_filter('post_rewrite_rules', $this->m('post_rewrite_rules'));
|
106 |
+
|
107 |
+
//Query
|
108 |
+
add_action('pre_get_posts', $this->m('pre_get_posts'));
|
109 |
+
|
110 |
+
//Permalink
|
111 |
+
add_filter('post_link', $this->m('post_link'), 10, 3);
|
112 |
+
add_filter('post_type_link', $this->m('post_link'), 10, 4);
|
113 |
+
|
114 |
+
//Navigation
|
115 |
+
add_filter('wp_nav_menu_objects', $this->m('nav_menu_objects'), 10, 2);
|
116 |
+
|
117 |
+
//Admin
|
118 |
+
add_action('admin_print_scripts', $this->m('admin_print_scripts'), 30);
|
119 |
+
add_action('update_option_permalink_structure', $this->m('update_permastruct'), 10, 3);
|
120 |
+
//Edit
|
121 |
+
//Add meta boxes
|
122 |
+
add_action('do_meta_boxes', $this->m('admin_post_sidebar'), 1, 3);
|
123 |
+
//Process post creation/updating
|
124 |
+
add_filter('wp_insert_post_data', $this->m('admin_post_insert_data'), 10, 2);
|
125 |
+
add_action('save_post', $this->m('admin_post_save'), 10, 2);
|
126 |
+
add_action('delete_post', $this->m('admin_post_delete'), 10, 1);
|
127 |
+
//Management
|
128 |
+
add_action('restrict_manage_posts', $this->m('admin_restrict_manage_posts'));
|
129 |
+
add_action('parse_request', $this->m('admin_manage_posts_filter_section'));
|
130 |
+
add_filter('manage_posts_columns', $this->m('admin_manage_posts_columns'));
|
131 |
+
add_action('manage_posts_custom_column', $this->m('admin_manage_posts_custom_column'), 10, 2);
|
132 |
+
add_action('quick_edit_custom_box', $this->m('admin_quick_edit_custom_box'), 10, 2);
|
133 |
+
add_action('bulk_edit_custom_box', $this->m('admin_bulk_edit_custom_box'), 10, 2);
|
134 |
+
}
|
135 |
+
|
136 |
+
/**
|
137 |
+
* Flushes rewrite rules
|
138 |
+
*/
|
139 |
+
function reset() {
|
140 |
+
global $wp_rewrite;
|
141 |
+
//Rebuild URL Rewrite rules
|
142 |
+
$wp_rewrite->flush_rules();
|
143 |
+
}
|
144 |
+
|
145 |
+
/**
|
146 |
+
* Plugin activation routines
|
147 |
+
* @global WP_Rewrite $wp_rewrite
|
148 |
+
*/
|
149 |
+
function activate() {
|
150 |
+
global $wp_rewrite;
|
151 |
+
//Update permalink structure
|
152 |
+
$struct = get_option($this->permalink_structure_old, null);
|
153 |
+
if ( $struct )
|
154 |
+
$wp_rewrite->set_permalink_structure($this->permalink_structure);
|
155 |
+
else {
|
156 |
+
delete_option($this->permalink_structure_old);
|
157 |
+
}
|
158 |
+
$this->reset();
|
159 |
+
}
|
160 |
+
|
161 |
+
/**
|
162 |
+
* Plugin deactivation routines
|
163 |
+
* @uses WP_Rewrite $wp_rewrite to Update permalink structure
|
164 |
+
*/
|
165 |
+
function deactivate() {
|
166 |
+
//Reset permalink structure
|
167 |
+
if ( $this->using_post_permastruct() ) {
|
168 |
+
global $wp_rewrite;
|
169 |
+
$permastruct = get_option($this->permalink_structure_old, '');
|
170 |
+
$wp_rewrite->set_permalink_structure($permastruct);
|
171 |
+
//Save permastruct setting
|
172 |
+
update_option($this->permalink_structure_old, true);
|
173 |
+
} else {
|
174 |
+
delete_option($this->permalink_structure_old);
|
175 |
+
}
|
176 |
+
//Flush Rules
|
177 |
+
$this->reset();
|
178 |
+
}
|
179 |
+
|
180 |
+
/**
|
181 |
+
* Returns formatted query variable for use in post requests
|
182 |
+
* @return string Custom query variable
|
183 |
+
*
|
184 |
+
* @global WP_Rewrite $wp_rewrite
|
185 |
+
*/
|
186 |
+
function get_query_var() {
|
187 |
+
static $qvar = '';
|
188 |
+
|
189 |
+
//Retrieve query var used for page queries
|
190 |
+
if ( empty($qvar) ) {
|
191 |
+
global $wp_rewrite;
|
192 |
+
//Get page permastruct
|
193 |
+
$page_tag = $wp_rewrite->get_page_permastruct();
|
194 |
+
|
195 |
+
//Extract tag for page
|
196 |
+
$page_tag = str_replace($wp_rewrite->index, '', $page_tag);
|
197 |
+
|
198 |
+
//Get query var for tag
|
199 |
+
if ( ($idx = array_search($page_tag, $wp_rewrite->rewritecode)) !== false ) {
|
200 |
+
$qvar = trim($wp_rewrite->queryreplace[$idx], '=');
|
201 |
+
}
|
202 |
+
}
|
203 |
+
|
204 |
+
return $qvar;
|
205 |
+
}
|
206 |
+
|
207 |
+
/**
|
208 |
+
* Checks if custom permalink structure is currently in use
|
209 |
+
* @return bool TRUE if custom permalink structure is in use, FALSE otherwise
|
210 |
+
*
|
211 |
+
* @global WP_Rewrite $wp_rewrite
|
212 |
+
*/
|
213 |
+
function using_post_permastruct() {
|
214 |
+
global $wp_rewrite;
|
215 |
+
return ( $wp_rewrite->using_permalinks() && get_option('permalink_structure') == $this->permalink_structure );
|
216 |
+
}
|
217 |
+
|
218 |
+
/**
|
219 |
+
* Save old permastruct when custom one set
|
220 |
+
* @uses `update_option_permalink_structure` Action hook
|
221 |
+
* @see update_option()
|
222 |
+
* @see register_hooks() Initialized
|
223 |
+
* @param string $option Option name
|
224 |
+
* @param string $old Previous permalink structure value
|
225 |
+
* @param string $new New permalink structure value
|
226 |
+
* @return void
|
227 |
+
*/
|
228 |
+
function update_permastruct($old, $new) {
|
229 |
+
//Validate request
|
230 |
+
if ( $old == $new || ( $new != $this->permalink_structure && $old != $this->permalink_structure ) )
|
231 |
+
return false;
|
232 |
+
//Save old permastruct
|
233 |
+
if ( $new == $this->permalink_structure)
|
234 |
+
update_option($this->permalink_structure_old, $old);
|
235 |
+
elseif ( $old == $this->permalink_structure )
|
236 |
+
delete_option($this->permalink_structure_old);
|
237 |
+
}
|
238 |
+
|
239 |
+
/**
|
240 |
+
* Returns path to post based on site structure
|
241 |
+
* @return string Path to post enclosed in '/' (forward slashes)
|
242 |
+
* Example: /path/to/post/
|
243 |
+
* @param object $post Post object
|
244 |
+
*/
|
245 |
+
function get_path($post, $sample = false) {
|
246 |
+
//Get post parents
|
247 |
+
$parents = CNR_Post::get_parents($post);
|
248 |
+
$sep = '/';
|
249 |
+
$path = $sep;
|
250 |
+
//Modify parent for sample permalink request
|
251 |
+
if ( $sample ) {
|
252 |
+
$this->util->check_post($post);
|
253 |
+
$parent = get_post_meta($post->ID, $this->get_parent_meta_key(), true);
|
254 |
+
|
255 |
+
if ( $parent && $parent = get_post($parent) ) {
|
256 |
+
if ( !empty($parents) ) {
|
257 |
+
//Remove last element
|
258 |
+
array_pop($parents);
|
259 |
+
}
|
260 |
+
//Add new parent
|
261 |
+
$parents[] = $parent;
|
262 |
+
}
|
263 |
+
}
|
264 |
+
//Build path
|
265 |
+
foreach ($parents as $post_parent) {
|
266 |
+
$path .= $post_parent->post_name . $sep;
|
267 |
+
}
|
268 |
+
return $path;
|
269 |
+
}
|
270 |
+
|
271 |
+
/**
|
272 |
+
* Modifies post permalink to reflect position of post in site structure
|
273 |
+
* Example: baseurl/section-name/post-name/
|
274 |
+
*
|
275 |
+
* @param string $permalink Current permalink url for post
|
276 |
+
* @param object|int $post Post object or Post ID
|
277 |
+
* @param bool $leavename Whether to leave post name
|
278 |
+
* @return string
|
279 |
+
*
|
280 |
+
* @global WP_Rewrite $wp_rewrite
|
281 |
+
* @global WP_Query $wp_query
|
282 |
+
*
|
283 |
+
* @see get_permalink
|
284 |
+
* @see get_post_permalink
|
285 |
+
*
|
286 |
+
* @todo Enable redirect_canonical functionality
|
287 |
+
*
|
288 |
+
*/
|
289 |
+
function post_link($permalink, $post, $leavename = false, $sample = false) {
|
290 |
+
global $wp_query;
|
291 |
+
|
292 |
+
/* Stop processing immediately if:
|
293 |
+
* Custom permalink structure is not activated by user
|
294 |
+
* Post data is not a valid post
|
295 |
+
* Post has no name (e.g. drafts)
|
296 |
+
* Custom post type NOT in a section
|
297 |
+
*/
|
298 |
+
if ( !$this->using_post_permastruct()
|
299 |
+
|| ( !$this->util->check_post($post) )
|
300 |
+
|| ( empty($post->post_name) && empty($post->post_title) )
|
301 |
+
|| ( 'draft' == $post->post_status && empty($post->post_name) && !$sample )
|
302 |
+
|| ( 'inherit' == $post->post_status )
|
303 |
+
|| ( !in_array($post->post_type, array('post', 'page', 'attachment', 'revision', 'nav_menu')) && empty($post->post_parent) ) )
|
304 |
+
return $permalink;
|
305 |
+
|
306 |
+
//Get base data
|
307 |
+
$base = get_bloginfo('url');
|
308 |
+
$name = '';
|
309 |
+
|
310 |
+
//Set mode
|
311 |
+
$sample = ( isset($post->filter) && 'sample' == $post->filter ) ? true : $sample;
|
312 |
+
|
313 |
+
//Get post slug
|
314 |
+
|
315 |
+
//Sample
|
316 |
+
if ( $sample ) {
|
317 |
+
/**
|
318 |
+
* Sample permalink
|
319 |
+
* Use permalink placeholder if sample permalink is being generated
|
320 |
+
* @see get_sample_permalink()
|
321 |
+
*/
|
322 |
+
$name = '%postname%';
|
323 |
+
|
324 |
+
//Remove temporary parent if not valid for current request
|
325 |
+
if ( !defined('DOING_AJAX') || !DOING_AJAX || !isset($_POST['action']) || 'sample-permalink' != $_POST['action'] ) {
|
326 |
+
delete_post_meta($post->ID, $this->get_parent_meta_key());
|
327 |
+
}
|
328 |
+
}
|
329 |
+
//Use existing name
|
330 |
+
elseif ( !empty($post->post_name) ) {
|
331 |
+
$name = $post->post_name;
|
332 |
+
}
|
333 |
+
//Build name from title (if not yet set)
|
334 |
+
if ( empty($name) ) {
|
335 |
+
$post->post_status = 'publish';
|
336 |
+
$name = sanitize_title($name ? $name : $post->post_title, $post->ID);
|
337 |
+
$name = wp_unique_post_slug($name, $post->ID, $post->post_status, $post->post_type, $post->post_parent);
|
338 |
+
}
|
339 |
+
|
340 |
+
//Get post path
|
341 |
+
$path = $this->get_path($post, $sample);
|
342 |
+
|
343 |
+
//Set permalink (Add trailing slash)
|
344 |
+
$permalink = trailingslashit($base . $path . $name);
|
345 |
+
|
346 |
+
return $permalink;
|
347 |
+
}
|
348 |
+
|
349 |
+
/**
|
350 |
+
* Resets certain query properties before post retrieval
|
351 |
+
* Checks if request is for a post (using value from pagename query var) and adjusts query to retrieve the post instead of a page
|
352 |
+
* @return void
|
353 |
+
* @param WP_Query $q Reference to global <tt>$wp_query</tt> variable
|
354 |
+
*
|
355 |
+
* @global wpdb $wpdb
|
356 |
+
*/
|
357 |
+
function pre_get_posts($q) {
|
358 |
+
//Do not process query if custom post permastruct is not in use
|
359 |
+
if ( !$this->using_post_permastruct() )
|
360 |
+
return;
|
361 |
+
$qvar = $this->get_query_var();
|
362 |
+
$qv =& $q->query_vars;
|
363 |
+
|
364 |
+
//Stop processing if custom query variable is not present in current query
|
365 |
+
if ( empty($qvar) || !isset($qv[$qvar]) || empty($qv[$qvar]) ) {
|
366 |
+
return;
|
367 |
+
}
|
368 |
+
global $wpdb;
|
369 |
+
|
370 |
+
$qval = $qv[$qvar];
|
371 |
+
|
372 |
+
//Get last segment
|
373 |
+
$slug = array_reverse( explode('/', $qval) );
|
374 |
+
if ( is_array($slug) && !empty($slug) )
|
375 |
+
$slug = $slug[0];
|
376 |
+
else
|
377 |
+
return;
|
378 |
+
|
379 |
+
//Determine if query is for page or post
|
380 |
+
$type = $wpdb->get_var($wpdb->prepare("SELECT post_type FROM $wpdb->posts WHERE post_name = %s LIMIT 1", $slug));
|
381 |
+
if ( empty($type) )
|
382 |
+
return;
|
383 |
+
|
384 |
+
//Adjust query if requested item is not a page
|
385 |
+
if ( 'page' != $type ) {
|
386 |
+
$new_var = 'name';
|
387 |
+
$qval = $slug;
|
388 |
+
//Set new query var
|
389 |
+
$qv[$new_var] = $qval;
|
390 |
+
unset($qv[$qvar]);
|
391 |
+
//Set post type
|
392 |
+
$qv['post_type'] = $type;
|
393 |
+
//Reparse query variables
|
394 |
+
$q->parse_query($qv);
|
395 |
+
}
|
396 |
+
}
|
397 |
+
|
398 |
+
/**
|
399 |
+
* Modifies post rewrite rules when using custom permalink structure
|
400 |
+
* Removes all post rewrite rules since we are modifying page rewrite rules to process the request
|
401 |
+
* @param array $r Post rewrite rules from WP_Rewrite::rewrite_rules
|
402 |
+
* @return array Modified post rewrite rules
|
403 |
+
*/
|
404 |
+
function post_rewrite_rules($r) {
|
405 |
+
if ( $this->using_post_permastruct() )
|
406 |
+
$r = array();
|
407 |
+
return $r;
|
408 |
+
}
|
409 |
+
|
410 |
+
/**
|
411 |
+
* Modifies rewrite rules array
|
412 |
+
* Removes unnecessary paged permalink structure for Pages/Content Types (/pagename/[0-9]/)
|
413 |
+
* - Conflicts with /pagename/postname/ permalink structure
|
414 |
+
* @param array $rules Generated rewrite rules
|
415 |
+
* @return array Modified rewrite rules
|
416 |
+
*/
|
417 |
+
function rewrite_rules_array($rules) {
|
418 |
+
$subpattern_old = '(/[0-9]+)?/?$';
|
419 |
+
$subpattern_new = '(/)?$';
|
420 |
+
$qvar = '&page=';
|
421 |
+
$rules_temp = array();
|
422 |
+
|
423 |
+
//Find rules containing subpattern
|
424 |
+
$patterns = array_keys($rules);
|
425 |
+
|
426 |
+
foreach ( $patterns as $idx => $patt ) {
|
427 |
+
$rule = '';
|
428 |
+
//Check if pattern contains subpattern
|
429 |
+
if ( strpos($patt, $subpattern_old) !== false && strpos($rules[$patt], $qvar) !== false ) {
|
430 |
+
//Generate new pattern
|
431 |
+
$rule = str_replace($subpattern_old, $subpattern_new, $patt);
|
432 |
+
} else {
|
433 |
+
$rule = $patt;
|
434 |
+
}
|
435 |
+
//Add rule to temp array
|
436 |
+
$rules_temp[$rule] = $rules[$patt];
|
437 |
+
}
|
438 |
+
|
439 |
+
//Return modified rewrite rules
|
440 |
+
return $rules_temp;
|
441 |
+
}
|
442 |
+
|
443 |
+
/**
|
444 |
+
* Performs additional processing on nav menu objects
|
445 |
+
* > Adds additional classes to menu items based on current request
|
446 |
+
* @see wp_nav_menu()
|
447 |
+
* @uses `wp_nav_menu_objects` filter hook to modify items
|
448 |
+
* @param array $menu_items Sorted menu items
|
449 |
+
* @param object $args Arguments passed to function
|
450 |
+
* @return array Menu items array
|
451 |
+
*/
|
452 |
+
function nav_menu_objects($menu_items, $args) {
|
453 |
+
$class_base = 'current-page-';
|
454 |
+
$class_ancestor = $class_base . 'ancestor';
|
455 |
+
$class_parent = $class_base . 'parent';
|
456 |
+
//Get current item
|
457 |
+
if ( is_singular()
|
458 |
+
&& ( $item = get_queried_object() )
|
459 |
+
&& !empty($item->post_type)
|
460 |
+
&& !is_post_type_hierarchical($item->post_type) //Only process non-hierarchical post types
|
461 |
+
) {
|
462 |
+
//Get ancestors of current item
|
463 |
+
$ancestors = get_ancestors($item->ID, $item->post_type);
|
464 |
+
|
465 |
+
//Loop through menu items and add classes to ancestors of current item
|
466 |
+
foreach ( $menu_items as $key => $m_item ) {
|
467 |
+
//Only process menu items representing posts/pages
|
468 |
+
if ( isset($m_item->type) && 'post_type' == $m_item->type && in_array($m_item->object_id, $ancestors) ) {
|
469 |
+
//Add ancestor class to item
|
470 |
+
if ( !is_array($m_item->classes) )
|
471 |
+
$m_item->classes = array();
|
472 |
+
$m_item->classes[] = $class_ancestor;
|
473 |
+
//Check if menu item is parent of current item
|
474 |
+
if ( $item->post_parent == $m_item->object_id )
|
475 |
+
$m_item->classes[] = $class_parent;
|
476 |
+
//Filter duplicate classes
|
477 |
+
$m_item->classes = array_unique($m_item->classes);
|
478 |
+
//Update menu array
|
479 |
+
$menu_items[$key] = $m_item;
|
480 |
+
}
|
481 |
+
}
|
482 |
+
}
|
483 |
+
|
484 |
+
return $menu_items;
|
485 |
+
}
|
486 |
+
|
487 |
+
/**
|
488 |
+
* Sets post parent
|
489 |
+
* @param obj|int $post Post object or ID
|
490 |
+
* @param obj|int $parent Parent post object or ID
|
491 |
+
* @param bool $temp (optional) Whether parent should be temporarily set (Default: NO)
|
492 |
+
*/
|
493 |
+
function set_parent($post, $parent = null, $temp = false) {
|
494 |
+
/* Validation */
|
495 |
+
//Post
|
496 |
+
if ( !$this->util->check_post($post) || ( is_null($parent) && !isset($post->post_parent) ) )
|
497 |
+
return false;
|
498 |
+
//Default parent
|
499 |
+
if ( is_null($parent) )
|
500 |
+
$parent = $post->post_parent;
|
501 |
+
//Request
|
502 |
+
if ( 0 != $parent && ( !$this->util->check_post($parent) || !isset($parent->ID) || $post->ID == $parent->ID ) )
|
503 |
+
return false;
|
504 |
+
|
505 |
+
if ( is_object($parent) ) {
|
506 |
+
$parent = $parent->ID;
|
507 |
+
}
|
508 |
+
|
509 |
+
/* Set Parent */
|
510 |
+
|
511 |
+
//Temporary
|
512 |
+
if ( $temp ) {
|
513 |
+
update_post_meta($post->ID, $this->get_parent_meta_key(), $parent);
|
514 |
+
}
|
515 |
+
//Standard
|
516 |
+
else {
|
517 |
+
//Remove temporary parent
|
518 |
+
delete_post_meta($post->ID, $this->get_parent_meta_key());
|
519 |
+
}
|
520 |
+
}
|
521 |
+
|
522 |
+
/*-** Admin **-*/
|
523 |
+
|
524 |
+
/**
|
525 |
+
* Outputs structure data to client
|
526 |
+
* @param string $page Current page
|
527 |
+
*/
|
528 |
+
function admin_print_scripts() {
|
529 |
+
$out = array();
|
530 |
+
$opts = array (
|
531 |
+
'using_permastruct' => $this->using_post_permastruct(),
|
532 |
+
'permalink_structure' => $this->permalink_structure,
|
533 |
+
'field_parent' => $this->field_parent
|
534 |
+
);
|
535 |
+
|
536 |
+
$out[] = $this->util->extend_client_object('structure.options', $opts);
|
537 |
+
|
538 |
+
if ( $this->util->is_context($this->scripts->structure_permalink->context) ) {
|
539 |
+
$opts_admin = array (
|
540 |
+
'label' => __('Structured', $this->get_prefix()),
|
541 |
+
'example' => get_option('home') . '/section-name/sample-post/'
|
542 |
+
);
|
543 |
+
|
544 |
+
$out[] = $this->util->extend_client_object('structure.admin.options', $opts_admin);
|
545 |
+
}
|
546 |
+
|
547 |
+
//Output data
|
548 |
+
echo $this->util->build_script_element(implode(PHP_EOL, $out), 'structure_options');
|
549 |
+
}
|
550 |
+
|
551 |
+
/**
|
552 |
+
* Set post parent for current post
|
553 |
+
* If custom field is present in $postarr, use value to set post's parent
|
554 |
+
* Post parent is set in WP posts table (as post_parent value) in calling function
|
555 |
+
* @see $this->admin_post_save() Saves parent as metadata for post
|
556 |
+
* @see wp_insert_post()
|
557 |
+
* @uses $this->field_parent as field name to check for
|
558 |
+
* @param array $data Post data (restricted default columns only)
|
559 |
+
* @param array $postarr Post data passed to wp_insert_post() and parsed with defaults
|
560 |
+
* @return array Modified post data
|
561 |
+
*/
|
562 |
+
function admin_post_insert_data($data, $postarr) {
|
563 |
+
if ( !in_array($data['post_type'], array('revision', 'page')) && !$this->is_processing() ) {
|
564 |
+
//Check for custom field and validate value
|
565 |
+
if ( isset($postarr[$this->field_parent]) ) {
|
566 |
+
$parent_val = intval($postarr[$this->field_parent]);
|
567 |
+
//If field is set, set as parent
|
568 |
+
if ( $parent_val >= 0 )
|
569 |
+
$data['post_parent'] = $parent_val;
|
570 |
+
//Set post name
|
571 |
+
// $this->data_insert_post_name = $data['post_name'];
|
572 |
+
} else {
|
573 |
+
//If field is not set, remove metadata (if previously set)
|
574 |
+
// $this->data_insert_post_name = null;
|
575 |
+
}
|
576 |
+
}
|
577 |
+
//Return updated post data
|
578 |
+
return $data;
|
579 |
+
}
|
580 |
+
|
581 |
+
/**
|
582 |
+
* Save post parent as metadata
|
583 |
+
* Metadata used for backwards compatibility and future-proofing
|
584 |
+
* @param int $post_ID Saved post ID
|
585 |
+
* @param obj $post Saved post object
|
586 |
+
*/
|
587 |
+
function admin_post_save($post_ID, $post) {
|
588 |
+
$parent = null;
|
589 |
+
$temp = false;
|
590 |
+
|
591 |
+
//Autosave
|
592 |
+
if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE && $pid = wp_is_post_autosave($post) ) {
|
593 |
+
//Get temporary parent ID
|
594 |
+
$parent = ( isset($_POST['parent_id']) ) ? $_POST['parent_id'] : 0;
|
595 |
+
//Save temporary parent as metadata
|
596 |
+
$temp = true;
|
597 |
+
$post_ID = $post->post_parent;
|
598 |
+
}
|
599 |
+
|
600 |
+
$this->set_parent($post_ID, $parent, $temp);
|
601 |
+
}
|
602 |
+
|
603 |
+
/**
|
604 |
+
* Move children of deleted section to parent
|
605 |
+
* Process only pages
|
606 |
+
* @uses $wpdb
|
607 |
+
* @param int $post_ID ID of deleted post
|
608 |
+
*/
|
609 |
+
function admin_post_delete($post_ID) {
|
610 |
+
static $p = null;
|
611 |
+
//Only set if post type is page
|
612 |
+
$page = get_post($post_ID);
|
613 |
+
if ( 'page' != $page->post_type )
|
614 |
+
return false;
|
615 |
+
if ( is_null($p) ) {
|
616 |
+
//Set static var and stop processing
|
617 |
+
$p = $post_ID;
|
618 |
+
}
|
619 |
+
elseif ( $p != $post_ID ) {
|
620 |
+
//Clear static var
|
621 |
+
$p = null;
|
622 |
+
}
|
623 |
+
else {
|
624 |
+
global $wpdb;
|
625 |
+
//Move children
|
626 |
+
$page = get_post($post_ID);
|
627 |
+
$children_ids = $wpdb->get_col($wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE post_parent = %d", $post_ID));
|
628 |
+
$parent_data = array( 'post_parent' => $page->post_parent );
|
629 |
+
$parent_where = array( 'post_parent' => $page->ID );
|
630 |
+
$wpdb->update($wpdb->posts, $parent_data, $parent_where);
|
631 |
+
|
632 |
+
//Clear child caches & update postmeta
|
633 |
+
foreach ( $children_ids as $child ) {
|
634 |
+
$this->set_parent($child, $page->post_parent);
|
635 |
+
clean_post_cache($child);
|
636 |
+
}
|
637 |
+
|
638 |
+
//Clear static var
|
639 |
+
$p = null;
|
640 |
+
}
|
641 |
+
}
|
642 |
+
|
643 |
+
/**
|
644 |
+
* Adds meta box for section selection on post edit form
|
645 |
+
*/
|
646 |
+
function admin_post_sidebar($type, $context = null, $post = null) {
|
647 |
+
$child_types = get_post_types(array('show_ui' => true, '_builtin' => false));
|
648 |
+
$child_types[] = 'post';
|
649 |
+
$side_context = 'side';
|
650 |
+
$priority = 'high';
|
651 |
+
if ( in_array($type, $child_types) && $side_context == $context )
|
652 |
+
add_meta_box($this->add_prefix('section'), __('Section'), $this->m('admin_post_sidebar_section'), $type, $context, $priority);
|
653 |
+
}
|
654 |
+
|
655 |
+
/**
|
656 |
+
* Adds Section selection box to post sidebar
|
657 |
+
* @return void
|
658 |
+
* @param object $post Post Object
|
659 |
+
*/
|
660 |
+
function admin_post_sidebar_section($post) {
|
661 |
+
?>
|
662 |
+
<div class="<?php echo $this->add_prefix('pages_dropdown'); ?>">
|
663 |
+
<?php
|
664 |
+
wp_dropdown_pages(array('exclude_tree' => $post->ID,
|
665 |
+
'selected' => $post->post_parent,
|
666 |
+
'name' => $this->field_parent,
|
667 |
+
'show_option_none' => __('- No Section -'),
|
668 |
+
'sort_column'=> 'menu_order, post_title'));
|
669 |
+
?>
|
670 |
+
</div>
|
671 |
+
<?php
|
672 |
+
}
|
673 |
+
|
674 |
+
/**
|
675 |
+
* Adds additional options to filter posts
|
676 |
+
*/
|
677 |
+
function admin_restrict_manage_posts() {
|
678 |
+
//Add to post edit only
|
679 |
+
$section_param = $this->add_prefix('section');
|
680 |
+
if ( $this->util->is_admin_management_page() ) {
|
681 |
+
$selected = ( isset($_GET[$section_param]) && is_numeric($_GET[$section_param]) ) ? $_GET[$section_param] : 0;
|
682 |
+
//Add post statuses
|
683 |
+
$options = array('name' => $section_param,
|
684 |
+
'selected' => $selected,
|
685 |
+
'show_option_none' => __( 'View all sections' ),
|
686 |
+
'sort_column' => 'menu_order, post_title');
|
687 |
+
wp_dropdown_pages($options);
|
688 |
+
}
|
689 |
+
}
|
690 |
+
|
691 |
+
/**
|
692 |
+
* Filters posts by specified section on the Manage Posts admin page
|
693 |
+
* Hooks into 'parse_request' action
|
694 |
+
* @see WP::parse_request()
|
695 |
+
* @param array $query_vars Parsed query variables
|
696 |
+
* @return array Modified query variables
|
697 |
+
*/
|
698 |
+
function admin_manage_posts_filter_section($q) {
|
699 |
+
//Determine if request is coming from manage posts admin page
|
700 |
+
$var = $this->add_prefix('section');
|
701 |
+
if ( $this->util->is_admin_management_page()
|
702 |
+
&& isset($_GET[$var])
|
703 |
+
&& is_numeric($_GET[$var])
|
704 |
+
) {
|
705 |
+
$q->query_vars['post_parent'] = intval($_GET[$var]);
|
706 |
+
}
|
707 |
+
}
|
708 |
+
|
709 |
+
/**
|
710 |
+
* Modifies the columns that are displayed on the Post Management Admin Page
|
711 |
+
* @param array $columns Array of columns for displaying post data on each post's row
|
712 |
+
* @return array Modified columns array
|
713 |
+
*/
|
714 |
+
function admin_manage_posts_columns($columns) {
|
715 |
+
$columns[$this->management_column['name']] = __($this->management_column['title']);
|
716 |
+
return $columns;
|
717 |
+
}
|
718 |
+
|
719 |
+
/**
|
720 |
+
* Adds section name that post belongs to in custom column on Post Management admin page
|
721 |
+
* @param string $column_name Name of current custom column
|
722 |
+
* @param int $post_id ID of current post
|
723 |
+
*/
|
724 |
+
function admin_manage_posts_custom_column($column_name, $post_id) {
|
725 |
+
//Only process for specific columns (stop processing otherwise)
|
726 |
+
if ( $this->management_column['name'] != $column_name )
|
727 |
+
return false;
|
728 |
+
//Get section
|
729 |
+
$section = CNR_Post::get_section($post_id);
|
730 |
+
|
731 |
+
if ( !empty($section) ) {
|
732 |
+
echo $section->post_title;
|
733 |
+
$data = array("post_$post_id" => (object) array('post_parent' => $section->ID));
|
734 |
+
echo $this->util->build_script_element($this->util->extend_client_object('posts.data', $data));
|
735 |
+
//echo '<script type="text/javascript">postData["post_' . $post_id . '"] = {"post_parent" : ' . $section->ID . '};</script>';
|
736 |
+
} else
|
737 |
+
_e('None', 'cornerstone');
|
738 |
+
}
|
739 |
+
|
740 |
+
/**
|
741 |
+
* Adds field for Section selection on the Quick Edit form for posts
|
742 |
+
* @param string $column_name Name of custom column
|
743 |
+
* @param string $type Type of current item (post, page, etc.)
|
744 |
+
*/
|
745 |
+
function admin_quick_edit_custom_box($column_name, $type, $bulk = false) {
|
746 |
+
global $post;
|
747 |
+
$child_types = get_post_types(array('show_ui' => true, '_builtin' => false));
|
748 |
+
$child_types[] = 'post';
|
749 |
+
if ( $column_name == $this->add_prefix('section') && in_array($type, $child_types) ) :
|
750 |
+
?>
|
751 |
+
<fieldset class="inline-edit-col-right">
|
752 |
+
<div class="inline-edit-col">
|
753 |
+
<div class="inline-edit-group">
|
754 |
+
<label><span class="title">Section</span></label>
|
755 |
+
<?php
|
756 |
+
$options = array('exclude_tree' => $post->ID,
|
757 |
+
'name' => $this->field_parent,
|
758 |
+
'show_option_none' => __('- No Section -'),
|
759 |
+
'option_none_value' => 0,
|
760 |
+
'show_option_no_change' => ($bulk) ? __('- No Change -') : '',
|
761 |
+
'sort_column' => 'menu_order, post_title');
|
762 |
+
wp_dropdown_pages($options);
|
763 |
+
?>
|
764 |
+
</div>
|
765 |
+
</div>
|
766 |
+
</fieldset>
|
767 |
+
<?php endif;
|
768 |
+
}
|
769 |
+
|
770 |
+
/**
|
771 |
+
* Adds field for Section selection on the bulk edit form for posts
|
772 |
+
* @see admin_quick_edit_custom_box()
|
773 |
+
* @param string $column_name Name of custom column
|
774 |
+
* @param string $type Type of current item (post, page, etc.)
|
775 |
+
*/
|
776 |
+
function admin_bulk_edit_custom_box($column_name, $type) {
|
777 |
+
$this->admin_quick_edit_custom_box($column_name, $type, true);
|
778 |
+
}
|
779 |
+
|
780 |
+
/* Helpers */
|
781 |
+
|
782 |
+
/**
|
783 |
+
* Return key for storing post parent metadata
|
784 |
+
* @return string Meta key value
|
785 |
+
*/
|
786 |
+
function get_parent_meta_key() {
|
787 |
+
return '_' . $this->field_parent;
|
788 |
+
}
|
789 |
+
|
790 |
+
/**
|
791 |
+
* Set processing flag
|
792 |
+
* @uses status_processing
|
793 |
+
* @param bool $status (optional) Set processing status
|
794 |
+
*/
|
795 |
+
function start_processing($status = true) {
|
796 |
+
$this->status_processing = !!$status;
|
797 |
+
}
|
798 |
+
|
799 |
+
/**
|
800 |
+
* Unset processing flag
|
801 |
+
* @uses start_processing() to set processing flag
|
802 |
+
*/
|
803 |
+
function stop_processing() {
|
804 |
+
$this->start_processing(false);
|
805 |
+
}
|
806 |
+
|
807 |
+
/**
|
808 |
+
* Check if request is currently being processed
|
809 |
+
* @uses status_processing
|
810 |
+
* return bool Processing status
|
811 |
+
*/
|
812 |
+
function is_processing() {
|
813 |
+
return !!$this->status_processing;
|
814 |
+
}
|
815 |
+
}
|
includes/class.utilities.php
ADDED
@@ -0,0 +1,1416 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Utility methods
|
5 |
+
*
|
6 |
+
* @package Cornerstone
|
7 |
+
* @subpackage Utilities
|
8 |
+
* @author Archetyped
|
9 |
+
*
|
10 |
+
*/
|
11 |
+
class CNR_Utilities {
|
12 |
+
|
13 |
+
/* Properties */
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Instance parent
|
17 |
+
* @var object
|
18 |
+
*/
|
19 |
+
var $parent = null;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Default plugin headers
|
23 |
+
* @var array
|
24 |
+
*/
|
25 |
+
var $plugin_headers = array (
|
26 |
+
'Name' => 'Plugin Name',
|
27 |
+
'PluginURI' => 'Plugin URI',
|
28 |
+
'Version' => 'Version',
|
29 |
+
'Description' => 'Description',
|
30 |
+
'Author' => 'Author',
|
31 |
+
'AuthorURI' => 'Author URI',
|
32 |
+
'TextDomain' => 'Text Domain',
|
33 |
+
'DomainPath' => 'Domain Path',
|
34 |
+
'Network' => 'Network',
|
35 |
+
);
|
36 |
+
|
37 |
+
/* Constructors */
|
38 |
+
|
39 |
+
function __construct(&$obj) {
|
40 |
+
if ( is_object($obj) )
|
41 |
+
$this->parent =& $obj;
|
42 |
+
}
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Returns callback array to instance method
|
46 |
+
* @param object $obj Instance object
|
47 |
+
* @param string $method Name of method
|
48 |
+
* @return array Callback array
|
49 |
+
*/
|
50 |
+
function &m(&$obj, $method = '') {
|
51 |
+
if ( $obj == null && isset($this) )
|
52 |
+
$obj =& $this;
|
53 |
+
$arr = array(&$obj, $method);
|
54 |
+
return $arr;
|
55 |
+
}
|
56 |
+
|
57 |
+
/* Helper Functions */
|
58 |
+
|
59 |
+
/*-** Prefix **-*/
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Get valid separator
|
63 |
+
* @param string $sep (optional) Separator supplied
|
64 |
+
* @return string Separator
|
65 |
+
*/
|
66 |
+
function get_sep($sep = false) {
|
67 |
+
if ( is_null($sep) )
|
68 |
+
$sep = '';
|
69 |
+
return ( is_string($sep) ) ? $sep : '_';
|
70 |
+
}
|
71 |
+
|
72 |
+
/**
|
73 |
+
* Retrieve class prefix (with separator if set)
|
74 |
+
* @param bool|string $sep Separator to append to class prefix (Default: no separator)
|
75 |
+
* @return string Class prefix
|
76 |
+
*/
|
77 |
+
function get_prefix($sep = null) {
|
78 |
+
$sep = $this->get_sep($sep);
|
79 |
+
$prefix = ( !empty($this->parent->prefix) ) ? $this->parent->prefix . $sep : '';
|
80 |
+
return $prefix;
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* Check if a string is prefixed
|
85 |
+
* @param string $text Text to check for prefix
|
86 |
+
* @param string $sep (optional) Separator used
|
87 |
+
*/
|
88 |
+
function has_prefix($text, $sep = null) {
|
89 |
+
return ( !empty($text) && strpos($text, $this->get_prefix($sep)) === 0 );
|
90 |
+
}
|
91 |
+
|
92 |
+
/**
|
93 |
+
* Prepend plugin prefix to some text
|
94 |
+
* @param string $text Text to add to prefix
|
95 |
+
* @param string $sep (optional) Text used to separate prefix and text
|
96 |
+
* @param bool $once (optional) Whether to add prefix to text that already contains a prefix or not
|
97 |
+
* @return string Text with prefix prepended
|
98 |
+
*/
|
99 |
+
function add_prefix($text, $sep = '_', $once = true) {
|
100 |
+
if ( $once && $this->has_prefix($text, $sep) )
|
101 |
+
return $text;
|
102 |
+
return $this->get_prefix($sep) . $text;
|
103 |
+
}
|
104 |
+
|
105 |
+
/**
|
106 |
+
* Prepend uppercased plugin prefix to some text
|
107 |
+
* @param string $text Text to add to prefix
|
108 |
+
* @param string $sep (optional) Text used to separate prefix and text
|
109 |
+
* @param bool $once (optional) Whether to add prefix to text that already contains a prefix or not
|
110 |
+
* @return string Text with prefix prepended
|
111 |
+
*/
|
112 |
+
function add_prefix_uc($text, $sep = '_', $once = true) {
|
113 |
+
$args = func_get_args();
|
114 |
+
$var = call_user_func_array($this->m($this, 'add_prefix'), $args);
|
115 |
+
$pre = $this->get_prefix();
|
116 |
+
return str_replace($pre . $sep, strtoupper($pre) . $sep, $var);
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* Add prefix to variable reference
|
121 |
+
* Updates actual variable rather than return value
|
122 |
+
* @uses add_prefix() to add prefix to variable
|
123 |
+
* @param string $var Variable to add prefix to
|
124 |
+
* @param string $sep (optional) Separator text
|
125 |
+
* @param bool $once (optional) Add prefix only once
|
126 |
+
* @return void
|
127 |
+
*/
|
128 |
+
function add_prefix_ref(&$var, $sep = null, $once = true) {
|
129 |
+
$args = func_get_args();
|
130 |
+
$var = call_user_func_array($this->m($this, 'add_prefix'), $args);
|
131 |
+
}
|
132 |
+
|
133 |
+
/**
|
134 |
+
* Remove prefix from specified string
|
135 |
+
* @param string $text String to remove prefix from
|
136 |
+
* @param string $sep (optional) Separator used with prefix
|
137 |
+
*/
|
138 |
+
function remove_prefix($text, $sep = '_') {
|
139 |
+
if ( $this->has_prefix($text,$sep) )
|
140 |
+
$text = substr($text, strlen($this->get_prefix($sep)));
|
141 |
+
return $text;
|
142 |
+
}
|
143 |
+
|
144 |
+
/**
|
145 |
+
* Returns Database prefix for plugin-related DB Tables
|
146 |
+
* @return string Database prefix
|
147 |
+
*/
|
148 |
+
function get_db_prefix() {
|
149 |
+
global $wpdb;
|
150 |
+
return $wpdb->prefix . $this->get_prefix('_');
|
151 |
+
}
|
152 |
+
|
153 |
+
/*-** Client **-*/
|
154 |
+
|
155 |
+
/**
|
156 |
+
* Parses client files array
|
157 |
+
* > Adds ID property (prefixed file key)
|
158 |
+
* > Parses and validates internal dependencies
|
159 |
+
* > Converts properties array to object
|
160 |
+
* @param array $files Files array
|
161 |
+
* @return object Client files
|
162 |
+
*/
|
163 |
+
function parse_client_files($files, $type = 'scripts') {
|
164 |
+
if ( is_array($files) && !empty($files) ) {
|
165 |
+
foreach ( $files as $h => $p ) {
|
166 |
+
//Defaults
|
167 |
+
$defaults = array(
|
168 |
+
'id' => $this->add_prefix($h),
|
169 |
+
'file' => null,
|
170 |
+
'deps' => array(),
|
171 |
+
'callback' => null,
|
172 |
+
'context' => array()
|
173 |
+
);
|
174 |
+
switch ( $type ) {
|
175 |
+
case 'styles':
|
176 |
+
$defaults['media'] = 'all';
|
177 |
+
break;
|
178 |
+
default:
|
179 |
+
$defaults['in_footer'] = false;
|
180 |
+
}
|
181 |
+
|
182 |
+
//Type Validation
|
183 |
+
foreach ( $defaults as $m => $d ) {
|
184 |
+
//Check if value requires validation
|
185 |
+
if ( !is_array($d) || !isset($p[$m]) || is_array($p[$m]) )
|
186 |
+
continue;
|
187 |
+
//Wrap value in array or destroy it
|
188 |
+
if ( is_scalar($p[$m]) )
|
189 |
+
$p[$m] = array($p[$m]);
|
190 |
+
else
|
191 |
+
unset($p[$m]);
|
192 |
+
}
|
193 |
+
|
194 |
+
$p = array_merge($defaults, $p);
|
195 |
+
|
196 |
+
//Validate file
|
197 |
+
$file =& $p['file'];
|
198 |
+
|
199 |
+
//Callback
|
200 |
+
if ( is_array($file) ) {
|
201 |
+
$file = $this->m($this->parent, array_shift($file));
|
202 |
+
if ( !is_callable($file) )
|
203 |
+
$file = null;
|
204 |
+
}
|
205 |
+
|
206 |
+
//Remove invalid files
|
207 |
+
if ( empty($file) ) {
|
208 |
+
unset($files[$h]);
|
209 |
+
continue;
|
210 |
+
}
|
211 |
+
|
212 |
+
//Validate callback
|
213 |
+
$cb =& $p['callback'];
|
214 |
+
if ( !is_null($cb) ) {
|
215 |
+
if ( is_array($cb) )
|
216 |
+
$cb = $this->m($this->parent, array_shift($cb));
|
217 |
+
if ( !is_callable($cb) )
|
218 |
+
$cb = null;
|
219 |
+
}
|
220 |
+
|
221 |
+
//Format internal dependencies
|
222 |
+
foreach ( $p['deps'] as $idx => $dep ) {
|
223 |
+
if ( substr($dep, 0, 1) == '[' && substr($dep, -1, 1) == ']' ) {
|
224 |
+
$dep = trim($dep, '[]');
|
225 |
+
$p['deps'][$idx] = $this->add_prefix($dep);
|
226 |
+
}
|
227 |
+
}
|
228 |
+
|
229 |
+
//Convert properties to object
|
230 |
+
$files[$h] = (object) $p;
|
231 |
+
|
232 |
+
unset($file, $cb);
|
233 |
+
}
|
234 |
+
}
|
235 |
+
//Cast to object before returning
|
236 |
+
if ( !is_object($files) )
|
237 |
+
$files = (object) $files;
|
238 |
+
return $files;
|
239 |
+
}
|
240 |
+
|
241 |
+
/**
|
242 |
+
* Build JS client object
|
243 |
+
* @param string (optional) $path Additional object path
|
244 |
+
* @return string Client object
|
245 |
+
*/
|
246 |
+
function get_client_object($path = null) {
|
247 |
+
$obj = strtoupper($this->get_prefix());
|
248 |
+
if ( !empty($path) && is_string($path) ) {
|
249 |
+
if ( 0 !== strpos($path, '[') )
|
250 |
+
$obj .= '.';
|
251 |
+
$obj .= $path;
|
252 |
+
}
|
253 |
+
return $obj;
|
254 |
+
}
|
255 |
+
|
256 |
+
/**
|
257 |
+
* Build jQuery JS expression to add data to specified client object
|
258 |
+
* @param string $obj Name of client object (Set to root object if not a valid name)
|
259 |
+
* @param mixed $data Data to add to client object
|
260 |
+
* @param bool (optional) $out Whether or not to output code (Default: false)
|
261 |
+
* @return string JS expression to extend client object
|
262 |
+
*/
|
263 |
+
function extend_client_object($obj, $data = null, $out = false) {
|
264 |
+
//Validate parameters
|
265 |
+
$args = func_get_args();
|
266 |
+
switch ( count($args) ) {
|
267 |
+
case 2:
|
268 |
+
if ( !is_scalar($args[0]) ) {
|
269 |
+
if ( is_bool($args[1]) )
|
270 |
+
$out = $args[1];
|
271 |
+
} else {
|
272 |
+
break;
|
273 |
+
}
|
274 |
+
case 1:
|
275 |
+
$data = $args[0];
|
276 |
+
$obj = null;
|
277 |
+
break;
|
278 |
+
}
|
279 |
+
//Default client object
|
280 |
+
if ( !is_string($obj) || empty($obj) )
|
281 |
+
$obj = null;
|
282 |
+
//Default data
|
283 |
+
if ( is_array($data) )
|
284 |
+
$data = (object)$data;
|
285 |
+
//Build expression
|
286 |
+
if ( empty($data) || ( empty($obj) && is_scalar($data) ) ) {
|
287 |
+
$ret = '';
|
288 |
+
} else {
|
289 |
+
$ret = array();
|
290 |
+
//Validate object(s) being extended
|
291 |
+
$c_obj = $this->get_client_object($obj);
|
292 |
+
$sep = '.';
|
293 |
+
$pos = strpos($c_obj, $sep);
|
294 |
+
$start = $offset = 0;
|
295 |
+
$objs = array();
|
296 |
+
if ( false !== $pos ) {
|
297 |
+
while ( false !== $pos ) {
|
298 |
+
$objs[] = substr($c_obj, $start, $pos);
|
299 |
+
$offset = $pos + 1;
|
300 |
+
$pos = strpos($c_obj, $sep, $offset);
|
301 |
+
}
|
302 |
+
} else {
|
303 |
+
$objs[] = $c_obj;
|
304 |
+
}
|
305 |
+
$condition = 'if ( ' . implode(' && ', $objs) . ' ) ';
|
306 |
+
$ret = $condition . '$.extend(' . $c_obj . ', ' . json_encode($data) . ');';
|
307 |
+
if ( $out )
|
308 |
+
echo $this->build_script_element($ret);
|
309 |
+
}
|
310 |
+
return $ret;
|
311 |
+
}
|
312 |
+
|
313 |
+
/**
|
314 |
+
* Build client method call
|
315 |
+
* @uses get_client_object() to generate the body of the method call
|
316 |
+
* @param string $method Method name
|
317 |
+
* @param mixed Parameters to pass to method (will be JSON-encoded)
|
318 |
+
* @return string Method call
|
319 |
+
*/
|
320 |
+
function call_client_method($method, $params = null) {
|
321 |
+
if ( !$method )
|
322 |
+
return '';
|
323 |
+
$params = ( !is_null($params) ) ? json_encode($params) : '';
|
324 |
+
return $this->get_client_object($method) . '(' . $params. ');';
|
325 |
+
}
|
326 |
+
|
327 |
+
/*-** WP **-*/
|
328 |
+
|
329 |
+
/**
|
330 |
+
* Checks if $post is a valid Post object
|
331 |
+
* If $post is not valid, assigns global post object to $post (if available)
|
332 |
+
* @return bool TRUE if $post is valid object by end of function processing
|
333 |
+
* @param object $post Post object to evaluate
|
334 |
+
*/
|
335 |
+
function check_post(&$post) {
|
336 |
+
if (empty($post)) {
|
337 |
+
if (isset($GLOBALS['post'])) {
|
338 |
+
$post = $GLOBALS['post'];
|
339 |
+
$GLOBALS['post'] =& $post;
|
340 |
+
}
|
341 |
+
else
|
342 |
+
return false;
|
343 |
+
}
|
344 |
+
if (is_array($post))
|
345 |
+
$post = (object) $post;
|
346 |
+
elseif (is_numeric($post))
|
347 |
+
$post = get_post($post);
|
348 |
+
if (!is_object($post))
|
349 |
+
return false;
|
350 |
+
return true;
|
351 |
+
}
|
352 |
+
|
353 |
+
/* Hooks */
|
354 |
+
|
355 |
+
function do_action($tag, $arg = '') {
|
356 |
+
do_action($this->add_prefix($tag), $arg);
|
357 |
+
}
|
358 |
+
|
359 |
+
function apply_filters($tag, $value) {
|
360 |
+
apply_filters($this->add_prefix($tag), $value);
|
361 |
+
}
|
362 |
+
|
363 |
+
function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
|
364 |
+
return add_action($this->add_prefix($tag), $function_to_add, $priority, $accepted_args);
|
365 |
+
}
|
366 |
+
|
367 |
+
function add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
|
368 |
+
return add_filter($this->add_prefix(tag), $function_to_add, $priority, $accepted_args);
|
369 |
+
}
|
370 |
+
|
371 |
+
/* Meta */
|
372 |
+
|
373 |
+
/**
|
374 |
+
* Retrieves post metadata for internal methods
|
375 |
+
* Metadata set internally is wrapped in an array so it is unwrapped before returned the retrieved value
|
376 |
+
* @see get_post_meta()
|
377 |
+
* @param int $post_id Post ID
|
378 |
+
* @param string $key Name of metadata to retrieve
|
379 |
+
* @param boolean $single Whether or not to retrieve single value or not
|
380 |
+
* @return mixed Retrieved post metadata
|
381 |
+
*/
|
382 |
+
function post_meta_get($post_id, $key, $single = false) {
|
383 |
+
$meta_value = get_post_meta($post_id, $this->post_meta_get_key($key), $single);
|
384 |
+
if (is_array($meta_value) && count($meta_value) == 1)
|
385 |
+
$meta_value = $meta_value[0];
|
386 |
+
return $meta_value;
|
387 |
+
}
|
388 |
+
|
389 |
+
/**
|
390 |
+
* Wraps metadata in array for storage in database
|
391 |
+
* @param mixed $meta_value Value to be set as metadata
|
392 |
+
* @return array Wrapped metadata value
|
393 |
+
*/
|
394 |
+
function post_meta_prepare_value($meta_value) {
|
395 |
+
return array($meta_value);
|
396 |
+
}
|
397 |
+
|
398 |
+
/**
|
399 |
+
* Adds Metadata for a post to database
|
400 |
+
* For internal methods
|
401 |
+
* @see add_post_meta
|
402 |
+
* @param $post_id
|
403 |
+
* @param $meta_key
|
404 |
+
* @param $meta_value
|
405 |
+
* @param $unique
|
406 |
+
* @return boolean Result of operation
|
407 |
+
*/
|
408 |
+
function post_meta_add($post_id, $meta_key, $meta_value, $unique = false) {
|
409 |
+
$meta_value = $this->post_meta_value_prepare($meta_value);
|
410 |
+
return add_post_meta($post_id, $meta_key, $meta_value, $unique);
|
411 |
+
}
|
412 |
+
|
413 |
+
/**
|
414 |
+
* Updates post metadata for internal data/methods
|
415 |
+
* @see update_post_meta()
|
416 |
+
* @param $post_id
|
417 |
+
* @param $meta_key
|
418 |
+
* @param $meta_value
|
419 |
+
* @param $prev_value
|
420 |
+
* @return boolean Result of operation
|
421 |
+
*/
|
422 |
+
function post_meta_update($post_id, $meta_key, $meta_value, $prev_value = '') {
|
423 |
+
$meta_value = $this->post_meta_prepare_value($meta_value);
|
424 |
+
return update_post_meta($post_id, $meta_key, $meta_value, $prev_value);
|
425 |
+
}
|
426 |
+
|
427 |
+
/**
|
428 |
+
* Builds postmeta key for custom data set by plugin
|
429 |
+
* @param string $key Base key name
|
430 |
+
* @return string Formatted postmeta key
|
431 |
+
*/
|
432 |
+
function post_meta_get_key($key) {
|
433 |
+
$sep = '_';
|
434 |
+
if ( strpos($key, $sep . $this->prefix) !== 0 ) {
|
435 |
+
$key_base = func_get_args();
|
436 |
+
if ( !empty($key_base) ) {
|
437 |
+
$key = array_merge((array)$this->prefix, $key_base);
|
438 |
+
return $sep . implode($sep, $key);
|
439 |
+
}
|
440 |
+
}
|
441 |
+
|
442 |
+
return $key;
|
443 |
+
}
|
444 |
+
|
445 |
+
/**
|
446 |
+
* Creates a meta key for storing post meta data
|
447 |
+
* Prefixes standard prefixed text with underscore to hide meta data on post edit forms
|
448 |
+
* @param string $text Text to use as base of meta key
|
449 |
+
* @return string Formatted meta key
|
450 |
+
*/
|
451 |
+
function make_meta_key($text = '') {
|
452 |
+
return '_' . $this->add_prefix($text);
|
453 |
+
}
|
454 |
+
|
455 |
+
/*-** Request **-*/
|
456 |
+
|
457 |
+
/**
|
458 |
+
* Checks if the currently executing file matches specified file name
|
459 |
+
* @param string $filename Filename to check for
|
460 |
+
* @return bool TRUE if current page matches specified filename, FALSE otherwise
|
461 |
+
*/
|
462 |
+
function is_file( $filename ) {
|
463 |
+
return ( $filename == basename( $_SERVER['SCRIPT_NAME'] ) );
|
464 |
+
}
|
465 |
+
|
466 |
+
/**
|
467 |
+
* Checks whether the current page is a management page
|
468 |
+
* @return bool TRUE if current page is a management page, FALSE otherwise
|
469 |
+
*/
|
470 |
+
function is_admin_management_page() {
|
471 |
+
return ( is_admin()
|
472 |
+
&& ( $this->is_file('edit.php')
|
473 |
+
|| ( $this->is_file('admin.php')
|
474 |
+
&& isset($_GET['page'])
|
475 |
+
&& strpos($_GET['page'], $this->get_prefix()) === 0 )
|
476 |
+
)
|
477 |
+
);
|
478 |
+
}
|
479 |
+
|
480 |
+
/* Context */
|
481 |
+
|
482 |
+
/**
|
483 |
+
* Retrieve context for current request
|
484 |
+
* @return array Context
|
485 |
+
*/
|
486 |
+
function get_context() {
|
487 |
+
//Context
|
488 |
+
static $ctx = null;
|
489 |
+
if ( !is_array($ctx) ) {
|
490 |
+
//Standard
|
491 |
+
$ctx = array($this->build_context());
|
492 |
+
//Action
|
493 |
+
$action = $this->get_action();
|
494 |
+
if ( !empty($action) )
|
495 |
+
$ctx[] = $this->build_context('action', $action);
|
496 |
+
//Admin page
|
497 |
+
if ( is_admin() ) {
|
498 |
+
global $pagenow;
|
499 |
+
$pg = $this->strip_file_extension($pagenow);
|
500 |
+
$ctx[] = $this->build_context('page', $pg);
|
501 |
+
if ( !empty($action) )
|
502 |
+
$ctx[] = $this->build_context('page', $pg, 'action', $action);
|
503 |
+
}
|
504 |
+
//User
|
505 |
+
$u = wp_get_current_user();
|
506 |
+
$ctx[] = $this->build_context('user', ( $u->ID ) ? 'registered' : 'guest', false);
|
507 |
+
}
|
508 |
+
|
509 |
+
return $ctx;
|
510 |
+
}
|
511 |
+
|
512 |
+
/**
|
513 |
+
* Builds context from multiple components
|
514 |
+
* Usage:
|
515 |
+
* > $prefix can be omitted and context strings can be added as needed
|
516 |
+
* > Multiple context strings may be passed to be joined together
|
517 |
+
*
|
518 |
+
* @param string (optional) $context Variable number of components to add to context
|
519 |
+
* @param bool (optional) $prefix Whether or not to prefix context with request type (public or admin) [Default: TRUE]
|
520 |
+
* @return string Context
|
521 |
+
*/
|
522 |
+
function build_context($context = null, $prefix = true) {
|
523 |
+
$args = func_get_args();
|
524 |
+
//Get prefix option
|
525 |
+
if ( !empty($args) ) {
|
526 |
+
$prefix = ( is_bool($args[count($args) - 1]) ) ? array_pop($args) : true;
|
527 |
+
}
|
528 |
+
|
529 |
+
//Validate
|
530 |
+
$context = array_filter($args, 'is_string');
|
531 |
+
$sep = '_';
|
532 |
+
|
533 |
+
//Context Prefix
|
534 |
+
if ( $prefix )
|
535 |
+
array_unshift($context, ( is_admin() ) ? 'admin' : 'public' );
|
536 |
+
return implode($sep, $context);
|
537 |
+
}
|
538 |
+
|
539 |
+
/**
|
540 |
+
* Check if context exists in current request
|
541 |
+
* @param string $context Context to check for
|
542 |
+
* @return bool TRUE if context exists FALSE otherwise
|
543 |
+
*/
|
544 |
+
function is_context($context) {
|
545 |
+
$ret = false;
|
546 |
+
if ( is_scalar($context) )
|
547 |
+
$context = array($context);
|
548 |
+
if ( is_array($context) && !empty($context) ) {
|
549 |
+
$ictx = array_intersect($this->get_context(), $context);
|
550 |
+
if ( !empty($ictx) )
|
551 |
+
$ret = true;
|
552 |
+
}
|
553 |
+
return $ret;
|
554 |
+
}
|
555 |
+
|
556 |
+
/**
|
557 |
+
* Joins and normalizes the slashes in the paths passed to method
|
558 |
+
* All forward/back slashes are converted to forward slashes
|
559 |
+
* Multiple path segments can be passed as additional argments
|
560 |
+
* @param string $path Path to normalize
|
561 |
+
* @param bool|array $trailing_slash (optional) Whether or not normalized path should have a trailing slash or not (Default: FALSE)
|
562 |
+
* If array is passed, first index is trailing, second is leading slash
|
563 |
+
* If multiple path segments are passed, $trailing_slash will be the LAST parameter (default value used if omitted)
|
564 |
+
*/
|
565 |
+
function normalize_path($path, $trailing_slash = false) {
|
566 |
+
$sl_f = '/';
|
567 |
+
$sl_b = '\\';
|
568 |
+
$parts = func_get_args();
|
569 |
+
//Slash defaults (trailing, leading);
|
570 |
+
$slashes = array(false, true);
|
571 |
+
if ( func_num_args() > 1 ) {
|
572 |
+
//Get last argument
|
573 |
+
$arg_last = $parts[count($parts) - 1];
|
574 |
+
if ( is_bool($arg_last) ) {
|
575 |
+
$arg_last = array($arg_last);
|
576 |
+
}
|
577 |
+
|
578 |
+
if ( is_array($arg_last) && count($arg_last) > 0 && is_bool($arg_last[0]) ) {
|
579 |
+
//Remove slash paramter from args array
|
580 |
+
array_pop($parts);
|
581 |
+
//Normalize slashes options
|
582 |
+
if ( isset($arg_last[0]) )
|
583 |
+
$slashes[0] = $arg_last[0];
|
584 |
+
if ( isset($arg_last[1]) )
|
585 |
+
$slashes[1] = $arg_last[1];
|
586 |
+
}
|
587 |
+
}
|
588 |
+
//Extract to slash options local variables
|
589 |
+
list($trailing_slash, $leading_slash) = $slashes;
|
590 |
+
|
591 |
+
//Clean path segments
|
592 |
+
foreach ( $parts as $key => $part ) {
|
593 |
+
//Trim slashes/spaces
|
594 |
+
$parts[$key] = trim($part, " " . $sl_f . $sl_b);
|
595 |
+
|
596 |
+
//Verify path segment still contains value
|
597 |
+
if ( empty($parts[$key]) ) {
|
598 |
+
unset($parts[$key]);
|
599 |
+
continue;
|
600 |
+
}
|
601 |
+
}
|
602 |
+
|
603 |
+
//Join path parts together
|
604 |
+
$parts = implode($sl_b, $parts);
|
605 |
+
$parts = str_replace($sl_b, $sl_f, $parts);
|
606 |
+
//Add trailing slash (if necessary)
|
607 |
+
if ( $trailing_slash )
|
608 |
+
$parts .= $sl_f;
|
609 |
+
//Add leading slash (if necessary)
|
610 |
+
$regex = '#^.+:[\\/]#';
|
611 |
+
if ( $leading_slash && !preg_match($regex, $parts) ) {
|
612 |
+
$parts = $sl_f . $parts;
|
613 |
+
}
|
614 |
+
return $parts;
|
615 |
+
}
|
616 |
+
|
617 |
+
/**
|
618 |
+
* Returns URL of file (assumes that it is in plugin directory)
|
619 |
+
* @param string $file name of file get URL
|
620 |
+
* @return string File URL
|
621 |
+
*/
|
622 |
+
function get_file_url($file) {
|
623 |
+
if ( is_string($file) && '' != trim($file) ) {
|
624 |
+
$file = str_replace(' ', '%20', $this->normalize_path($this->get_url_base(), $file));
|
625 |
+
}
|
626 |
+
return $file;
|
627 |
+
}
|
628 |
+
|
629 |
+
/**
|
630 |
+
* Returns path to plugin file
|
631 |
+
* @param string $file file name
|
632 |
+
* @return string File path
|
633 |
+
*/
|
634 |
+
function get_file_path($file) {
|
635 |
+
if ( is_string($file) && '' != trim($file) ) {
|
636 |
+
$file = $this->normalize_path($this->get_path_base(), $file);
|
637 |
+
}
|
638 |
+
return $file;
|
639 |
+
}
|
640 |
+
|
641 |
+
function get_plugin_file_path($file, $trailing_slash = false) {
|
642 |
+
if ( is_string($file) && '' != trim($file) )
|
643 |
+
$file = $this->normalize_path($this->get_plugin_base(), $file, $trailing_slash);
|
644 |
+
return $file;
|
645 |
+
}
|
646 |
+
|
647 |
+
/**
|
648 |
+
* Retrieves file extension
|
649 |
+
* @param string $file file name/path
|
650 |
+
* @param bool (optional) $lowercase Whether lowercase extension should be returned (Default: TRUE)
|
651 |
+
* @return string File's extension
|
652 |
+
*/
|
653 |
+
function get_file_extension($file, $lowercase = true) {
|
654 |
+
$ret = '';
|
655 |
+
$sep = '.';
|
656 |
+
if ( ( $rpos = strrpos($file, $sep) ) !== false )
|
657 |
+
$ret = substr($file, $rpos + 1);
|
658 |
+
if ( $lowercase )
|
659 |
+
$ret = strtolower($ret);
|
660 |
+
return $ret;
|
661 |
+
}
|
662 |
+
|
663 |
+
/**
|
664 |
+
* Checks if file has specified extension
|
665 |
+
* @uses get_file_extension()
|
666 |
+
* @param string $file File name/path
|
667 |
+
* @param string|array $extension File ending(s) to check $file for
|
668 |
+
* @param bool (optional) Whether check should be case senstive or not (Default: FALSE)
|
669 |
+
* @return bool TRUE if file has extension
|
670 |
+
*/
|
671 |
+
function has_file_extension($file, $extension, $case_sensitive = false) {
|
672 |
+
if ( !is_array($extension) )
|
673 |
+
$extension = array(strval($extension));
|
674 |
+
if ( !$case_sensitive ) {
|
675 |
+
//Normalize extensions
|
676 |
+
$extension = array_map('strtolower', $extension);
|
677 |
+
}
|
678 |
+
return ( in_array($this->get_file_extension($file, !$case_sensitive), $extension) ) ? true : false;
|
679 |
+
}
|
680 |
+
|
681 |
+
/**
|
682 |
+
* Removes file extension from file name
|
683 |
+
* The extension is the text following the last period ('.') in the file name
|
684 |
+
* @uses get_file_extension()
|
685 |
+
* @param string $file File name
|
686 |
+
* @return string File name without extension
|
687 |
+
*/
|
688 |
+
function strip_file_extension($file) {
|
689 |
+
$ext = $this->get_file_extension($file);
|
690 |
+
if ( !empty($ext) ) {
|
691 |
+
$file = substr($file, 0, (strlen($ext) + 1) * -1);
|
692 |
+
}
|
693 |
+
return $file;
|
694 |
+
}
|
695 |
+
|
696 |
+
/**
|
697 |
+
* Retrieve base URL for plugin-specific files
|
698 |
+
* @uses get_plugin_base()
|
699 |
+
* @uses normalize_path()
|
700 |
+
* @return string Base URL
|
701 |
+
*/
|
702 |
+
function get_url_base() {
|
703 |
+
static $url_base = '';
|
704 |
+
if ( '' == $url_base ) {
|
705 |
+
$url_base = $this->normalize_path(plugins_url(), $this->get_plugin_base());
|
706 |
+
}
|
707 |
+
return $url_base;
|
708 |
+
}
|
709 |
+
|
710 |
+
/**
|
711 |
+
* Retrieve plugin's base path
|
712 |
+
* @uses WP_PLUGIN_DIR
|
713 |
+
* @uses get_plugin_base()
|
714 |
+
* @uses normalize_path()
|
715 |
+
* @return string Base path
|
716 |
+
*/
|
717 |
+
function get_path_base() {
|
718 |
+
static $path_base = '';
|
719 |
+
if ( '' == $path_base ) {
|
720 |
+
$path_base = $this->normalize_path(WP_PLUGIN_DIR, $this->get_plugin_base());
|
721 |
+
}
|
722 |
+
return $path_base;
|
723 |
+
}
|
724 |
+
|
725 |
+
/**
|
726 |
+
* Retrieve plugin's base directory
|
727 |
+
* @uses WP_PLUGIN_DIR
|
728 |
+
* @uses normalize_path()
|
729 |
+
* @return string Base directory
|
730 |
+
*/
|
731 |
+
function get_plugin_base($trim = false) {
|
732 |
+
static $plugin_dir = '';
|
733 |
+
if ( '' == $plugin_dir ) {
|
734 |
+
$plugin_dir = str_replace($this->normalize_path(WP_PLUGIN_DIR), '', $this->normalize_path(dirname(dirname(__FILE__))));
|
735 |
+
}
|
736 |
+
if ( $trim )
|
737 |
+
$plugin_dir = trim($plugin_dir, ' \/');
|
738 |
+
return $plugin_dir;
|
739 |
+
}
|
740 |
+
|
741 |
+
/**
|
742 |
+
* Retrieve plugin's base file path
|
743 |
+
* @uses get_path_base()
|
744 |
+
* @uses get_file_path()
|
745 |
+
* @return string Base file path
|
746 |
+
*/
|
747 |
+
function get_plugin_base_file() {
|
748 |
+
static $file = '';
|
749 |
+
if ( empty($file) ) {
|
750 |
+
$dir = @ opendir($this->get_path_base());
|
751 |
+
if ( $dir ) {
|
752 |
+
while ( ($ftemp = readdir($dir)) !== false ) {
|
753 |
+
//Only process PHP files
|
754 |
+
$ftemp = $this->get_file_path($ftemp);
|
755 |
+
if ( !$this->has_file_extension($ftemp, 'php') || !is_readable($ftemp) )
|
756 |
+
continue;
|
757 |
+
//Check for data
|
758 |
+
$data = get_file_data($ftemp, $this->plugin_headers);
|
759 |
+
if ( !empty($data['Name']) ) {
|
760 |
+
//Set base file
|
761 |
+
$file = $ftemp;
|
762 |
+
break;
|
763 |
+
}
|
764 |
+
}
|
765 |
+
}
|
766 |
+
@closedir($dir);
|
767 |
+
}
|
768 |
+
//Return
|
769 |
+
return $file;
|
770 |
+
}
|
771 |
+
|
772 |
+
/**
|
773 |
+
* Retrieve plugin's internal name
|
774 |
+
* Internal name is used by WP core
|
775 |
+
* @uses get_plugin_base_file()
|
776 |
+
* @uses plugin_basename()
|
777 |
+
* @return string Internal plugin name
|
778 |
+
*/
|
779 |
+
function get_plugin_base_name() {
|
780 |
+
static $name = false;
|
781 |
+
if ( !$name ) {
|
782 |
+
$file = $this->get_plugin_base_file();
|
783 |
+
$name = plugin_basename($file);
|
784 |
+
}
|
785 |
+
return $name;
|
786 |
+
}
|
787 |
+
|
788 |
+
/**
|
789 |
+
* Retrieve plugin info
|
790 |
+
* Parses info comment in main plugin file
|
791 |
+
* @uses get_plugin_base_file()
|
792 |
+
*/
|
793 |
+
function get_plugin_info($field = '') {
|
794 |
+
static $data = array();
|
795 |
+
$ret = '';
|
796 |
+
//Get plugin data
|
797 |
+
if ( empty($data) ) {
|
798 |
+
$file = $this->get_plugin_base_file();
|
799 |
+
$data = get_file_data($file, $this->plugin_headers);
|
800 |
+
}
|
801 |
+
//Return specified field
|
802 |
+
if ( !empty($field) ) {
|
803 |
+
if ( isset($data[$field]) )
|
804 |
+
$ret = $data[$field];
|
805 |
+
} else {
|
806 |
+
$ret = $data;
|
807 |
+
}
|
808 |
+
return $ret;
|
809 |
+
}
|
810 |
+
|
811 |
+
/**
|
812 |
+
* Retrieve plugin version
|
813 |
+
* @uses get_plugin_info()
|
814 |
+
* @param bool $strip_desc Strip any additional version text
|
815 |
+
* @return string Plugin version
|
816 |
+
*/
|
817 |
+
function get_plugin_version($strip_desc = true) {
|
818 |
+
static $v = '';
|
819 |
+
//Retrieve version
|
820 |
+
if ( empty($v) ) {
|
821 |
+
$field = 'Version';
|
822 |
+
$v = $this->get_plugin_info($field);
|
823 |
+
}
|
824 |
+
//Format
|
825 |
+
$ret = $v;
|
826 |
+
if ( $strip_desc ) {
|
827 |
+
$ret = explode(' ', $ret, 2);
|
828 |
+
$ret = $ret[0];
|
829 |
+
}
|
830 |
+
//Return
|
831 |
+
return $ret;
|
832 |
+
}
|
833 |
+
|
834 |
+
/**
|
835 |
+
* Retrieve plugin textdomain (for localization)
|
836 |
+
* @return string
|
837 |
+
*/
|
838 |
+
function get_plugin_textdomain() {
|
839 |
+
static $dom = '';
|
840 |
+
if ( empty($dom) )
|
841 |
+
$dom = $this->get_plugin_base(true);
|
842 |
+
return $dom;
|
843 |
+
}
|
844 |
+
|
845 |
+
/**
|
846 |
+
* Retrieve current action based on URL query variables
|
847 |
+
* @param mixed $default (optional) Default action if no action exists
|
848 |
+
* @return string Current action
|
849 |
+
*/
|
850 |
+
function get_action($default = null) {
|
851 |
+
$action = '';
|
852 |
+
|
853 |
+
//Check if action is set in URL
|
854 |
+
if ( isset($_GET['action']) )
|
855 |
+
$action = $_GET['action'];
|
856 |
+
//Otherwise, Determine action based on plugin plugin admin page suffix
|
857 |
+
elseif ( isset($_GET['page']) && ($pos = strrpos($_GET['page'], '-')) && $pos !== false && ( $pos != count($_GET['page']) - 1 ) )
|
858 |
+
$action = trim(substr($_GET['page'], $pos + 1), '-_');
|
859 |
+
|
860 |
+
//Determine action for core admin pages
|
861 |
+
if ( ! isset($_GET['page']) || empty($action) ) {
|
862 |
+
$actions = array(
|
863 |
+
'add' => array('page-new', 'post-new'),
|
864 |
+
'edit-item' => array('page', 'post'),
|
865 |
+
'edit' => array('edit', 'edit-pages')
|
866 |
+
);
|
867 |
+
$page = basename($_SERVER['SCRIPT_NAME'], '.php');
|
868 |
+
|
869 |
+
foreach ( $actions as $act => $pages ) {
|
870 |
+
if ( in_array($page, $pages) ) {
|
871 |
+
$action = $act;
|
872 |
+
break;
|
873 |
+
}
|
874 |
+
}
|
875 |
+
}
|
876 |
+
if ( empty($action) )
|
877 |
+
$action = $default;
|
878 |
+
return $action;
|
879 |
+
}
|
880 |
+
|
881 |
+
/*-** General **-*/
|
882 |
+
|
883 |
+
/**
|
884 |
+
* Checks if last parameter sent to a function is an array of options and returns it
|
885 |
+
* Calling function should use `func_get_args()` and pass the value to this method
|
886 |
+
* @param array $args Parameters passed to calling function
|
887 |
+
* @return array Options array (Default: empty array)
|
888 |
+
*/
|
889 |
+
function func_get_options($args) {
|
890 |
+
$r = array();
|
891 |
+
if ( is_array($args) && !empty($args) ) {
|
892 |
+
$last = count($args) - 1;
|
893 |
+
if ( is_array($args[$last]) )
|
894 |
+
$r = $args[$last];
|
895 |
+
}
|
896 |
+
return $r;
|
897 |
+
}
|
898 |
+
|
899 |
+
/**
|
900 |
+
* Checks if a property exists in a class or object
|
901 |
+
* (Compatibility method for PHP 4
|
902 |
+
* @param mixed $class Class or object to check
|
903 |
+
* @param string $property Name of property to look for in $class
|
904 |
+
*/
|
905 |
+
function property_exists($class, $property) {
|
906 |
+
if ( !is_object($class) && !is_array($class) )
|
907 |
+
return false;
|
908 |
+
if ( function_exists('property_exists') && is_object($class) ) {
|
909 |
+
return property_exists($class, $property);
|
910 |
+
} else {
|
911 |
+
return array_key_exists($property, $class);
|
912 |
+
}
|
913 |
+
}
|
914 |
+
|
915 |
+
/**
|
916 |
+
* Retrieve specified property from object or array
|
917 |
+
* @param object|array $obj Object or array to get property from
|
918 |
+
* @param string $property Property name to retrieve
|
919 |
+
* @return mixed Property value
|
920 |
+
*/
|
921 |
+
static function &get_property($obj, $property) {
|
922 |
+
$property = trim($property);
|
923 |
+
//Object
|
924 |
+
if ( is_object($obj) )
|
925 |
+
return $obj->{$property};
|
926 |
+
//Array
|
927 |
+
if ( is_array($obj) )
|
928 |
+
return $obj[$property];
|
929 |
+
//Class
|
930 |
+
if ( is_string($obj) && class_exists($obj) ) {
|
931 |
+
$cvars = get_class_vars($obj);
|
932 |
+
if ( isset($cvars[$property]) )
|
933 |
+
return $cvars[$property];
|
934 |
+
}
|
935 |
+
}
|
936 |
+
|
937 |
+
/**
|
938 |
+
* Remap array members based on a
|
939 |
+
* mapping of source/destination keys
|
940 |
+
* @param array $properties Associative array of properties
|
941 |
+
* @param array $map Source/Destination mapping
|
942 |
+
* > Key: Source member
|
943 |
+
* > Val: Destination member
|
944 |
+
* @param bool $overwrite If TRUE, source value will be set in destination regardless of whether member already exists or not
|
945 |
+
* @return array Remapped properties
|
946 |
+
*/
|
947 |
+
function array_remap($arr, $map = array(), $overwrite = false) {
|
948 |
+
if ( !empty($map) && is_array($map) ) {
|
949 |
+
//Iterate through mappings
|
950 |
+
foreach ( $map as $from => $to ) {
|
951 |
+
if ( !array_key_exists($from, $arr) )
|
952 |
+
continue;
|
953 |
+
$move = $overwrite;
|
954 |
+
//Only remap if parent property doesn't already exist in array
|
955 |
+
if ( !array_key_exists($to, $arr) )
|
956 |
+
$move = true;
|
957 |
+
if ( $move ) {
|
958 |
+
//Move member value to new key
|
959 |
+
$arr[$to] = $arr[$from];
|
960 |
+
//Remove source member
|
961 |
+
unset($arr[$from]);
|
962 |
+
}
|
963 |
+
}
|
964 |
+
}
|
965 |
+
//Return remapped properties
|
966 |
+
return $arr;
|
967 |
+
}
|
968 |
+
|
969 |
+
function array_filter_keys($arr, $keys) {
|
970 |
+
if ( is_array($arr) && !empty($arr) && is_array($keys) && !empty($keys) ) {
|
971 |
+
foreach ( $keys as $rem ) {
|
972 |
+
if ( array_key_exists($rem, $arr) )
|
973 |
+
unset($arr[$rem]);
|
974 |
+
}
|
975 |
+
}
|
976 |
+
|
977 |
+
return $arr;
|
978 |
+
}
|
979 |
+
|
980 |
+
/**
|
981 |
+
* Insert an item into an array at the specified position
|
982 |
+
* @param mixed $item Item to insert into array
|
983 |
+
* @param int $pos Index position to insert item into array
|
984 |
+
* @return array Modified array
|
985 |
+
*/
|
986 |
+
function array_insert($array, $item, $pos = null) {
|
987 |
+
array_splice($array, $pos, 0, $item);
|
988 |
+
return $array;
|
989 |
+
}
|
990 |
+
|
991 |
+
/**
|
992 |
+
* Merges 1 or more arrays together
|
993 |
+
* Methodology
|
994 |
+
* - Set first parameter as base array
|
995 |
+
* - All other parameters will be merged into base array
|
996 |
+
* - Iterate through other parameters (arrays)
|
997 |
+
* - Skip all non-array parameters
|
998 |
+
* - Iterate though key/value pairs of current array
|
999 |
+
* - Merge item in base array with current item based on key name
|
1000 |
+
* - If the current item's value AND the corresponding item in the base array are BOTH arrays, recursively merge the the arrays
|
1001 |
+
* - If the current item's value OR the corresponding item in the base array is NOT an array, current item overwrites base item
|
1002 |
+
* @param array Variable number of arrays
|
1003 |
+
* @param array $arr1 Default array
|
1004 |
+
* @return array Merged array
|
1005 |
+
*/
|
1006 |
+
function array_merge_recursive_distinct($arr1) {
|
1007 |
+
//Get all arrays passed to function
|
1008 |
+
$args = func_get_args();
|
1009 |
+
if ( empty($args) )
|
1010 |
+
return false;
|
1011 |
+
//Return empty array if first parameter is not an array
|
1012 |
+
if ( !is_array($args[0]) )
|
1013 |
+
return array();
|
1014 |
+
//Set first array as base array
|
1015 |
+
$merged = $args[0];
|
1016 |
+
//Iterate through arrays to merge
|
1017 |
+
$arg_length = count($args);
|
1018 |
+
for ( $x = 1; $x < $arg_length; $x++ ) {
|
1019 |
+
//Skip if argument is not an array (only merge arrays)
|
1020 |
+
if ( !is_array($args[$x]) )
|
1021 |
+
continue;
|
1022 |
+
//Iterate through argument items
|
1023 |
+
foreach ( $args[$x] as $key => $val ) {
|
1024 |
+
//Generate key for numeric indexes
|
1025 |
+
if ( is_int($key) ) {
|
1026 |
+
//Add new item to merged array
|
1027 |
+
$merged[] = null;
|
1028 |
+
//Get key of new item
|
1029 |
+
$key = array_pop(array_keys($merged));
|
1030 |
+
}
|
1031 |
+
if ( !isset($merged[$key]) || !is_array($merged[$key]) || !is_array($val) ) {
|
1032 |
+
$merged[$key] = $val;
|
1033 |
+
} elseif ( is_array($merged[$key]) && is_array($val) ) {
|
1034 |
+
$merged[$key] = $this->array_merge_recursive_distinct($merged[$key], $val);
|
1035 |
+
}
|
1036 |
+
}
|
1037 |
+
}
|
1038 |
+
return $merged;
|
1039 |
+
}
|
1040 |
+
|
1041 |
+
/**
|
1042 |
+
* Replaces string value in one array with the value of the matching element in a another array
|
1043 |
+
*
|
1044 |
+
* @param string $search Text to search for in array
|
1045 |
+
* @param array $arr_replace Array to use for replacing values
|
1046 |
+
* @param array $arr_subject Array to search for specified value
|
1047 |
+
* @return array Searched array with replacements made
|
1048 |
+
*/
|
1049 |
+
function array_replace_recursive($search, $arr_replace, $arr_subject) {
|
1050 |
+
foreach ($arr_subject as $key => $val) {
|
1051 |
+
//Skip element if key does not exist in the replacement array
|
1052 |
+
if (!isset($arr_replace[$key]))
|
1053 |
+
continue;
|
1054 |
+
//If element values for both arrays are strings, replace text
|
1055 |
+
if (is_string($val) && strpos($val, $search) !== false && is_string($arr_replace[$key]))
|
1056 |
+
$arr_subject[$key] = str_replace($search, $arr_replace[$key], $val);
|
1057 |
+
//If value in both arrays are arrays, recursively replace text
|
1058 |
+
if (is_array($val) && is_array($arr_replace[$key]))
|
1059 |
+
$arr_subject[$key] = $this->array_replace_recursive($search, $arr_replace[$key], $val);
|
1060 |
+
}
|
1061 |
+
|
1062 |
+
return $arr_subject;
|
1063 |
+
}
|
1064 |
+
|
1065 |
+
/**
|
1066 |
+
* Checks if item at specified path in array is set
|
1067 |
+
* @param array $arr Array to check for item
|
1068 |
+
* @param array $path Array of segments that form path to array (each array item is a deeper dimension in the array)
|
1069 |
+
* @return boolean TRUE if item is set in array, FALSE otherwise
|
1070 |
+
*/
|
1071 |
+
function array_item_isset(&$arr, &$path) {
|
1072 |
+
$f_path = $this->get_array_path($path);
|
1073 |
+
return eval('return isset($arr' . $f_path . ');');
|
1074 |
+
}
|
1075 |
+
|
1076 |
+
/**
|
1077 |
+
* Returns value of item at specified path in array
|
1078 |
+
* @param array $arr Array to get item from
|
1079 |
+
* @param array $path Array of segments that form path to array (each array item is a deeper dimension in the array)
|
1080 |
+
* @return mixed Value of item in array (Default: empty string)
|
1081 |
+
*/
|
1082 |
+
function &get_array_item(&$arr, &$path) {
|
1083 |
+
$item = '';
|
1084 |
+
if ($this->array_item_isset($arr, $path)) {
|
1085 |
+
eval('$item =& $arr' . $this->get_array_path($path) . ';');
|
1086 |
+
}
|
1087 |
+
return $item;
|
1088 |
+
}
|
1089 |
+
|
1090 |
+
/**
|
1091 |
+
* Build formatted string based on array values
|
1092 |
+
* Array values in formatted string will be ordered by index order
|
1093 |
+
* @param array $attribute Values to build string with
|
1094 |
+
* @param string $format (optional) Format name (Default: Multidimensional array representation > ['value1']['value2']['value3'], etc.)
|
1095 |
+
* @return string Formatted string based on array values
|
1096 |
+
*/
|
1097 |
+
function get_array_path($attribute = '', $format = null) {
|
1098 |
+
//Formatted value
|
1099 |
+
$fmtd = '';
|
1100 |
+
if (!empty($attribute)) {
|
1101 |
+
//Make sure attribute is array
|
1102 |
+
if (!is_array($attribute)) {
|
1103 |
+
$attribute = array($attribute);
|
1104 |
+
}
|
1105 |
+
//Format attribute
|
1106 |
+
$format = strtolower($format);
|
1107 |
+
switch ($format) {
|
1108 |
+
case 'id':
|
1109 |
+
$fmtd = array_shift($attribute) . '[' . implode('][', $attribute) . ']';
|
1110 |
+
break;
|
1111 |
+
case 'metadata':
|
1112 |
+
case 'attribute':
|
1113 |
+
//Join segments
|
1114 |
+
$delim = '_';
|
1115 |
+
$fmtd = implode($delim, $attribute);
|
1116 |
+
//Replace white space and repeating delimiters
|
1117 |
+
$fmtd = str_replace(' ', $delim, $fmtd);
|
1118 |
+
while (strpos($fmtd, $delim.$delim) !== false)
|
1119 |
+
$fmtd = str_replace($delim.$delim, $delim, $fmtd);
|
1120 |
+
//Prefix formatted value with delimeter for metadata keys
|
1121 |
+
if ('metadata' == $format)
|
1122 |
+
$fmtd = $delim . $fmtd;
|
1123 |
+
break;
|
1124 |
+
case 'path':
|
1125 |
+
case 'post':
|
1126 |
+
default:
|
1127 |
+
$fmtd = '["' . implode('"]["', $attribute) . '"]';
|
1128 |
+
}
|
1129 |
+
}
|
1130 |
+
return $fmtd;
|
1131 |
+
}
|
1132 |
+
|
1133 |
+
/**
|
1134 |
+
* Builds array of path elements based on arguments
|
1135 |
+
* Each item in path array represents a deeper level in structure path is for (object, array, filesystem, etc.)
|
1136 |
+
* @param array|string Value to add to the path
|
1137 |
+
* @return array 1-dimensional array of path elements
|
1138 |
+
*/
|
1139 |
+
function build_path() {
|
1140 |
+
$path = array();
|
1141 |
+
$args = func_get_args();
|
1142 |
+
|
1143 |
+
//Iterate through parameters and build path
|
1144 |
+
foreach ( $args as $arg ) {
|
1145 |
+
if ( empty($arg) )
|
1146 |
+
continue;
|
1147 |
+
|
1148 |
+
if (is_array($arg)) {
|
1149 |
+
//Recurse through array items to pull out any more arrays
|
1150 |
+
foreach ($arg as $key => $val) {
|
1151 |
+
$path = array_merge($path, $this->build_path($val));
|
1152 |
+
}
|
1153 |
+
//$path = array_merge($path, array_values($arg));
|
1154 |
+
} elseif ( is_scalar($arg) ) {
|
1155 |
+
$path[] = $arg;
|
1156 |
+
}
|
1157 |
+
}
|
1158 |
+
|
1159 |
+
return $path;
|
1160 |
+
}
|
1161 |
+
|
1162 |
+
/**
|
1163 |
+
* Parse string of attributes into array
|
1164 |
+
* For XML/XHTML tag attributes
|
1165 |
+
* @param string $txt Attribute text (Can be full tag or just attributes)
|
1166 |
+
* @return array Attributes as associative array
|
1167 |
+
*/
|
1168 |
+
function parse_attribute_string($txt, $defaults = array()) {
|
1169 |
+
$txt = trim($txt, ' >');
|
1170 |
+
$matches = $attr = array();
|
1171 |
+
//Strip tag
|
1172 |
+
if ( $txt[0] == '<' && ($s = strpos($txt, ' ')) && $s !== false ) {
|
1173 |
+
$txt = trim(substr($txt, $s + 1));
|
1174 |
+
}
|
1175 |
+
//Parse attributes
|
1176 |
+
$rgx = "/\b(\w+.*?)=([\"'])(.*?)\\2(?:\s|$)/i";
|
1177 |
+
preg_match_all($rgx, $txt, $matches);
|
1178 |
+
if ( count($matches) > 3 ) {
|
1179 |
+
foreach ( $matches[1] as $sub_idx => $val ) {
|
1180 |
+
if ( isset($matches[3][$sub_idx]) )
|
1181 |
+
$attr[trim($val)] = trim($matches[3][$sub_idx]);
|
1182 |
+
}
|
1183 |
+
}
|
1184 |
+
//Destroy parsing vars
|
1185 |
+
unset($txt, $matches);
|
1186 |
+
|
1187 |
+
return array_merge($defaults, $attr);
|
1188 |
+
}
|
1189 |
+
|
1190 |
+
/**
|
1191 |
+
* Builds attribute string for HTML element
|
1192 |
+
* @param array $attr Attributes
|
1193 |
+
* @return string Formatted attribute string
|
1194 |
+
*/
|
1195 |
+
function build_attribute_string($attr) {
|
1196 |
+
$ret = '';
|
1197 |
+
if ( is_object($attr) )
|
1198 |
+
$attr = (array) $attr;
|
1199 |
+
if ( is_array($attr) ) {
|
1200 |
+
array_map('esc_attr', $attr);
|
1201 |
+
$attr_str = array();
|
1202 |
+
foreach ( $attr as $key => $val ) {
|
1203 |
+
$attr_str[] = $key . '="' . $val . '"';
|
1204 |
+
}
|
1205 |
+
$ret = implode(' ', $attr_str);
|
1206 |
+
}
|
1207 |
+
return $ret;
|
1208 |
+
}
|
1209 |
+
|
1210 |
+
/**
|
1211 |
+
* Generate external stylesheet element
|
1212 |
+
* @param $url Stylesheet URL
|
1213 |
+
* @return string Stylesheet element
|
1214 |
+
*/
|
1215 |
+
function build_stylesheet_element($url = '') {
|
1216 |
+
$attributes = array('href' => $url, 'type' => 'text/css', 'rel' => 'stylesheet');
|
1217 |
+
return $this->build_html_element(array('tag' => 'link', 'wrap' => false, 'attributes' => $attributes));
|
1218 |
+
}
|
1219 |
+
|
1220 |
+
function build_script_element($content = '', $id = '', $wrap_jquery = true, $wait_doc_ready = false) {
|
1221 |
+
//Stop processing invalid content
|
1222 |
+
if ( empty($content) || !is_string($content) )
|
1223 |
+
return '';
|
1224 |
+
$attributes = array('type' => 'text/javascript');
|
1225 |
+
$start = array('/* <![CDATA[ */');
|
1226 |
+
$end = array('/* ]]> */');
|
1227 |
+
if ( $wrap_jquery ) {
|
1228 |
+
$start[] = '(function($){';
|
1229 |
+
$end[] = '})(jQuery);';
|
1230 |
+
|
1231 |
+
//Add event handler (if necessary)
|
1232 |
+
if ( $wait_doc_ready ) {
|
1233 |
+
$start[] = '$(document).ready(function(){';
|
1234 |
+
$end[] = '})';
|
1235 |
+
}
|
1236 |
+
}
|
1237 |
+
|
1238 |
+
//Reverse order of end values
|
1239 |
+
$end = array_reverse($end);
|
1240 |
+
$content = implode('', array_merge($start, array($content), $end));
|
1241 |
+
if ( is_string($id) && !empty($id) ) {
|
1242 |
+
$attributes['id'] = $this->add_prefix($id);
|
1243 |
+
}
|
1244 |
+
return $this->build_html_element(array('tag' => 'script', 'content' => $content, 'attributes' => $attributes)) . PHP_EOL;
|
1245 |
+
}
|
1246 |
+
|
1247 |
+
/**
|
1248 |
+
* Generate external script element
|
1249 |
+
* @param $url Script URL
|
1250 |
+
* @return string Script element
|
1251 |
+
*/
|
1252 |
+
function build_ext_script_element($url = '') {
|
1253 |
+
$attributes = array('src' => $url, 'type' => 'text/javascript');
|
1254 |
+
return $this->build_html_element(array('tag' => 'script', 'attributes' => $attributes));
|
1255 |
+
}
|
1256 |
+
|
1257 |
+
/**
|
1258 |
+
* Generate HTML element based on values
|
1259 |
+
* @param $args Element arguments
|
1260 |
+
* @return string Generated HTML element
|
1261 |
+
*/
|
1262 |
+
function build_html_element($args) {
|
1263 |
+
$defaults = array(
|
1264 |
+
'tag' => 'span',
|
1265 |
+
'wrap' => true,
|
1266 |
+
'content' => '',
|
1267 |
+
'attributes' => array()
|
1268 |
+
);
|
1269 |
+
$el_start = '<';
|
1270 |
+
$el_end = '>';
|
1271 |
+
$el_close = '/';
|
1272 |
+
extract(wp_parse_args($args, $defaults), EXTR_SKIP);
|
1273 |
+
$content = trim($content);
|
1274 |
+
|
1275 |
+
if ( !$wrap && strlen($content) > 0 )
|
1276 |
+
$wrap = true;
|
1277 |
+
|
1278 |
+
$attributes = $this->build_attribute_string($attributes);
|
1279 |
+
if ( strlen($attributes) > 0 )
|
1280 |
+
$attributes = ' ' . $attributes;
|
1281 |
+
|
1282 |
+
$ret = $el_start . $tag . $attributes;
|
1283 |
+
|
1284 |
+
if ( $wrap )
|
1285 |
+
$ret .= $el_end . $content . $el_start . $el_close . $tag;
|
1286 |
+
else
|
1287 |
+
$ret .= ' ' . $el_close;
|
1288 |
+
|
1289 |
+
$ret .= $el_end;
|
1290 |
+
return $ret;
|
1291 |
+
}
|
1292 |
+
|
1293 |
+
/*-** Admin **-*/
|
1294 |
+
|
1295 |
+
/**
|
1296 |
+
* Add submenu page in the admin menu
|
1297 |
+
* Adds ability to set the position of the page in the menu
|
1298 |
+
* @see add_submenu_page (Wraps functionality)
|
1299 |
+
*
|
1300 |
+
* @param $parent
|
1301 |
+
* @param $page_title
|
1302 |
+
* @param $menu_title
|
1303 |
+
* @param $access_level
|
1304 |
+
* @param $file
|
1305 |
+
* @param $function
|
1306 |
+
* @param int $pos Index position of menu page
|
1307 |
+
*
|
1308 |
+
* @global array $submenu Admin page submenus
|
1309 |
+
*/
|
1310 |
+
function add_submenu_page($parent, $page_title, $menu_title, $capability, $file, $function = '', $pos = false) {
|
1311 |
+
//Add submenu page as usual
|
1312 |
+
$args = func_get_args();
|
1313 |
+
$hookname = call_user_func_array('add_submenu_page', $args);
|
1314 |
+
if ( is_int($pos) ) {
|
1315 |
+
global $submenu;
|
1316 |
+
//Get last submenu added
|
1317 |
+
$parent = $this->get_submenu_parent_file($parent);
|
1318 |
+
if ( isset($submenu[$parent]) ) {
|
1319 |
+
$subs =& $submenu[$parent];
|
1320 |
+
|
1321 |
+
//Make sure menu isn't already in the desired position
|
1322 |
+
if ( $pos <= ( count($subs) - 1 ) ) {
|
1323 |
+
//Get submenu that was just added
|
1324 |
+
$sub = array_pop($subs);
|
1325 |
+
//Insert into desired position
|
1326 |
+
if ( 0 == $pos ) {
|
1327 |
+
array_unshift($subs, $sub);
|
1328 |
+
} else {
|
1329 |
+
$top = array_slice($subs, 0, $pos);
|
1330 |
+
$bottom = array_slice($subs, $pos);
|
1331 |
+
array_push($top, $sub);
|
1332 |
+
$subs = array_merge($top, $bottom);
|
1333 |
+
}
|
1334 |
+
}
|
1335 |
+
}
|
1336 |
+
}
|
1337 |
+
|
1338 |
+
return $hookname;
|
1339 |
+
}
|
1340 |
+
|
1341 |
+
/**
|
1342 |
+
* Remove admin submenu
|
1343 |
+
* @param string $parent Submenu parent file
|
1344 |
+
* @param string $file Submenu file name
|
1345 |
+
* @return int|null Index of removed submenu (NULL if submenu not found)
|
1346 |
+
*
|
1347 |
+
* @global array $submenu
|
1348 |
+
* @global array $_registered_pages
|
1349 |
+
*/
|
1350 |
+
function remove_submenu_page($parent, $file) {
|
1351 |
+
global $submenu, $_registered_pages;
|
1352 |
+
$ret = null;
|
1353 |
+
|
1354 |
+
$parent = $this->get_submenu_parent_file($parent);
|
1355 |
+
$file = plugin_basename($file);
|
1356 |
+
$file_index = 2;
|
1357 |
+
|
1358 |
+
//Find submenu
|
1359 |
+
if ( isset($submenu[$parent]) ) {
|
1360 |
+
$subs =& $submenu[$parent];
|
1361 |
+
for ($x = 0; $x < count($subs); $x++) {
|
1362 |
+
if ( $subs[$x][$file_index] == $file ) {
|
1363 |
+
//Remove matching submenu
|
1364 |
+
$hookname = get_plugin_page_hookname($file, $parent);
|
1365 |
+
remove_all_actions($hookname);
|
1366 |
+
unset($_registered_pages[$hookname]);
|
1367 |
+
unset($subs[$x]);
|
1368 |
+
$subs = array_values($subs);
|
1369 |
+
//Set index and stop processing
|
1370 |
+
$ret = $x;
|
1371 |
+
break;
|
1372 |
+
}
|
1373 |
+
}
|
1374 |
+
}
|
1375 |
+
|
1376 |
+
return $ret;
|
1377 |
+
}
|
1378 |
+
|
1379 |
+
/**
|
1380 |
+
* Replace a submenu page
|
1381 |
+
* Adds a submenu page in the place of an existing submenu page that has the same $file value
|
1382 |
+
*
|
1383 |
+
* @param $parent
|
1384 |
+
* @param $page_title
|
1385 |
+
* @param $menu_title
|
1386 |
+
* @param $access_level
|
1387 |
+
* @param $file
|
1388 |
+
* @param $function
|
1389 |
+
* @return string Hookname
|
1390 |
+
*
|
1391 |
+
* @global array $submenu
|
1392 |
+
*/
|
1393 |
+
function replace_submenu_page($parent, $page_title, $menu_title, $access_level, $file, $function = '') {
|
1394 |
+
global $submenu;
|
1395 |
+
//Remove matching submenu (if exists)
|
1396 |
+
$pos = $this->remove_submenu_page($parent, $file);
|
1397 |
+
//Insert submenu page
|
1398 |
+
$hookname = $this->add_submenu_page($parent, $page_title, $menu_title, $access_level, $file, $function, $pos);
|
1399 |
+
return $hookname;
|
1400 |
+
}
|
1401 |
+
|
1402 |
+
/**
|
1403 |
+
* Retrieves parent file for submenu
|
1404 |
+
* @param string $parent Parent file
|
1405 |
+
* @return string Formatted parent file name
|
1406 |
+
*
|
1407 |
+
* @global array $_wp_real_parent_file;
|
1408 |
+
*/
|
1409 |
+
function get_submenu_parent_file($parent) {
|
1410 |
+
global $_wp_real_parent_file;
|
1411 |
+
$parent = plugin_basename($parent);
|
1412 |
+
if ( isset($_wp_real_parent_file[$parent]) )
|
1413 |
+
$parent = $_wp_real_parent_file[$parent];
|
1414 |
+
return $parent;
|
1415 |
+
}
|
1416 |
+
}
|
js/lib.admin.edit_form.js
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
var page = ( adminpage ) ? adminpage : 'post';
|
2 |
+
jQuery(document).ready( function($) {
|
3 |
+
if ( postboxes && postboxes.add_postbox_toggles )
|
4 |
+
postboxes.add_postbox_toggles(page);
|
5 |
+
});
|
js/lib.admin.js
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* @package Cornerstone
|
3 |
+
* @subpackage Admin
|
4 |
+
* @author Archetyped
|
5 |
+
*/
|
6 |
+
|
7 |
+
(function ($) {
|
8 |
+
|
9 |
+
if ( CNR && CNR.extend ) CNR.extend('admin', {});
|
10 |
+
|
11 |
+
})(jQuery);
|
js/lib.core.js
ADDED
@@ -0,0 +1,190 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* Core
|
3 |
+
* @package Cornerstone
|
4 |
+
* @author Archetyped
|
5 |
+
*/
|
6 |
+
|
7 |
+
var CNR = {};
|
8 |
+
|
9 |
+
(function($){
|
10 |
+
/* Quick Hide */
|
11 |
+
|
12 |
+
$('html').addClass('js');
|
13 |
+
|
14 |
+
/* Classes */
|
15 |
+
|
16 |
+
/* CNR */
|
17 |
+
CNR = {
|
18 |
+
prefix: 'cnr',
|
19 |
+
context: [], //Context
|
20 |
+
options: { //Options
|
21 |
+
},
|
22 |
+
|
23 |
+
extend: function(member, data) {
|
24 |
+
if ( $.type(member) == 'string' && $.isPlainObject(data) ) {
|
25 |
+
//Add initial member
|
26 |
+
var obj = {};
|
27 |
+
obj[member] = $.extend({}, data);
|
28 |
+
$.extend(this, obj);
|
29 |
+
|
30 |
+
if ( member in this ) {
|
31 |
+
//Add additional objects
|
32 |
+
var args = ( arguments.length > 2 ) ? [].slice.apply(arguments).slice(2) : [];
|
33 |
+
args.unshift(this[member]);
|
34 |
+
//Add base properties
|
35 |
+
args.push({
|
36 |
+
base: CNR,
|
37 |
+
parent: this,
|
38 |
+
extend: this.extend
|
39 |
+
});
|
40 |
+
$.extend.apply($, args);
|
41 |
+
}
|
42 |
+
}
|
43 |
+
},
|
44 |
+
|
45 |
+
/* Prefix */
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Retrieve valid separator
|
49 |
+
* If supplied argument is not a valid separator, use default separator
|
50 |
+
* @param string (optional) sep Separator text
|
51 |
+
* @return string Separator text
|
52 |
+
*/
|
53 |
+
get_sep: function(sep) {
|
54 |
+
if ( typeof sep == 'undefined' || sep == null )
|
55 |
+
sep = '';
|
56 |
+
|
57 |
+
return ( $.type(sep) == 'string' ) ? sep : '_';
|
58 |
+
},
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Retrieve prefix
|
62 |
+
* @param string (optional) sep Separator text
|
63 |
+
* @return string Prefix (with separator if specified)
|
64 |
+
*/
|
65 |
+
get_prefix: function(sep) {
|
66 |
+
return ( this.prefix && this.prefix.length ) ? this.prefix + this.get_sep(sep) : '';
|
67 |
+
},
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Check if string is prefixed
|
71 |
+
*/
|
72 |
+
has_prefix: function(val, sep) {
|
73 |
+
return ( $.type(val) == 'string' && val.length && val.indexOf(this.get_prefix(sep)) === 0 );
|
74 |
+
},
|
75 |
+
|
76 |
+
/**
|
77 |
+
* Add Prefix to value
|
78 |
+
* @param string val Value to add prefix to
|
79 |
+
* @param string sep (optional) Separator (Default: `_`)
|
80 |
+
* @param bool (optional) once If text should only be prefixed once (Default: true)
|
81 |
+
*/
|
82 |
+
add_prefix: function(val, sep, once) {
|
83 |
+
if ( typeof sep == 'undefined' )
|
84 |
+
sep = '_';
|
85 |
+
once = ( typeof once == 'undefined' ) ? true : !!once;
|
86 |
+
if ( once && this.has_prefix(val, sep) )
|
87 |
+
return val;
|
88 |
+
return this.get_prefix(sep) + val;
|
89 |
+
}
|
90 |
+
}
|
91 |
+
|
92 |
+
/* Utilities */
|
93 |
+
CNR.extend('util', {
|
94 |
+
/**
|
95 |
+
* Return formatted string
|
96 |
+
*/
|
97 |
+
sprintf: function() {
|
98 |
+
var format = '',
|
99 |
+
params = [];
|
100 |
+
if (arguments.length < 1)
|
101 |
+
return format;
|
102 |
+
if (arguments.length == 1) {
|
103 |
+
format = arguments[0];
|
104 |
+
return format;
|
105 |
+
}
|
106 |
+
params = arguments.slice(1);
|
107 |
+
return format;
|
108 |
+
},
|
109 |
+
|
110 |
+
/* Request */
|
111 |
+
|
112 |
+
/**
|
113 |
+
* Retrieve valid context
|
114 |
+
* @return array Context
|
115 |
+
*/
|
116 |
+
get_context: function() {
|
117 |
+
//Valid context
|
118 |
+
if ( !$.isArray(this.base.context) )
|
119 |
+
this.base.context = [];
|
120 |
+
//Return context
|
121 |
+
return this.base.context;
|
122 |
+
},
|
123 |
+
|
124 |
+
/**
|
125 |
+
* Check if a context exists in current request
|
126 |
+
* If multiple contexts are supplied, result will be TRUE if at least ONE context exists
|
127 |
+
*
|
128 |
+
* @param string|array ctx Context to check for
|
129 |
+
* @return bool TRUE if context exists, FALSE otherwise
|
130 |
+
*/
|
131 |
+
is_context: function(ctx) {
|
132 |
+
var ret = false;
|
133 |
+
//Validate context
|
134 |
+
if ( typeof ctx == 'string' )
|
135 |
+
ctx = [ctx];
|
136 |
+
if ( $.isArray(ctx) && this.arr_intersect(this.get_context(), ctx).length ) {
|
137 |
+
ret = true;
|
138 |
+
}
|
139 |
+
return ret;
|
140 |
+
},
|
141 |
+
|
142 |
+
/* DOM */
|
143 |
+
|
144 |
+
/**
|
145 |
+
* Strip selector (ID, class, etc.)
|
146 |
+
* @param string id_value Original element
|
147 |
+
*/
|
148 |
+
esc_selector: function(id_value) {
|
149 |
+
return id_value.toString().replace(/(\[|\])/gi, '\\$1');
|
150 |
+
},
|
151 |
+
|
152 |
+
/**
|
153 |
+
* Find common elements of 2 arrays
|
154 |
+
* @param array arr1 First array
|
155 |
+
* @param array arr2 Second array
|
156 |
+
* @return array Elements common to both arrays
|
157 |
+
*/
|
158 |
+
arr_intersect: function(arr1, arr2) {
|
159 |
+
var ret = [];
|
160 |
+
if ( arr1 == arr2 ) {
|
161 |
+
return arr2;
|
162 |
+
}
|
163 |
+
if ( !$.isArray(arr2) || !arr2.length || !arr1.length ) {
|
164 |
+
return ret;
|
165 |
+
}
|
166 |
+
//Compare elements in arrays
|
167 |
+
var a1;
|
168 |
+
var a2;
|
169 |
+
var val;
|
170 |
+
if ( arr1.length < arr2.length ) {
|
171 |
+
a1 = arr1;
|
172 |
+
a2 = arr2;
|
173 |
+
} else {
|
174 |
+
a1 = arr2;
|
175 |
+
a2 = arr1;
|
176 |
+
}
|
177 |
+
|
178 |
+
for ( var x = 0; x < a1.length; x++ ) {
|
179 |
+
//Add mutual elements into intersection array
|
180 |
+
val = a1[x];
|
181 |
+
if ( a2.indexOf(val) != -1 && ret.indexOf(val) == -1 )
|
182 |
+
ret.push(val);
|
183 |
+
}
|
184 |
+
|
185 |
+
//Return intersection results
|
186 |
+
return ret;
|
187 |
+
}
|
188 |
+
});
|
189 |
+
|
190 |
+
})(jQuery); //END CNR init
|
js/lib.media.js
ADDED
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* Media
|
3 |
+
* @package Cornerstone
|
4 |
+
* @author Archetyped
|
5 |
+
*/
|
6 |
+
|
7 |
+
(function($) {
|
8 |
+
|
9 |
+
if ( CNR && CNR.extend ) CNR.extend('media', {
|
10 |
+
/**
|
11 |
+
* Convert array-based element IDs to standalone IDs
|
12 |
+
* @param string el_id Element ID
|
13 |
+
* @return string Converted ID
|
14 |
+
*/
|
15 |
+
convertId : function (el_id) {
|
16 |
+
return 'cnr_field_' + el_id.replace('cnr[attributes]', '').replace('][', '_').replace('[', '').replace(']', '')
|
17 |
+
},
|
18 |
+
/**
|
19 |
+
* Set selected media as value of specified post field
|
20 |
+
* @param object a Media arguments
|
21 |
+
* Arguments
|
22 |
+
* id: Attachment ID
|
23 |
+
* field: Field ID
|
24 |
+
* url: Source URL
|
25 |
+
* type: Mime type
|
26 |
+
* preview: URL of preview image (file type icon, etc.)
|
27 |
+
*/
|
28 |
+
setPostMedia : function (a) {
|
29 |
+
if ( typeof(a) != 'undefined' ) {
|
30 |
+
var selContainer = '#' + CNR.util.esc_selector(a.field) + '-data';
|
31 |
+
var container = $(selContainer);
|
32 |
+
var mediaElName = CNR.util.esc_selector(a.field);
|
33 |
+
//Set Media ID
|
34 |
+
var mediaId = $(container).find("#" + mediaElName);
|
35 |
+
if ( mediaId.length == 0 ) {
|
36 |
+
mediaId = $('<input />').attr({
|
37 |
+
type: 'hidden',
|
38 |
+
id: a.field,
|
39 |
+
name: a.field
|
40 |
+
});
|
41 |
+
$(container).prepend(mediaId);
|
42 |
+
}
|
43 |
+
$(mediaId).attr('value', a.id);
|
44 |
+
|
45 |
+
//Add source file link
|
46 |
+
media_el = $(container).find('#' + mediaElName + '-link');
|
47 |
+
if ( media_el.length == 0 ) {
|
48 |
+
//Build link
|
49 |
+
media_el = $('<a>').attr({
|
50 |
+
title: 'Media Link',
|
51 |
+
'class': 'media_link',
|
52 |
+
'target': '_blank',
|
53 |
+
id: a.field + '-link'
|
54 |
+
});
|
55 |
+
$(container).prepend(media_el);
|
56 |
+
}
|
57 |
+
$(media_el).attr('href', a.url).text( a.url.substr(a.url.lastIndexOf('/') + 1) );
|
58 |
+
|
59 |
+
//Build preview based on media type
|
60 |
+
var media_el;
|
61 |
+
if ( a.preview.length > 0 ) {
|
62 |
+
media_el = $(container).find("#" + mediaElName + '-frame');
|
63 |
+
if (media_el.length == 0) {
|
64 |
+
media_el = $("<img />").attr({
|
65 |
+
title: 'Media Preview',
|
66 |
+
alt: 'Media Preview',
|
67 |
+
'class': 'media_frame',
|
68 |
+
id: a.field + '-frame'
|
69 |
+
});
|
70 |
+
$(container).prepend(media_el);
|
71 |
+
}
|
72 |
+
$(media_el).attr('src', a.preview);
|
73 |
+
}
|
74 |
+
|
75 |
+
//Show Media Options
|
76 |
+
var opts = $('#' + mediaElName + '-options').removeClass('options-default');
|
77 |
+
//Hide confirmation options
|
78 |
+
opts.find('.confirmation').addClass('confirmation-default');
|
79 |
+
}
|
80 |
+
//Close popup
|
81 |
+
tb_remove();
|
82 |
+
},
|
83 |
+
/**
|
84 |
+
* Execute an action
|
85 |
+
* Uses attributes (class, etc.) of triggering element to determine action to execute
|
86 |
+
* @param {Object} el Element that triggered the action
|
87 |
+
*/
|
88 |
+
doAction : function (el) {
|
89 |
+
if (!el.id || el.id.length < 1)
|
90 |
+
return false;
|
91 |
+
var sep = '-';
|
92 |
+
//Get Element Parts
|
93 |
+
var parts = el.id.split(sep);
|
94 |
+
//Base
|
95 |
+
var base = (parts.length > 2) ? parts.slice(0, parts.length - 1).join(sep) : parts[0];
|
96 |
+
//Action
|
97 |
+
var action = parts[parts.length - 1];
|
98 |
+
var actEl;
|
99 |
+
var getEl = function (ident) {
|
100 |
+
ident = (typeof(ident) != 'undefined' && ident.length > 0) ? sep + ident : '';
|
101 |
+
return $('#' + CNR.util.esc_selector(base) + ident);
|
102 |
+
};
|
103 |
+
switch (action) {
|
104 |
+
case 'option_remove':
|
105 |
+
actEl = getEl('remove_confirmation').removeClass('confirmation-default');
|
106 |
+
break;
|
107 |
+
case 'remove_cancel':
|
108 |
+
getEl('remove_confirmation').addClass('confirmation-default');
|
109 |
+
break;
|
110 |
+
case 'remove':
|
111 |
+
//Remove Image Frame
|
112 |
+
getEl('data').empty();
|
113 |
+
//Reset Image ID
|
114 |
+
getEl().attr('value', 0);
|
115 |
+
//Hide remove options
|
116 |
+
$($(el).parents('.options').get(0)).addClass('options-default');
|
117 |
+
//Hide confirmation options
|
118 |
+
$(el).parents('.confirmation').addClass('confirmation-default');
|
119 |
+
break;
|
120 |
+
}
|
121 |
+
|
122 |
+
return false;
|
123 |
+
}
|
124 |
+
});
|
125 |
+
})(jQuery);
|
js/lib.posts.inline_edit.js
ADDED
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function($) {
|
2 |
+
if ( inlineEditPost && CNR && CNR.posts ) {
|
3 |
+
//Move init method to guarantee execution order
|
4 |
+
inlineEditPost.initSaved = inlineEditPost.init;
|
5 |
+
inlineEditPost.init = function() {};
|
6 |
+
|
7 |
+
//Extend inlineEditPost object
|
8 |
+
CNR.posts.extend('inline_edit', inlineEditPost, {
|
9 |
+
field_parent: '',
|
10 |
+
|
11 |
+
init: function() {
|
12 |
+
this.field_parent = this.base.add_prefix('post_parent');
|
13 |
+
var t = this;
|
14 |
+
//Execute default init method
|
15 |
+
if ( inlineEditPost.initSaved ) {
|
16 |
+
inlineEditPost.initSaved();
|
17 |
+
}
|
18 |
+
//Unbind quick edit click events
|
19 |
+
$('.wp-list-table tbody').off( 'click', '.editinline' );
|
20 |
+
//Bind new quick edit click handler
|
21 |
+
$('.wp-list-table tbody').on( 'click', '.editinline',
|
22 |
+
function() {
|
23 |
+
t.editHandler(this);
|
24 |
+
return false;
|
25 |
+
}
|
26 |
+
);
|
27 |
+
var qeRow = $('#inline-edit');
|
28 |
+
$('a.save', qeRow).click(function() { return t.save(this); });
|
29 |
+
$('td', qeRow).keydown(function(e) { if (e.which == 13) { return t.save(this); } });
|
30 |
+
//Restore original init method for future use
|
31 |
+
if ( inlineEditPost.initSaved ) {
|
32 |
+
inlineEditPost.init = inlineEditPost.initSaved;
|
33 |
+
}
|
34 |
+
},
|
35 |
+
|
36 |
+
save: function(id) {
|
37 |
+
var t = this, post_parent;
|
38 |
+
//Update post data
|
39 |
+
if (typeof(id) == 'object') {
|
40 |
+
id = t.getId(id);
|
41 |
+
}
|
42 |
+
if (id) {
|
43 |
+
//Get post parent
|
44 |
+
post_parent = $('#edit-' + id + ' #' + t.field_parent + ' option:selected');
|
45 |
+
if (post_parent.length) {
|
46 |
+
//Set post parent in postData
|
47 |
+
this.parent.set_data(id, 'post_parent', post_parent.val());
|
48 |
+
}
|
49 |
+
}
|
50 |
+
return true;
|
51 |
+
},
|
52 |
+
|
53 |
+
editHandler: function(id) {
|
54 |
+
this.preEdit(id);
|
55 |
+
inlineEditPost.edit(id);
|
56 |
+
this.postEdit(id);
|
57 |
+
},
|
58 |
+
|
59 |
+
preEdit: function(id) {
|
60 |
+
var t = this, post_id, section_select, parent_id;
|
61 |
+
if (typeof(id) == 'object') {
|
62 |
+
id = t.getId(id);
|
63 |
+
}
|
64 |
+
//Get master section selection
|
65 |
+
section_select = $('#inline-edit #' + t.field_parent);
|
66 |
+
//Get Parent ID
|
67 |
+
if ( section_select.length && t.parent.has_data(id, 'post_parent') ) {
|
68 |
+
parent_id = t.parent.get_data(id, 'post_parent');
|
69 |
+
//Set selected
|
70 |
+
$('option[value=' + parent_id + ']', section_select).get(0).defaultSelected = true;
|
71 |
+
}
|
72 |
+
},
|
73 |
+
|
74 |
+
postEdit: function(id) {
|
75 |
+
$('#inline-edit #' + this.field_parent + ' option').removeAttr('selected');
|
76 |
+
}
|
77 |
+
});
|
78 |
+
}
|
79 |
+
|
80 |
+
if ( CNR && CNR.admin && CNR.posts.inline_edit && CNR.posts.inline_edit.init ) {
|
81 |
+
$(document).ready(function() { CNR.posts.inline_edit.init(); });
|
82 |
+
}
|
83 |
+
|
84 |
+
})(jQuery);
|
js/lib.posts.js
ADDED
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function($) {
|
2 |
+
|
3 |
+
if ( !CNR || !CNR.extend )
|
4 |
+
return false;
|
5 |
+
|
6 |
+
CNR.extend('posts', {
|
7 |
+
data: {},
|
8 |
+
|
9 |
+
item_section: false,
|
10 |
+
title_sep: '',
|
11 |
+
|
12 |
+
get_data_id: function(id) {
|
13 |
+
var pre = 'post_';
|
14 |
+
if ( id.indexOf(pre) !== 0 )
|
15 |
+
id = pre + id;
|
16 |
+
return id;
|
17 |
+
},
|
18 |
+
|
19 |
+
has_data: function(id, prop) {
|
20 |
+
var ret = false;
|
21 |
+
id = this.get_data_id(id);
|
22 |
+
if ( id in this.data ) {
|
23 |
+
ret = true;
|
24 |
+
if ( $.type(prop) == 'string' )
|
25 |
+
ret = ( prop in this.data[id] ) ? true : false;
|
26 |
+
}
|
27 |
+
return ret;
|
28 |
+
},
|
29 |
+
|
30 |
+
get_data: function(id, prop) {
|
31 |
+
var ret = '';
|
32 |
+
id = this.get_data_id(id);
|
33 |
+
if ( this.has_data(id) && prop in this.data[id] ) {
|
34 |
+
ret = this.data[id][prop];
|
35 |
+
}
|
36 |
+
return ret;
|
37 |
+
},
|
38 |
+
|
39 |
+
set_data: function(id, prop, val) {
|
40 |
+
//Validate args
|
41 |
+
if ( $.type(id) != 'string' || $.type(prop) != 'string' || typeof val == 'undefined' )
|
42 |
+
return false;
|
43 |
+
//Create data object (if necessary)
|
44 |
+
id = this.get_data_id(id);
|
45 |
+
if ( ! this.has_data(id) )
|
46 |
+
this.data[id] = {};
|
47 |
+
//Set data
|
48 |
+
this.data[id][prop] = val;
|
49 |
+
},
|
50 |
+
|
51 |
+
set_wpseo_title: function() {
|
52 |
+
if ( typeof wpseo_title_template == 'undefined' || !this.item_section )
|
53 |
+
return false;
|
54 |
+
var ph_title = '%%title%%';
|
55 |
+
wpseo_title_template = wpseo_title_template.replace(ph_title, ph_title + this.title_sep + this.item_section);
|
56 |
+
}
|
57 |
+
});
|
58 |
+
|
59 |
+
$(document).ready(function() {
|
60 |
+
CNR.posts.set_wpseo_title();
|
61 |
+
})
|
62 |
+
|
63 |
+
})(jQuery);
|
js/lib.structure.admin.js
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* Structure Administration
|
3 |
+
* @package Cornerstone
|
4 |
+
* @subpackage Structure
|
5 |
+
*/
|
6 |
+
|
7 |
+
(function($) {
|
8 |
+
|
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 |
+
//Get options table
|
19 |
+
var opts_list = $('.wrap .form-table tbody:first');
|
20 |
+
var rows = $(opts_list).find('tr');
|
21 |
+
//Insert custom option
|
22 |
+
if (rows.length) {
|
23 |
+
var last = $(rows).get(rows.length - 1);
|
24 |
+
var option = $(opts_list).find('tr:first').clone();
|
25 |
+
var input = $(option).find('input');
|
26 |
+
//Input
|
27 |
+
$(input).val(po.permalink_structure);
|
28 |
+
if (po.permalink_structure == $('#permalink_structure').val()) {
|
29 |
+
$(input).attr('checked', 'checked');
|
30 |
+
} else {
|
31 |
+
$(input).removeAttr('checked');
|
32 |
+
}
|
33 |
+
//Label
|
34 |
+
var label = $(option).find('label');
|
35 |
+
//Move input element out of label element
|
36 |
+
$(label).text(o.label).prepend(input);
|
37 |
+
//Example text
|
38 |
+
$(option).find('td code').text(o.example);
|
39 |
+
//Insert element before last option in table
|
40 |
+
$(last).before($(option));
|
41 |
+
}
|
42 |
+
}
|
43 |
+
}
|
44 |
+
});
|
45 |
+
|
46 |
+
if ( CNR.structure.admin.manage_option )
|
47 |
+
$(document).ready(function() {CNR.structure.admin.manage_option();});
|
48 |
+
|
49 |
+
}) (jQuery);
|
js/lib.structure.js
ADDED
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* @package Cornerstone
|
3 |
+
* @subpackage Structure
|
4 |
+
* @author Archetyped
|
5 |
+
*/
|
6 |
+
|
7 |
+
(function ($) {
|
8 |
+
|
9 |
+
if ( CNR && CNR.extend ) CNR.extend('structure', {
|
10 |
+
options: { //Options
|
11 |
+
using_permastruct: false
|
12 |
+
},
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Init
|
16 |
+
*/
|
17 |
+
init: function() {
|
18 |
+
if ( this.base.util.is_context(['admin_page_post-new', 'admin_page_post']) ) {
|
19 |
+
this.setup_item_edit();
|
20 |
+
}
|
21 |
+
},
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Setup item edit form
|
25 |
+
*/
|
26 |
+
setup_item_edit: function() {
|
27 |
+
var id = 'parent_id',
|
28 |
+
sel = '#' + id;
|
29 |
+
//Check for parent element
|
30 |
+
if ( !$(sel).length ) {
|
31 |
+
//Add parent element
|
32 |
+
var el_par = $('<input type="hidden" />').attr({
|
33 |
+
'id': id
|
34 |
+
}).appendTo('body');
|
35 |
+
}
|
36 |
+
|
37 |
+
//Setup parent field
|
38 |
+
if ( this.options.field_parent && $('#' + this.options.field_parent).length ) {
|
39 |
+
var f_par = $('#' + this.options.field_parent);
|
40 |
+
//Define event handler
|
41 |
+
var setParent = function(p) {
|
42 |
+
if ( typeof p == 'undefined' || p.toString().length == 0 ) {
|
43 |
+
p = 0;
|
44 |
+
}
|
45 |
+
$(sel).val(p);
|
46 |
+
};
|
47 |
+
//Set initial value
|
48 |
+
setParent();
|
49 |
+
//Add event handler
|
50 |
+
$(f_par).change(function(e) {
|
51 |
+
//Set parent
|
52 |
+
setParent($(this).val());
|
53 |
+
//Autosave (if possible)
|
54 |
+
if ( $('#title').val().length > 0 && window.delayed_autosave ) {
|
55 |
+
$('#edit-slug-box').html('');
|
56 |
+
autosaveLast = '';
|
57 |
+
delayed_autosave();
|
58 |
+
}
|
59 |
+
});
|
60 |
+
}
|
61 |
+
|
62 |
+
}
|
63 |
+
});
|
64 |
+
|
65 |
+
if ( CNR.structure.init ) {
|
66 |
+
$(document).ready(function() {
|
67 |
+
CNR.structure.init();
|
68 |
+
});
|
69 |
+
}
|
70 |
+
|
71 |
+
})(jQuery);
|
load.php
ADDED
@@ -0,0 +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();
|
main.php
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Cornerstone
|
4 |
+
*
|
5 |
+
* @package Cornerstone
|
6 |
+
* @author Archetyped <support@archetyped.com>
|
7 |
+
* @copyright 2020 Archetyped
|
8 |
+
*
|
9 |
+
* Plugin Name: Cornerstone
|
10 |
+
* Plugin URI: http://archetyped.com/tools/cornerstone/
|
11 |
+
* Description: Enhanced content management for WordPress
|
12 |
+
* Version: 0.7.7
|
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/Reporting-Issues
|
18 |
+
*/
|
19 |
+
|
20 |
+
$cnr = null;
|
21 |
+
/**
|
22 |
+
* Initialize CNR
|
23 |
+
*/
|
24 |
+
function cnr_init() {
|
25 |
+
$path = dirname(__FILE__) . '/';
|
26 |
+
require_once $path . 'load.php';
|
27 |
+
}
|
28 |
+
|
29 |
+
add_action('init', 'cnr_init', 1);
|
package.json
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "cornerstone",
|
3 |
+
"version": "0.7.7",
|
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
ADDED
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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: 5.3
|
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/Reporting-Issues).
|
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.7.7 =
|
64 |
+
|
65 |
+
* Fix: Spelling.
|
66 |
+
|
67 |
+
= 0.7.6 =
|
68 |
+
|
69 |
+
* Add: Block editor support for Custom Post Types (CPTs).
|
70 |
+
* Update: WordPress 5.3 Compatibility confirmed.
|
71 |
+
* Update: WordPress minimum version requirement (5.3).
|
72 |
+
* Fix: Plugin initialization.
|
73 |
+
* Fix: Display/Save custom field data in post/page editor.
|
74 |
+
* Fix: Display/Save section in Quick Editor.
|
75 |
+
* Optimize: Code cleanup.
|
76 |
+
|
77 |
+
= 0.7.5 =
|
78 |
+
|
79 |
+
* Update: Use plugin-specific text domain for localized strings
|
80 |
+
|
81 |
+
= 0.7.4 =
|
82 |
+
|
83 |
+
* Add: Text Domain header for translations
|
84 |
+
|
85 |
+
= 0.7.3 =
|
86 |
+
|
87 |
+
* Fix: Registration of Media field types
|
88 |
+
* Update: Confirm compatibility with WordPress v4.3.1
|
89 |
+
|
90 |
+
= 0.7.2 =
|
91 |
+
|
92 |
+
* Update: WordPress 4.2.3 support
|
93 |
+
* Optimize: Autoload classes
|
94 |
+
* Optimize: Remove legacy PHP code
|
95 |
+
|
96 |
+
= 0.7.1 =
|
97 |
+
|
98 |
+
* Update: WordPress 3.8 support
|
99 |
+
* Update: Readme content (description, features, links)
|
100 |
+
|
101 |
+
= 0.7 =
|
102 |
+
|
103 |
+
* Update: WordPress 3.6 support
|
104 |
+
* Update: Remove deprecated jQuery code
|
105 |
+
* Update: Remove legacy shortcode code
|
106 |
+
* Optimize: Custom content types only added to main and CNR-initiated queries (Props Andy Owen)
|
107 |
+
* Optimize: Remove legacy files/code
|
108 |
+
* Optimize: Permalink structure reset when plugin activated/deactivated
|
109 |
+
* Optimize: Post Editor: Update permalink preview when section changed
|
110 |
+
* Optimize: Improved client-side file loading
|
111 |
+
* Optimize: Encapsulate client-side code
|
112 |
+
* Fix: Custom content types not displayed in sections (Unflappable Andy)
|
113 |
+
|
114 |
+
= 0.6 =
|
115 |
+
|
116 |
+
* Add: Content type support
|
117 |
+
* Add: Media support
|
118 |
+
* Add: Display tagline in home page title
|
119 |
+
* Optimize: Internal performance
|
120 |
+
* Optimize: Code cleanup
|
121 |
+
* Fix: Section not always saved
|
122 |
+
* Fix: Section posts fetched prior to checking posts' properties
|
123 |
+
* Fix: Pagination compatibility for WordPress 3.1+
|
124 |
+
* Fix: Various bug fixes
|
125 |
+
|
126 |
+
= 0.5.1b =
|
127 |
+
|
128 |
+
* Structure
|
129 |
+
* Fix: Section column duplication bug
|
130 |
+
|
131 |
+
= 0.5b =
|
132 |
+
|
133 |
+
* Public beta
|
screenshot-1.png
ADDED
Binary file
|
screenshot-2.png
ADDED
Binary file
|
screenshot-3.png
ADDED
Binary file
|