Cornerstone - Version 0.8.0

Version Description

  • Update: Custom permalink structure option integration for WordPress 6.1+.
Download this release

Release Info

Developer Archetyped
Plugin Icon wp plugin Cornerstone
Version 0.8.0
Comparing to
See all releases

Code changes from version 0.7.8 to 0.8.0

COPYING CHANGED
@@ -1,339 +1,339 @@
1
- GNU GENERAL PUBLIC LICENSE
2
- Version 2, June 1991
3
-
4
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6
- Everyone is permitted to copy and distribute verbatim copies
7
- of this license document, but changing it is not allowed.
8
-
9
- Preamble
10
-
11
- The licenses for most software are designed to take away your
12
- freedom to share and change it. By contrast, the GNU General Public
13
- License is intended to guarantee your freedom to share and change free
14
- software--to make sure the software is free for all its users. This
15
- General Public License applies to most of the Free Software
16
- Foundation's software and to any other program whose authors commit to
17
- using it. (Some other Free Software Foundation software is covered by
18
- the GNU Lesser General Public License instead.) You can apply it to
19
- your programs, too.
20
-
21
- When we speak of free software, we are referring to freedom, not
22
- price. Our General Public Licenses are designed to make sure that you
23
- have the freedom to distribute copies of free software (and charge for
24
- this service if you wish), that you receive source code or can get it
25
- if you want it, that you can change the software or use pieces of it
26
- in new free programs; and that you know you can do these things.
27
-
28
- To protect your rights, we need to make restrictions that forbid
29
- anyone to deny you these rights or to ask you to surrender the rights.
30
- These restrictions translate to certain responsibilities for you if you
31
- distribute copies of the software, or if you modify it.
32
-
33
- For example, if you distribute copies of such a program, whether
34
- gratis or for a fee, you must give the recipients all the rights that
35
- you have. You must make sure that they, too, receive or can get the
36
- source code. And you must show them these terms so they know their
37
- rights.
38
-
39
- We protect your rights with two steps: (1) copyright the software, and
40
- (2) offer you this license which gives you legal permission to copy,
41
- distribute and/or modify the software.
42
-
43
- Also, for each author's protection and ours, we want to make certain
44
- that everyone understands that there is no warranty for this free
45
- software. If the software is modified by someone else and passed on, we
46
- want its recipients to know that what they have is not the original, so
47
- that any problems introduced by others will not reflect on the original
48
- authors' reputations.
49
-
50
- Finally, any free program is threatened constantly by software
51
- patents. We wish to avoid the danger that redistributors of a free
52
- program will individually obtain patent licenses, in effect making the
53
- program proprietary. To prevent this, we have made it clear that any
54
- patent must be licensed for everyone's free use or not licensed at all.
55
-
56
- The precise terms and conditions for copying, distribution and
57
- modification follow.
58
-
59
- GNU GENERAL PUBLIC LICENSE
60
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61
-
62
- 0. This License applies to any program or other work which contains
63
- a notice placed by the copyright holder saying it may be distributed
64
- under the terms of this General Public License. The "Program", below,
65
- refers to any such program or work, and a "work based on the Program"
66
- means either the Program or any derivative work under copyright law:
67
- that is to say, a work containing the Program or a portion of it,
68
- either verbatim or with modifications and/or translated into another
69
- language. (Hereinafter, translation is included without limitation in
70
- the term "modification".) Each licensee is addressed as "you".
71
-
72
- Activities other than copying, distribution and modification are not
73
- covered by this License; they are outside its scope. The act of
74
- running the Program is not restricted, and the output from the Program
75
- is covered only if its contents constitute a work based on the
76
- Program (independent of having been made by running the Program).
77
- Whether that is true depends on what the Program does.
78
-
79
- 1. You may copy and distribute verbatim copies of the Program's
80
- source code as you receive it, in any medium, provided that you
81
- conspicuously and appropriately publish on each copy an appropriate
82
- copyright notice and disclaimer of warranty; keep intact all the
83
- notices that refer to this License and to the absence of any warranty;
84
- and give any other recipients of the Program a copy of this License
85
- along with the Program.
86
-
87
- You may charge a fee for the physical act of transferring a copy, and
88
- you may at your option offer warranty protection in exchange for a fee.
89
-
90
- 2. You may modify your copy or copies of the Program or any portion
91
- of it, thus forming a work based on the Program, and copy and
92
- distribute such modifications or work under the terms of Section 1
93
- above, provided that you also meet all of these conditions:
94
-
95
- a) You must cause the modified files to carry prominent notices
96
- stating that you changed the files and the date of any change.
97
-
98
- b) You must cause any work that you distribute or publish, that in
99
- whole or in part contains or is derived from the Program or any
100
- part thereof, to be licensed as a whole at no charge to all third
101
- parties under the terms of this License.
102
-
103
- c) If the modified program normally reads commands interactively
104
- when run, you must cause it, when started running for such
105
- interactive use in the most ordinary way, to print or display an
106
- announcement including an appropriate copyright notice and a
107
- notice that there is no warranty (or else, saying that you provide
108
- a warranty) and that users may redistribute the program under
109
- these conditions, and telling the user how to view a copy of this
110
- License. (Exception: if the Program itself is interactive but
111
- does not normally print such an announcement, your work based on
112
- the Program is not required to print an announcement.)
113
-
114
- These requirements apply to the modified work as a whole. If
115
- identifiable sections of that work are not derived from the Program,
116
- and can be reasonably considered independent and separate works in
117
- themselves, then this License, and its terms, do not apply to those
118
- sections when you distribute them as separate works. But when you
119
- distribute the same sections as part of a whole which is a work based
120
- on the Program, the distribution of the whole must be on the terms of
121
- this License, whose permissions for other licensees extend to the
122
- entire whole, and thus to each and every part regardless of who wrote it.
123
-
124
- Thus, it is not the intent of this section to claim rights or contest
125
- your rights to work written entirely by you; rather, the intent is to
126
- exercise the right to control the distribution of derivative or
127
- collective works based on the Program.
128
-
129
- In addition, mere aggregation of another work not based on the Program
130
- with the Program (or with a work based on the Program) on a volume of
131
- a storage or distribution medium does not bring the other work under
132
- the scope of this License.
133
-
134
- 3. You may copy and distribute the Program (or a work based on it,
135
- under Section 2) in object code or executable form under the terms of
136
- Sections 1 and 2 above provided that you also do one of the following:
137
-
138
- a) Accompany it with the complete corresponding machine-readable
139
- source code, which must be distributed under the terms of Sections
140
- 1 and 2 above on a medium customarily used for software interchange; or,
141
-
142
- b) Accompany it with a written offer, valid for at least three
143
- years, to give any third party, for a charge no more than your
144
- cost of physically performing source distribution, a complete
145
- machine-readable copy of the corresponding source code, to be
146
- distributed under the terms of Sections 1 and 2 above on a medium
147
- customarily used for software interchange; or,
148
-
149
- c) Accompany it with the information you received as to the offer
150
- to distribute corresponding source code. (This alternative is
151
- allowed only for noncommercial distribution and only if you
152
- received the program in object code or executable form with such
153
- an offer, in accord with Subsection b above.)
154
-
155
- The source code for a work means the preferred form of the work for
156
- making modifications to it. For an executable work, complete source
157
- code means all the source code for all modules it contains, plus any
158
- associated interface definition files, plus the scripts used to
159
- control compilation and installation of the executable. However, as a
160
- special exception, the source code distributed need not include
161
- anything that is normally distributed (in either source or binary
162
- form) with the major components (compiler, kernel, and so on) of the
163
- operating system on which the executable runs, unless that component
164
- itself accompanies the executable.
165
-
166
- If distribution of executable or object code is made by offering
167
- access to copy from a designated place, then offering equivalent
168
- access to copy the source code from the same place counts as
169
- distribution of the source code, even though third parties are not
170
- compelled to copy the source along with the object code.
171
-
172
- 4. You may not copy, modify, sublicense, or distribute the Program
173
- except as expressly provided under this License. Any attempt
174
- otherwise to copy, modify, sublicense or distribute the Program is
175
- void, and will automatically terminate your rights under this License.
176
- However, parties who have received copies, or rights, from you under
177
- this License will not have their licenses terminated so long as such
178
- parties remain in full compliance.
179
-
180
- 5. You are not required to accept this License, since you have not
181
- signed it. However, nothing else grants you permission to modify or
182
- distribute the Program or its derivative works. These actions are
183
- prohibited by law if you do not accept this License. Therefore, by
184
- modifying or distributing the Program (or any work based on the
185
- Program), you indicate your acceptance of this License to do so, and
186
- all its terms and conditions for copying, distributing or modifying
187
- the Program or works based on it.
188
-
189
- 6. Each time you redistribute the Program (or any work based on the
190
- Program), the recipient automatically receives a license from the
191
- original licensor to copy, distribute or modify the Program subject to
192
- these terms and conditions. You may not impose any further
193
- restrictions on the recipients' exercise of the rights granted herein.
194
- You are not responsible for enforcing compliance by third parties to
195
- this License.
196
-
197
- 7. If, as a consequence of a court judgment or allegation of patent
198
- infringement or for any other reason (not limited to patent issues),
199
- conditions are imposed on you (whether by court order, agreement or
200
- otherwise) that contradict the conditions of this License, they do not
201
- excuse you from the conditions of this License. If you cannot
202
- distribute so as to satisfy simultaneously your obligations under this
203
- License and any other pertinent obligations, then as a consequence you
204
- may not distribute the Program at all. For example, if a patent
205
- license would not permit royalty-free redistribution of the Program by
206
- all those who receive copies directly or indirectly through you, then
207
- the only way you could satisfy both it and this License would be to
208
- refrain entirely from distribution of the Program.
209
-
210
- If any portion of this section is held invalid or unenforceable under
211
- any particular circumstance, the balance of the section is intended to
212
- apply and the section as a whole is intended to apply in other
213
- circumstances.
214
-
215
- It is not the purpose of this section to induce you to infringe any
216
- patents or other property right claims or to contest validity of any
217
- such claims; this section has the sole purpose of protecting the
218
- integrity of the free software distribution system, which is
219
- implemented by public license practices. Many people have made
220
- generous contributions to the wide range of software distributed
221
- through that system in reliance on consistent application of that
222
- system; it is up to the author/donor to decide if he or she is willing
223
- to distribute software through any other system and a licensee cannot
224
- impose that choice.
225
-
226
- This section is intended to make thoroughly clear what is believed to
227
- be a consequence of the rest of this License.
228
-
229
- 8. If the distribution and/or use of the Program is restricted in
230
- certain countries either by patents or by copyrighted interfaces, the
231
- original copyright holder who places the Program under this License
232
- may add an explicit geographical distribution limitation excluding
233
- those countries, so that distribution is permitted only in or among
234
- countries not thus excluded. In such case, this License incorporates
235
- the limitation as if written in the body of this License.
236
-
237
- 9. The Free Software Foundation may publish revised and/or new versions
238
- of the General Public License from time to time. Such new versions will
239
- be similar in spirit to the present version, but may differ in detail to
240
- address new problems or concerns.
241
-
242
- Each version is given a distinguishing version number. If the Program
243
- specifies a version number of this License which applies to it and "any
244
- later version", you have the option of following the terms and conditions
245
- either of that version or of any later version published by the Free
246
- Software Foundation. If the Program does not specify a version number of
247
- this License, you may choose any version ever published by the Free Software
248
- Foundation.
249
-
250
- 10. If you wish to incorporate parts of the Program into other free
251
- programs whose distribution conditions are different, write to the author
252
- to ask for permission. For software which is copyrighted by the Free
253
- Software Foundation, write to the Free Software Foundation; we sometimes
254
- make exceptions for this. Our decision will be guided by the two goals
255
- of preserving the free status of all derivatives of our free software and
256
- of promoting the sharing and reuse of software generally.
257
-
258
- NO WARRANTY
259
-
260
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261
- FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262
- OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263
- PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264
- OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265
- MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266
- TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267
- PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268
- REPAIR OR CORRECTION.
269
-
270
- 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271
- WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272
- REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273
- INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274
- OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275
- TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276
- YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277
- PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278
- POSSIBILITY OF SUCH DAMAGES.
279
-
280
- END OF TERMS AND CONDITIONS
281
-
282
- How to Apply These Terms to Your New Programs
283
-
284
- If you develop a new program, and you want it to be of the greatest
285
- possible use to the public, the best way to achieve this is to make it
286
- free software which everyone can redistribute and change under these terms.
287
-
288
- To do so, attach the following notices to the program. It is safest
289
- to attach them to the start of each source file to most effectively
290
- convey the exclusion of warranty; and each file should have at least
291
- the "copyright" line and a pointer to where the full notice is found.
292
-
293
- <one line to give the program's name and a brief idea of what it does.>
294
- Copyright (C) <year> <name of author>
295
-
296
- This program is free software; you can redistribute it and/or modify
297
- it under the terms of the GNU General Public License as published by
298
- the Free Software Foundation; either version 2 of the License, or
299
- (at your option) any later version.
300
-
301
- This program is distributed in the hope that it will be useful,
302
- but WITHOUT ANY WARRANTY; without even the implied warranty of
303
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304
- GNU General Public License for more details.
305
-
306
- You should have received a copy of the GNU General Public License along
307
- with this program; if not, write to the Free Software Foundation, Inc.,
308
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309
-
310
- Also add information on how to contact you by electronic and paper mail.
311
-
312
- If the program is interactive, make it output a short notice like this
313
- when it starts in an interactive mode:
314
-
315
- Gnomovision version 69, Copyright (C) year name of author
316
- Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317
- This is free software, and you are welcome to redistribute it
318
- under certain conditions; type `show c' for details.
319
-
320
- The hypothetical commands `show w' and `show c' should show the appropriate
321
- parts of the General Public License. Of course, the commands you use may
322
- be called something other than `show w' and `show c'; they could even be
323
- mouse-clicks or menu items--whatever suits your program.
324
-
325
- You should also get your employer (if you work as a programmer) or your
326
- school, if any, to sign a "copyright disclaimer" for the program, if
327
- necessary. Here is a sample; alter the names:
328
-
329
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330
- `Gnomovision' (which makes passes at compilers) written by James Hacker.
331
-
332
- <signature of Ty Coon>, 1 April 1989
333
- Ty Coon, President of Vice
334
-
335
- This General Public License does not permit incorporating your program into
336
- proprietary programs. If your program is a subroutine library, you may
337
- consider it more useful to permit linking proprietary applications with the
338
- library. If this is what you want to do, use the GNU Lesser General
339
- Public License instead of this License.
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 2, June 1991
3
+
4
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6
+ Everyone is permitted to copy and distribute verbatim copies
7
+ of this license document, but changing it is not allowed.
8
+
9
+ Preamble
10
+
11
+ The licenses for most software are designed to take away your
12
+ freedom to share and change it. By contrast, the GNU General Public
13
+ License is intended to guarantee your freedom to share and change free
14
+ software--to make sure the software is free for all its users. This
15
+ General Public License applies to most of the Free Software
16
+ Foundation's software and to any other program whose authors commit to
17
+ using it. (Some other Free Software Foundation software is covered by
18
+ the GNU Lesser General Public License instead.) You can apply it to
19
+ your programs, too.
20
+
21
+ When we speak of free software, we are referring to freedom, not
22
+ price. Our General Public Licenses are designed to make sure that you
23
+ have the freedom to distribute copies of free software (and charge for
24
+ this service if you wish), that you receive source code or can get it
25
+ if you want it, that you can change the software or use pieces of it
26
+ in new free programs; and that you know you can do these things.
27
+
28
+ To protect your rights, we need to make restrictions that forbid
29
+ anyone to deny you these rights or to ask you to surrender the rights.
30
+ These restrictions translate to certain responsibilities for you if you
31
+ distribute copies of the software, or if you modify it.
32
+
33
+ For example, if you distribute copies of such a program, whether
34
+ gratis or for a fee, you must give the recipients all the rights that
35
+ you have. You must make sure that they, too, receive or can get the
36
+ source code. And you must show them these terms so they know their
37
+ rights.
38
+
39
+ We protect your rights with two steps: (1) copyright the software, and
40
+ (2) offer you this license which gives you legal permission to copy,
41
+ distribute and/or modify the software.
42
+
43
+ Also, for each author's protection and ours, we want to make certain
44
+ that everyone understands that there is no warranty for this free
45
+ software. If the software is modified by someone else and passed on, we
46
+ want its recipients to know that what they have is not the original, so
47
+ that any problems introduced by others will not reflect on the original
48
+ authors' reputations.
49
+
50
+ Finally, any free program is threatened constantly by software
51
+ patents. We wish to avoid the danger that redistributors of a free
52
+ program will individually obtain patent licenses, in effect making the
53
+ program proprietary. To prevent this, we have made it clear that any
54
+ patent must be licensed for everyone's free use or not licensed at all.
55
+
56
+ The precise terms and conditions for copying, distribution and
57
+ modification follow.
58
+
59
+ GNU GENERAL PUBLIC LICENSE
60
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61
+
62
+ 0. This License applies to any program or other work which contains
63
+ a notice placed by the copyright holder saying it may be distributed
64
+ under the terms of this General Public License. The "Program", below,
65
+ refers to any such program or work, and a "work based on the Program"
66
+ means either the Program or any derivative work under copyright law:
67
+ that is to say, a work containing the Program or a portion of it,
68
+ either verbatim or with modifications and/or translated into another
69
+ language. (Hereinafter, translation is included without limitation in
70
+ the term "modification".) Each licensee is addressed as "you".
71
+
72
+ Activities other than copying, distribution and modification are not
73
+ covered by this License; they are outside its scope. The act of
74
+ running the Program is not restricted, and the output from the Program
75
+ is covered only if its contents constitute a work based on the
76
+ Program (independent of having been made by running the Program).
77
+ Whether that is true depends on what the Program does.
78
+
79
+ 1. You may copy and distribute verbatim copies of the Program's
80
+ source code as you receive it, in any medium, provided that you
81
+ conspicuously and appropriately publish on each copy an appropriate
82
+ copyright notice and disclaimer of warranty; keep intact all the
83
+ notices that refer to this License and to the absence of any warranty;
84
+ and give any other recipients of the Program a copy of this License
85
+ along with the Program.
86
+
87
+ You may charge a fee for the physical act of transferring a copy, and
88
+ you may at your option offer warranty protection in exchange for a fee.
89
+
90
+ 2. You may modify your copy or copies of the Program or any portion
91
+ of it, thus forming a work based on the Program, and copy and
92
+ distribute such modifications or work under the terms of Section 1
93
+ above, provided that you also meet all of these conditions:
94
+
95
+ a) You must cause the modified files to carry prominent notices
96
+ stating that you changed the files and the date of any change.
97
+
98
+ b) You must cause any work that you distribute or publish, that in
99
+ whole or in part contains or is derived from the Program or any
100
+ part thereof, to be licensed as a whole at no charge to all third
101
+ parties under the terms of this License.
102
+
103
+ c) If the modified program normally reads commands interactively
104
+ when run, you must cause it, when started running for such
105
+ interactive use in the most ordinary way, to print or display an
106
+ announcement including an appropriate copyright notice and a
107
+ notice that there is no warranty (or else, saying that you provide
108
+ a warranty) and that users may redistribute the program under
109
+ these conditions, and telling the user how to view a copy of this
110
+ License. (Exception: if the Program itself is interactive but
111
+ does not normally print such an announcement, your work based on
112
+ the Program is not required to print an announcement.)
113
+
114
+ These requirements apply to the modified work as a whole. If
115
+ identifiable sections of that work are not derived from the Program,
116
+ and can be reasonably considered independent and separate works in
117
+ themselves, then this License, and its terms, do not apply to those
118
+ sections when you distribute them as separate works. But when you
119
+ distribute the same sections as part of a whole which is a work based
120
+ on the Program, the distribution of the whole must be on the terms of
121
+ this License, whose permissions for other licensees extend to the
122
+ entire whole, and thus to each and every part regardless of who wrote it.
123
+
124
+ Thus, it is not the intent of this section to claim rights or contest
125
+ your rights to work written entirely by you; rather, the intent is to
126
+ exercise the right to control the distribution of derivative or
127
+ collective works based on the Program.
128
+
129
+ In addition, mere aggregation of another work not based on the Program
130
+ with the Program (or with a work based on the Program) on a volume of
131
+ a storage or distribution medium does not bring the other work under
132
+ the scope of this License.
133
+
134
+ 3. You may copy and distribute the Program (or a work based on it,
135
+ under Section 2) in object code or executable form under the terms of
136
+ Sections 1 and 2 above provided that you also do one of the following:
137
+
138
+ a) Accompany it with the complete corresponding machine-readable
139
+ source code, which must be distributed under the terms of Sections
140
+ 1 and 2 above on a medium customarily used for software interchange; or,
141
+
142
+ b) Accompany it with a written offer, valid for at least three
143
+ years, to give any third party, for a charge no more than your
144
+ cost of physically performing source distribution, a complete
145
+ machine-readable copy of the corresponding source code, to be
146
+ distributed under the terms of Sections 1 and 2 above on a medium
147
+ customarily used for software interchange; or,
148
+
149
+ c) Accompany it with the information you received as to the offer
150
+ to distribute corresponding source code. (This alternative is
151
+ allowed only for noncommercial distribution and only if you
152
+ received the program in object code or executable form with such
153
+ an offer, in accord with Subsection b above.)
154
+
155
+ The source code for a work means the preferred form of the work for
156
+ making modifications to it. For an executable work, complete source
157
+ code means all the source code for all modules it contains, plus any
158
+ associated interface definition files, plus the scripts used to
159
+ control compilation and installation of the executable. However, as a
160
+ special exception, the source code distributed need not include
161
+ anything that is normally distributed (in either source or binary
162
+ form) with the major components (compiler, kernel, and so on) of the
163
+ operating system on which the executable runs, unless that component
164
+ itself accompanies the executable.
165
+
166
+ If distribution of executable or object code is made by offering
167
+ access to copy from a designated place, then offering equivalent
168
+ access to copy the source code from the same place counts as
169
+ distribution of the source code, even though third parties are not
170
+ compelled to copy the source along with the object code.
171
+
172
+ 4. You may not copy, modify, sublicense, or distribute the Program
173
+ except as expressly provided under this License. Any attempt
174
+ otherwise to copy, modify, sublicense or distribute the Program is
175
+ void, and will automatically terminate your rights under this License.
176
+ However, parties who have received copies, or rights, from you under
177
+ this License will not have their licenses terminated so long as such
178
+ parties remain in full compliance.
179
+
180
+ 5. You are not required to accept this License, since you have not
181
+ signed it. However, nothing else grants you permission to modify or
182
+ distribute the Program or its derivative works. These actions are
183
+ prohibited by law if you do not accept this License. Therefore, by
184
+ modifying or distributing the Program (or any work based on the
185
+ Program), you indicate your acceptance of this License to do so, and
186
+ all its terms and conditions for copying, distributing or modifying
187
+ the Program or works based on it.
188
+
189
+ 6. Each time you redistribute the Program (or any work based on the
190
+ Program), the recipient automatically receives a license from the
191
+ original licensor to copy, distribute or modify the Program subject to
192
+ these terms and conditions. You may not impose any further
193
+ restrictions on the recipients' exercise of the rights granted herein.
194
+ You are not responsible for enforcing compliance by third parties to
195
+ this License.
196
+
197
+ 7. If, as a consequence of a court judgment or allegation of patent
198
+ infringement or for any other reason (not limited to patent issues),
199
+ conditions are imposed on you (whether by court order, agreement or
200
+ otherwise) that contradict the conditions of this License, they do not
201
+ excuse you from the conditions of this License. If you cannot
202
+ distribute so as to satisfy simultaneously your obligations under this
203
+ License and any other pertinent obligations, then as a consequence you
204
+ may not distribute the Program at all. For example, if a patent
205
+ license would not permit royalty-free redistribution of the Program by
206
+ all those who receive copies directly or indirectly through you, then
207
+ the only way you could satisfy both it and this License would be to
208
+ refrain entirely from distribution of the Program.
209
+
210
+ If any portion of this section is held invalid or unenforceable under
211
+ any particular circumstance, the balance of the section is intended to
212
+ apply and the section as a whole is intended to apply in other
213
+ circumstances.
214
+
215
+ It is not the purpose of this section to induce you to infringe any
216
+ patents or other property right claims or to contest validity of any
217
+ such claims; this section has the sole purpose of protecting the
218
+ integrity of the free software distribution system, which is
219
+ implemented by public license practices. Many people have made
220
+ generous contributions to the wide range of software distributed
221
+ through that system in reliance on consistent application of that
222
+ system; it is up to the author/donor to decide if he or she is willing
223
+ to distribute software through any other system and a licensee cannot
224
+ impose that choice.
225
+
226
+ This section is intended to make thoroughly clear what is believed to
227
+ be a consequence of the rest of this License.
228
+
229
+ 8. If the distribution and/or use of the Program is restricted in
230
+ certain countries either by patents or by copyrighted interfaces, the
231
+ original copyright holder who places the Program under this License
232
+ may add an explicit geographical distribution limitation excluding
233
+ those countries, so that distribution is permitted only in or among
234
+ countries not thus excluded. In such case, this License incorporates
235
+ the limitation as if written in the body of this License.
236
+
237
+ 9. The Free Software Foundation may publish revised and/or new versions
238
+ of the General Public License from time to time. Such new versions will
239
+ be similar in spirit to the present version, but may differ in detail to
240
+ address new problems or concerns.
241
+
242
+ Each version is given a distinguishing version number. If the Program
243
+ specifies a version number of this License which applies to it and "any
244
+ later version", you have the option of following the terms and conditions
245
+ either of that version or of any later version published by the Free
246
+ Software Foundation. If the Program does not specify a version number of
247
+ this License, you may choose any version ever published by the Free Software
248
+ Foundation.
249
+
250
+ 10. If you wish to incorporate parts of the Program into other free
251
+ programs whose distribution conditions are different, write to the author
252
+ to ask for permission. For software which is copyrighted by the Free
253
+ Software Foundation, write to the Free Software Foundation; we sometimes
254
+ make exceptions for this. Our decision will be guided by the two goals
255
+ of preserving the free status of all derivatives of our free software and
256
+ of promoting the sharing and reuse of software generally.
257
+
258
+ NO WARRANTY
259
+
260
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261
+ FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262
+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263
+ PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264
+ OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266
+ TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267
+ PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268
+ REPAIR OR CORRECTION.
269
+
270
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272
+ REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273
+ INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274
+ OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275
+ TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276
+ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277
+ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278
+ POSSIBILITY OF SUCH DAMAGES.
279
+
280
+ END OF TERMS AND CONDITIONS
281
+
282
+ How to Apply These Terms to Your New Programs
283
+
284
+ If you develop a new program, and you want it to be of the greatest
285
+ possible use to the public, the best way to achieve this is to make it
286
+ free software which everyone can redistribute and change under these terms.
287
+
288
+ To do so, attach the following notices to the program. It is safest
289
+ to attach them to the start of each source file to most effectively
290
+ convey the exclusion of warranty; and each file should have at least
291
+ the "copyright" line and a pointer to where the full notice is found.
292
+
293
+ <one line to give the program's name and a brief idea of what it does.>
294
+ Copyright (C) <year> <name of author>
295
+
296
+ This program is free software; you can redistribute it and/or modify
297
+ it under the terms of the GNU General Public License as published by
298
+ the Free Software Foundation; either version 2 of the License, or
299
+ (at your option) any later version.
300
+
301
+ This program is distributed in the hope that it will be useful,
302
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
303
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304
+ GNU General Public License for more details.
305
+
306
+ You should have received a copy of the GNU General Public License along
307
+ with this program; if not, write to the Free Software Foundation, Inc.,
308
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309
+
310
+ Also add information on how to contact you by electronic and paper mail.
311
+
312
+ If the program is interactive, make it output a short notice like this
313
+ when it starts in an interactive mode:
314
+
315
+ Gnomovision version 69, Copyright (C) year name of author
316
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317
+ This is free software, and you are welcome to redistribute it
318
+ under certain conditions; type `show c' for details.
319
+
320
+ The hypothetical commands `show w' and `show c' should show the appropriate
321
+ parts of the General Public License. Of course, the commands you use may
322
+ be called something other than `show w' and `show c'; they could even be
323
+ mouse-clicks or menu items--whatever suits your program.
324
+
325
+ You should also get your employer (if you work as a programmer) or your
326
+ school, if any, to sign a "copyright disclaimer" for the program, if
327
+ necessary. Here is a sample; alter the names:
328
+
329
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
331
+
332
+ <signature of Ty Coon>, 1 April 1989
333
+ Ty Coon, President of Vice
334
+
335
+ This General Public License does not permit incorporating your program into
336
+ proprietary programs. If your program is a subroutine library, you may
337
+ consider it more useful to permit linking proprietary applications with the
338
+ library. If this is what you want to do, use the GNU Lesser General
339
+ Public License instead of this License.
Gruntfile.js DELETED
@@ -1,49 +0,0 @@
1
- module.exports = function(grunt) {
2
- // Load tasks
3
- require('load-grunt-tasks')(grunt);
4
- // Display task timing
5
- require('time-grunt')(grunt);
6
- // Project configuration.
7
- grunt.initConfig({
8
- // Metadata
9
- pkg : grunt.file.readJSON('package.json'),
10
- // Variables
11
- paths : {
12
- // Base dir assets dir
13
- base : 'client',
14
-
15
- // PHP assets
16
- php : {
17
- files_std : ['*.php', '**/*.php', '!node_modules/**/*.php'], // Standard file match
18
- files : '<%= paths.php.files_std %>' // Dynamic file match
19
- },
20
-
21
- // JavaScript assets
22
- js : {
23
- base : 'js', // Base dir
24
- src : '<%= paths.js.base %>/dev', // Development code
25
- dest : '<%= paths.js.base %>/prod', // Production code
26
- files_std : '**/<%= paths.js.src %>/**/*.js', // Standard file match
27
- files : '<%= paths.js.files_std %>' // Dynamic file match
28
- },
29
-
30
- // Sass assets
31
- sass : {
32
- src : 'sass', // Source files dir
33
- dest : 'css', // Compiled files dir
34
- ext : '.css', // Compiled extension
35
- target : '*.scss', // Only Sass files in CWD
36
- exclude : '!_*.scss', // Do not process partials
37
- base_src : '<%= paths.base %>/<%= paths.sass.src %>', // Base source dir
38
- base_dest : '<%= paths.base %>/<%= paths.sass.dest %>', // Base compile dir
39
- }
40
- },
41
- });
42
-
43
- // Load task configurations
44
- grunt.loadTasks('grunt');
45
-
46
- // Default Tasks
47
- grunt.registerTask('build', ['phplint', 'jshint:all', 'uglify', 'sass']);
48
- grunt.registerTask('watch_all', ['watch:js', 'watch:sass']);
49
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
README.md CHANGED
@@ -1,12 +1,12 @@
1
- # Cornerstone
2
- Enhanced Content Management for WordPress
3
-
4
- Cornerstone transforms WordPress into a full-fledged Content Management System. Say *adios* to the hacks and tricks used to shoehorn your content into something that resembles a non-blog site and say hello to content management simplified.
5
-
6
- [Learn more][home]
7
-
8
- ## Support
9
- Found a bug or otherwise experiencing an issue with Cornerstone? [Report the issue here][issue-report]
10
-
11
- [issue-report]: https://github.com/archetyped/cornerstone/wiki/Reporting-Issues "Report an issue"
12
- [home]: http://archetyped.com/tools/cornerstone/ "Cornerstone home page"
1
+ # Cornerstone
2
+ Enhanced Content Management for WordPress
3
+
4
+ Cornerstone transforms WordPress into a full-fledged Content Management System. Say *adios* to the hacks and tricks used to shoehorn your content into something that resembles a non-blog site and say hello to content management simplified.
5
+
6
+ [Learn more][home]
7
+
8
+ ## Support
9
+ Found a bug or otherwise experiencing an issue with Cornerstone? [Report the issue here][issue-report]
10
+
11
+ [issue-report]: https://github.com/archetyped/cornerstone/wiki/Support-&-Feedback "Report an issue"
12
+ [home]: http://archetyped.com/tools/cornerstone/ "Cornerstone home page"
controller.php CHANGED
@@ -1,235 +1,235 @@
1
- <?php
2
-
3
- /**
4
- * @package Cornerstone
5
- */
6
- class Cornerstone extends CNR_Base {
7
- /* Variables */
8
-
9
- /**
10
- * Script files
11
- * @var array
12
- * @see CNR_Base::files
13
- */
14
- var $scripts = array (
15
- 'core' => array (
16
- 'file' => 'js/lib.core.js',
17
- 'deps' => 'jquery'
18
- ),
19
- 'admin' => array (
20
- 'file' => 'js/lib.admin.js',
21
- 'deps' => array('jquery', '[core]'),
22
- 'context' => 'admin'
23
- ),
24
- 'inline_edit' => array (
25
- 'file' => 'js/lib.posts.inline_edit.js',
26
- 'deps' => array('inline-edit-post','jquery', '[posts]'),
27
- 'context' => 'admin_page_edit'
28
- )
29
- );
30
-
31
- /**
32
- * Style files
33
- * @var array
34
- * @see CNR_Base::files
35
- */
36
- var $styles = array (
37
- 'admin' => array (
38
- 'file' => 'css/admin.css',
39
- 'context' => 'admin'
40
- )
41
- );
42
-
43
- /* Featured Content variables */
44
-
45
- /**
46
- * Category slug value that denotes a "featured" post
47
- * @var string
48
- * @see posts_featured_cat()
49
- * @todo Remove need for this property
50
- */
51
- var $posts_featured_cat = "feature";
52
-
53
- /**
54
- * Featured posts container
55
- * @var CNR_Post_Query
56
- */
57
- var $posts_featured = null;
58
-
59
- /* Children Content Variables */
60
-
61
- /**
62
- * Children posts
63
- * @var CNR_Post_Query
64
- */
65
- var $post_children_collection = null;
66
-
67
- /* Instance Variables */
68
-
69
- /**
70
- * Structure instance
71
- * @var CNR_Structure
72
- */
73
- var $structure = null;
74
-
75
- /**
76
- * Media instance
77
- * @var CNR_Media
78
- */
79
- var $media = null;
80
-
81
- /**
82
- * Post class instance
83
- * @var CNR_Post
84
- */
85
- var $post = null;
86
-
87
- /**
88
- * Feeds instance
89
- * @var CNR_Feeds
90
- */
91
- var $feeds = null;
92
-
93
- /* Constructor */
94
-
95
- function __construct() {
96
- //Parent Constructor
97
- parent::__construct();
98
-
99
- //Init
100
- $this->init();
101
-
102
- //Special Queries
103
- $this->posts_featured = new CNR_Post_Query( array( 'category' => $this->posts_featured_get_cat_id(), 'numberposts' => 4 ) );
104
- $this->post_children_collection = new CNR_Post_Query();
105
-
106
- $this->post = new CNR_Post();
107
- $this->post->init();
108
-
109
-
110
- //Init class instances
111
- $this->structure = new CNR_Structure();
112
- $this->structure->init();
113
-
114
- $this->media = new CNR_Media();
115
- $this->media->init();
116
-
117
- $this->feeds = new CNR_Feeds();
118
- $this->feeds->init();
119
- }
120
-
121
- /* Init */
122
-
123
- /**
124
- * Initialize environment
125
- * Overrides parent method
126
- * @see parent::init_env
127
- * @return void
128
- */
129
- function init_env() {
130
- //Localization
131
- $ldir = 'l10n';
132
- $lpath = $this->util->get_plugin_file_path($ldir, array(false, false));
133
- $lpath_abs = $this->util->get_file_path($ldir);
134
- if ( is_dir($lpath_abs) ) {
135
- load_plugin_textdomain($this->util->get_plugin_textdomain(), false, $lpath);
136
- }
137
-
138
- //Context
139
- add_action(( is_admin() ) ? 'admin_head' : 'wp_head', $this->m('set_client_context'));
140
- }
141
-
142
- /* Methods */
143
-
144
- /*-** Request **-*/
145
-
146
- /**
147
- * Output current context to client-side
148
- * @uses `wp_head` action hook
149
- * @uses `admin_head` action hook
150
- * @return void
151
- */
152
- function set_client_context() {
153
- $ctx = new stdClass();
154
- $ctx->context = $this->util->get_context();
155
- $this->util->extend_client_object($ctx, true);
156
- }
157
-
158
- /*-** Child Content **-*/
159
-
160
- /**
161
- * Gets children posts of specified page and stores them for later use
162
- * This method hooks into 'the_posts' filter to retrieve child posts for any single page retrieved by WP
163
- * @return array $posts Posts array (required by 'the_posts' filter)
164
- * @param array $posts Array of Posts (@see WP_QUERY)
165
- */
166
- function post_children_get($posts) {
167
- //Global variables
168
- global $wp_query;
169
-
170
- //Reset post children collection
171
- $this->post_children_collection->init();
172
-
173
- //Stop here if post is not a page
174
- if ( ! is_page() || empty($posts) )
175
- return $posts;
176
-
177
- //Get children posts
178
- $post =& $posts[0];
179
- $this->post_children_collection =& CNR_Post::get_children($post);
180
-
181
- //Return posts (required by filter)
182
- return $posts;
183
- }
184
-
185
- /*-** Featured Content **-*/
186
-
187
- /**
188
- * Retrieves featured post category object
189
- * @return object Featured post category object
190
- * @todo integrate into CNR_Post_Query
191
- */
192
- function posts_featured_get_cat() {
193
- static $cat = null;
194
-
195
- //Only fetch category object if it hasn't already been retrieved
196
- if (is_null($cat) || !is_object($cat)) {
197
- //Retrieve category object
198
- if (is_int($this->posts_featured_cat)) {
199
- $cat = get_category((int)$this->posts_featured_cat);
200
- }
201
- elseif (is_string($this->posts_featured_cat) && strlen($this->posts_featured_cat) > 0) {
202
- $cat = get_category_by_slug($this->posts_featured_cat);
203
- }
204
- }
205
-
206
- return $cat;
207
- }
208
-
209
- /**
210
- * @todo integrate into CNR_Post_Query
211
- */
212
- function posts_featured_get_cat_id() {
213
- static $id = '';
214
- if ($id == '') {
215
- $cat = $this->posts_featured_get_cat();
216
- if (!is_null($cat) && is_object($cat) && $this->util->property_exists($cat, 'cat_ID'))
217
- $id = $cat->cat_ID;
218
- }
219
- return $id;
220
- }
221
-
222
- /**
223
- * Checks if post has content to display
224
- * @param object $post (optional) Post object
225
- * @return bool TRUE if post has content, FALSE otherwise
226
- * @todo Review for deletion/relocation
227
- */
228
- function post_has_content($post = null) {
229
- if ( !$this->util->check_post($post) )
230
- return false;
231
- if ( isset($post->post_content) && trim($post->post_content) != '' )
232
- return true;
233
- return false;
234
- }
235
- }
1
+ <?php
2
+
3
+ /**
4
+ * @package Cornerstone
5
+ */
6
+ class Cornerstone extends CNR_Base {
7
+ /* Variables */
8
+
9
+ /**
10
+ * Script files
11
+ * @var array
12
+ * @see CNR_Base::files
13
+ */
14
+ var $scripts = array (
15
+ 'core' => array (
16
+ 'file' => 'js/lib.core.js',
17
+ 'deps' => 'jquery'
18
+ ),
19
+ 'admin' => array (
20
+ 'file' => 'js/lib.admin.js',
21
+ 'deps' => array('jquery', '[core]'),
22
+ 'context' => 'admin'
23
+ ),
24
+ 'inline_edit' => array (
25
+ 'file' => 'js/lib.posts.inline_edit.js',
26
+ 'deps' => array('inline-edit-post','jquery', '[posts]'),
27
+ 'context' => 'admin_page_edit'
28
+ )
29
+ );
30
+
31
+ /**
32
+ * Style files
33
+ * @var array
34
+ * @see CNR_Base::files
35
+ */
36
+ var $styles = array (
37
+ 'admin' => array (
38
+ 'file' => 'css/admin.css',
39
+ 'context' => 'admin'
40
+ )
41
+ );
42
+
43
+ /* Featured Content variables */
44
+
45
+ /**
46
+ * Category slug value that denotes a "featured" post
47
+ * @var string
48
+ * @see posts_featured_cat()
49
+ * @todo Remove need for this property
50
+ */
51
+ var $posts_featured_cat = "feature";
52
+
53
+ /**
54
+ * Featured posts container
55
+ * @var CNR_Post_Query
56
+ */
57
+ var $posts_featured = null;
58
+
59
+ /* Children Content Variables */
60
+
61
+ /**
62
+ * Children posts
63
+ * @var CNR_Post_Query
64
+ */
65
+ var $post_children_collection = null;
66
+
67
+ /* Instance Variables */
68
+
69
+ /**
70
+ * Structure instance
71
+ * @var CNR_Structure
72
+ */
73
+ var $structure = null;
74
+
75
+ /**
76
+ * Media instance
77
+ * @var CNR_Media
78
+ */
79
+ var $media = null;
80
+
81
+ /**
82
+ * Post class instance
83
+ * @var CNR_Post
84
+ */
85
+ var $post = null;
86
+
87
+ /**
88
+ * Feeds instance
89
+ * @var CNR_Feeds
90
+ */
91
+ var $feeds = null;
92
+
93
+ /* Constructor */
94
+
95
+ function __construct() {
96
+ //Parent Constructor
97
+ parent::__construct();
98
+
99
+ //Init
100
+ $this->init();
101
+
102
+ //Special Queries
103
+ $this->posts_featured = new CNR_Post_Query( array( 'category' => $this->posts_featured_get_cat_id(), 'numberposts' => 4 ) );
104
+ $this->post_children_collection = new CNR_Post_Query();
105
+
106
+ $this->post = new CNR_Post();
107
+ $this->post->init();
108
+
109
+
110
+ //Init class instances
111
+ $this->structure = new CNR_Structure();
112
+ $this->structure->init();
113
+
114
+ $this->media = new CNR_Media();
115
+ $this->media->init();
116
+
117
+ $this->feeds = new CNR_Feeds();
118
+ $this->feeds->init();
119
+ }
120
+
121
+ /* Init */
122
+
123
+ /**
124
+ * Initialize environment
125
+ * Overrides parent method
126
+ * @see parent::init_env
127
+ * @return void
128
+ */
129
+ function init_env() {
130
+ //Localization
131
+ $ldir = 'l10n';
132
+ $lpath = $this->util->get_plugin_file_path($ldir, array(false, false));
133
+ $lpath_abs = $this->util->get_file_path($ldir);
134
+ if ( is_dir($lpath_abs) ) {
135
+ load_plugin_textdomain($this->util->get_plugin_textdomain(), false, $lpath);
136
+ }
137
+
138
+ //Context
139
+ add_action(( is_admin() ) ? 'admin_head' : 'wp_head', $this->m('set_client_context'));
140
+ }
141
+
142
+ /* Methods */
143
+
144
+ /*-** Request **-*/
145
+
146
+ /**
147
+ * Output current context to client-side
148
+ * @uses `wp_head` action hook
149
+ * @uses `admin_head` action hook
150
+ * @return void
151
+ */
152
+ function set_client_context() {
153
+ $ctx = new stdClass();
154
+ $ctx->context = $this->util->get_context();
155
+ $this->util->extend_client_object($ctx, true);
156
+ }
157
+
158
+ /*-** Child Content **-*/
159
+
160
+ /**
161
+ * Gets children posts of specified page and stores them for later use
162
+ * This method hooks into 'the_posts' filter to retrieve child posts for any single page retrieved by WP
163
+ * @return array $posts Posts array (required by 'the_posts' filter)
164
+ * @param array $posts Array of Posts (@see WP_QUERY)
165
+ */
166
+ function post_children_get($posts) {
167
+ //Global variables
168
+ global $wp_query;
169
+
170
+ //Reset post children collection
171
+ $this->post_children_collection->init();
172
+
173
+ //Stop here if post is not a page
174
+ if ( ! is_page() || empty($posts) )
175
+ return $posts;
176
+
177
+ //Get children posts
178
+ $post =& $posts[0];
179
+ $this->post_children_collection =& CNR_Post::get_children($post);
180
+
181
+ //Return posts (required by filter)
182
+ return $posts;
183
+ }
184
+
185
+ /*-** Featured Content **-*/
186
+
187
+ /**
188
+ * Retrieves featured post category object
189
+ * @return object Featured post category object
190
+ * @todo integrate into CNR_Post_Query
191
+ */
192
+ function posts_featured_get_cat() {
193
+ static $cat = null;
194
+
195
+ //Only fetch category object if it hasn't already been retrieved
196
+ if (is_null($cat) || !is_object($cat)) {
197
+ //Retrieve category object
198
+ if (is_int($this->posts_featured_cat)) {
199
+ $cat = get_category((int)$this->posts_featured_cat);
200
+ }
201
+ elseif (is_string($this->posts_featured_cat) && strlen($this->posts_featured_cat) > 0) {
202
+ $cat = get_category_by_slug($this->posts_featured_cat);
203
+ }
204
+ }
205
+
206
+ return $cat;
207
+ }
208
+
209
+ /**
210
+ * @todo integrate into CNR_Post_Query
211
+ */
212
+ function posts_featured_get_cat_id() {
213
+ static $id = '';
214
+ if ($id == '') {
215
+ $cat = $this->posts_featured_get_cat();
216
+ if (!is_null($cat) && is_object($cat) && $this->util->property_exists($cat, 'cat_ID'))
217
+ $id = $cat->cat_ID;
218
+ }
219
+ return $id;
220
+ }
221
+
222
+ /**
223
+ * Checks if post has content to display
224
+ * @param object $post (optional) Post object
225
+ * @return bool TRUE if post has content, FALSE otherwise
226
+ * @todo Review for deletion/relocation
227
+ */
228
+ function post_has_content($post = null) {
229
+ if ( !$this->util->check_post($post) )
230
+ return false;
231
+ if ( isset($post->post_content) && trim($post->post_content) != '' )
232
+ return true;
233
+ return false;
234
+ }
235
+ }
functions.php CHANGED
@@ -1,256 +1,256 @@
1
- <?php
2
- /**
3
- * Functions
4
- * Provides global access to specific functionality
5
- * @package Cornerstone
6
- * @author Archetyped
7
- */
8
-
9
- /* Template tags */
10
-
11
- /**
12
- * Outputs feed links based on current page
13
- * @return void
14
- */
15
- function cnr_the_feed_links() {
16
- global $cnr;
17
- $cnr->feeds->the_links();
18
- }
19
-
20
- /*-** Child Content **-*/
21
-
22
- function cnr_is_section() {
23
- return ( is_page() && cnr_have_children() ) ? true : false;
24
- }
25
-
26
- /**
27
- * Checks if current post/page has children elements
28
- * @return bool TRUE if post/page has children, FALSE otherwise
29
- */
30
- function cnr_have_children() {
31
- global $cnr;
32
- return $cnr->post_children_collection->has();
33
- }
34
-
35
- /**
36
- * Prepares next child post for output to page
37
- *
38
- * @return void
39
- */
40
- function cnr_next_child() {
41
- global $cnr;
42
- $cnr->post_children_collection->next();
43
- }
44
-
45
- /**
46
- * Returns number of children in current request
47
- * May not return total number of existing children (e.g. if output is paged, etc.)
48
- * @return int Number of children returned in current request
49
- */
50
- function cnr_children_count() {
51
- global $cnr;
52
- return $cnr->post_children_collection->count();
53
- }
54
-
55
- /**
56
- * Returns total number of existing children
57
- * @return int Total number of children
58
- */
59
- function cnr_children_found() {
60
- global $cnr;
61
- return $cnr->post_children_collection->found();
62
- }
63
-
64
- /**
65
- * Returns total number of pages of children
66
- * Based on 'posts_per_page' option
67
- * @return int Maximum number of pages
68
- */
69
- function cnr_children_max_num_pages() {
70
- global $cnr;
71
- return $cnr->post_children_collection->max_num_pages();
72
- }
73
-
74
- /**
75
- * Checks if current child item is the first child item
76
- * @return bool TRUE if current item is first, FALSE otherwise
77
- */
78
- function cnr_is_first_child() {
79
- global $cnr;
80
- return $cnr->post_children_collection->is_first();
81
- }
82
-
83
- /**
84
- * Checks if current child item is the last child item
85
- * @return bool TRUE if current item is last, FALSE otherwise
86
- */
87
- function cnr_is_last_child() {
88
- global $cnr;
89
- return $cnr->post_children_collection->is_last();
90
- }
91
-
92
- /*-** Featured Content **-*/
93
-
94
- /**
95
- * Retrieves featured posts
96
- * @return array Featured posts matching criteria
97
- * @param int $limit (optional) Maximum number of featured posts to retrieve
98
- * @param int|bool $parent (optional) Section to get featured posts of (Defaults to current section). FALSE if latest featured posts should be retrieved regardless of section
99
- */
100
- function cnr_get_featured($limit = 0, $parent = null) {
101
- global $cnr;
102
- return $cnr->posts_featured->get($limit, $parent);
103
- }
104
-
105
- function cnr_in_featured($post_id = null) {
106
- global $cnr;
107
- return $cnr->posts_featured->contains($post_id);
108
- }
109
-
110
- function cnr_have_featured() {
111
- global $cnr;
112
- return $cnr->posts_featured->has();
113
- }
114
-
115
- function cnr_next_featured() {
116
- global $cnr;
117
- return $cnr->posts_featured->next();
118
-
119
- }
120
-
121
- function cnr_current_featured() {
122
- global $cnr;
123
- return $cnr->posts_featured->current();
124
- }
125
-
126
- function cnr_is_first_featured() {
127
- global $cnr;
128
- return $cnr->posts_featured->is_first();
129
- }
130
-
131
- function cnr_is_last_featured() {
132
- global $cnr;
133
- return $cnr->posts_featured->is_last();
134
- }
135
-
136
- function cnr_featured_count() {
137
- global $cnr;
138
- return $cnr->posts_featured->count();
139
- }
140
-
141
- /**
142
- * Returns total number of found posts
143
- * @return int Total number of posts
144
- */
145
- function cnr_featured_found() {
146
- global $cnr;
147
- return $cnr->posts_featured->found();
148
- }
149
-
150
- /*-** Post-Specific **-*/
151
-
152
- /**
153
- * Checks if post has content to display
154
- * @param object $post (optional) Post object
155
- * @return bool TRUE if post has content, FALSE otherwise
156
- */
157
- function cnr_has_content($post = null) {
158
- global $cnr;
159
- return $cnr->post_has_content($post);
160
- }
161
-
162
- /* Images */
163
-
164
- function cnr_get_attachments($post = null) {
165
- $m = new CNR_Media();
166
- return $m->post_get_attachments($post);
167
- }
168
-
169
- function cnr_get_filesize($post = null, $formatted = true) {
170
- $m = new CNR_Media();
171
- return $m->get_attachment_filesize($post, $formatted);
172
- }
173
-
174
- /* Section */
175
-
176
- /**
177
- * Retrieves the post's section data
178
- * @uses CNR_Post::get_section()
179
- * @param string $data (optional) Type of data to return (Default: ID)
180
- * Possible values:
181
- * NULL Full section post object
182
- * Column name Post column data (if exists)
183
- *
184
- * @param int $id (optional) Post ID (Default: current post)
185
- * @return mixed post's section (or column data if specified via $data parameter)
186
- */
187
- function cnr_get_the_section($data = 'ID', $id = null) {
188
- return CNR_Post::get_section($id, $data);
189
- }
190
-
191
- /**
192
- * Prints the post's section data
193
- * @uses CNR_Post::the_section()
194
- * @param string $data (optional) Type of data to return (Default: ID)
195
- */
196
- function cnr_the_section($data = 'ID') {
197
- CNR_Post::the_section(null, $data);
198
- }
199
-
200
- /* Content Types */
201
-
202
- /**
203
- * Register handler for a placeholder in a content type template
204
- * Placeholders allow templates to be populated with dynamic content at runtime
205
- * Multiple handlers can be registered for a placeholder,
206
- * thus allowing custom handlers to override default processing, etc.
207
- * @uses CNR_Field_Type::register_placeholder_handler() to register placeholder
208
- * @param string $placeholder Placeholder identifier
209
- * @param callback $handler Callback function to use as handler for placeholder
210
- * @param int $priority (optional) Priority of registered handler (Default: 10)
211
- */
212
- function cnr_register_placeholder_handler($placeholder, $handler, $priority = 10) {
213
- CNR_Field_Type::register_placeholder_handler($placeholder, $handler, $priority);
214
- }
215
-
216
- /**
217
- * Checks if data exists for specified field
218
- * @global $cnr_content_utilities
219
- * @param string $field_id ID of field to check for data
220
- * @param int|obj $item (optional) Post ID or object to check for field data (Default: global post)
221
- * @return bool TRUE if field data exists
222
- */
223
- function cnr_has_data($field_id = null, $item = null) {
224
- global $cnr_content_utilities;
225
- return $cnr_content_utilities->has_item_data($item, $field_id);
226
- }
227
-
228
- /**
229
- * Retrieve data from a field
230
- * @global $cnr_content_utilities
231
- * @see CNR_Content_Utilities::get_item_data() for more information
232
- * @param string $field_id ID of field to retrieve
233
- * @param string $layout (optional) Name of layout to use when returning data
234
- * @param array $attr (optional) Additional attributes to pass to field
235
- * @param int|object $item (optional) Post object to retrieve data from (Default: global post object)
236
- * @param mixed $default Default value to return in case of errors (invalid field, no data, etc.)
237
- * @return mixed Specified field data
238
- */
239
- function cnr_get_data($field_id = null, $layout = 'display', $attr = null, $item = null, $default = '') {
240
- global $cnr_content_utilities;
241
- return $cnr_content_utilities->get_item_data($item, $field_id, $layout, $default, $attr);
242
- }
243
-
244
- /**
245
- * Prints an item's field data
246
- * @see CNR_Content_Utilities::the_item_data() for more information
247
- * @param string $field_id Name of field to retrieve
248
- * @param string $layout(optional) Layout to use when returning field data (Default: display)
249
- * @param array $attr Additional items to pass to field
250
- * @param int|object $item(optional) Content item to retrieve field from (Default: null - global $post object will be used)
251
- * @param mixed $default Default value to return in case of errors, etc.
252
- */
253
- function cnr_the_data($field_id = null, $layout = 'display', $attr = null, $item = null, $default = '') {
254
- global $cnr_content_utilities;
255
- $cnr_content_utilities->the_item_data($item, $field_id, $layout, $default, $attr);
256
  }
1
+ <?php
2
+ /**
3
+ * Functions
4
+ * Provides global access to specific functionality
5
+ * @package Cornerstone
6
+ * @author Archetyped
7
+ */
8
+
9
+ /* Template tags */
10
+
11
+ /**
12
+ * Outputs feed links based on current page
13
+ * @return void
14
+ */
15
+ function cnr_the_feed_links() {
16
+ global $cnr;
17
+ $cnr->feeds->the_links();
18
+ }
19
+
20
+ /*-** Child Content **-*/
21
+
22
+ function cnr_is_section() {
23
+ return ( is_page() && cnr_have_children() ) ? true : false;
24
+ }
25
+
26
+ /**
27
+ * Checks if current post/page has children elements
28
+ * @return bool TRUE if post/page has children, FALSE otherwise
29
+ */
30
+ function cnr_have_children() {
31
+ global $cnr;
32
+ return $cnr->post_children_collection->has();
33
+ }
34
+
35
+ /**
36
+ * Prepares next child post for output to page
37
+ *
38
+ * @return void
39
+ */
40
+ function cnr_next_child() {
41
+ global $cnr;
42
+ $cnr->post_children_collection->next();
43
+ }
44
+
45
+ /**
46
+ * Returns number of children in current request
47
+ * May not return total number of existing children (e.g. if output is paged, etc.)
48
+ * @return int Number of children returned in current request
49
+ */
50
+ function cnr_children_count() {
51
+ global $cnr;
52
+ return $cnr->post_children_collection->count();
53
+ }
54
+
55
+ /**
56
+ * Returns total number of existing children
57
+ * @return int Total number of children
58
+ */
59
+ function cnr_children_found() {
60
+ global $cnr;
61
+ return $cnr->post_children_collection->found();
62
+ }
63
+
64
+ /**
65
+ * Returns total number of pages of children
66
+ * Based on 'posts_per_page' option
67
+ * @return int Maximum number of pages
68
+ */
69
+ function cnr_children_max_num_pages() {
70
+ global $cnr;
71
+ return $cnr->post_children_collection->max_num_pages();
72
+ }
73
+
74
+ /**
75
+ * Checks if current child item is the first child item
76
+ * @return bool TRUE if current item is first, FALSE otherwise
77
+ */
78
+ function cnr_is_first_child() {
79
+ global $cnr;
80
+ return $cnr->post_children_collection->is_first();
81
+ }
82
+
83
+ /**
84
+ * Checks if current child item is the last child item
85
+ * @return bool TRUE if current item is last, FALSE otherwise
86
+ */
87
+ function cnr_is_last_child() {
88
+ global $cnr;
89
+ return $cnr->post_children_collection->is_last();
90
+ }
91
+
92
+ /*-** Featured Content **-*/
93
+
94
+ /**
95
+ * Retrieves featured posts
96
+ * @return array Featured posts matching criteria
97
+ * @param int $limit (optional) Maximum number of featured posts to retrieve
98
+ * @param int|bool $parent (optional) Section to get featured posts of (Defaults to current section). FALSE if latest featured posts should be retrieved regardless of section
99
+ */
100
+ function cnr_get_featured($limit = 0, $parent = null) {
101
+ global $cnr;
102
+ return $cnr->posts_featured->get($limit, $parent);
103
+ }
104
+
105
+ function cnr_in_featured($post_id = null) {
106
+ global $cnr;
107
+ return $cnr->posts_featured->contains($post_id);
108
+ }
109
+
110
+ function cnr_have_featured() {
111
+ global $cnr;
112
+ return $cnr->posts_featured->has();
113
+ }
114
+
115
+ function cnr_next_featured() {
116
+ global $cnr;
117
+ return $cnr->posts_featured->next();
118
+
119
+ }
120
+
121
+ function cnr_current_featured() {
122
+ global $cnr;
123
+ return $cnr->posts_featured->current();
124
+ }
125
+
126
+ function cnr_is_first_featured() {
127
+ global $cnr;
128
+ return $cnr->posts_featured->is_first();
129
+ }
130
+
131
+ function cnr_is_last_featured() {
132
+ global $cnr;
133
+ return $cnr->posts_featured->is_last();
134
+ }
135
+
136
+ function cnr_featured_count() {
137
+ global $cnr;
138
+ return $cnr->posts_featured->count();
139
+ }
140
+
141
+ /**
142
+ * Returns total number of found posts
143
+ * @return int Total number of posts
144
+ */
145
+ function cnr_featured_found() {
146
+ global $cnr;
147
+ return $cnr->posts_featured->found();
148
+ }
149
+
150
+ /*-** Post-Specific **-*/
151
+
152
+ /**
153
+ * Checks if post has content to display
154
+ * @param object $post (optional) Post object
155
+ * @return bool TRUE if post has content, FALSE otherwise
156
+ */
157
+ function cnr_has_content($post = null) {
158
+ global $cnr;
159
+ return $cnr->post_has_content($post);
160
+ }
161
+
162
+ /* Images */
163
+
164
+ function cnr_get_attachments($post = null) {
165
+ $m = new CNR_Media();
166
+ return $m->post_get_attachments($post);
167
+ }
168
+
169
+ function cnr_get_filesize($post = null, $formatted = true) {
170
+ $m = new CNR_Media();
171
+ return $m->get_attachment_filesize($post, $formatted);
172
+ }
173
+
174
+ /* Section */
175
+
176
+ /**
177
+ * Retrieves the post's section data
178
+ * @uses CNR_Post::get_section()
179
+ * @param string $data (optional) Type of data to return (Default: ID)
180
+ * Possible values:
181
+ * NULL Full section post object
182
+ * Column name Post column data (if exists)
183
+ *
184
+ * @param int $id (optional) Post ID (Default: current post)
185
+ * @return mixed post's section (or column data if specified via $data parameter)
186
+ */
187
+ function cnr_get_the_section($data = 'ID', $id = null) {
188
+ return CNR_Post::get_section($id, $data);
189
+ }
190
+
191
+ /**
192
+ * Prints the post's section data
193
+ * @uses CNR_Post::the_section()
194
+ * @param string $data (optional) Type of data to return (Default: ID)
195
+ */
196
+ function cnr_the_section($data = 'ID') {
197
+ CNR_Post::the_section(null, $data);
198
+ }
199
+
200
+ /* Content Types */
201
+
202
+ /**
203
+ * Register handler for a placeholder in a content type template
204
+ * Placeholders allow templates to be populated with dynamic content at runtime
205
+ * Multiple handlers can be registered for a placeholder,
206
+ * thus allowing custom handlers to override default processing, etc.
207
+ * @uses CNR_Field_Type::register_placeholder_handler() to register placeholder
208
+ * @param string $placeholder Placeholder identifier
209
+ * @param callback $handler Callback function to use as handler for placeholder
210
+ * @param int $priority (optional) Priority of registered handler (Default: 10)
211
+ */
212
+ function cnr_register_placeholder_handler($placeholder, $handler, $priority = 10) {
213
+ CNR_Field_Type::register_placeholder_handler($placeholder, $handler, $priority);
214
+ }
215
+
216
+ /**
217
+ * Checks if data exists for specified field
218
+ * @global $cnr_content_utilities
219
+ * @param string $field_id ID of field to check for data
220
+ * @param int|obj $item (optional) Post ID or object to check for field data (Default: global post)
221
+ * @return bool TRUE if field data exists
222
+ */
223
+ function cnr_has_data($field_id = null, $item = null) {
224
+ global $cnr_content_utilities;
225
+ return $cnr_content_utilities->has_item_data($item, $field_id);
226
+ }
227
+
228
+ /**
229
+ * Retrieve data from a field
230
+ * @global $cnr_content_utilities
231
+ * @see CNR_Content_Utilities::get_item_data() for more information
232
+ * @param string $field_id ID of field to retrieve
233
+ * @param string $layout (optional) Name of layout to use when returning data
234
+ * @param array $attr (optional) Additional attributes to pass to field
235
+ * @param int|object $item (optional) Post object to retrieve data from (Default: global post object)
236
+ * @param mixed $default Default value to return in case of errors (invalid field, no data, etc.)
237
+ * @return mixed Specified field data
238
+ */
239
+ function cnr_get_data($field_id = null, $layout = 'display', $attr = null, $item = null, $default = '') {
240
+ global $cnr_content_utilities;
241
+ return $cnr_content_utilities->get_item_data($item, $field_id, $layout, $default, $attr);
242
+ }
243
+
244
+ /**
245
+ * Prints an item's field data
246
+ * @see CNR_Content_Utilities::the_item_data() for more information
247
+ * @param string $field_id Name of field to retrieve
248
+ * @param string $layout(optional) Layout to use when returning field data (Default: display)
249
+ * @param array $attr Additional items to pass to field
250
+ * @param int|object $item(optional) Content item to retrieve field from (Default: null - global $post object will be used)
251
+ * @param mixed $default Default value to return in case of errors, etc.
252
+ */
253
+ function cnr_the_data($field_id = null, $layout = 'display', $attr = null, $item = null, $default = '') {
254
+ global $cnr_content_utilities;
255
+ $cnr_content_utilities->the_item_data($item, $field_id, $layout, $default, $attr);
256
  }
grunt/jshint.js DELETED
@@ -1,38 +0,0 @@
1
- module.exports = function(grunt) {
2
-
3
- grunt.config('jshint', {
4
- options : {
5
- reporter: require('jshint-stylish'),
6
- curly : true,
7
- eqeqeq : true,
8
- immed : true,
9
- latedef : true,
10
- newcap : false,
11
- noarg : true,
12
- sub : true,
13
- undef : true,
14
- unused : true,
15
- boss : true,
16
- eqnull : true,
17
- browser : true,
18
- jquery : true,
19
- globals : {}
20
- },
21
- grunt : {
22
- options : {
23
- node : true
24
- },
25
- src : ['Gruntfile.js', 'grunt/*.js']
26
- },
27
- all : {
28
- options : {
29
- globals : {
30
- 'SLB' : true,
31
- 'console' : true
32
- }
33
- },
34
- src : ['<%= paths.js.files %>']
35
- },
36
- });
37
-
38
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
grunt/phplint.js DELETED
@@ -1,14 +0,0 @@
1
- module.exports = function(grunt) {
2
-
3
- grunt.config('phplint', {
4
- options : {
5
- phpArgs : {
6
- '-lf': null
7
- }
8
- },
9
- all : {
10
- src : '<%= paths.php.files %>'
11
- }
12
- });
13
-
14
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
grunt/sass.js DELETED
@@ -1,35 +0,0 @@
1
- module.exports = function(grunt) {
2
-
3
- grunt.config('sass', {
4
- options : {
5
- outputStyle : 'compressed',
6
- },
7
- core : {
8
- files : [{
9
- expand : true,
10
- cwd : '<%= paths.sass.base_src %>/',
11
- dest : '<%= paths.sass.base_dest %>/',
12
- src : ['<%= paths.sass.target %>', '<%= paths.sass.exclude %>'],
13
- ext : '<%= paths.sass.ext %>'
14
- }]
15
- },
16
- themes : {
17
- options : {
18
- //includePaths : require('node-bourbon').includePaths
19
- },
20
- files : [{
21
- expand : true,
22
- cwd : 'themes/',
23
- src : ['*/**/*.scss', '<%= paths.sass.exclude %>'],
24
- dest : '<%= paths.sass.dest %>/',
25
- srcd : '<%= paths.sass.src %>/',
26
- ext : '<%= paths.sass.ext %>',
27
- rename : function(dest, matchedSrcPath, options) {
28
- var path = [options.cwd, matchedSrcPath.replace(options.srcd, dest)].join('');
29
- return path;
30
- }
31
- }]
32
- }
33
- });
34
-
35
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
grunt/uglify.js DELETED
@@ -1,21 +0,0 @@
1
- module.exports = function(grunt) {
2
-
3
- grunt.config('uglify', {
4
- options : {
5
- mangle: false,
6
- report: 'min'
7
- },
8
- all : {
9
- files : [{
10
- expand : true,
11
- cwd : '',
12
- dest : '',
13
- src : ['<%= paths.js.files %>'],
14
- rename : function(dest, srcPath) {
15
- return srcPath.replace('/' + grunt.config.get('paths.js.src') + '/', '/' + grunt.config.get('paths.js.dest') + '/');
16
- }
17
- }]
18
- },
19
- });
20
-
21
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
grunt/watch.js DELETED
@@ -1,57 +0,0 @@
1
- module.exports = function(grunt) {
2
-
3
- grunt.config('watch', {
4
- phplint : {
5
- files : '<%= paths.php.files_std %>',
6
- tasks : ['phplint'],
7
- options : {
8
- spawn : false
9
- }
10
- },
11
- sass_core : {
12
- files : ['<%= paths.sass.base_src %>/**/*.scss'],
13
- tasks : ['sass:core']
14
- },
15
- sass_themes : {
16
- files : ['themes/**/<%= paths.sass.src %>/**/*.scss'],
17
- tasks : ['sass:themes']
18
- },
19
- jshint : {
20
- files : '<%= paths.js.files_std %>',
21
- tasks : ['jshint:all'],
22
- options : {
23
- spawn : false
24
- }
25
- },
26
- js : {
27
- files : '<%= paths.js.files_std %>',
28
- tasks : ['jshint:all', 'uglify:all'],
29
- options : {
30
- spawn : false
31
- }
32
- }
33
- });
34
-
35
- grunt.event.on('watch', function(action, filepath) {
36
- // Determine task based on filepath
37
- var get_ext = function(path) {
38
- var ret = '';
39
- var i = path.lastIndexOf('.');
40
- if ( -1 !== i && i <= path.length ) {
41
- ret = path.substr(i + 1);
42
- }
43
- return ret;
44
- };
45
- switch ( get_ext(filepath) ) {
46
- // PHP
47
- case 'php' :
48
- grunt.config('paths.php.files', [filepath]);
49
- break;
50
- // JavaScript
51
- case 'js' :
52
- grunt.config('paths.js.files', [filepath]);
53
- break;
54
- }
55
- });
56
-
57
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/class.content_base.php CHANGED
@@ -1,615 +1,615 @@
1
- <?php
2
- /**
3
- * Content Types - Base Class
4
- * Core properties/methods for Content Type derivative classes
5
- * @package Cornerstone
6
- * @subpackage Content Types
7
- * @author Archetyped
8
- */
9
- class CNR_Content_Base extends CNR_Base {
10
-
11
- /**
12
- * Base class name
13
- * @var string
14
- */
15
- var $base_class = 'cnr_content_base';
16
-
17
- /**
18
- * @var string Unique name
19
- */
20
- var $id = '';
21
-
22
- /**
23
- * Reference to parent object that current instance inherits from
24
- * @var object
25
- */
26
- var $parent = null;
27
-
28
- /**
29
- * Title
30
- * @var string
31
- */
32
- var $title = '';
33
-
34
- /**
35
- * Plural Title
36
- * @var string
37
- */
38
- var $title_plural = '';
39
-
40
- /**
41
- * @var string Short description
42
- */
43
- var $description = '';
44
-
45
- /**
46
- * @var array Object Properties
47
- */
48
- var $properties = array();
49
-
50
- /**
51
- * Data for object
52
- * May also contain data for nested objects
53
- * @var mixed
54
- */
55
- var $data = null;
56
-
57
- /**
58
- * @var array Script resources to include for object
59
- */
60
- var $scripts = array();
61
-
62
- /**
63
- * @var array CSS style resources to include for object
64
- */
65
- var $styles = array();
66
-
67
- /**
68
- * Hooks (Filters/Actions) for object
69
- * @var array
70
- */
71
- var $hooks = array();
72
-
73
- /**
74
- * Constructor
75
- */
76
- function __construct($id = '', $parent = null) {
77
- parent::__construct();
78
- $id = trim($id);
79
- $this->id = $id;
80
- if ( is_bool($parent) && $parent )
81
- $parent = $id;
82
- $this->set_parent($parent);
83
- }
84
-
85
- /* Getters/Setters */
86
-
87
- /**
88
- * Checks if the specified path exists in the object
89
- * @param array $path Path to check for
90
- * @return bool TRUE if path exists in object, FALSE otherwise
91
- */
92
- function path_isset($path = '') {
93
- //Stop execution if no path is supplied
94
- if ( empty($path) )
95
- return false;
96
- $args = func_get_args();
97
- $path = $this->util->build_path($args);
98
- $item =& $this;
99
- //Iterate over path and check if each level exists before moving on to the next
100
- for ($x = 0; $x < count($path); $x++) {
101
- if ( $this->util->property_exists($item, $path[$x]) ) {
102
- //Set $item as reference to next level in path for next iteration
103
- $item =& $this->util->get_property($item, $path[$x]);
104
- //$item =& $item[ $path[$x] ];
105
- } else {
106
- return false;
107
- }
108
- }
109
- return true;
110
- }
111
-
112
- /**
113
- * Retrieves a value from object using a specified path
114
- * Checks to make sure path exists in object before retrieving value
115
- * @param array $path Path to retrieve value from. Each item in array is a deeper dimension
116
- * @return mixed Value at specified path
117
- */
118
- function &get_path_value($path = '') {
119
- $ret = '';
120
- $path = $this->util->build_path(func_get_args());
121
- if ( $this->path_isset($path) ) {
122
- $ret =& $this;
123
- for ($x = 0; $x < count($path); $x++) {
124
- if ( 0 == $x )
125
- $ret =& $ret->{ $path[$x] };
126
- else
127
- $ret =& $ret[ $path[$x] ];
128
- }
129
- }
130
- return $ret;
131
- }
132
-
133
- /**
134
- * Search for specified member value in field type ancestors
135
- * @param string $member Name of object member to search (e.g. properties, layout, etc.)
136
- * @param string $name Value to retrieve from member
137
- * @return mixed Member value if found (Default: empty string)
138
- */
139
- function get_parent_value($member, $name = '', $default = '') {
140
- $parent =& $this->get_parent();
141
- return $this->get_object_value($parent, $member, $name, $default, 'parent');
142
- }
143
-
144
- /**
145
- * Retrieves specified member value
146
- * Handles inherited values
147
- * Merging corresponding parents if value is an array (e.g. for property groups)
148
- * @param string|array $member Member to search. May also contain a path to the desired member
149
- * @param string $name Value to retrieve from member
150
- * @param mixed $default Default value if no value found (Default: empty string)
151
- * @param string $dir Direction to move through hierarchy to find value
152
- * Possible Values:
153
- * parent (default) - Search through field parents
154
- * current - Do not search through connected objects
155
- * container - Search through field containers
156
- * caller - Search through field callers
157
- * @return mixed Specified member value
158
- */
159
- function get_member_value($member, $name = '', $default = '', $dir = 'parent') {
160
- //Check if path to member is supplied
161
- $path = array();
162
- if ( is_array($member) && isset($member['tag']) ) {
163
- if ( isset($member['attributes']['ref_base']) ) {
164
- if ( 'root' != $member['attributes']['ref_base'] )
165
- $path[] = $member['attributes']['ref_base'];
166
- } else {
167
- $path[] = 'properties';
168
- }
169
-
170
- $path[] = $member['tag'];
171
- } else {
172
- $path = $member;
173
- }
174
-
175
- $path = $this->util->build_path($path, $name);
176
- //Set defaults and prepare data
177
- $val = $default;
178
- $inherit = false;
179
- $inherit_tag = '{inherit}';
180
-
181
- /* Determine whether the value must be retrieved from a parent/container object
182
- * Conditions:
183
- * > Path does not exist in current field
184
- * > Path exists and is not an object, but at least one of the following is true:
185
- * > Value at path is an array (e.g. properties, elements, etc. array)
186
- * > Parent/container values should be merged with retrieved array
187
- * > Value at path is a string that inherits from another field
188
- * > Value from other field will be retrieved and will replace inheritance placeholder in retrieved value
189
- */
190
-
191
- $deeper = false;
192
-
193
- if ( !$this->path_isset($path) )
194
- $deeper = true;
195
- else {
196
- $val = $this->get_path_value($path);
197
- if ( !is_object($val) && ( is_array($val) || ($inherit = strpos($val, $inherit_tag)) !== false ) )
198
- $deeper = true;
199
- else
200
- $deeper = false;
201
- }
202
- if ( $deeper && 'current' != $dir ) {
203
- //Get Parent value (recursive)
204
- $ex_val = ( 'parent' != $dir ) ? $this->get_container_value($member, $name, $default) : $this->get_parent_value($member, $name, $default);
205
- //Handle inheritance
206
- if ( is_array($val) ) {
207
- //Combine Arrays
208
- if ( is_array($ex_val) )
209
- $val = array_merge($ex_val, $val);
210
- } elseif ( $inherit !== false ) {
211
- //Replace placeholder with inherited string
212
- $val = str_replace($inherit_tag, $ex_val, $val);
213
- } else {
214
- //Default: Set parent value as value
215
- $val = $ex_val;
216
- }
217
- }
218
-
219
- return $val;
220
- }
221
-
222
- /**
223
- * Search for specified member value in an object
224
- * @param object $object Reference to object to retrieve value from
225
- * @param string $member Name of object member to search (e.g. properties, layout, etc.)
226
- * @param string $name (optional) Value to retrieve from member
227
- * @param mixed $default (optional) Default value to use if no value found (Default: empty string)
228
- * @param string $dir Direction to move through hierarchy to find value @see CNR_Field_Type::get_member_value() for possible values
229
- * @return mixed Member value if found (Default: $default)
230
- */
231
- function get_object_value(&$object, $member, $name = '', $default = '', $dir = 'parent') {
232
- $ret = $default;
233
- if ( is_object($object) && method_exists($object, 'get_member_value') )
234
- $ret = $object->get_member_value($member, $name, $default, $dir);
235
- return $ret;
236
- }
237
-
238
- /**
239
- * Retrieve value from data member
240
- * @param bool $top (optional) Whether to traverse through the field hierarchy to get data for field (Default: TRUE)
241
- * @return mixed Value at specified path
242
- */
243
- function get_data($top = true) {
244
- $top = !!$top;
245
- $obj = $this;
246
- $obj_path = array($this);
247
- $path = array();
248
- //Iterate through hiearchy to get top-most object
249
- while ( !empty($obj) ) {
250
- $new = null;
251
- //Try to get caller first
252
- if ( method_exists($obj, 'get_caller') ) {
253
- $checked = true;
254
- $new = $obj->get_caller();
255
- }
256
- //Try to get container if no caller found
257
- if ( empty($new) && method_exists($obj, 'get_container') ) {
258
- $checked = true;
259
- $new = $obj->get_container();
260
- }
261
-
262
- $obj = $new;
263
-
264
- //Stop iteration
265
- if ( !empty($obj) ) {
266
- //Add object to path if it is valid
267
- $obj_path[] = $obj;
268
- }
269
- }
270
-
271
- //Check each object (starting with top-most) for matching data for current field
272
-
273
- //Reverse array
274
- $obj_path = array_reverse($obj_path);
275
- //Build path for data location
276
- foreach ( $obj_path as $obj ) {
277
- if ( $this->util->property_exists($obj, 'id') )
278
- $path[] = $obj->id;
279
- }
280
-
281
- //Iterate through objects
282
- while ( !empty($obj_path) ) {
283
- //Get next object
284
- $obj = array_shift($obj_path);
285
- //Shorten path
286
- array_shift($path);
287
- //Check for value in object and stop iteration if matching data found
288
- if ( ($val = $this->get_object_value($obj, 'data', $path, null, 'current')) && !is_null($val) ) {
289
- break;
290
- }
291
- }
292
-
293
- return $val;
294
- }
295
-
296
- /**
297
- * Sets value in data member
298
- * Sets value to data member itself by default
299
- * @param mixed $value Value to set
300
- * @param string|array $name Name of value to set (Can also be path to value)
301
- */
302
- function set_data($value, $name = '') {
303
- $ref =& $this->get_path_value('data', $name);
304
- $ref = $value;
305
- }
306
-
307
- /**
308
- * Retrieve base_class property
309
- * @return string base_class property of current class/instance object
310
- */
311
- static function get_base_class() {
312
- $ret = '';
313
- if ( isset($this) )
314
- $ret = $this->base_class;
315
- else {
316
- $ret = CNR_Utilities::get_property(__CLASS__, 'base_class');
317
- }
318
-
319
- return $ret;
320
- }
321
-
322
- /**
323
- * Sets parent object of current instance
324
- * Parent objects must be the same object type as current instance
325
- * @param string|object $parent Parent ID or reference
326
- */
327
- function set_parent($parent) {
328
- if ( !empty($parent) ) {
329
- //Validate parent object
330
- if ( is_array($parent) )
331
- $parent =& $parent[0];
332
-
333
- //Retrieve reference object if ID was supplied
334
- if ( is_string($parent) ) {
335
- $parent = trim($parent);
336
- //Check for existence of parent
337
- $lookup = $this->base_class . 's';
338
- if ( isset($GLOBALS[$lookup][$parent]) ) {
339
- //Get reference to parent
340
- $parent =& $GLOBALS[$lookup][$parent];
341
- }
342
- }
343
-
344
- //Set reference to parent field type
345
- if ( is_a($parent, $this->base_class) ) {
346
- $this->parent =& $parent;
347
- }
348
- }
349
- }
350
-
351
- /**
352
- * Retrieve field type parent
353
- * @return CNR_Field_Type Reference to parent field
354
- */
355
- function &get_parent() {
356
- return $this->parent;
357
- }
358
-
359
- /**
360
- * Retrieves field ID
361
- * @param string|CNR_Field|array $field (optional) Field object or ID of field or options array
362
- * @return string|bool Field ID, FALSE if $field is invalid
363
- */
364
- function get_id($field = null) {
365
- $ret = false;
366
-
367
- // Get options.
368
- $num_args = func_num_args();
369
- $options = ( $num_args > 0 && ( $last_arg = func_get_arg($num_args - 1) ) && is_array($last_arg) ) ? $last_arg : array();
370
-
371
- // Validate field parameter.
372
- if ( ( !is_object($field) || !is_a($field, 'cnr_field_type') ) && isset($this) ) {
373
- $field =& $this;
374
- }
375
-
376
- if ( is_a($field, CNR_Field_Type::get_base_class()) )
377
- $id = $field->id;
378
-
379
- if ( is_string($id) )
380
- $ret = trim($id);
381
-
382
- // Setup options.
383
- $options = wp_parse_args( $options, [ 'format' => null ] );
384
- //Check if field should be formatted
385
- if ( is_string($ret) && !empty($options['format']) ) {
386
- //Clear format option if it is an invalid value
387
- if ( is_bool($options['format']) || is_int($options['format']) )
388
- $options['format'] = null;
389
- //Setup values
390
- $wrap = array('open' => '[', 'close' => ']');
391
- if ( isset($options['wrap']) && is_array($options['wrap']) )
392
- $wrap = wp_parse_args($options['wrap'], $wrap);
393
- $wrap_trailing = ( isset($options['wrap_trailing']) ) ? !!$options['wrap_trailing'] : true;
394
- switch ( $options['format'] ) {
395
- case 'attr_id' :
396
- $wrap = (array('open' => '_', 'close' => '_'));
397
- $wrap_trailing = false;
398
- break;
399
- }
400
- $c = $field->get_caller();
401
- $field_id = array($ret);
402
- while ( !!$c ) {
403
- //Add ID of current field to array
404
- if ( isset($c->id) && is_a($c, $this->base_class) )
405
- $field_id[] = $c->id;
406
- $c = ( method_exists($c, 'get_caller') ) ? $c->get_caller() : null;
407
- }
408
-
409
- //Add prefix to ID value
410
- $field_id[] = 'attributes';
411
-
412
- //Convert array to string
413
- $ret = $field->prefix . $wrap['open'] . implode($wrap['close'] . $wrap['open'], array_reverse($field_id)) . ( $wrap_trailing ? $wrap['close'] : '');
414
- }
415
- return $ret;
416
- }
417
-
418
- /**
419
- * Set object title
420
- * @param string $title Title for object
421
- * @param string $plural Plural form of title
422
- */
423
- function set_title($title = '', $plural = '') {
424
- $this->title = strip_tags(trim($title));
425
- if ( isset($plural) )
426
- $this->title_plural = strip_tags(trim($plural));
427
- }
428
-
429
- /**
430
- * Retrieve object title
431
- * @param bool $plural TRUE if plural title should be retrieved, FALSE otherwise (Default: FALSE)
432
- */
433
- function get_title($plural = false) {
434
- $dir = 'current';
435
- //Singular
436
- if ( !$plural )
437
- return $this->get_member_value('title', '','', $dir);
438
- //Plural
439
- $title = $this->get_member_value('title_plural', '', '', $dir);
440
- if ( empty($title) ) {
441
- //Use singular title for plural base
442
- $title = $this->get_member_value('title', '', '', $dir);
443
- //Determine technique for making title plural
444
- //Get last letter
445
- if ( !empty($title) ) {
446
- $tail = substr($title, -1);
447
- switch ( $tail ) {
448
- case 's' :
449
- $title .= 'es';
450
- break;
451
- case 'y' :
452
- $title = substr($title, 0, -1) . 'ies';
453
- break;
454
- default :
455
- $title .= 's';
456
- }
457
- }
458
- }
459
- return $title;
460
- }
461
-
462
- /**
463
- * Set object description
464
- * @param string $description Description for object
465
- */
466
- function set_description($description = '') {
467
- $this->description = strip_tags(trim($description));
468
- }
469
-
470
- /**
471
- * Retrieve object description
472
- * @return string Object description
473
- */
474
- function get_description() {
475
- $dir = 'current';
476
- return $this->get_member_value('description', '','', $dir);
477
- return $desc;
478
- }
479
-
480
- /*-** Hooks **-*/
481
-
482
- /**
483
- * Retrieve hooks added to object
484
- * @return array Hooks
485
- */
486
- function get_hooks() {
487
- return $this->get_member_value('hooks', '', array());
488
- }
489
-
490
- /**
491
- * Add hook for object
492
- * @see add_filter() for parameter defaults
493
- * @param $tag
494
- * @param $function_to_add
495
- * @param $priority
496
- * @param $accepted_args
497
- */
498
- function add_hook($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
499
- //Create new array for tag (if not already set)
500
- if ( !isset($this->hooks[$tag]) )
501
- $this->hooks[$tag] = array();
502
- //Build Unique ID
503
- if ( is_string($function_to_add) )
504
- $id = $function_to_add;
505
- elseif ( is_array($function_to_add) && !empty($function_to_add) )
506
- $id = strval($function_to_add[count($function_to_add) - 1]);
507
- else
508
- $id = 'function_' . ( count($this->hooks[$tag]) + 1 );
509
- //Add hook
510
- $this->hooks[$tag][$id] = func_get_args();
511
- }
512
-
513
- /**
514
- * Convenience method for adding an action for object
515
- * @see add_filter() for parameter defaults
516
- * @param $tag
517
- * @param $function_to_add
518
- * @param $priority
519
- * @param $accepted_args
520
- */
521
- function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
522
- $this->add_hook($tag, $function_to_add, $priority, $accepted_args);
523
- }
524
-
525
- /**
526
- * Convenience method for adding a filter for object
527
- * @see add_filter() for parameter defaults
528
- * @param $tag
529
- * @param $function_to_add
530
- * @param $priority
531
- * @param $accepted_args
532
- */
533
- function add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
534
- $this->add_hook($tag, $function_to_add, $priority, $accepted_args);
535
- }
536
-
537
- /*-** Dependencies **-*/
538
-
539
- /**
540
- * Adds dependency to object
541
- * @param string $type Type of dependency to add (script, style)
542
- * @param array|string $context When dependency will be added (@see CNR_Utilities::get_action() for possible contexts)
543
- * @see wp_enqueue_script for the following of the parameters
544
- * @param $handle
545
- * @param $src
546
- * @param $deps
547
- * @param $ver
548
- * @param $ex
549
- */
550
- function add_dependency($type, $context, $handle, $src = false, $deps = array(), $ver = false, $ex = false) {
551
- $args = func_get_args();
552
- //Remove type/context from arguments
553
- $args = array_slice($args, 2);
554
-
555
- //Set context
556
- if ( !is_array($context) ) {
557
- //Wrap single contexts in an array
558
- if ( is_string($context) )
559
- $context = array($context);
560
- else
561
- $context = array();
562
- }
563
- //Add file to instance property
564
- $this->{$type}[$handle] = array('context' => $context, 'params' => $args);
565
- }
566
-
567
- /**
568
- * Add script to object to be added in specified contexts
569
- * @param array|string $context Array of contexts to add script to page
570
- * @see wp_enqueue_script for the following of the parameters
571
- * @param $handle
572
- * @param $src
573
- * @param $deps
574
- * @param $ver
575
- * @param $in_footer
576
- */
577
- function add_script( $context, $handle, $src = false, $deps = array(), $ver = false, $in_footer = false ) {
578
- $args = func_get_args();
579
- //Add file type to front of arguments array
580
- array_unshift($args, 'scripts');
581
- call_user_func_array(array(&$this, 'add_dependency'), $args);
582
- }
583
-
584
- /**
585
- * Retrieve script dependencies for object
586
- * @return array Script dependencies
587
- */
588
- function get_scripts() {
589
- return $this->get_member_value('scripts', '', array());
590
- }
591
-
592
- /**
593
- * Add style to object to be added in specified contexts
594
- * @param array|string $context Array of contexts to add style to page
595
- * @see wp_enqueue_style for the following of the parameters
596
- * @param $handle
597
- * @param $src
598
- * @param $deps
599
- * @param $ver
600
- * @param $in_footer
601
- */
602
- function add_style( $handle, $src = false, $deps = array(), $ver = false, $media = false ) {
603
- $args = func_get_args();
604
- array_unshift($args, 'styles');
605
- call_user_method_array('add_dependency', $this, $args);
606
- }
607
-
608
- /**
609
- * Retrieve Style dependencies for object
610
- * @return array Style dependencies
611
- */
612
- function get_styles() {
613
- return $this->get_member_value('styles', '', array());
614
- }
615
  }
1
+ <?php
2
+ /**
3
+ * Content Types - Base Class
4
+ * Core properties/methods for Content Type derivative classes
5
+ * @package Cornerstone
6
+ * @subpackage Content Types
7
+ * @author Archetyped
8
+ */
9
+ class CNR_Content_Base extends CNR_Base {
10
+
11
+ /**
12
+ * Base class name
13
+ * @var string
14
+ */
15
+ var $base_class = 'cnr_content_base';
16
+
17
+ /**
18
+ * @var string Unique name
19
+ */
20
+ var $id = '';
21
+
22
+ /**
23
+ * Reference to parent object that current instance inherits from
24
+ * @var object
25
+ */
26
+ var $parent = null;
27
+
28
+ /**
29
+ * Title
30
+ * @var string
31
+ */
32
+ var $title = '';
33
+
34
+ /**
35
+ * Plural Title
36
+ * @var string
37
+ */
38
+ var $title_plural = '';
39
+
40
+ /**
41
+ * @var string Short description
42
+ */
43
+ var $description = '';
44
+
45
+ /**
46
+ * @var array Object Properties
47
+ */
48
+ var $properties = array();
49
+
50
+ /**
51
+ * Data for object
52
+ * May also contain data for nested objects
53
+ * @var mixed
54
+ */
55
+ var $data = null;
56
+
57
+ /**
58
+ * @var array Script resources to include for object
59
+ */
60
+ var $scripts = array();
61
+
62
+ /**
63
+ * @var array CSS style resources to include for object
64
+ */
65
+ var $styles = array();
66
+
67
+ /**
68
+ * Hooks (Filters/Actions) for object
69
+ * @var array
70
+ */
71
+ var $hooks = array();
72
+
73
+ /**
74
+ * Constructor
75
+ */
76
+ function __construct($id = '', $parent = null) {
77
+ parent::__construct();
78
+ $id = trim($id);
79
+ $this->id = $id;
80
+ if ( is_bool($parent) && $parent )
81
+ $parent = $id;
82
+ $this->set_parent($parent);
83
+ }
84
+
85
+ /* Getters/Setters */
86
+
87
+ /**
88
+ * Checks if the specified path exists in the object
89
+ * @param array $path Path to check for
90
+ * @return bool TRUE if path exists in object, FALSE otherwise
91
+ */
92
+ function path_isset($path = '') {
93
+ //Stop execution if no path is supplied
94
+ if ( empty($path) )
95
+ return false;
96
+ $args = func_get_args();
97
+ $path = $this->util->build_path($args);
98
+ $item =& $this;
99
+ //Iterate over path and check if each level exists before moving on to the next
100
+ for ($x = 0; $x < count($path); $x++) {
101
+ if ( $this->util->property_exists($item, $path[$x]) ) {
102
+ //Set $item as reference to next level in path for next iteration
103
+ $item =& $this->util->get_property($item, $path[$x]);
104
+ //$item =& $item[ $path[$x] ];
105
+ } else {
106
+ return false;
107
+ }
108
+ }
109
+ return true;
110
+ }
111
+
112
+ /**
113
+ * Retrieves a value from object using a specified path
114
+ * Checks to make sure path exists in object before retrieving value
115
+ * @param array $path Path to retrieve value from. Each item in array is a deeper dimension
116
+ * @return mixed Value at specified path
117
+ */
118
+ function &get_path_value($path = '') {
119
+ $ret = '';
120
+ $path = $this->util->build_path(func_get_args());
121
+ if ( $this->path_isset($path) ) {
122
+ $ret =& $this;
123
+ for ($x = 0; $x < count($path); $x++) {
124
+ if ( 0 == $x )
125
+ $ret =& $ret->{ $path[$x] };
126
+ else
127
+ $ret =& $ret[ $path[$x] ];
128
+ }
129
+ }
130
+ return $ret;
131
+ }
132
+
133
+ /**
134
+ * Search for specified member value in field type ancestors
135
+ * @param string $member Name of object member to search (e.g. properties, layout, etc.)
136
+ * @param string $name Value to retrieve from member
137
+ * @return mixed Member value if found (Default: empty string)
138
+ */
139
+ function get_parent_value($member, $name = '', $default = '') {
140
+ $parent =& $this->get_parent();
141
+ return $this->get_object_value($parent, $member, $name, $default, 'parent');
142
+ }
143
+
144
+ /**
145
+ * Retrieves specified member value
146
+ * Handles inherited values
147
+ * Merging corresponding parents if value is an array (e.g. for property groups)
148
+ * @param string|array $member Member to search. May also contain a path to the desired member
149
+ * @param string $name Value to retrieve from member
150
+ * @param mixed $default Default value if no value found (Default: empty string)
151
+ * @param string $dir Direction to move through hierarchy to find value
152
+ * Possible Values:
153
+ * parent (default) - Search through field parents
154
+ * current - Do not search through connected objects
155
+ * container - Search through field containers
156
+ * caller - Search through field callers
157
+ * @return mixed Specified member value
158
+ */
159
+ function get_member_value($member, $name = '', $default = '', $dir = 'parent') {
160
+ //Check if path to member is supplied
161
+ $path = array();
162
+ if ( is_array($member) && isset($member['tag']) ) {
163
+ if ( isset($member['attributes']['ref_base']) ) {
164
+ if ( 'root' != $member['attributes']['ref_base'] )
165
+ $path[] = $member['attributes']['ref_base'];
166
+ } else {
167
+ $path[] = 'properties';
168
+ }
169
+
170
+ $path[] = $member['tag'];
171
+ } else {
172
+ $path = $member;
173
+ }
174
+
175
+ $path = $this->util->build_path($path, $name);
176
+ //Set defaults and prepare data
177
+ $val = $default;
178
+ $inherit = false;
179
+ $inherit_tag = '{inherit}';
180
+
181
+ /* Determine whether the value must be retrieved from a parent/container object
182
+ * Conditions:
183
+ * > Path does not exist in current field
184
+ * > Path exists and is not an object, but at least one of the following is true:
185
+ * > Value at path is an array (e.g. properties, elements, etc. array)
186
+ * > Parent/container values should be merged with retrieved array
187
+ * > Value at path is a string that inherits from another field
188
+ * > Value from other field will be retrieved and will replace inheritance placeholder in retrieved value
189
+ */
190
+
191
+ $deeper = false;
192
+
193
+ if ( !$this->path_isset($path) )
194
+ $deeper = true;
195
+ else {
196
+ $val = $this->get_path_value($path);
197
+ if ( !is_object($val) && ( is_array($val) || ($inherit = strpos($val, $inherit_tag)) !== false ) )
198
+ $deeper = true;
199
+ else
200
+ $deeper = false;
201
+ }
202
+ if ( $deeper && 'current' != $dir ) {
203
+ //Get Parent value (recursive)
204
+ $ex_val = ( 'parent' != $dir ) ? $this->get_container_value($member, $name, $default) : $this->get_parent_value($member, $name, $default);
205
+ //Handle inheritance
206
+ if ( is_array($val) ) {
207
+ //Combine Arrays
208
+ if ( is_array($ex_val) )
209
+ $val = array_merge($ex_val, $val);
210
+ } elseif ( $inherit !== false ) {
211
+ //Replace placeholder with inherited string
212
+ $val = str_replace($inherit_tag, $ex_val, $val);
213
+ } else {
214
+ //Default: Set parent value as value
215
+ $val = $ex_val;
216
+ }
217
+ }
218
+
219
+ return $val;
220
+ }
221
+
222
+ /**
223
+ * Search for specified member value in an object
224
+ * @param object $object Reference to object to retrieve value from
225
+ * @param string $member Name of object member to search (e.g. properties, layout, etc.)
226
+ * @param string $name (optional) Value to retrieve from member
227
+ * @param mixed $default (optional) Default value to use if no value found (Default: empty string)
228
+ * @param string $dir Direction to move through hierarchy to find value @see CNR_Field_Type::get_member_value() for possible values
229
+ * @return mixed Member value if found (Default: $default)
230
+ */
231
+ function get_object_value(&$object, $member, $name = '', $default = '', $dir = 'parent') {
232
+ $ret = $default;
233
+ if ( is_object($object) && method_exists($object, 'get_member_value') )
234
+ $ret = $object->get_member_value($member, $name, $default, $dir);
235
+ return $ret;
236
+ }
237
+
238
+ /**
239
+ * Retrieve value from data member
240
+ * @param bool $top (optional) Whether to traverse through the field hierarchy to get data for field (Default: TRUE)
241
+ * @return mixed Value at specified path
242
+ */
243
+ function get_data($top = true) {
244
+ $top = !!$top;
245
+ $obj = $this;
246
+ $obj_path = array($this);
247
+ $path = array();
248
+ //Iterate through hiearchy to get top-most object
249
+ while ( !empty($obj) ) {
250
+ $new = null;
251
+ //Try to get caller first
252
+ if ( method_exists($obj, 'get_caller') ) {
253
+ $checked = true;
254
+ $new = $obj->get_caller();
255
+ }
256
+ //Try to get container if no caller found
257
+ if ( empty($new) && method_exists($obj, 'get_container') ) {
258
+ $checked = true;
259
+ $new = $obj->get_container();
260
+ }
261
+
262
+ $obj = $new;
263
+
264
+ //Stop iteration
265
+ if ( !empty($obj) ) {
266
+ //Add object to path if it is valid
267
+ $obj_path[] = $obj;
268
+ }
269
+ }
270
+
271
+ //Check each object (starting with top-most) for matching data for current field
272
+
273
+ //Reverse array
274
+ $obj_path = array_reverse($obj_path);
275
+ //Build path for data location
276
+ foreach ( $obj_path as $obj ) {
277
+ if ( $this->util->property_exists($obj, 'id') )
278
+ $path[] = $obj->id;
279
+ }
280
+
281
+ //Iterate through objects
282
+ while ( !empty($obj_path) ) {
283
+ //Get next object
284
+ $obj = array_shift($obj_path);
285
+ //Shorten path
286
+ array_shift($path);
287
+ //Check for value in object and stop iteration if matching data found
288
+ if ( ($val = $this->get_object_value($obj, 'data', $path, null, 'current')) && !is_null($val) ) {
289
+ break;
290
+ }
291
+ }
292
+
293
+ return $val;
294
+ }
295
+
296
+ /**
297
+ * Sets value in data member
298
+ * Sets value to data member itself by default
299
+ * @param mixed $value Value to set
300
+ * @param string|array $name Name of value to set (Can also be path to value)
301
+ */
302
+ function set_data($value, $name = '') {
303
+ $ref =& $this->get_path_value('data', $name);
304
+ $ref = $value;
305
+ }
306
+
307
+ /**
308
+ * Retrieve base_class property
309
+ * @return string base_class property of current class/instance object
310
+ */
311
+ static function get_base_class() {
312
+ $ret = '';
313
+ if ( isset($this) )
314
+ $ret = $this->base_class;
315
+ else {
316
+ $ret = CNR_Utilities::get_property(__CLASS__, 'base_class');
317
+ }
318
+
319
+ return $ret;
320
+ }
321
+
322
+ /**
323
+ * Sets parent object of current instance
324
+ * Parent objects must be the same object type as current instance
325
+ * @param string|object $parent Parent ID or reference
326
+ */
327
+ function set_parent($parent) {
328
+ if ( !empty($parent) ) {
329
+ //Validate parent object
330
+ if ( is_array($parent) )
331
+ $parent =& $parent[0];
332
+
333
+ //Retrieve reference object if ID was supplied
334
+ if ( is_string($parent) ) {
335
+ $parent = trim($parent);
336
+ //Check for existence of parent
337
+ $lookup = $this->base_class . 's';
338
+ if ( isset($GLOBALS[$lookup][$parent]) ) {
339
+ //Get reference to parent
340
+ $parent =& $GLOBALS[$lookup][$parent];
341
+ }
342
+ }
343
+
344
+ //Set reference to parent field type
345
+ if ( is_a($parent, $this->base_class) ) {
346
+ $this->parent =& $parent;
347
+ }
348
+ }
349
+ }
350
+
351
+ /**
352
+ * Retrieve field type parent
353
+ * @return CNR_Field_Type Reference to parent field
354
+ */
355
+ function &get_parent() {
356
+ return $this->parent;
357
+ }
358
+
359
+ /**
360
+ * Retrieves field ID
361
+ * @param string|CNR_Field|array $field (optional) Field object or ID of field or options array
362
+ * @return string|bool Field ID, FALSE if $field is invalid
363
+ */
364
+ function get_id($field = null) {
365
+ $ret = false;
366
+
367
+ // Get options.
368
+ $num_args = func_num_args();
369
+ $options = ( $num_args > 0 && ( $last_arg = func_get_arg($num_args - 1) ) && is_array($last_arg) ) ? $last_arg : array();
370
+
371
+ // Validate field parameter.
372
+ if ( ( !is_object($field) || !is_a($field, 'cnr_field_type') ) && isset($this) ) {
373
+ $field =& $this;
374
+ }
375
+
376
+ if ( is_a($field, CNR_Field_Type::get_base_class()) )
377
+ $id = $field->id;
378
+
379
+ if ( is_string($id) )
380
+ $ret = trim($id);
381
+
382
+ // Setup options.
383
+ $options = wp_parse_args( $options, [ 'format' => null ] );
384
+ //Check if field should be formatted
385
+ if ( is_string($ret) && !empty($options['format']) ) {
386
+ //Clear format option if it is an invalid value
387
+ if ( is_bool($options['format']) || is_int($options['format']) )
388
+ $options['format'] = null;
389
+ //Setup values
390
+ $wrap = array('open' => '[', 'close' => ']');
391
+ if ( isset($options['wrap']) && is_array($options['wrap']) )
392
+ $wrap = wp_parse_args($options['wrap'], $wrap);
393
+ $wrap_trailing = ( isset($options['wrap_trailing']) ) ? !!$options['wrap_trailing'] : true;
394
+ switch ( $options['format'] ) {
395
+ case 'attr_id' :
396
+ $wrap = (array('open' => '_', 'close' => '_'));
397
+ $wrap_trailing = false;
398
+ break;
399
+ }
400
+ $c = $field->get_caller();
401
+ $field_id = array($ret);
402
+ while ( !!$c ) {
403
+ //Add ID of current field to array
404
+ if ( isset($c->id) && is_a($c, $this->base_class) )
405
+ $field_id[] = $c->id;
406
+ $c = ( method_exists($c, 'get_caller') ) ? $c->get_caller() : null;
407
+ }
408
+
409
+ //Add prefix to ID value
410
+ $field_id[] = 'attributes';
411
+
412
+ //Convert array to string
413
+ $ret = $field->prefix . $wrap['open'] . implode($wrap['close'] . $wrap['open'], array_reverse($field_id)) . ( $wrap_trailing ? $wrap['close'] : '');
414
+ }
415
+ return $ret;
416
+ }
417
+
418
+ /**
419
+ * Set object title
420
+ * @param string $title Title for object
421
+ * @param string $plural Plural form of title
422
+ */
423
+ function set_title($title = '', $plural = '') {
424
+ $this->title = strip_tags(trim($title));
425
+ if ( isset($plural) )
426
+ $this->title_plural = strip_tags(trim($plural));
427
+ }
428
+
429
+ /**
430
+ * Retrieve object title
431
+ * @param bool $plural TRUE if plural title should be retrieved, FALSE otherwise (Default: FALSE)
432
+ */
433
+ function get_title($plural = false) {
434
+ $dir = 'current';
435
+ //Singular
436
+ if ( !$plural )
437
+ return $this->get_member_value('title', '','', $dir);
438
+ //Plural
439
+ $title = $this->get_member_value('title_plural', '', '', $dir);
440
+ if ( empty($title) ) {
441
+ //Use singular title for plural base
442
+ $title = $this->get_member_value('title', '', '', $dir);
443
+ //Determine technique for making title plural
444
+ //Get last letter
445
+ if ( !empty($title) ) {
446
+ $tail = substr($title, -1);
447
+ switch ( $tail ) {
448
+ case 's' :
449
+ $title .= 'es';
450
+ break;
451
+ case 'y' :
452
+ $title = substr($title, 0, -1) . 'ies';
453
+ break;
454
+ default :
455
+ $title .= 's';
456
+ }
457
+ }
458
+ }
459
+ return $title;
460
+ }
461
+
462
+ /**
463
+ * Set object description
464
+ * @param string $description Description for object
465
+ */
466
+ function set_description($description = '') {
467
+ $this->description = strip_tags(trim($description));
468
+ }
469
+
470
+ /**
471
+ * Retrieve object description
472
+ * @return string Object description
473
+ */
474
+ function get_description() {
475
+ $dir = 'current';
476
+ return $this->get_member_value('description', '','', $dir);
477
+ return $desc;
478
+ }
479
+
480
+ /*-** Hooks **-*/
481
+
482
+ /**
483
+ * Retrieve hooks added to object
484
+ * @return array Hooks
485
+ */
486
+ function get_hooks() {
487
+ return $this->get_member_value('hooks', '', array());
488
+ }
489
+
490
+ /**
491
+ * Add hook for object
492
+ * @see add_filter() for parameter defaults
493
+ * @param $tag
494
+ * @param $function_to_add
495
+ * @param $priority
496
+ * @param $accepted_args
497
+ */
498
+ function add_hook($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
499
+ //Create new array for tag (if not already set)
500
+ if ( !isset($this->hooks[$tag]) )
501
+ $this->hooks[$tag] = array();
502
+ //Build Unique ID
503
+ if ( is_string($function_to_add) )
504
+ $id = $function_to_add;
505
+ elseif ( is_array($function_to_add) && !empty($function_to_add) )
506
+ $id = strval($function_to_add[count($function_to_add) - 1]);
507
+ else
508
+ $id = 'function_' . ( count($this->hooks[$tag]) + 1 );
509
+ //Add hook
510
+ $this->hooks[$tag][$id] = func_get_args();
511
+ }
512
+
513
+ /**
514
+ * Convenience method for adding an action for object
515
+ * @see add_filter() for parameter defaults
516
+ * @param $tag
517
+ * @param $function_to_add
518
+ * @param $priority
519
+ * @param $accepted_args
520
+ */
521
+ function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
522
+ $this->add_hook($tag, $function_to_add, $priority, $accepted_args);
523
+ }
524
+
525
+ /**
526
+ * Convenience method for adding a filter for object
527
+ * @see add_filter() for parameter defaults
528
+ * @param $tag
529
+ * @param $function_to_add
530
+ * @param $priority
531
+ * @param $accepted_args
532
+ */
533
+ function add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
534
+ $this->add_hook($tag, $function_to_add, $priority, $accepted_args);
535
+ }
536
+
537
+ /*-** Dependencies **-*/
538
+
539
+ /**
540
+ * Adds dependency to object
541
+ * @param string $type Type of dependency to add (script, style)
542
+ * @param array|string $context When dependency will be added (@see CNR_Utilities::get_action() for possible contexts)
543
+ * @see wp_enqueue_script for the following of the parameters
544
+ * @param $handle
545
+ * @param $src
546
+ * @param $deps
547
+ * @param $ver
548
+ * @param $ex
549
+ */
550
+ function add_dependency($type, $context, $handle, $src = false, $deps = array(), $ver = false, $ex = false) {
551
+ $args = func_get_args();
552
+ //Remove type/context from arguments
553
+ $args = array_slice($args, 2);
554
+
555
+ //Set context
556
+ if ( !is_array($context) ) {
557
+ //Wrap single contexts in an array
558
+ if ( is_string($context) )
559
+ $context = array($context);
560
+ else
561
+ $context = array();
562
+ }
563
+ //Add file to instance property
564
+ $this->{$type}[$handle] = array('context' => $context, 'params' => $args);
565
+ }
566
+
567
+ /**
568
+ * Add script to object to be added in specified contexts
569
+ * @param array|string $context Array of contexts to add script to page
570
+ * @see wp_enqueue_script for the following of the parameters
571
+ * @param $handle
572
+ * @param $src
573
+ * @param $deps
574
+ * @param $ver
575
+ * @param $in_footer
576
+ */
577
+ function add_script( $context, $handle, $src = false, $deps = array(), $ver = false, $in_footer = false ) {
578
+ $args = func_get_args();
579
+ //Add file type to front of arguments array
580
+ array_unshift($args, 'scripts');
581
+ call_user_func_array(array(&$this, 'add_dependency'), $args);
582
+ }
583
+
584
+ /**
585
+ * Retrieve script dependencies for object
586
+ * @return array Script dependencies
587
+ */
588
+ function get_scripts() {
589
+ return $this->get_member_value('scripts', '', array());
590
+ }
591
+
592
+ /**
593
+ * Add style to object to be added in specified contexts
594
+ * @param array|string $context Array of contexts to add style to page
595
+ * @see wp_enqueue_style for the following of the parameters
596
+ * @param $handle
597
+ * @param $src
598
+ * @param $deps
599
+ * @param $ver
600
+ * @param $in_footer
601
+ */
602
+ function add_style( $handle, $src = false, $deps = array(), $ver = false, $media = false ) {
603
+ $args = func_get_args();
604
+ array_unshift($args, 'styles');
605
+ call_user_method_array('add_dependency', $this, $args);
606
+ }
607
+
608
+ /**
609
+ * Retrieve Style dependencies for object
610
+ * @return array Style dependencies
611
+ */
612
+ function get_styles() {
613
+ return $this->get_member_value('styles', '', array());
614
+ }
615
  }
includes/class.content_type.php CHANGED
@@ -1,402 +1,402 @@
1
- <?php
2
- class CNR_Content_Type extends CNR_Content_Base {
3
-
4
- /**
5
- * Base class for instance objects
6
- * @var string
7
- */
8
- var $base_class = 'cnr_content_type';
9
-
10
- /**
11
- * Indexed array of fields in content type
12
- * @var array
13
- */
14
- var $fields = array();
15
-
16
- /**
17
- * Associative array of groups in conten type
18
- * Key: Group name
19
- * Value: object of group properties
20
- * > description string Group description
21
- * > location string Location of group on edit form
22
- * > fields array Fields in group
23
- * @var array
24
- */
25
- var $groups = array();
26
-
27
- /* Constructors */
28
-
29
- /**
30
- * Class constructor
31
- * @param string $id Content type ID
32
- * @param string|bool $parent (optional) Parent to inherit properties from (Default: none)
33
- * @param array $properties (optional) Properties to set for content type (Default: none)
34
- */
35
- function __construct($id = '', $parent = null, $properties = null) {
36
- parent::__construct($id, $parent);
37
-
38
- //Set properties
39
- //TODO Iterate through additional arguments and set instance properties
40
- }
41
-
42
- /* Registration */
43
-
44
- /**
45
- * Registers current content type w/CNR
46
- */
47
- function register() {
48
- global $cnr_content_utilities;
49
- $cnr_content_utilities->register_content_type($this);
50
- }
51
-
52
- /* Getters/Setters */
53
-
54
- /**
55
- * Adds group to content type
56
- * Groups are used to display related fields in the UI
57
- * @param string $id Unique name for group
58
- * @param string $title Group title
59
- * @param string $description Short description of group's purpose
60
- * @param string $location Where group will be displayed on post edit form (Default: main)
61
- * @param array $fields (optional) ID's of existing fields to add to group
62
- * @return object Group object
63
- */
64
- function &add_group($id, $title = '', $description = '', $location = 'normal', $fields = array()) {
65
- //Create new group and set properties
66
- $id = trim($id);
67
- $this->groups[$id] =& $this->create_group($title, $description, $location);
68
- //Add fields to group (if supplied)
69
- if ( !empty($fields) && is_array($fields) )
70
- $this->add_to_group($id, $fields);
71
- return $this->groups[$id];
72
- }
73
-
74
- /**
75
- * Remove specified group from content type
76
- * @param string $id Group ID to remove
77
- */
78
- function remove_group($id) {
79
- $id = trim($id);
80
- if ( $this->group_exists($id) ) {
81
- unset($this->groups[$id]);
82
- }
83
- }
84
-
85
- /**
86
- * Standardized method to create a new field group
87
- * @param string $title Group title (used in meta boxes, etc.)
88
- * @param string $description Short description of group's purpose
89
- * @param string $location Where group will be displayed on post edit form (Default: main)
90
- * @return object Group object
91
- */
92
- function &create_group($title = '', $description = '', $location = 'normal') {
93
- $group = new stdClass();
94
- $title = ( is_scalar($title) ) ? trim($title) : '';
95
- $group->title = $title;
96
- $description = ( is_scalar($description) ) ? trim($description) : '';
97
- $group->description = $description;
98
- $location = ( is_scalar($location) ) ? trim($location) : 'normal';
99
- $group->location = $location;
100
- $group->fields = array();
101
- return $group;
102
- }
103
-
104
- /**
105
- * Checks if group exists
106
- * @param string $id Group name
107
- * @return bool TRUE if group exists, FALSE otherwise
108
- */
109
- function group_exists($id) {
110
- $id = trim($id);
111
- //Check if group exists in content type
112
- return ( !is_null($this->get_member_value('groups', $id, null)) );
113
- }
114
-
115
- /**
116
- * Adds field to content type
117
- * @param string $id Unique name for field
118
- * @param CNR_Field_Type|string $parent Field type that this field is based on
119
- * @param array $properties (optional) Field properties
120
- * @param string $group (optional) Group ID to add field to
121
- * @return CNR_Field Reference to new field
122
- */
123
- function &add_field($id, $parent, $properties = array(), $group = null) {
124
- //Create new field
125
- $id = trim(strval($id));
126
- $field = new CNR_Field($id);
127
- $field->set_parent($parent);
128
- $field->set_container($this);
129
- $field->set_properties($properties);
130
-
131
- //Add field to content type
132
- $this->fields[$id] =& $field;
133
- //Add field to group
134
- $this->add_to_group($group, $field->id);
135
- return $field;
136
- }
137
-
138
- /**
139
- * Removes field from content type
140
- * @param string|CNR_Field $field Object or Field ID to remove
141
- */
142
- function remove_field($field) {
143
- if ( $field instanceof CNR_Field_Type ) {
144
- $field = $field->get_id();
145
- }
146
- if ( !is_string($field) || empty($field) )
147
- return false;
148
-
149
- //Remove from fields array
150
- //$this->fields[$field] = null;
151
- unset($this->fields[$field]);
152
-
153
- //Remove field from groups
154
- $this->remove_from_group($field);
155
- }
156
-
157
- /**
158
- * Retrieve specified field in Content Type
159
- * @param string $field Field ID
160
- * @return CNR_Field Specified field
161
- */
162
- function &get_field($field) {
163
- if ( $this->has_field($field) ) {
164
- $field = trim($field);
165
- $field = $this->get_member_value('fields', $field);
166
- } else {
167
- //Return empty field if no field exists
168
- $field = new CNR_Field('');
169
- }
170
- return $field;
171
- }
172
-
173
- /**
174
- * Checks if field exists in the content type
175
- * @param string $field Field ID
176
- * @return bool TRUE if field exists, FALSE otherwise
177
- */
178
- function has_field($field) {
179
- return ( !is_string($field) || empty($field) || is_null($this->get_member_value('fields', $field, null)) ) ? false : true;
180
- }
181
-
182
- /**
183
- * Adds field to a group in the content type
184
- * Group is created if it does not already exist
185
- * @param string|array $group ID of group (or group parameters if new group) to add field to
186
- * @param string|array $fields Name or array of field(s) to add to group
187
- */
188
- function add_to_group($group, $fields) {
189
- //Validate parameters
190
- $group_id = '';
191
- if ( !empty($group) ) {
192
- if ( !is_array($group) ) {
193
- $group = array($group, $group);
194
- }
195
-
196
- $group[0] = $group_id = trim(sanitize_title_with_dashes($group[0]));
197
- }
198
- if ( empty($group_id) || empty($fields) )
199
- return false;
200
- //Create group if it doesn't exist
201
- if ( !$this->group_exists($group_id) ) {
202
- call_user_func_array($this->m('add_group'), $group);
203
- }
204
- if ( ! is_array($fields) )
205
- $fields = array($fields);
206
- foreach ( $fields as $field ) {
207
- unset($fref);
208
- if ( ! $this->has_field($field) )
209
- continue;
210
- $fref =& $this->get_field($field);
211
- //Remove field from any other group it's in (fields can only be in one group)
212
- foreach ( array_keys($this->groups) as $group_name ) {
213
- if ( isset($this->groups[$group_name]->fields[$fref->id]) )
214
- unset($this->groups[$group_name]->fields[$fref->id]);
215
- }
216
- //Add reference to field in group
217
- $this->groups[$group_id]->fields[$fref->id] =& $fref;
218
- }
219
- }
220
-
221
- /**
222
- * Remove field from a group
223
- * If no group is specified, then field is removed from all groups
224
- * @param string|CNR_Field $field Field object or ID of field to remove from group
225
- * @param string $group (optional) Group ID to remove field from
226
- */
227
- function remove_from_group($field, $group = '') {
228
- //Get ID of field to remove or stop execution if field invalid
229
- if ( $field instanceof CNR_Field_Type ) {
230
- $field = $field->get_id();
231
- }
232
- if ( !is_string($field) || empty($field) )
233
- return false;
234
-
235
- //Remove field from group
236
- if ( !empty($group) ) {
237
- //Remove field from single group
238
- if ( ($group =& $this->get_group($group)) && isset($group->fields[$field]) ) {
239
- unset($group->fields[$field]);
240
- }
241
- } else {
242
- //Remove field from all groups
243
- foreach ( array_keys($this->groups) as $group ) {
244
- if ( ($group =& $this->get_group($group)) && isset($group->fields[$field]) ) {
245
- unset($group->fields[$field]);
246
- }
247
- }
248
- }
249
- }
250
-
251
- /**
252
- * Retrieve specified group
253
- * @param string $group ID of group to retrieve
254
- * @return object Reference to specified group
255
- */
256
- function &get_group($group) {
257
- $group = trim($group);
258
- //Create group if it doesn't already exist
259
- if ( ! $this->group_exists($group) )
260
- $this->add_group($group);
261
- $group = $this->get_member_value('groups', $group);
262
- return $group;
263
- }
264
-
265
- /**
266
- * Retrieve all groups in content type
267
- * @return array Reference to group objects
268
- */
269
- function &get_groups() {
270
- $groups = $this->get_member_value('groups');
271
- return $groups;
272
- }
273
-
274
- /**
275
- * Output fields in a group
276
- * @param string $group ID of Group to output
277
- * @return string Group output
278
- */
279
- function build_group($group) {
280
- $out = array();
281
- $classnames = (object) array(
282
- 'multi' => 'multi_field',
283
- 'single' => 'single_field',
284
- 'elements' => 'has_elements'
285
- );
286
-
287
- //Stop execution if group does not exist
288
- if ( $this->group_exists($group) && $group =& $this->get_group($group) ) {
289
- $group_fields = ( count($group->fields) > 1 ) ? $classnames->multi : $classnames->single . ( ( ( $fs = array_keys($group->fields) ) && ( $f =& $group->fields[$fs[0]] ) && ( $els = $f->get_member_value('elements', '', null) ) && !empty($els) ) ? '_' . $classnames->elements : '' );
290
- $classname = array('cnr_attributes_wrap', $group_fields);
291
- $out[] = '<div class="' . implode(' ', $classname) . '">'; //Wrap all fields in group
292
-
293
- //Build layout for each field in group
294
- foreach ( array_keys($group->fields) as $field_id ) {
295
- /**
296
- * CNR_Field_Type
297
- */
298
- $field =& $group->fields[$field_id];
299
- $field->set_caller($this);
300
- //Start field output
301
- $id = 'cnr_field_' . $field->get_id();
302
- $class = array('cnr_attribute_wrap');
303
- //If single field in group, check if field title matches group
304
- if ( count($group->fields) == 1 && $group->title == $field->get_property('label') )
305
- $class[] = 'group_field_title';
306
- //Add flag to indicate that field was loaded on page
307
- $inc = 'cnr[fields_loaded][' . $field->get_id() . ']';
308
- $out[] = '<input type="hidden" id="' . $inc . '" name="' . $inc . '" value="1" />';
309
- $out[] = '<div id="' . $id . '_wrap" class="' . implode(' ', $class) . '">';
310
- //Build field layout
311
- $out[] = $field->build_layout();
312
- //end field output
313
- $out[] = '</div>';
314
- $field->clear_caller();
315
- }
316
- $out[] = '</div>'; //Close fields container
317
- //Add description if exists
318
- if ( !empty($group->description) )
319
- $out[] = '<p class="cnr_group_description">' . $group->description . '</p>';
320
- }
321
-
322
- //Return group output
323
- return implode($out);
324
- }
325
-
326
- /**
327
- * Set data for a field
328
- * @param string|CNR_Field $field Reference or ID of Field to set data for
329
- * @param mixed $value Data to set
330
- */
331
- function set_data($field, $value = '') {
332
- if ( 1 == func_num_args() && is_array($field) )
333
- $this->data = $field;
334
- else {
335
- if ( $field instanceof CNR_Field_Type ) {
336
- $field = $field->get_id();
337
- }
338
- if ( !is_string($field) || empty($field) )
339
- return false;
340
- $this->data[$field] = $value;
341
- }
342
- }
343
-
344
- /*-** Admin **-*/
345
-
346
- /**
347
- * Adds meta boxes for post's content type
348
- * Each group in content type is a separate meta box
349
- * @param string $type Type of item meta boxes are being build for (post, page, link)
350
- * @param string $context Location of meta box (normal, advanced, side)
351
- * @param object $post Post object
352
- */
353
- function admin_do_meta_boxes($type, $context, $post) {
354
- //Add post data to content type
355
- global $cnr_content_utilities;
356
- $this->set_data($cnr_content_utilities->get_item_data($post));
357
-
358
- //Get Groups
359
- $groups = array_keys($this->get_groups());
360
- $priority = 'default';
361
- //Iterate through groups and add meta box if it fits the context (location)
362
- foreach ( $groups as $group_id ) {
363
- $group =& $this->get_group($group_id);
364
- if ( $context == $group->location && count($group->fields) ) {
365
- //Format ID for meta box
366
- $meta_box_id = $this->prefix . '_group_' . $group_id;
367
- $group_args = array( 'group' => $group_id );
368
- add_meta_box($meta_box_id, $group->title, $this->m('admin_build_meta_box'), $type, $context, $priority, $group_args);
369
- }
370
- }
371
- }
372
-
373
- /**
374
- * Outputs group fields for a meta box
375
- * @param object $post Post object
376
- * @param array $box Meta box properties
377
- */
378
- function admin_build_meta_box($post, $box) {
379
- //Stop execution if group not specified
380
- if ( !isset($box['args']['group']) )
381
- return false;
382
-
383
- //Get ID of group to output
384
- $group_id =& $box['args']['group'];
385
-
386
- $output = array();
387
- $output[] = '<div class="cnr_group_wrap">';
388
- $output[] = $this->build_group($group_id);
389
- $output[] = '</div>';
390
-
391
- //Output group content to screen
392
- echo implode($output);
393
- }
394
-
395
- /**
396
- * Retrieves type ID formatted as a meta value
397
- * @return string
398
- */
399
- function get_meta_value() {
400
- return serialize(array($this->id));
401
- }
402
  }
1
+ <?php
2
+ class CNR_Content_Type extends CNR_Content_Base {
3
+
4
+ /**
5
+ * Base class for instance objects
6
+ * @var string
7
+ */
8
+ var $base_class = 'cnr_content_type';
9
+
10
+ /**
11
+ * Indexed array of fields in content type
12
+ * @var array
13
+ */
14
+ var $fields = array();
15
+
16
+ /**
17
+ * Associative array of groups in conten type
18
+ * Key: Group name
19
+ * Value: object of group properties
20
+ * > description string Group description
21
+ * > location string Location of group on edit form
22
+ * > fields array Fields in group
23
+ * @var array
24
+ */
25
+ var $groups = array();
26
+
27
+ /* Constructors */
28
+
29
+ /**
30
+ * Class constructor
31
+ * @param string $id Content type ID
32
+ * @param string|bool $parent (optional) Parent to inherit properties from (Default: none)
33
+ * @param array $properties (optional) Properties to set for content type (Default: none)
34
+ */
35
+ function __construct($id = '', $parent = null, $properties = null) {
36
+ parent::__construct($id, $parent);
37
+
38
+ //Set properties
39
+ //TODO Iterate through additional arguments and set instance properties
40
+ }
41
+
42
+ /* Registration */
43
+
44
+ /**
45
+ * Registers current content type w/CNR
46
+ */
47
+ function register() {
48
+ global $cnr_content_utilities;
49
+ $cnr_content_utilities->register_content_type($this);
50
+ }
51
+
52
+ /* Getters/Setters */
53
+
54
+ /**
55
+ * Adds group to content type
56
+ * Groups are used to display related fields in the UI
57
+ * @param string $id Unique name for group
58
+ * @param string $title Group title
59
+ * @param string $description Short description of group's purpose
60
+ * @param string $location Where group will be displayed on post edit form (Default: main)
61
+ * @param array $fields (optional) ID's of existing fields to add to group
62
+ * @return object Group object
63
+ */
64
+ function &add_group($id, $title = '', $description = '', $location = 'normal', $fields = array()) {
65
+ //Create new group and set properties
66
+ $id = trim($id);
67
+ $this->groups[$id] =& $this->create_group($title, $description, $location);
68
+ //Add fields to group (if supplied)
69
+ if ( !empty($fields) && is_array($fields) )
70
+ $this->add_to_group($id, $fields);
71
+ return $this->groups[$id];
72
+ }
73
+
74
+ /**
75
+ * Remove specified group from content type
76
+ * @param string $id Group ID to remove
77
+ */
78
+ function remove_group($id) {
79
+ $id = trim($id);
80
+ if ( $this->group_exists($id) ) {
81
+ unset($this->groups[$id]);
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Standardized method to create a new field group
87
+ * @param string $title Group title (used in meta boxes, etc.)
88
+ * @param string $description Short description of group's purpose
89
+ * @param string $location Where group will be displayed on post edit form (Default: main)
90
+ * @return object Group object
91
+ */
92
+ function &create_group($title = '', $description = '', $location = 'normal') {
93
+ $group = new stdClass();
94
+ $title = ( is_scalar($title) ) ? trim($title) : '';
95
+ $group->title = $title;
96
+ $description = ( is_scalar($description) ) ? trim($description) : '';
97
+ $group->description = $description;
98
+ $location = ( is_scalar($location) ) ? trim($location) : 'normal';
99
+ $group->location = $location;
100
+ $group->fields = array();
101
+ return $group;
102
+ }
103
+
104
+ /**
105
+ * Checks if group exists
106
+ * @param string $id Group name
107
+ * @return bool TRUE if group exists, FALSE otherwise
108
+ */
109
+ function group_exists($id) {
110
+ $id = trim($id);
111
+ //Check if group exists in content type
112
+ return ( !is_null($this->get_member_value('groups', $id, null)) );
113
+ }
114
+
115
+ /**
116
+ * Adds field to content type
117
+ * @param string $id Unique name for field
118
+ * @param CNR_Field_Type|string $parent Field type that this field is based on
119
+ * @param array $properties (optional) Field properties
120
+ * @param string $group (optional) Group ID to add field to
121
+ * @return CNR_Field Reference to new field
122
+ */
123
+ function &add_field($id, $parent, $properties = array(), $group = null) {
124
+ //Create new field
125
+ $id = trim(strval($id));
126
+ $field = new CNR_Field($id);
127
+ $field->set_parent($parent);
128
+ $field->set_container($this);
129
+ $field->set_properties($properties);
130
+
131
+ //Add field to content type
132
+ $this->fields[$id] =& $field;
133
+ //Add field to group
134
+ $this->add_to_group($group, $field->id);
135
+ return $field;
136
+ }
137
+
138
+ /**
139
+ * Removes field from content type
140
+ * @param string|CNR_Field $field Object or Field ID to remove
141
+ */
142
+ function remove_field($field) {
143
+ if ( $field instanceof CNR_Field_Type ) {
144
+ $field = $field->get_id();
145
+ }
146
+ if ( !is_string($field) || empty($field) )
147
+ return false;
148
+
149
+ //Remove from fields array
150
+ //$this->fields[$field] = null;
151
+ unset($this->fields[$field]);
152
+
153
+ //Remove field from groups
154
+ $this->remove_from_group($field);
155
+ }
156
+
157
+ /**
158
+ * Retrieve specified field in Content Type
159
+ * @param string $field Field ID
160
+ * @return CNR_Field Specified field
161
+ */
162
+ function &get_field($field) {
163
+ if ( $this->has_field($field) ) {
164
+ $field = trim($field);
165
+ $field = $this->get_member_value('fields', $field);
166
+ } else {
167
+ //Return empty field if no field exists
168
+ $field = new CNR_Field('');
169
+ }
170
+ return $field;
171
+ }
172
+
173
+ /**
174
+ * Checks if field exists in the content type
175
+ * @param string $field Field ID
176
+ * @return bool TRUE if field exists, FALSE otherwise
177
+ */
178
+ function has_field($field) {
179
+ return ( !is_string($field) || empty($field) || is_null($this->get_member_value('fields', $field, null)) ) ? false : true;
180
+ }
181
+
182
+ /**
183
+ * Adds field to a group in the content type
184
+ * Group is created if it does not already exist
185
+ * @param string|array $group ID of group (or group parameters if new group) to add field to
186
+ * @param string|array $fields Name or array of field(s) to add to group
187
+ */
188
+ function add_to_group($group, $fields) {
189
+ //Validate parameters
190
+ $group_id = '';
191
+ if ( !empty($group) ) {
192
+ if ( !is_array($group) ) {
193
+ $group = array($group, $group);
194
+ }
195
+
196
+ $group[0] = $group_id = trim(sanitize_title_with_dashes($group[0]));
197
+ }
198
+ if ( empty($group_id) || empty($fields) )
199
+ return false;
200
+ //Create group if it doesn't exist
201
+ if ( !$this->group_exists($group_id) ) {
202
+ call_user_func_array($this->m('add_group'), $group);
203
+ }
204
+ if ( ! is_array($fields) )
205
+ $fields = array($fields);
206
+ foreach ( $fields as $field ) {
207
+ unset($fref);
208
+ if ( ! $this->has_field($field) )
209
+ continue;
210
+ $fref =& $this->get_field($field);
211
+ //Remove field from any other group it's in (fields can only be in one group)
212
+ foreach ( array_keys($this->groups) as $group_name ) {
213
+ if ( isset($this->groups[$group_name]->fields[$fref->id]) )
214
+ unset($this->groups[$group_name]->fields[$fref->id]);
215
+ }
216
+ //Add reference to field in group
217
+ $this->groups[$group_id]->fields[$fref->id] =& $fref;
218
+ }
219
+ }
220
+
221
+ /**
222
+ * Remove field from a group
223
+ * If no group is specified, then field is removed from all groups
224
+ * @param string|CNR_Field $field Field object or ID of field to remove from group
225
+ * @param string $group (optional) Group ID to remove field from
226
+ */
227
+ function remove_from_group($field, $group = '') {
228
+ //Get ID of field to remove or stop execution if field invalid
229
+ if ( $field instanceof CNR_Field_Type ) {
230
+ $field = $field->get_id();
231
+ }
232
+ if ( !is_string($field) || empty($field) )
233
+ return false;
234
+
235
+ //Remove field from group
236
+ if ( !empty($group) ) {
237
+ //Remove field from single group
238
+ if ( ($group =& $this->get_group($group)) && isset($group->fields[$field]) ) {
239
+ unset($group->fields[$field]);
240
+ }
241
+ } else {
242
+ //Remove field from all groups
243
+ foreach ( array_keys($this->groups) as $group ) {
244
+ if ( ($group =& $this->get_group($group)) && isset($group->fields[$field]) ) {
245
+ unset($group->fields[$field]);
246
+ }
247
+ }
248
+ }
249
+ }
250
+
251
+ /**
252
+ * Retrieve specified group
253
+ * @param string $group ID of group to retrieve
254
+ * @return object Reference to specified group
255
+ */
256
+ function &get_group($group) {
257
+ $group = trim($group);
258
+ //Create group if it doesn't already exist
259
+ if ( ! $this->group_exists($group) )
260
+ $this->add_group($group);
261
+ $group = $this->get_member_value('groups', $group);
262
+ return $group;
263
+ }
264
+
265
+ /**
266
+ * Retrieve all groups in content type
267
+ * @return array Reference to group objects
268
+ */
269
+ function &get_groups() {
270
+ $groups = $this->get_member_value('groups');
271
+ return $groups;
272
+ }
273
+
274
+ /**
275
+ * Output fields in a group
276
+ * @param string $group ID of Group to output
277
+ * @return string Group output
278
+ */
279
+ function build_group($group) {
280
+ $out = array();
281
+ $classnames = (object) array(
282
+ 'multi' => 'multi_field',
283
+ 'single' => 'single_field',
284
+ 'elements' => 'has_elements'
285
+ );
286
+
287
+ //Stop execution if group does not exist
288
+ if ( $this->group_exists($group) && $group =& $this->get_group($group) ) {
289
+ $group_fields = ( count($group->fields) > 1 ) ? $classnames->multi : $classnames->single . ( ( ( $fs = array_keys($group->fields) ) && ( $f =& $group->fields[$fs[0]] ) && ( $els = $f->get_member_value('elements', '', null) ) && !empty($els) ) ? '_' . $classnames->elements : '' );
290
+ $classname = array('cnr_attributes_wrap', $group_fields);
291
+ $out[] = '<div class="' . implode(' ', $classname) . '">'; //Wrap all fields in group
292
+
293
+ //Build layout for each field in group
294
+ foreach ( array_keys($group->fields) as $field_id ) {
295
+ /**
296
+ * CNR_Field_Type
297
+ */
298
+ $field =& $group->fields[$field_id];
299
+ $field->set_caller($this);
300
+ //Start field output
301
+ $id = 'cnr_field_' . $field->get_id();
302
+ $class = array('cnr_attribute_wrap');
303
+ //If single field in group, check if field title matches group
304
+ if ( count($group->fields) == 1 && $group->title == $field->get_property('label') )
305
+ $class[] = 'group_field_title';
306
+ //Add flag to indicate that field was loaded on page
307
+ $inc = 'cnr[fields_loaded][' . $field->get_id() . ']';
308
+ $out[] = '<input type="hidden" id="' . $inc . '" name="' . $inc . '" value="1" />';
309
+ $out[] = '<div id="' . $id . '_wrap" class="' . implode(' ', $class) . '">';
310
+ //Build field layout
311
+ $out[] = $field->build_layout();
312
+ //end field output
313
+ $out[] = '</div>';
314
+ $field->clear_caller();
315
+ }
316
+ $out[] = '</div>'; //Close fields container
317
+ //Add description if exists
318
+ if ( !empty($group->description) )
319
+ $out[] = '<p class="cnr_group_description">' . $group->description . '</p>';
320
+ }
321
+
322
+ //Return group output
323
+ return implode($out);
324
+ }
325
+
326
+ /**
327
+ * Set data for a field
328
+ * @param string|CNR_Field $field Reference or ID of Field to set data for
329
+ * @param mixed $value Data to set
330
+ */
331
+ function set_data($field, $value = '') {
332
+ if ( 1 == func_num_args() && is_array($field) )
333
+ $this->data = $field;
334
+ else {
335
+ if ( $field instanceof CNR_Field_Type ) {
336
+ $field = $field->get_id();
337
+ }
338
+ if ( !is_string($field) || empty($field) )
339
+ return false;
340
+ $this->data[$field] = $value;
341
+ }
342
+ }
343
+
344
+ /*-** Admin **-*/
345
+
346
+ /**
347
+ * Adds meta boxes for post's content type
348
+ * Each group in content type is a separate meta box
349
+ * @param string $type Type of item meta boxes are being build for (post, page, link)
350
+ * @param string $context Location of meta box (normal, advanced, side)
351
+ * @param object $post Post object
352
+ */
353
+ function admin_do_meta_boxes($type, $context, $post) {
354
+ //Add post data to content type
355
+ global $cnr_content_utilities;
356
+ $this->set_data($cnr_content_utilities->get_item_data($post));
357
+
358
+ //Get Groups
359
+ $groups = array_keys($this->get_groups());
360
+ $priority = 'default';
361
+ //Iterate through groups and add meta box if it fits the context (location)
362
+ foreach ( $groups as $group_id ) {
363
+ $group =& $this->get_group($group_id);
364
+ if ( $context == $group->location && count($group->fields) ) {
365
+ //Format ID for meta box
366
+ $meta_box_id = $this->prefix . '_group_' . $group_id;
367
+ $group_args = array( 'group' => $group_id );
368
+ add_meta_box($meta_box_id, $group->title, $this->m('admin_build_meta_box'), $type, $context, $priority, $group_args);
369
+ }
370
+ }
371
+ }
372
+
373
+ /**
374
+ * Outputs group fields for a meta box
375
+ * @param object $post Post object
376
+ * @param array $box Meta box properties
377
+ */
378
+ function admin_build_meta_box($post, $box) {
379
+ //Stop execution if group not specified
380
+ if ( !isset($box['args']['group']) )
381
+ return false;
382
+
383
+ //Get ID of group to output
384
+ $group_id =& $box['args']['group'];
385
+
386
+ $output = array();
387
+ $output[] = '<div class="cnr_group_wrap">';
388
+ $output[] = $this->build_group($group_id);
389
+ $output[] = '</div>';
390
+
391
+ //Output group content to screen
392
+ echo implode($output);
393
+ }
394
+
395
+ /**
396
+ * Retrieves type ID formatted as a meta value
397
+ * @return string
398
+ */
399
+ function get_meta_value() {
400
+ return serialize(array($this->id));
401
+ }
402
  }
includes/class.content_utilities.php CHANGED
@@ -1,1248 +1,1248 @@
1
- <?php
2
- /**
3
- * Utilities for Content Type functionality
4
- * @package Cornerstone
5
- * @subpackage Content Types
6
- * @author Archetyped
7
- */
8
- class CNR_Content_Utilities extends CNR_Base {
9
-
10
- /**
11
- * Array of hooks called
12
- * @var array
13
- */
14
- var $hooks_processed = array();
15
-
16
- /**
17
- * Initialize content type functionality
18
- */
19
- function init() {
20
- $this->register_hooks();
21
- }
22
-
23
- /**
24
- * Registers hooks for content types
25
- * @todo 2010-07-30: Check hooks for 3.0 compatibility
26
- */
27
- function register_hooks() {
28
- //Register types
29
- add_action('init', $this->m('register_types'));
30
- add_action('init', $this->m('add_hooks'), 11);
31
-
32
- //Enqueue scripts for fields in current post type
33
- add_action('admin_enqueue_scripts', $this->m('enqueue_files'));
34
-
35
- //Add menus
36
- //add_action('admin_menu', $this->m('admin_menu'));
37
-
38
- //Build UI on post edit form
39
- add_action('do_meta_boxes', $this->m('admin_do_meta_boxes'), 10, 3);
40
-
41
- //Get edit link for items
42
- //add_filter('get_edit_post_link', $this->m('get_edit_item_url'), 10, 3);
43
-
44
- //add_action('edit_form_advanced', $this->m('admin_page_edit_form'));
45
-
46
- //Save Field data/Content type
47
- add_action('save_post', $this->m('save_item_data'), 10, 2);
48
-
49
- //Modify post query for content type compatibility
50
- add_action('pre_get_posts', $this->m('pre_get_posts'), 20);
51
- }
52
-
53
- /**
54
- * Initialize fields and content types
55
- */
56
- function register_types() {
57
- //Global variables
58
- global $cnr_field_types, $cnr_content_types;
59
-
60
- /* Field Types */
61
-
62
- //Base
63
- $base = new CNR_Field_Type('base');
64
- $base->set_description('Default Element');
65
- $base->set_property('tag', 'span');
66
- $base->set_property('class', '', 'attr');
67
- $base->set_layout('form', '<{tag} name="{field_name}" id="{field_id}" {properties ref_base="root" group="attr"} />');
68
- $base->set_layout('label', '<label for="{field_id}">{label}</label>');
69
- $base->set_layout('display', '{data format="display"}');
70
- $this->register_field($base);
71
-
72
- //Base closed
73
- $base_closed = new CNR_Field_Type('base_closed');
74
- $base_closed->set_parent('base');
75
- $base_closed->set_description('Default Element (Closed Tag)');
76
- $base_closed->set_layout('form_start', '<{tag} id="{field_id}" name="{field_name}" {properties ref_base="root" group="attr"}>');
77
- $base_closed->set_layout('form_end', '</{tag}>');
78
- $base_closed->set_layout('form', '{form_start ref_base="layout"}{data}{form_end ref_base="layout"}');
79
- $this->register_field($base_closed);
80
-
81
- //Input
82
- $input = new CNR_Field_Type('input');
83
- $input->set_parent('base');
84
- $input->set_description('Default Input Element');
85
- $input->set_property('tag', 'input');
86
- $input->set_property('type', 'text', 'attr');
87
- $input->set_property('value', CNR_Field::USES_DATA, 'attr');
88
- $this->register_field($input);
89
-
90
- //Text input
91
- $text = new CNR_Field_Type('text', 'input');
92
- $text->set_description('Text Box');
93
- $text->set_property('size', 15, 'attr');
94
- $text->set_property('label');
95
- $text->set_layout('form', '{label ref_base="layout"} {inherit}');
96
- $this->register_field($text);
97
-
98
- //Checkbox
99
- $checkbox = new CNR_Field_Type('checkbox', 'input');
100
- $checkbox->set_description('Checkbox');
101
- $checkbox->set_property('type', 'checkbox', 'attr');
102
- $checkbox->set_property('label');
103
- $checkbox->set_property('checked', '', 'attr');
104
- $checkbox->set_layout('form', '{inherit} {label ref_base="layout"}');
105
- $this->register_field($checkbox);
106
-
107
- //Textarea
108
- $ta = new CNR_Field_Type('textarea', 'base_closed');
109
- $ta->set_property('tag', 'textarea');
110
- $ta->set_property('cols', 40, 'attr');
111
- $ta->set_property('rows', 3, 'attr');
112
- $this->register_field($ta);
113
-
114
- //Rich Text
115
- $rt = new CNR_Field_Type('richtext', 'textarea');
116
- $rt->set_layout('form', '<div class="rt_container">{rich_editor}</div>');
117
- $this->register_field($rt);
118
-
119
- //Location
120
- $location = new CNR_Field_Type('location');
121
- $location->set_description('Geographic Coordinates');
122
- $location->set_element('latitude', 'text', array( 'size' => 3, 'label' => 'Latitude' ));
123
- $location->set_element('longitude', 'text', array( 'size' => 3, 'label' => 'Longitude' ));
124
- $location->set_layout('form', '<span>{latitude ref_base="elements"}</span>, <span>{longitude ref_base="elements"}</span>');
125
- $this->register_field($location);
126
-
127
- //Phone
128
- $phone = new CNR_Field_Type('phone');
129
- $phone->set_description('Phone Number');
130
- $phone->set_element('area', 'text', array( 'size' => 3 ));
131
- $phone->set_element('prefix', 'text', array( 'size' => 3 ));
132
- $phone->set_element('suffix', 'text', array( 'size' => 4 ));
133
- $phone->set_layout('form', '({area ref_base="elements"}) {prefix ref_base="elements"} - {suffix ref_base="elements"}');
134
- $this->register_field($phone);
135
-
136
- //Hidden
137
- $hidden = new CNR_Field_Type('hidden');
138
- $hidden->set_parent('input');
139
- $hidden->set_description('Hidden Field');
140
- $hidden->set_property('type', 'hidden');
141
- $this->register_field($hidden);
142
-
143
- //Span
144
- $span = new CNR_Field_Type('span');
145
- $span->set_description('Inline wrapper');
146
- $span->set_parent('base_closed');
147
- $span->set_property('tag', 'span');
148
- $span->set_property('value', 'Hello there!');
149
- $this->register_field($span);
150
-
151
- //Select
152
- $select = new CNR_Field_Type('select');
153
- $select->set_description('Select tag');
154
- $select->set_parent('base_closed');
155
- $select->set_property('tag', 'select');
156
- $select->set_property('tag_option', 'option');
157
- $select->set_property('options', array());
158
- $select->set_layout('form', '{label ref_base="layout"} {form_start ref_base="layout"}{loop data="properties.options" layout="option" layout_data="option_data"}{form_end ref_base="layout"}');
159
- $select->set_layout('option', '<{tag_option} value="{data_ext id="option_value"}">{data_ext id="option_text"}</{tag_option}>');
160
- $select->set_layout('option_data', '<{tag_option} value="{data_ext id="option_value"}" selected="selected">{data_ext id="option_text"}</{tag_option}>');
161
- $this->register_field($select);
162
-
163
- //Enable plugins to modify (add, remove, etc.) field types
164
- do_action_ref_array('cnr_register_field_types', array(&$cnr_field_types));
165
-
166
- //Content Types
167
-
168
- //Enable plugins to add/remove content types
169
- do_action_ref_array('cnr_register_content_types', array(&$cnr_content_types));
170
-
171
- //Enable plugins to modify content types after they have all been registered
172
- do_action_ref_array('cnr_content_types_registered', array(&$cnr_content_types));
173
- }
174
-
175
- /**
176
- * Add content type to global array of content types
177
- * @param CNR_Content_Type $ct Content type to register
178
- * @global array $cnr_content_types Content types array
179
- */
180
- function register_content_type(&$ct) {
181
- //Add content type to CNR array
182
- if ( $this->is_content_type($ct) && !empty($ct->id) ) {
183
- global $cnr_content_types;
184
- $cnr_content_types[$ct->id] =& $ct;
185
- }
186
- //WP Post Type Registration
187
- global $wp_post_types;
188
- if ( !empty($ct->id) && !isset($wp_post_types[$ct->id]) )
189
- register_post_type($ct->id, $this->build_post_type_args($ct));
190
- }
191
-
192
- /**
193
- * Generates arguments array for WP Post Type Registration
194
- * @param CNR_Content_Type $ct Content type being registered
195
- * @return array Arguments array
196
- * @todo Enable custom taxonomies
197
- */
198
- function build_post_type_args(&$ct) {
199
- //Setup labels
200
-
201
- //Build labels
202
- $labels = array (
203
- 'name' => _( $ct->get_title(true) ),
204
- 'singular_name' => _( $ct->get_title(false) ),
205
- 'all_items' => sprintf( _( 'All %s' ), $ct->get_title(true) ),
206
- );
207
-
208
- //Action labels
209
- $item_actions = array(
210
- 'add_new' => 'Add New %s',
211
- 'edit' => 'Edit %s',
212
- 'new' => 'New %s',
213
- 'view' => 'View %s',
214
- 'search' => array('Search %s', true),
215
- 'not_found' => array('No %s found', true, false),
216
- 'not_found_in_trash' => array('No %s found in Trash', true, false)
217
- );
218
-
219
- foreach ( $item_actions as $key => $val ) {
220
- $excluded = false;
221
- $plural = false;
222
- if ( is_array($val) ) {
223
- if ( count($val) > 1 && true == $val[1] ) {
224
- $plural = true;
225
- }
226
- if ( count($val) > 2 && false == $val[2] )
227
- $excluded = true;
228
- $val = $val[0];
229
- }
230
- $title = ( $plural ) ? $labels['name'] : $labels['singular_name'];
231
- if ( $excluded )
232
- $item = $key;
233
- else {
234
- $item = $key . '_item' . ( ( $plural ) ? 's' : '' );
235
- }
236
- $labels[$item] = sprintf($val, $title);
237
- }
238
-
239
- //Setup args
240
- $args = array (
241
- 'labels' => $labels,
242
- 'description' => $ct->get_description(),
243
- 'public' => true,
244
- 'capability_type' => 'post',
245
- 'rewrite' => array( 'slug' => strtolower($labels['name']) ),
246
- 'has_archive' => true,
247
- 'hierarchical' => false,
248
- 'menu_position' => 5,
249
- 'supports' => array('title', 'editor', 'author', 'thumbnail', 'excerpt', 'trackbacks', 'custom-fields', 'comments', 'revisions'),
250
- 'taxonomies' => get_object_taxonomies('post'),
251
- 'show_in_rest' => true,
252
- );
253
-
254
- return $args;
255
- }
256
-
257
- /**
258
- * Add field type to global array of field types
259
- * @param CNR_Field_Type $field Field to register
260
- *
261
- * @global array $cnr_field_types Field types array
262
- */
263
- function register_field(&$field) {
264
- if ( $this->is_field($field) && !empty($field->id) ) {
265
- global $cnr_field_types;
266
- $cnr_field_types[$field->id] =& $field;
267
- }
268
- }
269
-
270
- /*-** Helpers **-*/
271
-
272
- /**
273
- * Checks whether an object is a valid content type instance
274
- * @param obj $ct Object to evaluate
275
- * @return bool TRUE if object is a valid content type instance, FALSE otherwise
276
- */
277
- function is_content_type(&$ct) {
278
- return is_a($ct, 'cnr_content_type');
279
- }
280
-
281
- /**
282
- * Checks whether an object is a valid field instance
283
- * @param obj $field Object to evaluate
284
- * @return bool TRUE if object is a valid field instance, FALSE otherwise
285
- */
286
- function is_field(&$field) {
287
- return is_a($field, 'cnr_field_type');
288
- }
289
-
290
- /*-** Handlers **-*/
291
-
292
- /**
293
- * Modifies query parameters to include custom content types
294
- * Adds custom content types to default post query so these items are retrieved as well
295
- * @param WP_Query $q Reference to WP_Query object being used to perform posts query
296
- * @see WP_Query for reference
297
- */
298
- function pre_get_posts($q) {
299
- $pt =& $q->query_vars['post_type'];
300
- /* Do not continue processing if:
301
- * > In admin section
302
- * > Not main query (or CNR-initiated query)
303
- * > Single object requested
304
- * > More than one post type is already specified
305
- * > Post type other than 'post' is supplied
306
- */
307
- if ( is_admin()
308
- || ( !$q->is_main_query() && !isset($q->query_vars[$this->get_prefix()]) )
309
- || $q->is_singular()
310
- || ( is_array($pt)
311
- && ( count($pt) > 1
312
- || 'post' != $pt[0] )
313
- )
314
- || !in_array($pt, array('post', null))
315
- ) {
316
- return false;
317
- }
318
-
319
- $default_types = $this->get_default_post_types();
320
- $custom_types = array_diff(array_keys($this->get_types()), $default_types);
321
- if ( !count($custom_types) )
322
- return false;
323
- //Wrap post type in array
324
- if ( empty($pt) || is_null($pt) )
325
- $pt = array('post');
326
- if ( !is_array($pt) )
327
- $pt = array($pt);
328
- //Add custom types to query
329
- foreach ( $custom_types as $type ) {
330
- $pt[] = $type;
331
- }
332
- }
333
-
334
- /**
335
- * Retrieves current context (content type, action)
336
- * @return array Content Type and Action of current request
337
- */
338
- function get_context() {
339
- $post = false;
340
- if ( isset($GLOBALS['post']) && !is_null($GLOBALS['post']) )
341
- $post = $GLOBALS['post'];
342
- elseif ( isset($_REQUEST['post_id']) )
343
- $post = $_REQUEST['post_id'];
344
- elseif ( isset($_REQUEST['post']) )
345
- $post = $_REQUEST['post'];
346
- elseif ( isset($_REQUEST['post_type']) )
347
- $post = $_REQUEST['post_type'];
348
- //Get action
349
- $action = $this->util->get_action();
350
- if ( empty($post) )
351
- $post = $this->get_page_type();
352
- //Get post's content type
353
- $ct =& $this->get_type($post);
354
-
355
- return array(&$ct, $action);
356
- }
357
-
358
- /**
359
- * Enqueues files for fields in current content type
360
- * @param string $page Current context
361
- */
362
- function enqueue_files($page = null) {
363
- list($ct, $action) = $this->get_context();
364
- $file_types = array('scripts' => 'script', 'styles' => 'style');
365
- //Get content type fields
366
- foreach ( $ct->fields as $field ) {
367
- //Enqueue scripts/styles for each field
368
- foreach ( $file_types as $type => $func_base ) {
369
- $deps = $field->{"get_$type"}();
370
- foreach ( $deps as $handle => $args ) {
371
- //Confirm context
372
- if ( in_array('all', $args['context']) || in_array($page, $args['context']) || in_array($action, $args['context']) ) {
373
- $this->enqueue_file($func_base, $args['params']);
374
- }
375
- }
376
- }
377
- }
378
- }
379
-
380
- /**
381
- * Add plugin hooks for fields used in current request
382
- */
383
- function add_hooks() {
384
- list($ct, $action) = $this->get_context();
385
- //Iterate through content type fields and add hooks from fields
386
- foreach ( $ct->fields as $field ) {
387
- //Iterate through hooks added to field
388
- $hooks = $field->get_hooks();
389
- foreach ( $hooks as $tag => $callback ) {
390
- //Iterate through function callbacks added to tag
391
- foreach ( $callback as $id => $args ) {
392
- //Check if hook/function was already processed
393
- if ( isset($this->hooks_processed[$tag][$id]) )
394
- continue;
395
- //Add hook/function to list of processed hooks
396
- if ( !isset($this->hooks_processed[$tag]) || !is_array($this->hooks_processed[$tag]) )
397
- $this->hooks_processed[$tag] = array($id => true);
398
- //Add hook to WP
399
- call_user_func_array('add_filter', $args);
400
- }
401
- }
402
- }
403
- }
404
-
405
- /**
406
- * Enqueues files
407
- * @param string $type Type of file to enqueue (script or style)
408
- * @param array $args (optional) Arguments to pass to enqueue function
409
- */
410
- function enqueue_file($type = 'script', $args = array()) {
411
- $func = 'wp_enqueue_' . $type;
412
- if ( function_exists($func) ) {
413
- call_user_func_array($func, $args);
414
- }
415
- }
416
-
417
- /**
418
- * Add admin menus for content types
419
- * @deprecated Not needed for 3.0+
420
- */
421
- function admin_menu() {
422
- global $cnr_content_types;
423
-
424
- $pos = 21;
425
- foreach ( $cnr_content_types as $id => $type ) {
426
- if ( $this->is_default_post_type($id) )
427
- continue;
428
- $page = $this->get_admin_page_file($id);
429
- $callback = $this->m('admin_page');
430
- $access = 8;
431
- $pos += 1;
432
- $title = $type->get_title(true);
433
- if ( !empty($title) ) {
434
- //Main menu
435
- add_menu_page($type->get_title(true), $type->get_title(true), $access, $page, $callback, '', $pos);
436
- //Edit
437
- add_submenu_page($page, __('Edit ' . $type->get_title(true)), __('Edit'), $access, $page, $callback);
438
- $hook = get_plugin_page_hookname($page, $page);
439
- add_action('load-' . $hook, $this->m('admin_menu_load_plugin'));
440
- //Add
441
- $page_add = $this->get_admin_page_file($id, 'add');
442
- add_submenu_page($page, __('Add New ' . $type->get_title()), __('Add New'), $access, $page_add, $callback);
443
- $hook = get_plugin_page_hook($page_add, $page);
444
- add_action('load-' . $hook, $this->m('admin_menu_load_plugin'));
445
- //Hook for additional menus
446
- $menu_hook = 'cnr_admin_menu_type';
447
- //Type specific
448
- do_action_ref_array($menu_hook . '_' . $id, array(&$type));
449
- //General
450
- do_action_ref_array($menu_hook, array(&$type));
451
- }
452
- }
453
- }
454
-
455
- /**
456
- * Load data for plugin admin page prior to admin-header.php is loaded
457
- * Useful for enqueueing scripts/styles, etc.
458
- */
459
- function admin_menu_load_plugin() {
460
- //Get Action
461
- global $editing, $post, $post_ID, $p;
462
- $action = $this->util->get_action();
463
- if ( isset($_GET['delete_all']) )
464
- $action = 'delete_all';
465
- if ( isset($_GET['action']) && 'edit' == $_GET['action'] && ! isset($_GET['bulk_edit']))
466
- $action = 'manage';
467
- switch ( $action ) {
468
- case 'delete_all' :
469
- case 'edit' :
470
- //Handle bulk actions
471
- //Redirect to edit.php for processing
472
-
473
- //Build query string
474
- $qs = $_GET;
475
- unset($qs['page']);
476
- $edit_uri = admin_url('edit.php') . '?' . build_query($qs);
477
- wp_redirect($edit_uri);
478
- break;
479
- case 'edit-item' :
480
- wp_enqueue_script('admin_comments');
481
- enqueue_comment_hotkeys_js();
482
- //Get post being edited
483
- if ( empty($_GET['post']) ) {
484
- wp_redirect("post.php"); //TODO redirect to appropriate manage page
485
- exit();
486
- }
487
- $post_ID = $p = (int) $_GET['post'];
488
- $post = get_post($post_ID);
489
- if ( !current_user_can('edit_post', $post_ID) )
490
- wp_die( __('You are not allowed to edit this item') );
491
-
492
- if ( $last = wp_check_post_lock($post->ID) ) {
493
- add_action('admin_notices', '_admin_notice_post_locked');
494
- } else {
495
- wp_set_post_lock($post->ID);
496
- $locked = true;
497
- }
498
- //Continue on to add case
499
- case 'add' :
500
- $editing = true;
501
- wp_enqueue_script('autosave');
502
- wp_enqueue_script('post');
503
- if ( user_can_richedit() )
504
- wp_enqueue_script('editor');
505
- add_thickbox();
506
- wp_enqueue_script('media-upload');
507
- wp_enqueue_script('word-count');
508
- add_action( 'admin_print_footer_scripts', 'wp_tiny_mce', 25 );
509
- wp_enqueue_script('quicktags');
510
- wp_enqueue_script($this->add_prefix('edit_form'), $this->util->get_file_url('js/lib.admin.edit_form.js'), array('jquery', 'postbox'), false, true);
511
- break;
512
- default :
513
- wp_enqueue_script( $this->add_prefix('inline-edit-post') );
514
- }
515
- }
516
-
517
- /**
518
- * Build admin page file name for the specified post type
519
- * @param string|CNR_Content_Type $type Content type ID or object
520
- * @param string $action Action to build file name for
521
- * @param bool $sep_action Whether action should be a separate query variable (Default: false)
522
- * @return string Admin page file name
523
- */
524
- function get_admin_page_file($type, $action = '', $sep_action = false) {
525
- if ( isset($type->id) )
526
- $type = $type->id;
527
- $page = $this->add_prefix('post_type_' . $type);
528
- if ( !empty($action) ) {
529
- if ( $sep_action )
530
- $page .= '&action=';
531
- else
532
- $page .= '-';
533
-
534
- $page .= $action;
535
- }
536
- return $page;
537
- }
538
-
539
- /**
540
- * Determine content type based on URL query variables
541
- * Uses $_GET['page'] variable to determine content type
542
- * @return string Content type of page (NULL if no type defined by page)
543
- */
544
- function get_page_type() {
545
- $type = null;
546
- //Extract type from query variable
547
- if ( isset($_GET['page']) ) {
548
- $type = $_GET['page'];
549
- $prefix = $this->add_prefix('post_type_');
550
- //Remove plugin page prefix
551
- if ( ($pos = strpos($type, $prefix)) === 0 )
552
- $type = substr($type, strlen($prefix));
553
- //Remove action (if present)
554
- if ( ($pos = strrpos($type, '-')) && $pos !== false )
555
- $type = substr($type, 0, $pos);
556
- }
557
- return $type;
558
- }
559
-
560
- /**
561
- * Populate administration page for content type
562
- */
563
- function admin_page() {
564
- $prefix = $this->add_prefix('post_type_');
565
- if ( strpos($_GET['page'], $prefix) !== 0 )
566
- return false;
567
-
568
- //Get action
569
- $action = $this->util->get_action('manage');
570
- //Get content type
571
- $type =& $this->get_type($this->get_page_type());
572
- global $title, $parent_file, $submenu_file;
573
- $title = $type->get_title(true);
574
- //$parent_file = $prefix . $type->id;
575
- //$submenu_file = $parent_file;
576
-
577
- switch ( $action ) {
578
- case 'edit-item' :
579
- case 'add' :
580
- $this->admin_page_edit($type, $action);
581
- break;
582
- default :
583
- $this->admin_page_manage($type, $action);
584
- }
585
- }
586
-
587
- /**
588
- * Queries content items for admin management pages
589
- * Also retrieves available post status for specified content type
590
- * @see wp_edit_posts_query
591
- * @param CNR_Content_Type|string $type Content type instance or ID
592
- * @return array All item statuses and Available item status
593
- */
594
- function admin_manage_query($type = 'post') {
595
- global $wp_query;
596
- $q = array();
597
- //Get post type
598
- if ( ! is_a($type, 'CNR_Content_Type') ) {
599
- $type = $this->get_type($type);
600
- }
601
- $q = array('post_type' => $type->id);
602
- $g = $_GET;
603
- //Date
604
- $q['m'] = isset($g['m']) ? (int) $g['m'] : 0;
605
- //Category
606
- $q['cat'] = isset($g['cat']) ? (int) $g['cat'] : 0;
607
- $post_stati = array( // array( adj, noun )
608
- 'publish' => array(_x('Published', 'post'), __('Published posts'), _n_noop('Published <span class="count">(%s)</span>', 'Published <span class="count">(%s)</span>')),
609
- 'future' => array(_x('Scheduled', 'post'), __('Scheduled posts'), _n_noop('Scheduled <span class="count">(%s)</span>', 'Scheduled <span class="count">(%s)</span>')),
610
- 'pending' => array(_x('Pending Review', 'post'), __('Pending posts'), _n_noop('Pending Review <span class="count">(%s)</span>', 'Pending Review <span class="count">(%s)</span>')),
611
- 'draft' => array(_x('Draft', 'post'), _x('Drafts', 'manage posts header'), _n_noop('Draft <span class="count">(%s)</span>', 'Drafts <span class="count">(%s)</span>')),
612
- 'private' => array(_x('Private', 'post'), __('Private posts'), _n_noop('Private <span class="count">(%s)</span>', 'Private <span class="count">(%s)</span>')),
613
- 'trash' => array(_x('Trash', 'post'), __('Trash posts'), _n_noop('Trash <span class="count">(%s)</span>', 'Trash <span class="count">(%s)</span>')),
614
- );
615
-
616
- $post_stati = apply_filters('post_stati', $post_stati);
617
-
618
- $avail_post_stati = get_available_post_statuses('post');
619
-
620
- //Status
621
- if ( isset($g['post_status']) && in_array( $g['post_status'], array_keys($post_stati) ) ) {
622
- $q['post_status'] = $g['post_status'];
623
- $q['perm'] = 'readable';
624
- } else {
625
- unset($q['post_status']);
626
- }
627
-
628
- //Order
629
- if ( isset($q['post_status']) && 'pending' === $q['post_status'] ) {
630
- $q['order'] = 'ASC';
631
- $q['orderby'] = 'modified';
632
- } elseif ( isset($q['post_status']) && 'draft' === $q['post_status'] ) {
633
- $q['order'] = 'DESC';
634
- $q['orderby'] = 'modified';
635
- } else {
636
- $q['order'] = 'DESC';
637
- $q['orderby'] = 'date';
638
- }
639
-
640
- //Pagination
641
- $posts_per_page = (int) get_user_option( 'edit_per_page', 0, false );
642
- if ( empty( $posts_per_page ) || $posts_per_page < 1 )
643
- $posts_per_page = 15;
644
- if ( isset($g['paged']) && (int) $g['paged'] > 1 )
645
- $q['paged'] = (int) $g['paged'];
646
- $q['posts_per_page'] = apply_filters( 'edit_posts_per_page', $posts_per_page );
647
- //Search
648
- $q[s] = ( isset($g['s']) ) ? $g[s] : '';
649
- $wp_query->query($q);
650
-
651
- return array($post_stati, $avail_post_stati);
652
- }
653
-
654
- /**
655
- * Counts the number of items in the specified content type
656
- * @see wp_count_posts
657
- * @param CNR_Content_Type|string $type Content Type instance or ID
658
- * @param string $perm Permission level for items (e.g. readable)
659
- * @return array Associative array of item counts by post status (published, draft, etc.)
660
- */
661
- function count_posts( $type, $perm = '' ) {
662
- global $wpdb;
663
-
664
- $user = wp_get_current_user();
665
-
666
- if ( !is_a($type, 'CNR_Content_Type') )
667
- $type = $this->get_type($type);
668
- $type_val = $type->get_meta_value();
669
- $type = $type->id;
670
- $cache_key = $type;
671
-
672
- //$query = "SELECT post_status, COUNT( * ) AS num_posts FROM {$wpdb->posts} WHERE post_type = %s";
673
- $query = "SELECT p.post_status, COUNT( * ) as num_posts FROM {$wpdb->posts} p JOIN {$wpdb->postmeta} m ON m.post_id = p.id WHERE m.meta_key = '" . $this->get_type_meta_key() . "' AND m.meta_value = '$type_val'";
674
- if ( 'readable' == $perm && is_user_logged_in() ) {
675
- //TODO enable check for custom post types "read_private_{$type}s"
676
- if ( !current_user_can("read_private_posts") ) {
677
- $cache_key .= '_' . $perm . '_' . $user->ID;
678
- $query .= " AND (p.post_status != 'private' OR ( p.post_author = '$user->ID' AND p.post_status = 'private' ))";
679
- }
680
- }
681
- $query .= ' GROUP BY p.post_status';
682
-
683
- $count = wp_cache_get($cache_key, 'counts');
684
- if ( false !== $count )
685
- return $count;
686
-
687
- $count = $wpdb->get_results( $wpdb->prepare( $query, $type ), ARRAY_A );
688
-
689
- $stats = array( 'publish' => 0, 'private' => 0, 'draft' => 0, 'pending' => 0, 'future' => 0, 'trash' => 0 );
690
- foreach( (array) $count as $row_num => $row ) {
691
- $stats[$row['post_status']] = $row['num_posts'];
692
- }
693
-
694
- $stats = (object) $stats;
695
- wp_cache_set($cache_key, $stats, 'counts');
696
-
697
- return $stats;
698
- }
699
-
700
- /**
701
- * Builds management page for items of a specific custom content type
702
- * @param CNR_Content_Type $type Content Type to manage
703
- * @param string $action Current action
704
- *
705
- * @global string $title
706
- * @global string $parent_file
707
- * @global string $plugin_page
708
- * @global string $page_hook
709
- * @global WP_User $current_user
710
- * @global WP_Query $wp_query
711
- * @global wpdb $wpdb
712
- * @global WP_Locale $wp_locale
713
- */
714
- function admin_page_manage($type, $action) {
715
- if ( !current_user_can('edit_posts') )
716
- wp_die(__('You do not have sufficient permissions to access this page.'));
717
-
718
- global $title, $parent_file, $plugin_page, $page_hook, $current_user, $wp_query, $wpdb, $wp_locale;
719
- $title = __('Edit ' . $type->get_title(true));
720
- $admin_path = ABSPATH . 'wp-admin/';
721
-
722
- //Pagination
723
- if ( ! isset($_GET['paged']) )
724
- $_GET['paged'] = 1;
725
-
726
- $add_url = $this->get_admin_page_url($type->id, 'add');
727
- $is_trash = isset($_GET['post_status']) && $_GET['post_status'] == 'trash';
728
- //User posts
729
- $user_posts = false;
730
- if ( !current_user_can('edit_others_posts') ) {
731
- $user_posts_count = $wpdb->get_var( $wpdb->prepare("SELECT COUNT(1) FROM $wpdb->posts p JOIN $wpdb->postmeta m ON m.post_id = p.id WHERE m.meta_key = '_cnr_post_type' AND m.meta_value = %s AND p.post_status != 'trash' AND p.post_author = %d", $type->get_meta_value(), $current_user->ID) );
732
- $user_posts = true;
733
- if ( $user_posts_count && empty($_GET['post_status']) && empty($_GET['all_posts']) && empty($_GET['author']) )
734
- $_GET['author'] = $current_user->ID;
735
- }
736
- //Get content type items
737
- list($post_stati, $avail_post_stati) = $this->admin_manage_query($type->id);
738
- ?>
739
- <div class="wrap">
740
- <?php screen_icon('edit'); ?>
741
- <h2><?php echo esc_html( $title ); ?> <a href="<?php echo $add_url; ?>" class="button add-new-h2"><?php echo esc_html_x('Add New', 'post'); ?></a> <?php
742
- if ( isset($_GET['s']) && $_GET['s'] )
743
- printf( '<span class="subtitle">' . __('Search results for &#8220;%s&#8221;') . '</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' => __('&laquo;'),
803
- 'next_text' => __('&raquo;'),
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&#8211;%s of %s' ) . '</span>%s',
871
- number_format_i18n( ( $_GET['paged'] - 1 ) * $wp_query->query_vars['posts_per_page'] + 1 ),
872
- number_format_i18n( min( $_GET['paged'] * $wp_query->query_vars['posts_per_page'], $wp_query->found_posts ) ),
873
- number_format_i18n( $wp_query->found_posts ),
874
- $page_links
875
- ); echo $page_links_text; ?></div>
876
- <?php } //page links ?>
877
- <div class="clear"></div>
878
- </div>
879
- <?php
880
- include ($admin_path . 'edit-post-rows.php');
881
- } else { //have_posts() ?>
882
- <div class="clear"></div>
883
- <p><?php
884
- if ( $is_trash )
885
- _e('No posts found in the trash', 'cornerstone');
886
- else
887
- _e('No posts found', 'cornerstone');
888
- ?></p>
889
- <?php } ?>
890
- </form>
891
- <?php inline_edit_row('post'); ?>
892
- <div id="ajax-response"></div>
893
- <br class="clear" />
894
- </div>
895
- <?php
896
- }
897
-
898
- /**
899
- * Build admin edit page for custom type item
900
- * @param CNR_Content_Type $type Content type being edited
901
- * @param string $action Current action (add, edit, manage, etc.)
902
- */
903
- function admin_page_edit($type, $action) {
904
- global $title, $hook_suffix, $parent_file, $screen_layout_columns, $post, $post_ID, $p;
905
- $screen_layout_columns = 2;
906
- //TODO Add default icon for content type
907
- $parent_file = 'edit.php'; //Makes screen_icon() use edit icon on post edit form
908
- switch ( $action ) {
909
- case 'edit-item' :
910
- $title = 'Edit';
911
- $post = get_post_to_edit($post_ID);
912
- break;
913
- default :
914
- $title = 'Add New';
915
- $post = get_default_post_to_edit();
916
- break;
917
- }
918
- $title = __($title . ' ' . $type->get_title());
919
- $admin_path = ABSPATH . 'wp-admin/';
920
- include ($admin_path . 'edit-form-advanced.php');
921
- }
922
-
923
- /**
924
- * Adds hidden field declaring content type on post edit form
925
- * @deprecated no longer needed for WP 3.0+
926
- */
927
- function admin_page_edit_form() {
928
- global $post, $plugin_page;
929
- if ( empty($post) || !$post->ID ) {
930
- $type = $this->get_type($post);
931
- if ( ! empty($type) && ! empty($type->id) ) {
932
- ?>
933
- <input type="hidden" name="cnr[content_type]" id="cnr[content_type]" value="<?php echo $type->id; ?>" />
934
- <?php
935
- }
936
- }
937
- }
938
-
939
- /**
940
- * Adds meta boxes for post's content type
941
- * Each group in content type is a separate meta box
942
- * @param string $type Type of item meta boxes are being build for (post, page, link)
943
- * @param string $context Location of meta box (normal, advanced, side)
944
- * @param object $post Post object
945
- */
946
- function admin_do_meta_boxes($type, $context, $post) {
947
- //Validate $type. Should be 'post','page', or a custom post type for our purposes
948
- if ( in_array($type, array_merge(array_keys($this->get_types()), array('post', 'page'))) ) {
949
- //Get content type definition
950
- $ct =& $this->get_type($post);
951
- //Pass processing to content type instance
952
- $ct->admin_do_meta_boxes($type, $context, $post);
953
- }
954
- }
955
-
956
- /**
957
- * Saves field data submitted for current post
958
- * @param int $post_id ID of current post
959
- * @param object $post Post object
960
- */
961
- function save_item_data($post_id, $post) {
962
- if ( empty($post_id) || empty($post) || !isset($_POST['cnr']) || !is_array($_POST['cnr']) )
963
- return false;
964
- $pdata = $_POST['cnr'];
965
-
966
- if ( isset($pdata['attributes']) && is_array($pdata['attributes']) && isset($pdata['fields_loaded']) && is_array($pdata['fields_loaded']) ) {
967
-
968
- $prev_data = (array) $this->get_item_data($post_id);
969
-
970
- //Remove loaded fields from prev data
971
- $prev_data = array_diff_key($prev_data, $pdata['fields_loaded']);
972
-
973
- //Get current field data
974
- $curr_data = $pdata['attributes'];
975
-
976
- //Merge arrays together (new data overwrites old data)
977
- if ( is_array($prev_data) && is_array($curr_data) ) {
978
- $curr_data = array_merge($prev_data, $curr_data);
979
- }
980
-
981
- //Save to database
982
- update_post_meta($post_id, $this->get_fields_meta_key(), $curr_data);
983
- }
984
- //Save content type
985
- if ( isset($_POST['cnr']['content_type']) ) {
986
- $type = $_POST['cnr']['content_type'];
987
- $saved_type = get_post_meta($post_id, $this->get_type_meta_key(), true);
988
- if ( is_array($saved_type) )
989
- $saved_type = implode($saved_type);
990
- if ( $type != $saved_type ) {
991
- //Continue processing if submitted content type is different from previously-saved content type (or no type was previously set)
992
- update_post_meta($post_id, $this->get_type_meta_key(), array($type));
993
- }
994
- }
995
- }
996
-
997
- /*-** Helpers **-*/
998
-
999
- /**
1000
- * Get array of default post types
1001
- * @return array Default post types
1002
- */
1003
- function get_default_post_types() {
1004
- return array('post', 'page', 'attachment', 'revision', 'nav_menu');
1005
- }
1006
-
1007
- /**
1008
- * Checks if post's post type is a standard WP post type
1009
- * @param mixed $post_type Post type (default) or post ID/object to evaluate
1010
- * @see CNR_Content_Utilities::get_type() for possible parameter values
1011
- * @return bool TRUE if post is default type, FALSE if it is a custom type
1012
- */
1013
- function is_default_post_type($post_type) {
1014
- if ( !is_string($post_type) ) {
1015
- $post_type = $this->get_type($post_type);
1016
- $post_type = $post_type->id;
1017
- }
1018
- return in_array($post_type, $this->get_default_post_types());
1019
- }
1020
-
1021
- /**
1022
- * Checks if specified content type has been defined
1023
- * @param string|CNR_Content_Type $type Content type ID or object
1024
- * @return bool TRUE if content type exists, FALSE otherwise
1025
- *
1026
- * @uses array $cnr_content_types
1027
- */
1028
- function type_exists($type) {
1029
- global $cnr_content_types;
1030
- if ( ! is_scalar($type) ) {
1031
- if ( is_a($type, 'CNR_Content_Type') )
1032
- $type = $type->id;
1033
- else
1034
- $type = null;
1035
- }
1036
- return ( isset($cnr_content_types[$type]) );
1037
- }
1038
-
1039
- /**
1040
- * Retrieves content type definition for specified content item (post, page, etc.)
1041
- * If content type does not exist, a new instance object will be created and returned
1042
- * > New content types are automatically registered (since we are looking for registered types when using this method)
1043
- * @param string|object $item Post object, or item type (string)
1044
- * @return CNR_Content_Type Reference to matching content type, empty content type if no matching type exists
1045
- *
1046
- * @uses array $cnr_content_types
1047
- */
1048
- function &get_type($item) {
1049
- //Return immediately if $item is a content type instance
1050
- if ( is_a($item, 'CNR_Content_Type') )
1051
- return $item;
1052
-
1053
- $type = null;
1054
-
1055
- if ( is_string($item) )
1056
- $type = $item;
1057
-
1058
- if ( !$this->type_exists($type) ) {
1059
- $post = $item;
1060
-
1061
- //Check if $item is a post (object or ID)
1062
- if ( $this->util->check_post($post) && isset($post->post_type) ) {
1063
- $type = $post->post_type;
1064
- }
1065
- }
1066
- global $cnr_content_types;
1067
- if ( $this->type_exists($type) ) {
1068
- //Retrieve content type from global array
1069
- $type =& $cnr_content_types[$type];
1070
- } else {
1071
- //Create new empty content type if it does not already exist
1072
- $type = new CNR_Content_Type($type);
1073
- //Automatically register newly initialized content type if it extends an existing WP post type
1074
- if ( $this->is_default_post_type($type->id) )
1075
- $type->register();
1076
- }
1077
-
1078
- return $type;
1079
- }
1080
-
1081
- /**
1082
- * Retrieve content types
1083
- * @return Reference to content types array
1084
- */
1085
- function &get_types() {
1086
- return $GLOBALS['cnr_content_types'];
1087
- }
1088
-
1089
- /**
1090
- * Retrieve meta key for post fields
1091
- * @return string Fields meta key
1092
- */
1093
- function get_fields_meta_key() {
1094
- return $this->util->make_meta_key('fields');
1095
- }
1096
-
1097
- /**
1098
- * Retrieve meta key for post type
1099
- * @return string Post type meta key
1100
- */
1101
- function get_type_meta_key() {
1102
- return $this->util->make_meta_key('post_type');
1103
- }
1104
-
1105
- /**
1106
- * Checks if post contains specified field data
1107
- * @param Object $post (optional) Post to check data for
1108
- * @param string $field (optional) Field ID to check for
1109
- * @return bool TRUE if data exists, FALSE otherwise
1110
- */
1111
- function has_item_data($item = null, $field = null) {
1112
- $ret = $this->get_item_data($item, $field, 'raw', null);
1113
- if ( is_scalar($ret) )
1114
- return ( !empty($ret) || $ret === 0 );
1115
- if ( is_array($ret) ) {
1116
- foreach ( $ret as $key => $val ) {
1117
- if ( !empty($val) || $val === 0 )
1118
- return true;
1119
- }
1120
- }
1121
- return false;
1122
- }
1123
-
1124
- /**
1125
- * Retrieve specified field data from content item (e.g. post)
1126
- * Usage Examples:
1127
- * get_item_data($post_id, 'field_id')
1128
- * - Retrieves field_id data from global $post object
1129
- * - Field data is formatted using 'display' layout of field
1130
- *
1131
- * get_item_data($post_id, 'field_id', 'raw')
1132
- * - Retrieves field_id data from global $post object
1133
- * - Raw field data is returned (no formatting)
1134
- *
1135
- * get_item_data($post_id, 'field_id', 'display', $post_id)
1136
- * - Retrieves field_id data from post matching $post_id
1137
- * - Field data is formatted using 'display' layout of field
1138
- *
1139
- * get_item_data($post_id, 'field_id', null)
1140
- * - Retrieves field_id data from post matching $post_id
1141
- * - Field data is formatted using 'display' layout of field
1142
- * - The default layout is used when no valid layout is specified
1143
- *
1144
- * get_item_data($post_id)
1145
- * - Retrieves full data array from post matching $post_id
1146
- *
1147
- * @param int|object $item(optional) Content item to retrieve field from (Default: null - global $post object will be used)
1148
- * @param string $field ID of field to retrieve
1149
- * @param string $layout(optional) Layout to use when returning field data (Default: display)
1150
- * @param array $attr (optional) Additional attributes to pass along to field object (e.g. for building layout, etc.)
1151
- * @see CNR_Field_Type::build_layout for more information on attribute usage
1152
- * @return mixed Specified field data
1153
- */
1154
- function get_item_data($item = null, $field = null, $layout = null, $default = '', $attr = null) {
1155
- $ret = $default;
1156
-
1157
- //Get item
1158
- $item = get_post($item);
1159
-
1160
- if ( !isset($item->ID) )
1161
- return $ret;
1162
-
1163
- //Get item data
1164
- $data = get_post_meta($item->ID, $this->get_fields_meta_key(), true);
1165
-
1166
- //Get field data
1167
-
1168
- //Set return value to data if no field specified
1169
- if ( empty($field) || !is_string($field) )
1170
- $ret = $data;
1171
- //Stop if no valid field specified
1172
- if ( !isset($data[$field]) ) {
1173
- //TODO Check $item object to see if specified field exists (e.g. title, post_status, etc.)
1174
- return $ret;
1175
- }
1176
-
1177
- $ret = $data[$field];
1178
-
1179
- //Initialize layout value
1180
- $layout_def = 'display';
1181
-
1182
- if ( !is_scalar($layout) || empty($layout) )
1183
- $layout = $layout_def;
1184
-
1185
- $layout = strtolower($layout);
1186
-
1187
- //Check if raw data requested
1188
- if ( 'raw' == $layout )
1189
- return $ret;
1190
-
1191
- /* Build specified layout */
1192
-
1193
- //Get item's content type
1194
- $ct =& $this->get_type($item);
1195
- $ct->set_data($data);
1196
-
1197
- //Get field definition
1198
- $fdef =& $ct->get_field($field);
1199
-
1200
- //Validate layout
1201
- if ( !$fdef->has_layout($layout) )
1202
- $layout = $layout_def;
1203
-
1204
- //Build layout
1205
- $fdef->set_caller($ct);
1206
- $ret = $fdef->build_layout($layout, $attr);
1207
- $fdef->clear_caller();
1208
-
1209
- //Return formatted value
1210
- return $ret;
1211
- }
1212
-
1213
- /**
1214
- * Prints an item's field data
1215
- * @see CNR_Content_Utilities::get_item_data() for more information
1216
- * @param int|object $item(optional) Content item to retrieve field from (Default: null - global $post object will be used)
1217
- * @param string $field ID of field to retrieve
1218
- * @param string $layout(optional) Layout to use when returning field data (Default: display)
1219
- * @param mixed $default (optional) Default value to return in case of errors, etc.
1220
- * @param array $attr Additional attributes to pass to field
1221
- */
1222
- function the_item_data($item = null, $field = null, $layout = null, $default = '', $attr = null) {
1223
- echo apply_filters('cnr_the_item_data', $this->get_item_data($item, $field, $layout, $default, $attr), $item, $field, $layout, $default, $attr);
1224
- }
1225
-
1226
- /**
1227
- * Build Admin URL for specified post type
1228
- * @param string|CNR_Content_Type $type Content type ID or object
1229
- * @param string $action Action to build URL for
1230
- * @param bool $sep_action Whether action should be a separate query variable (Default: false)
1231
- * @return string Admin page URL
1232
- */
1233
- function get_admin_page_url($type, $action = '', $sep_action = false) {
1234
- $url = admin_url('admin.php');
1235
- $url .= '?page=' . $this->get_admin_page_file($type, $action, $sep_action);
1236
- return $url;
1237
- }
1238
-
1239
- function get_edit_item_url($edit_url, $item_id, $context) {
1240
- //Get post type
1241
- $type = $this->get_type($item_id);
1242
- if ( ! $this->is_default_post_type($type->id) && $this->type_exists($type) ) {
1243
- $edit_url = $this->get_admin_page_url($type, 'edit-item', true) . '&post=' . $item_id;
1244
- }
1245
-
1246
- return $edit_url;
1247
- }
1248
  }
1
+ <?php
2
+ /**
3
+ * Utilities for Content Type functionality
4
+ * @package Cornerstone
5
+ * @subpackage Content Types
6
+ * @author Archetyped
7
+ */
8
+ class CNR_Content_Utilities extends CNR_Base {
9
+
10
+ /**
11
+ * Array of hooks called
12
+ * @var array
13
+ */
14
+ var $hooks_processed = array();
15
+
16
+ /**
17
+ * Initialize content type functionality
18
+ */
19
+ function init() {
20
+ $this->register_hooks();
21
+ }
22
+
23
+ /**
24
+ * Registers hooks for content types
25
+ * @todo 2010-07-30: Check hooks for 3.0 compatibility
26
+ */
27
+ function register_hooks() {
28
+ //Register types
29
+ add_action('init', $this->m('register_types'));
30
+ add_action('init', $this->m('add_hooks'), 11);
31
+
32
+ //Enqueue scripts for fields in current post type
33
+ add_action('admin_enqueue_scripts', $this->m('enqueue_files'));
34
+
35
+ //Add menus
36
+ //add_action('admin_menu', $this->m('admin_menu'));
37
+
38
+ //Build UI on post edit form
39
+ add_action('do_meta_boxes', $this->m('admin_do_meta_boxes'), 10, 3);
40
+
41
+ //Get edit link for items
42
+ //add_filter('get_edit_post_link', $this->m('get_edit_item_url'), 10, 3);
43
+
44
+ //add_action('edit_form_advanced', $this->m('admin_page_edit_form'));
45
+
46
+ //Save Field data/Content type
47
+ add_action('save_post', $this->m('save_item_data'), 10, 2);
48
+
49
+ //Modify post query for content type compatibility
50
+ add_action('pre_get_posts', $this->m('pre_get_posts'), 20);
51
+ }
52
+
53
+ /**
54
+ * Initialize fields and content types
55
+ */
56
+ function register_types() {
57
+ //Global variables
58
+ global $cnr_field_types, $cnr_content_types;
59
+
60
+ /* Field Types */
61
+
62
+ //Base
63
+ $base = new CNR_Field_Type('base');
64
+ $base->set_description('Default Element');
65
+ $base->set_property('tag', 'span');
66
+ $base->set_property('class', '', 'attr');
67
+ $base->set_layout('form', '<{tag} name="{field_name}" id="{field_id}" {properties ref_base="root" group="attr"} />');
68
+ $base->set_layout('label', '<label for="{field_id}">{label}</label>');
69
+ $base->set_layout('display', '{data format="display"}');
70
+ $this->register_field($base);
71
+
72
+ //Base closed
73
+ $base_closed = new CNR_Field_Type('base_closed');
74
+ $base_closed->set_parent('base');
75
+ $base_closed->set_description('Default Element (Closed Tag)');
76
+ $base_closed->set_layout('form_start', '<{tag} id="{field_id}" name="{field_name}" {properties ref_base="root" group="attr"}>');
77
+ $base_closed->set_layout('form_end', '</{tag}>');
78
+ $base_closed->set_layout('form', '{form_start ref_base="layout"}{data}{form_end ref_base="layout"}');
79
+ $this->register_field($base_closed);
80
+
81
+ //Input
82
+ $input = new CNR_Field_Type('input');
83
+ $input->set_parent('base');
84
+ $input->set_description('Default Input Element');
85
+ $input->set_property('tag', 'input');
86
+ $input->set_property('type', 'text', 'attr');
87
+ $input->set_property('value', CNR_Field::USES_DATA, 'attr');
88
+ $this->register_field($input);
89
+
90
+ //Text input
91
+ $text = new CNR_Field_Type('text', 'input');
92
+ $text->set_description('Text Box');
93
+ $text->set_property('size', 15, 'attr');
94
+ $text->set_property('label');
95
+ $text->set_layout('form', '{label ref_base="layout"} {inherit}');
96
+ $this->register_field($text);
97
+
98
+ //Checkbox
99
+ $checkbox = new CNR_Field_Type('checkbox', 'input');
100
+ $checkbox->set_description('Checkbox');
101
+ $checkbox->set_property('type', 'checkbox', 'attr');
102
+ $checkbox->set_property('label');
103
+ $checkbox->set_property('checked', '', 'attr');
104
+ $checkbox->set_layout('form', '{inherit} {label ref_base="layout"}');
105
+ $this->register_field($checkbox);
106
+
107
+ //Textarea
108
+ $ta = new CNR_Field_Type('textarea', 'base_closed');
109
+ $ta->set_property('tag', 'textarea');
110
+ $ta->set_property('cols', 40, 'attr');
111
+ $ta->set_property('rows', 3, 'attr');
112
+ $this->register_field($ta);
113
+
114
+ //Rich Text
115
+ $rt = new CNR_Field_Type('richtext', 'textarea');
116
+ $rt->set_layout('form', '<div class="rt_container">{rich_editor}</div>');
117
+ $this->register_field($rt);
118
+
119
+ //Location
120
+ $location = new CNR_Field_Type('location');
121
+ $location->set_description('Geographic Coordinates');
122
+ $location->set_element('latitude', 'text', array( 'size' => 3, 'label' => 'Latitude' ));
123
+ $location->set_element('longitude', 'text', array( 'size' => 3, 'label' => 'Longitude' ));
124
+ $location->set_layout('form', '<span>{latitude ref_base="elements"}</span>, <span>{longitude ref_base="elements"}</span>');
125
+ $this->register_field($location);
126
+
127
+ //Phone
128
+ $phone = new CNR_Field_Type('phone');
129
+ $phone->set_description('Phone Number');
130
+ $phone->set_element('area', 'text', array( 'size' => 3 ));
131
+ $phone->set_element('prefix', 'text', array( 'size' => 3 ));
132
+ $phone->set_element('suffix', 'text', array( 'size' => 4 ));
133
+ $phone->set_layout('form', '({area ref_base="elements"}) {prefix ref_base="elements"} - {suffix ref_base="elements"}');
134
+ $this->register_field($phone);
135
+
136
+ //Hidden
137
+ $hidden = new CNR_Field_Type('hidden');
138
+ $hidden->set_parent('input');
139
+ $hidden->set_description('Hidden Field');
140
+ $hidden->set_property('type', 'hidden');
141
+ $this->register_field($hidden);
142
+
143
+ //Span
144
+ $span = new CNR_Field_Type('span');
145
+ $span->set_description('Inline wrapper');
146
+ $span->set_parent('base_closed');
147
+ $span->set_property('tag', 'span');
148
+ $span->set_property('value', 'Hello there!');
149
+ $this->register_field($span);
150
+
151
+ //Select
152
+ $select = new CNR_Field_Type('select');
153
+ $select->set_description('Select tag');
154
+ $select->set_parent('base_closed');
155
+ $select->set_property('tag', 'select');
156
+ $select->set_property('tag_option', 'option');
157
+ $select->set_property('options', array());
158
+ $select->set_layout('form', '{label ref_base="layout"} {form_start ref_base="layout"}{loop data="properties.options" layout="option" layout_data="option_data"}{form_end ref_base="layout"}');
159
+ $select->set_layout('option', '<{tag_option} value="{data_ext id="option_value"}">{data_ext id="option_text"}</{tag_option}>');
160
+ $select->set_layout('option_data', '<{tag_option} value="{data_ext id="option_value"}" selected="selected">{data_ext id="option_text"}</{tag_option}>');
161
+ $this->register_field($select);
162
+
163
+ //Enable plugins to modify (add, remove, etc.) field types
164
+ do_action_ref_array('cnr_register_field_types', array(&$cnr_field_types));
165
+
166
+ //Content Types
167
+
168
+ //Enable plugins to add/remove content types
169
+ do_action_ref_array('cnr_register_content_types', array(&$cnr_content_types));
170
+
171
+ //Enable plugins to modify content types after they have all been registered
172
+ do_action_ref_array('cnr_content_types_registered', array(&$cnr_content_types));
173
+ }
174
+
175
+ /**
176
+ * Add content type to global array of content types
177
+ * @param CNR_Content_Type $ct Content type to register
178
+ * @global array $cnr_content_types Content types array
179
+ */
180
+ function register_content_type(&$ct) {
181
+ //Add content type to CNR array
182
+ if ( $this->is_content_type($ct) && !empty($ct->id) ) {
183
+ global $cnr_content_types;
184
+ $cnr_content_types[$ct->id] =& $ct;
185
+ }
186
+ //WP Post Type Registration
187
+ global $wp_post_types;
188
+ if ( !empty($ct->id) && !isset($wp_post_types[$ct->id]) )
189
+ register_post_type($ct->id, $this->build_post_type_args($ct));
190
+ }
191
+
192
+ /**
193
+ * Generates arguments array for WP Post Type Registration
194
+ * @param CNR_Content_Type $ct Content type being registered
195
+ * @return array Arguments array
196
+ * @todo Enable custom taxonomies
197
+ */
198
+ function build_post_type_args(&$ct) {
199
+ //Setup labels
200
+
201
+ //Build labels
202
+ $labels = array (
203
+ 'name' => _( $ct->get_title(true) ),
204
+ 'singular_name' => _( $ct->get_title(false) ),
205
+ 'all_items' => sprintf( _( 'All %s' ), $ct->get_title(true) ),
206
+ );
207
+
208
+ //Action labels
209
+ $item_actions = array(
210
+ 'add_new' => 'Add New %s',
211
+ 'edit' => 'Edit %s',
212
+ 'new' => 'New %s',
213
+ 'view' => 'View %s',
214
+ 'search' => array('Search %s', true),
215
+ 'not_found' => array('No %s found', true, false),
216
+ 'not_found_in_trash' => array('No %s found in Trash', true, false)
217
+ );
218
+
219
+ foreach ( $item_actions as $key => $val ) {
220
+ $excluded = false;
221
+ $plural = false;
222
+ if ( is_array($val) ) {
223
+ if ( count($val) > 1 && true == $val[1] ) {
224
+ $plural = true;
225
+ }
226
+ if ( count($val) > 2 && false == $val[2] )
227
+ $excluded = true;
228
+ $val = $val[0];
229
+ }
230
+ $title = ( $plural ) ? $labels['name'] : $labels['singular_name'];
231
+ if ( $excluded )
232
+ $item = $key;
233
+ else {
234
+ $item = $key . '_item' . ( ( $plural ) ? 's' : '' );
235
+ }
236
+ $labels[$item] = sprintf($val, $title);
237
+ }
238
+
239
+ //Setup args
240
+ $args = array (
241
+ 'labels' => $labels,
242
+ 'description' => $ct->get_description(),
243
+ 'public' => true,
244
+ 'capability_type' => 'post',
245
+ 'rewrite' => array( 'slug' => strtolower($labels['name']) ),
246
+ 'has_archive' => true,
247
+ 'hierarchical' => false,
248
+ 'menu_position' => 5,
249
+ 'supports' => array('title', 'editor', 'author', 'thumbnail', 'excerpt', 'trackbacks', 'custom-fields', 'comments', 'revisions'),
250
+ 'taxonomies' => get_object_taxonomies('post'),
251
+ 'show_in_rest' => true,
252
+ );
253
+
254
+ return $args;
255
+ }
256
+
257
+ /**
258
+ * Add field type to global array of field types
259
+ * @param CNR_Field_Type $field Field to register
260
+ *
261
+ * @global array $cnr_field_types Field types array
262
+ */
263
+ function register_field(&$field) {
264
+ if ( $this->is_field($field) && !empty($field->id) ) {
265
+ global $cnr_field_types;
266
+ $cnr_field_types[$field->id] =& $field;
267
+ }
268
+ }
269
+
270
+ /*-** Helpers **-*/
271
+
272
+ /**
273
+ * Checks whether an object is a valid content type instance
274
+ * @param obj $ct Object to evaluate
275
+ * @return bool TRUE if object is a valid content type instance, FALSE otherwise
276
+ */
277
+ function is_content_type(&$ct) {
278
+ return is_a($ct, 'cnr_content_type');
279
+ }
280
+
281
+ /**
282
+ * Checks whether an object is a valid field instance
283
+ * @param obj $field Object to evaluate
284
+ * @return bool TRUE if object is a valid field instance, FALSE otherwise
285
+ */
286
+ function is_field(&$field) {
287
+ return is_a($field, 'cnr_field_type');
288
+ }
289
+
290
+ /*-** Handlers **-*/
291
+
292
+ /**
293
+ * Modifies query parameters to include custom content types
294
+ * Adds custom content types to default post query so these items are retrieved as well
295
+ * @param WP_Query $q Reference to WP_Query object being used to perform posts query
296
+ * @see WP_Query for reference
297
+ */
298
+ function pre_get_posts($q) {
299
+ $pt =& $q->query_vars['post_type'];
300
+ /* Do not continue processing if:
301
+ * > In admin section
302
+ * > Not main query (or CNR-initiated query)
303
+ * > Single object requested
304
+ * > More than one post type is already specified
305
+ * > Post type other than 'post' is supplied
306
+ */
307
+ if ( is_admin()
308
+ || ( !$q->is_main_query() && !isset($q->query_vars[$this->get_prefix()]) )
309
+ || $q->is_singular()
310
+ || ( is_array($pt)
311
+ && ( count($pt) > 1
312
+ || 'post' != $pt[0] )
313
+ )
314
+ || !in_array($pt, array('post', null))
315
+ ) {
316
+ return false;
317
+ }
318
+
319
+ $default_types = $this->get_default_post_types();
320
+ $custom_types = array_diff(array_keys($this->get_types()), $default_types);
321
+ if ( !count($custom_types) )
322
+ return false;
323
+ //Wrap post type in array
324
+ if ( empty($pt) || is_null($pt) )
325
+ $pt = array('post');
326
+ if ( !is_array($pt) )
327
+ $pt = array($pt);
328
+ //Add custom types to query
329
+ foreach ( $custom_types as $type ) {
330
+ $pt[] = $type;
331
+ }
332
+ }
333
+
334
+ /**
335
+ * Retrieves current context (content type, action)
336
+ * @return array Content Type and Action of current request
337
+ */
338
+ function get_context() {
339
+ $post = false;
340
+ if ( isset($GLOBALS['post']) && !is_null($GLOBALS['post']) )
341
+ $post = $GLOBALS['post'];
342
+ elseif ( isset($_REQUEST['post_id']) )
343
+ $post = $_REQUEST['post_id'];
344
+ elseif ( isset($_REQUEST['post']) )
345
+ $post = $_REQUEST['post'];
346
+ elseif ( isset($_REQUEST['post_type']) )
347
+ $post = $_REQUEST['post_type'];
348
+ //Get action
349
+ $action = $this->util->get_action();
350
+ if ( empty($post) )
351
+ $post = $this->get_page_type();
352
+ //Get post's content type
353
+ $ct =& $this->get_type($post);
354
+
355
+ return array(&$ct, $action);
356
+ }
357
+
358
+ /**
359
+ * Enqueues files for fields in current content type
360
+ * @param string $page Current context
361
+ */
362
+ function enqueue_files($page = null) {
363
+ list($ct, $action) = $this->get_context();
364
+ $file_types = array('scripts' => 'script', 'styles' => 'style');
365
+ //Get content type fields
366
+ foreach ( $ct->fields as $field ) {
367
+ //Enqueue scripts/styles for each field
368
+ foreach ( $file_types as $type => $func_base ) {
369
+ $deps = $field->{"get_$type"}();
370
+ foreach ( $deps as $handle => $args ) {
371
+ //Confirm context
372
+ if ( in_array('all', $args['context']) || in_array($page, $args['context']) || in_array($action, $args['context']) ) {
373
+ $this->enqueue_file($func_base, $args['params']);
374
+ }
375
+ }
376
+ }
377
+ }
378
+ }
379
+
380
+ /**
381
+ * Add plugin hooks for fields used in current request
382
+ */
383
+ function add_hooks() {
384
+ list($ct, $action) = $this->get_context();
385
+ //Iterate through content type fields and add hooks from fields
386
+ foreach ( $ct->fields as $field ) {
387
+ //Iterate through hooks added to field
388
+ $hooks = $field->get_hooks();
389
+ foreach ( $hooks as $tag => $callback ) {
390
+ //Iterate through function callbacks added to tag
391
+ foreach ( $callback as $id => $args ) {
392
+ //Check if hook/function was already processed
393
+ if ( isset($this->hooks_processed[$tag][$id]) )
394
+ continue;
395
+ //Add hook/function to list of processed hooks
396
+ if ( !isset($this->hooks_processed[$tag]) || !is_array($this->hooks_processed[$tag]) )
397
+ $this->hooks_processed[$tag] = array($id => true);
398
+ //Add hook to WP
399
+ call_user_func_array('add_filter', $args);
400
+ }
401
+ }
402
+ }
403
+ }
404
+
405
+ /**
406
+ * Enqueues files
407
+ * @param string $type Type of file to enqueue (script or style)
408
+ * @param array $args (optional) Arguments to pass to enqueue function
409
+ */
410
+ function enqueue_file($type = 'script', $args = array()) {
411
+ $func = 'wp_enqueue_' . $type;
412
+ if ( function_exists($func) ) {
413
+ call_user_func_array($func, $args);
414
+ }
415
+ }
416
+
417
+ /**
418
+ * Add admin menus for content types
419
+ * @deprecated Not needed for 3.0+
420
+ */
421
+ function admin_menu() {
422
+ global $cnr_content_types;
423
+
424
+ $pos = 21;
425
+ foreach ( $cnr_content_types as $id => $type ) {
426
+ if ( $this->is_default_post_type($id) )
427
+ continue;
428
+ $page = $this->get_admin_page_file($id);
429
+ $callback = $this->m('admin_page');
430
+ $access = 8;
431
+ $pos += 1;
432
+ $title = $type->get_title(true);
433
+ if ( !empty($title) ) {
434
+ //Main menu
435
+ add_menu_page($type->get_title(true), $type->get_title(true), $access, $page, $callback, '', $pos);
436
+ //Edit
437
+ add_submenu_page($page, __('Edit ' . $type->get_title(true)), __('Edit'), $access, $page, $callback);
438
+ $hook = get_plugin_page_hookname($page, $page);
439
+ add_action('load-' . $hook, $this->m('admin_menu_load_plugin'));
440
+ //Add
441
+ $page_add = $this->get_admin_page_file($id, 'add');
442
+ add_submenu_page($page, __('Add New ' . $type->get_title()), __('Add New'), $access, $page_add, $callback);
443
+ $hook = get_plugin_page_hook($page_add, $page);
444
+ add_action('load-' . $hook, $this->m('admin_menu_load_plugin'));
445
+ //Hook for additional menus
446
+ $menu_hook = 'cnr_admin_menu_type';
447
+ //Type specific
448
+ do_action_ref_array($menu_hook . '_' . $id, array(&$type));
449
+ //General
450
+ do_action_ref_array($menu_hook, array(&$type));
451
+ }
452
+ }
453
+ }
454
+
455
+ /**
456
+ * Load data for plugin admin page prior to admin-header.php is loaded
457
+ * Useful for enqueueing scripts/styles, etc.
458
+ */
459
+ function admin_menu_load_plugin() {
460
+ //Get Action
461
+ global $editing, $post, $post_ID, $p;
462
+ $action = $this->util->get_action();
463
+ if ( isset($_GET['delete_all']) )
464
+ $action = 'delete_all';
465
+ if ( isset($_GET['action']) && 'edit' == $_GET['action'] && ! isset($_GET['bulk_edit']))
466
+ $action = 'manage';
467
+ switch ( $action ) {
468
+ case 'delete_all' :
469
+ case 'edit' :
470
+ //Handle bulk actions
471
+ //Redirect to edit.php for processing
472
+
473
+ //Build query string
474
+ $qs = $_GET;
475
+ unset($qs['page']);
476
+ $edit_uri = admin_url('edit.php') . '?' . build_query($qs);
477
+ wp_redirect($edit_uri);
478
+ break;
479
+ case 'edit-item' :
480
+ wp_enqueue_script('admin_comments');
481
+ enqueue_comment_hotkeys_js();
482
+ //Get post being edited
483
+ if ( empty($_GET['post']) ) {
484
+ wp_redirect("post.php"); //TODO redirect to appropriate manage page
485
+ exit();
486
+ }
487
+ $post_ID = $p = (int) $_GET['post'];
488
+ $post = get_post($post_ID);
489
+ if ( !current_user_can('edit_post', $post_ID) )
490
+ wp_die( __('You are not allowed to edit this item') );
491
+
492
+ if ( $last = wp_check_post_lock($post->ID) ) {
493
+ add_action('admin_notices', '_admin_notice_post_locked');
494
+ } else {
495
+ wp_set_post_lock($post->ID);
496
+ $locked = true;
497
+ }
498
+ //Continue on to add case
499
+ case 'add' :
500
+ $editing = true;
501
+ wp_enqueue_script('autosave');
502
+ wp_enqueue_script('post');
503
+ if ( user_can_richedit() )
504
+ wp_enqueue_script('editor');
505
+ add_thickbox();
506
+ wp_enqueue_script('media-upload');
507
+ wp_enqueue_script('word-count');
508
+ add_action( 'admin_print_footer_scripts', 'wp_tiny_mce', 25 );
509
+ wp_enqueue_script('quicktags');
510
+ wp_enqueue_script($this->add_prefix('edit_form'), $this->util->get_file_url('js/lib.admin.edit_form.js'), array('jquery', 'postbox'), false, true);
511
+ break;
512
+ default :
513
+ wp_enqueue_script( $this->add_prefix('inline-edit-post') );
514
+ }
515
+ }
516
+
517
+ /**
518
+ * Build admin page file name for the specified post type
519
+ * @param string|CNR_Content_Type $type Content type ID or object
520
+ * @param string $action Action to build file name for
521
+ * @param bool $sep_action Whether action should be a separate query variable (Default: false)
522
+ * @return string Admin page file name
523
+ */
524
+ function get_admin_page_file($type, $action = '', $sep_action = false) {
525
+ if ( isset($type->id) )
526
+ $type = $type->id;
527
+ $page = $this->add_prefix('post_type_' . $type);
528
+ if ( !empty($action) ) {
529
+ if ( $sep_action )
530
+ $page .= '&action=';
531
+ else
532
+ $page .= '-';
533
+
534
+ $page .= $action;
535
+ }
536
+ return $page;
537
+ }
538
+
539
+ /**
540
+ * Determine content type based on URL query variables
541
+ * Uses $_GET['page'] variable to determine content type
542
+ * @return string Content type of page (NULL if no type defined by page)
543
+ */
544
+ function get_page_type() {
545
+ $type = null;
546
+ //Extract type from query variable
547
+ if ( isset($_GET['page']) ) {
548
+ $type = $_GET['page'];
549
+ $prefix = $this->add_prefix('post_type_');
550
+ //Remove plugin page prefix
551
+ if ( ($pos = strpos($type, $prefix)) === 0 )
552
+ $type = substr($type, strlen($prefix));
553
+ //Remove action (if present)
554
+ if ( ($pos = strrpos($type, '-')) && $pos !== false )
555
+ $type = substr($type, 0, $pos);
556
+ }
557
+ return $type;
558
+ }
559
+
560
+ /**
561
+ * Populate administration page for content type
562
+ */
563
+ function admin_page() {
564
+ $prefix = $this->add_prefix('post_type_');
565
+ if ( strpos($_GET['page'], $prefix) !== 0 )
566
+ return false;
567
+
568
+ //Get action
569
+ $action = $this->util->get_action('manage');
570
+ //Get content type
571
+ $type =& $this->get_type($this->get_page_type());
572
+ global $title, $parent_file, $submenu_file;
573
+ $title = $type->get_title(true);
574
+ //$parent_file = $prefix . $type->id;
575
+ //$submenu_file = $parent_file;
576
+
577
+ switch ( $action ) {
578
+ case 'edit-item' :
579
+ case 'add' :
580
+ $this->admin_page_edit($type, $action);
581
+ break;
582
+ default :
583
+ $this->admin_page_manage($type, $action);
584
+ }
585
+ }
586
+
587
+ /**
588
+ * Queries content items for admin management pages
589
+ * Also retrieves available post status for specified content type
590
+ * @see wp_edit_posts_query
591
+ * @param CNR_Content_Type|string $type Content type instance or ID
592
+ * @return array All item statuses and Available item status
593
+ */
594
+ function admin_manage_query($type = 'post') {
595
+ global $wp_query;
596
+ $q = array();
597
+ //Get post type
598
+ if ( ! is_a($type, 'CNR_Content_Type') ) {
599
+ $type = $this->get_type($type);
600
+ }
601
+ $q = array('post_type' => $type->id);
602
+ $g = $_GET;
603
+ //Date
604
+ $q['m'] = isset($g['m']) ? (int) $g['m'] : 0;
605
+ //Category
606
+ $q['cat'] = isset($g['cat']) ? (int) $g['cat'] : 0;
607
+ $post_stati = array( // array( adj, noun )
608
+ 'publish' => array(_x('Published', 'post'), __('Published posts'), _n_noop('Published <span class="count">(%s)</span>', 'Published <span class="count">(%s)</span>')),
609
+ 'future' => array(_x('Scheduled', 'post'), __('Scheduled posts'), _n_noop('Scheduled <span class="count">(%s)</span>', 'Scheduled <span class="count">(%s)</span>')),
610
+ 'pending' => array(_x('Pending Review', 'post'), __('Pending posts'), _n_noop('Pending Review <span class="count">(%s)</span>', 'Pending Review <span class="count">(%s)</span>')),
611
+ 'draft' => array(_x('Draft', 'post'), _x('Drafts', 'manage posts header'), _n_noop('Draft <span class="count">(%s)</span>', 'Drafts <span class="count">(%s)</span>')),
612
+ 'private' => array(_x('Private', 'post'), __('Private posts'), _n_noop('Private <span class="count">(%s)</span>', 'Private <span class="count">(%s)</span>')),
613
+ 'trash' => array(_x('Trash', 'post'), __('Trash posts'), _n_noop('Trash <span class="count">(%s)</span>', 'Trash <span class="count">(%s)</span>')),
614
+ );
615
+
616
+ $post_stati = apply_filters('post_stati', $post_stati);
617
+
618
+ $avail_post_stati = get_available_post_statuses('post');
619
+
620
+ //Status
621
+ if ( isset($g['post_status']) && in_array( $g['post_status'], array_keys($post_stati) ) ) {
622
+ $q['post_status'] = $g['post_status'];
623
+ $q['perm'] = 'readable';
624
+ } else {
625
+ unset($q['post_status']);
626
+ }
627
+
628
+ //Order
629
+ if ( isset($q['post_status']) && 'pending' === $q['post_status'] ) {
630
+ $q['order'] = 'ASC';
631
+ $q['orderby'] = 'modified';
632
+ } elseif ( isset($q['post_status']) && 'draft' === $q['post_status'] ) {
633
+ $q['order'] = 'DESC';
634
+ $q['orderby'] = 'modified';
635
+ } else {
636
+ $q['order'] = 'DESC';
637
+ $q['orderby'] = 'date';
638
+ }
639
+
640
+ //Pagination
641
+ $posts_per_page = (int) get_user_option( 'edit_per_page', 0, false );
642
+ if ( empty( $posts_per_page ) || $posts_per_page < 1 )
643
+ $posts_per_page = 15;
644
+ if ( isset($g['paged']) && (int) $g['paged'] > 1 )
645
+ $q['paged'] = (int) $g['paged'];
646
+ $q['posts_per_page'] = apply_filters( 'edit_posts_per_page', $posts_per_page );
647
+ //Search
648
+ $q[s] = ( isset($g['s']) ) ? $g[s] : '';
649
+ $wp_query->query($q);
650
+
651
+ return array($post_stati, $avail_post_stati);
652
+ }
653
+
654
+ /**
655
+ * Counts the number of items in the specified content type
656
+ * @see wp_count_posts
657
+ * @param CNR_Content_Type|string $type Content Type instance or ID
658
+ * @param string $perm Permission level for items (e.g. readable)
659
+ * @return array Associative array of item counts by post status (published, draft, etc.)
660
+ */
661
+ function count_posts( $type, $perm = '' ) {
662
+ global $wpdb;
663
+
664
+ $user = wp_get_current_user();
665
+
666
+ if ( !is_a($type, 'CNR_Content_Type') )
667
+ $type = $this->get_type($type);
668
+ $type_val = $type->get_meta_value();
669
+ $type = $type->id;
670
+ $cache_key = $type;
671
+
672
+ //$query = "SELECT post_status, COUNT( * ) AS num_posts FROM {$wpdb->posts} WHERE post_type = %s";
673
+ $query = "SELECT p.post_status, COUNT( * ) as num_posts FROM {$wpdb->posts} p JOIN {$wpdb->postmeta} m ON m.post_id = p.id WHERE m.meta_key = '" . $this->get_type_meta_key() . "' AND m.meta_value = '$type_val'";
674
+ if ( 'readable' == $perm && is_user_logged_in() ) {
675
+ //TODO enable check for custom post types "read_private_{$type}s"
676
+ if ( !current_user_can("read_private_posts") ) {
677
+ $cache_key .= '_' . $perm . '_' . $user->ID;
678
+ $query .= " AND (p.post_status != 'private' OR ( p.post_author = '$user->ID' AND p.post_status = 'private' ))";
679
+ }
680
+ }
681
+ $query .= ' GROUP BY p.post_status';
682
+
683
+ $count = wp_cache_get($cache_key, 'counts');
684
+ if ( false !== $count )
685
+ return $count;
686
+
687
+ $count = $wpdb->get_results( $wpdb->prepare( $query, $type ), ARRAY_A );
688
+
689
+ $stats = array( 'publish' => 0, 'private' => 0, 'draft' => 0, 'pending' => 0, 'future' => 0, 'trash' => 0 );
690
+ foreach( (array) $count as $row_num => $row ) {
691
+ $stats[$row['post_status']] = $row['num_posts'];
692
+ }
693
+
694
+ $stats = (object) $stats;
695
+ wp_cache_set($cache_key, $stats, 'counts');
696
+
697
+ return $stats;
698
+ }
699
+
700
+ /**
701
+ * Builds management page for items of a specific custom content type
702
+ * @param CNR_Content_Type $type Content Type to manage
703
+ * @param string $action Current action
704
+ *
705
+ * @global string $title
706
+ * @global string $parent_file
707
+ * @global string $plugin_page
708
+ * @global string $page_hook
709
+ * @global WP_User $current_user
710
+ * @global WP_Query $wp_query
711
+ * @global wpdb $wpdb
712
+ * @global WP_Locale $wp_locale
713
+ */
714
+ function admin_page_manage($type, $action) {
715
+ if ( !current_user_can('edit_posts') )
716
+ wp_die(__('You do not have sufficient permissions to access this page.'));
717
+
718
+ global $title, $parent_file, $plugin_page, $page_hook, $current_user, $wp_query, $wpdb, $wp_locale;
719
+ $title = __('Edit ' . $type->get_title(true));
720
+ $admin_path = ABSPATH . 'wp-admin/';
721
+
722
+ //Pagination
723
+ if ( ! isset($_GET['paged']) )
724
+ $_GET['paged'] = 1;
725
+
726
+ $add_url = $this->get_admin_page_url($type->id, 'add');
727
+ $is_trash = isset($_GET['post_status']) && $_GET['post_status'] == 'trash';
728
+ //User posts
729
+ $user_posts = false;
730
+ if ( !current_user_can('edit_others_posts') ) {
731
+ $user_posts_count = $wpdb->get_var( $wpdb->prepare("SELECT COUNT(1) FROM $wpdb->posts p JOIN $wpdb->postmeta m ON m.post_id = p.id WHERE m.meta_key = '_cnr_post_type' AND m.meta_value = %s AND p.post_status != 'trash' AND p.post_author = %d", $type->get_meta_value(), $current_user->ID) );
732
+ $user_posts = true;
733
+ if ( $user_posts_count && empty($_GET['post_status']) && empty($_GET['all_posts']) && empty($_GET['author']) )
734
+ $_GET['author'] = $current_user->ID;
735
+ }
736
+ //Get content type items
737
+ list($post_stati, $avail_post_stati) = $this->admin_manage_query($type->id);
738
+ ?>
739
+ <div class="wrap">
740
+ <?php screen_icon('edit'); ?>
741
+ <h2><?php echo esc_html( $title ); ?> <a href="<?php echo $add_url; ?>" class="button add-new-h2"><?php echo esc_html_x('Add New', 'post'); ?></a> <?php
742
+ if ( isset($_GET['s']) && $_GET['s'] )
743
+ printf( '<span class="subtitle">' . __('Search results for &#8220;%s&#8221;') . '</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' => __('&laquo;'),
803
+ 'next_text' => __('&raquo;'),
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&#8211;%s of %s' ) . '</span>%s',
871
+ number_format_i18n( ( $_GET['paged'] - 1 ) * $wp_query->query_vars['posts_per_page'] + 1 ),
872
+ number_format_i18n( min( $_GET['paged'] * $wp_query->query_vars['posts_per_page'], $wp_query->found_posts ) ),
873
+ number_format_i18n( $wp_query->found_posts ),
874
+ $page_links
875
+ ); echo $page_links_text; ?></div>
876
+ <?php } //page links ?>
877
+ <div class="clear"></div>
878
+ </div>
879
+ <?php
880
+ include ($admin_path . 'edit-post-rows.php');
881
+ } else { //have_posts() ?>
882
+ <div class="clear"></div>
883
+ <p><?php
884
+ if ( $is_trash )
885
+ _e('No posts found in the trash', 'cornerstone');
886
+ else
887
+ _e('No posts found', 'cornerstone');
888
+ ?></p>
889
+ <?php } ?>
890
+ </form>
891
+ <?php inline_edit_row('post'); ?>
892
+ <div id="ajax-response"></div>
893
+ <br class="clear" />
894
+ </div>
895
+ <?php
896
+ }
897
+
898
+ /**
899
+ * Build admin edit page for custom type item
900
+ * @param CNR_Content_Type $type Content type being edited
901
+ * @param string $action Current action (add, edit, manage, etc.)
902
+ */
903
+ function admin_page_edit($type, $action) {
904
+ global $title, $hook_suffix, $parent_file, $screen_layout_columns, $post, $post_ID, $p;
905
+ $screen_layout_columns = 2;
906
+ //TODO Add default icon for content type
907
+ $parent_file = 'edit.php'; //Makes screen_icon() use edit icon on post edit form
908
+ switch ( $action ) {
909
+ case 'edit-item' :
910
+ $title = 'Edit';
911
+ $post = get_post_to_edit($post_ID);
912
+ break;
913
+ default :
914
+ $title = 'Add New';
915
+ $post = get_default_post_to_edit();
916
+ break;
917
+ }
918
+ $title = __($title . ' ' . $type->get_title());
919
+ $admin_path = ABSPATH . 'wp-admin/';
920
+ include ($admin_path . 'edit-form-advanced.php');
921
+ }
922
+
923
+ /**
924
+ * Adds hidden field declaring content type on post edit form
925
+ * @deprecated no longer needed for WP 3.0+
926
+ */
927
+ function admin_page_edit_form() {
928
+ global $post, $plugin_page;
929
+ if ( empty($post) || !$post->ID ) {
930
+ $type = $this->get_type($post);
931
+ if ( ! empty($type) && ! empty($type->id) ) {
932
+ ?>
933
+ <input type="hidden" name="cnr[content_type]" id="cnr[content_type]" value="<?php echo $type->id; ?>" />
934
+ <?php
935
+ }
936
+ }
937
+ }
938
+
939
+ /**
940
+ * Adds meta boxes for post's content type
941
+ * Each group in content type is a separate meta box
942
+ * @param string $type Type of item meta boxes are being build for (post, page, link)
943
+ * @param string $context Location of meta box (normal, advanced, side)
944
+ * @param object $post Post object
945
+ */
946
+ function admin_do_meta_boxes($type, $context, $post) {
947
+ //Validate $type. Should be 'post','page', or a custom post type for our purposes
948
+ if ( in_array($type, array_merge(array_keys($this->get_types()), array('post', 'page'))) ) {
949
+ //Get content type definition
950
+ $ct =& $this->get_type($post);
951
+ //Pass processing to content type instance
952
+ $ct->admin_do_meta_boxes($type, $context, $post);
953
+ }
954
+ }
955
+
956
+ /**
957
+ * Saves field data submitted for current post
958
+ * @param int $post_id ID of current post
959
+ * @param object $post Post object
960
+ */
961
+ function save_item_data($post_id, $post) {
962
+ if ( empty($post_id) || empty($post) || !isset($_POST['cnr']) || !is_array($_POST['cnr']) )
963
+ return false;
964
+ $pdata = $_POST['cnr'];
965
+
966
+ if ( isset($pdata['attributes']) && is_array($pdata['attributes']) && isset($pdata['fields_loaded']) && is_array($pdata['fields_loaded']) ) {
967
+
968
+ $prev_data = (array) $this->get_item_data($post_id);
969
+
970
+ //Remove loaded fields from prev data
971
+ $prev_data = array_diff_key($prev_data, $pdata['fields_loaded']);
972
+
973
+ //Get current field data
974
+ $curr_data = $pdata['attributes'];
975
+
976
+ //Merge arrays together (new data overwrites old data)
977
+ if ( is_array($prev_data) && is_array($curr_data) ) {
978
+ $curr_data = array_merge($prev_data, $curr_data);
979
+ }
980
+
981
+ //Save to database
982
+ update_post_meta($post_id, $this->get_fields_meta_key(), $curr_data);
983
+ }
984
+ //Save content type
985
+ if ( isset($_POST['cnr']['content_type']) ) {
986
+ $type = $_POST['cnr']['content_type'];
987
+ $saved_type = get_post_meta($post_id, $this->get_type_meta_key(), true);
988
+ if ( is_array($saved_type) )
989
+ $saved_type = implode($saved_type);
990
+ if ( $type != $saved_type ) {
991
+ //Continue processing if submitted content type is different from previously-saved content type (or no type was previously set)
992
+ update_post_meta($post_id, $this->get_type_meta_key(), array($type));
993
+ }
994
+ }
995
+ }
996
+
997
+ /*-** Helpers **-*/
998
+
999
+ /**
1000
+ * Get array of default post types
1001
+ * @return array Default post types
1002
+ */
1003
+ function get_default_post_types() {
1004
+ return array('post', 'page', 'attachment', 'revision', 'nav_menu');
1005
+ }
1006
+
1007
+ /**
1008
+ * Checks if post's post type is a standard WP post type
1009
+ * @param mixed $post_type Post type (default) or post ID/object to evaluate
1010
+ * @see CNR_Content_Utilities::get_type() for possible parameter values
1011
+ * @return bool TRUE if post is default type, FALSE if it is a custom type
1012
+ */
1013
+ function is_default_post_type($post_type) {
1014
+ if ( !is_string($post_type) ) {
1015
+ $post_type = $this->get_type($post_type);
1016
+ $post_type = $post_type->id;
1017
+ }
1018
+ return in_array($post_type, $this->get_default_post_types());
1019
+ }
1020
+
1021
+ /**
1022
+ * Checks if specified content type has been defined
1023
+ * @param string|CNR_Content_Type $type Content type ID or object
1024
+ * @return bool TRUE if content type exists, FALSE otherwise
1025
+ *
1026
+ * @uses array $cnr_content_types
1027
+ */
1028
+ function type_exists($type) {
1029
+ global $cnr_content_types;
1030
+ if ( ! is_scalar($type) ) {
1031
+ if ( is_a($type, 'CNR_Content_Type') )
1032
+ $type = $type->id;
1033
+ else
1034
+ $type = null;
1035
+ }
1036
+ return ( isset($cnr_content_types[$type]) );
1037
+ }
1038
+
1039
+ /**
1040
+ * Retrieves content type definition for specified content item (post, page, etc.)
1041
+ * If content type does not exist, a new instance object will be created and returned
1042
+ * > New content types are automatically registered (since we are looking for registered types when using this method)
1043
+ * @param string|object $item Post object, or item type (string)
1044
+ * @return CNR_Content_Type Reference to matching content type, empty content type if no matching type exists
1045
+ *
1046
+ * @uses array $cnr_content_types
1047
+ */
1048
+ function &get_type($item) {
1049
+ //Return immediately if $item is a content type instance
1050
+ if ( is_a($item, 'CNR_Content_Type') )
1051
+ return $item;
1052
+
1053
+ $type = null;
1054
+
1055
+ if ( is_string($item) )
1056
+ $type = $item;
1057
+
1058
+ if ( !$this->type_exists($type) ) {
1059
+ $post = $item;
1060
+
1061
+ //Check if $item is a post (object or ID)
1062
+ if ( $this->util->check_post($post) && isset($post->post_type) ) {
1063
+ $type = $post->post_type;
1064
+ }
1065
+ }
1066
+ global $cnr_content_types;
1067
+ if ( $this->type_exists($type) ) {
1068
+ //Retrieve content type from global array
1069
+ $type =& $cnr_content_types[$type];
1070
+ } else {
1071
+ //Create new empty content type if it does not already exist
1072
+ $type = new CNR_Content_Type($type);
1073
+ //Automatically register newly initialized content type if it extends an existing WP post type
1074
+ if ( $this->is_default_post_type($type->id) )
1075
+ $type->register();
1076
+ }
1077
+
1078
+ return $type;
1079
+ }
1080
+
1081
+ /**
1082
+ * Retrieve content types
1083
+ * @return Reference to content types array
1084
+ */
1085
+ function &get_types() {
1086
+ return $GLOBALS['cnr_content_types'];
1087
+ }
1088
+
1089
+ /**
1090
+ * Retrieve meta key for post fields
1091
+ * @return string Fields meta key
1092
+ */
1093
+ function get_fields_meta_key() {
1094
+ return $this->util->make_meta_key('fields');
1095
+ }
1096
+
1097
+ /**
1098
+ * Retrieve meta key for post type
1099
+ * @return string Post type meta key
1100
+ */
1101
+ function get_type_meta_key() {
1102
+ return $this->util->make_meta_key('post_type');
1103
+ }
1104
+
1105
+ /**
1106
+ * Checks if post contains specified field data
1107
+ * @param Object $post (optional) Post to check data for
1108
+ * @param string $field (optional) Field ID to check for
1109
+ * @return bool TRUE if data exists, FALSE otherwise
1110
+ */
1111
+ function has_item_data($item = null, $field = null) {
1112
+ $ret = $this->get_item_data($item, $field, 'raw', null);
1113
+ if ( is_scalar($ret) )
1114
+ return ( !empty($ret) || $ret === 0 );
1115
+ if ( is_array($ret) ) {
1116
+ foreach ( $ret as $key => $val ) {
1117
+ if ( !empty($val) || $val === 0 )
1118
+ return true;
1119
+ }
1120
+ }
1121
+ return false;
1122
+ }
1123
+
1124
+ /**
1125
+ * Retrieve specified field data from content item (e.g. post)
1126
+ * Usage Examples:
1127
+ * get_item_data($post_id, 'field_id')
1128
+ * - Retrieves field_id data from global $post object
1129
+ * - Field data is formatted using 'display' layout of field
1130
+ *
1131
+ * get_item_data($post_id, 'field_id', 'raw')
1132
+ * - Retrieves field_id data from global $post object
1133
+ * - Raw field data is returned (no formatting)
1134
+ *
1135
+ * get_item_data($post_id, 'field_id', 'display', $post_id)
1136
+ * - Retrieves field_id data from post matching $post_id
1137
+ * - Field data is formatted using 'display' layout of field
1138
+ *
1139
+ * get_item_data($post_id, 'field_id', null)
1140
+ * - Retrieves field_id data from post matching $post_id
1141
+ * - Field data is formatted using 'display' layout of field
1142
+ * - The default layout is used when no valid layout is specified
1143
+ *
1144
+ * get_item_data($post_id)
1145
+ * - Retrieves full data array from post matching $post_id
1146
+ *
1147
+ * @param int|object $item(optional) Content item to retrieve field from (Default: null - global $post object will be used)
1148
+ * @param string $field ID of field to retrieve
1149
+ * @param string $layout(optional) Layout to use when returning field data (Default: display)
1150
+ * @param array $attr (optional) Additional attributes to pass along to field object (e.g. for building layout, etc.)
1151
+ * @see CNR_Field_Type::build_layout for more information on attribute usage
1152
+ * @return mixed Specified field data
1153
+ */
1154
+ function get_item_data($item = null, $field = null, $layout = null, $default = '', $attr = null) {
1155
+ $ret = $default;
1156
+
1157
+ //Get item
1158
+ $item = get_post($item);
1159
+
1160
+ if ( !isset($item->ID) )
1161
+ return $ret;
1162
+
1163
+ //Get item data
1164
+ $data = get_post_meta($item->ID, $this->get_fields_meta_key(), true);
1165
+
1166
+ //Get field data
1167
+
1168
+ //Set return value to data if no field specified
1169
+ if ( empty($field) || !is_string($field) )
1170
+ $ret = $data;
1171
+ //Stop if no valid field specified
1172
+ if ( !isset($data[$field]) ) {
1173
+ //TODO Check $item object to see if specified field exists (e.g. title, post_status, etc.)
1174
+ return $ret;
1175
+ }
1176
+
1177
+ $ret = $data[$field];
1178
+
1179
+ //Initialize layout value
1180
+ $layout_def = 'display';
1181
+
1182
+ if ( !is_scalar($layout) || empty($layout) )
1183
+ $layout = $layout_def;
1184
+
1185
+ $layout = strtolower($layout);
1186
+
1187
+ //Check if raw data requested
1188
+ if ( 'raw' == $layout )
1189
+ return $ret;
1190
+
1191
+ /* Build specified layout */
1192
+
1193
+ //Get item's content type
1194
+ $ct =& $this->get_type($item);
1195
+ $ct->set_data($data);
1196
+
1197
+ //Get field definition
1198
+ $fdef =& $ct->get_field($field);
1199
+
1200
+ //Validate layout
1201
+ if ( !$fdef->has_layout($layout) )
1202
+ $layout = $layout_def;
1203
+
1204
+ //Build layout
1205
+ $fdef->set_caller($ct);
1206
+ $ret = $fdef->build_layout($layout, $attr);
1207
+ $fdef->clear_caller();
1208
+
1209
+ //Return formatted value
1210
+ return $ret;
1211
+ }
1212
+
1213
+ /**
1214
+ * Prints an item's field data
1215
+ * @see CNR_Content_Utilities::get_item_data() for more information
1216
+ * @param int|object $item(optional) Content item to retrieve field from (Default: null - global $post object will be used)
1217
+ * @param string $field ID of field to retrieve
1218
+ * @param string $layout(optional) Layout to use when returning field data (Default: display)
1219
+ * @param mixed $default (optional) Default value to return in case of errors, etc.
1220
+ * @param array $attr Additional attributes to pass to field
1221
+ */
1222
+ function the_item_data($item = null, $field = null, $layout = null, $default = '', $attr = null) {
1223
+ echo apply_filters('cnr_the_item_data', $this->get_item_data($item, $field, $layout, $default, $attr), $item, $field, $layout, $default, $attr);
1224
+ }
1225
+
1226
+ /**
1227
+ * Build Admin URL for specified post type
1228
+ * @param string|CNR_Content_Type $type Content type ID or object
1229
+ * @param string $action Action to build URL for
1230
+ * @param bool $sep_action Whether action should be a separate query variable (Default: false)
1231
+ * @return string Admin page URL
1232
+ */
1233
+ function get_admin_page_url($type, $action = '', $sep_action = false) {
1234
+ $url = admin_url('admin.php');
1235
+ $url .= '?page=' . $this->get_admin_page_file($type, $action, $sep_action);
1236
+ return $url;
1237
+ }
1238
+
1239
+ function get_edit_item_url($edit_url, $item_id, $context) {
1240
+ //Get post type
1241
+ $type = $this->get_type($item_id);
1242
+ if ( ! $this->is_default_post_type($type->id) && $this->type_exists($type) ) {
1243
+ $edit_url = $this->get_admin_page_url($type, 'edit-item', true) . '&post=' . $item_id;
1244
+ }
1245
+
1246
+ return $edit_url;
1247
+ }
1248
  }
includes/class.field.php CHANGED
@@ -1,4 +1,4 @@
1
- <?php
2
- class CNR_Field extends CNR_Field_Type {
3
-
4
  }
1
+ <?php
2
+ class CNR_Field extends CNR_Field_Type {
3
+
4
  }
includes/class.field_type.php CHANGED
@@ -1,698 +1,698 @@
1
- <?php
2
- /**
3
- * Content Type - Field Types
4
- * Stores properties for a specific field
5
- * @package Cornerstone
6
- * @subpackage Content Types
7
- * @author Archetyped
8
- */
9
- class CNR_Field_Type extends CNR_Content_Base {
10
- /* Properties */
11
-
12
- const USES_DATA = '{data}';
13
-
14
- /**
15
- * Base class name
16
- * @var string
17
- */
18
- var $base_class = 'cnr_field_type';
19
-
20
- /**
21
- * @var array Array of Field types that make up current Field type
22
- */
23
- var $elements = array();
24
-
25
- /**
26
- * Structure: Property names stored as keys in group
27
- * Root
28
- * -> Group Name
29
- * -> Property Name => Null
30
- * Reason: Faster searching over large arrays
31
- * @var array Groupings of Properties
32
- */
33
- var $property_groups = array();
34
-
35
- /**
36
- * @var array Field type layouts
37
- */
38
- var $layout = array();
39
-
40
- /**
41
- * @var CNR_Field_Type Parent field type (reference)
42
- */
43
- var $parent = null;
44
-
45
- /**
46
- * Object that field is in
47
- * @var CNR_Field|CNR_Field_Type|CNR_Content_Type
48
- */
49
- var $container = null;
50
-
51
- /**
52
- * Object that called field
53
- * Used to determine field hierarchy/nesting
54
- * @var CNR_Field|CNR_Field_Type|CNR_Content_Type
55
- */
56
- var $caller = null;
57
-
58
- /**
59
- * Constructor
60
- */
61
- function __construct($id = '', $parent = null) {
62
- parent::__construct($id);
63
-
64
- $this->id = $id;
65
- $this->set_parent($parent);
66
- }
67
-
68
- /* Getters/Setters */
69
-
70
- /**
71
- * Search for specified member value in field's container object (if exists)
72
- * @param string $member Name of object member to search (e.g. properties, layout, etc.)
73
- * @param string $name Value to retrieve from member
74
- * @return mixed Member value if found (Default: empty string)
75
- */
76
- function get_container_value($member, $name = '', $default = '') {
77
- $container =& $this->get_container();
78
- return $this->get_object_value($container, $member, $name, $default, 'container');
79
- }
80
-
81
- /**
82
- * Search for specified member value in field's container object (if exists)
83
- * @param string $member Name of object member to search (e.g. properties, layout, etc.)
84
- * @param string $name Value to retrieve from member
85
- * @return mixed Member value if found (Default: empty string)
86
- */
87
- function get_caller_value($member, $name = '', $default = '') {
88
- $caller =& $this->get_caller();
89
- return $this->get_object_value($caller, $member, $name, $default, 'caller');
90
- }
91
-
92
- /**
93
- * Sets reference to container object of current field
94
- * Reference is cleared if no valid object is passed to method
95
- * @param object $container
96
- */
97
- function set_container(&$container) {
98
- if ( !empty($container) && is_object($container) ) {
99
- //Set as param as container for current field
100
- $this->container =& $container;
101
- } else {
102
- //Clear container member if argument is invalid
103
- $this->clear_container();
104
- }
105
- }
106
-
107
- /**
108
- * Clears reference to container object of current field
109
- */
110
- function clear_container() {
111
- $this->container = null;
112
- }
113
-
114
- /**
115
- * Retrieves reference to container object of current field
116
- * @return object Reference to container object
117
- */
118
- function &get_container() {
119
- $ret = null;
120
- if ( $this->has_container() )
121
- $ret =& $this->container;
122
- return $ret;
123
- }
124
-
125
- /**
126
- * Checks if field has a container reference
127
- * @return bool TRUE if field is contained, FALSE otherwise
128
- */
129
- function has_container() {
130
- return !empty($this->container);
131
- }
132
-
133
- /**
134
- * Sets reference to calling object of current field
135
- * Any existing reference is cleared if no valid object is passed to method
136
- * @param object $caller Calling object
137
- */
138
- function set_caller(&$caller) {
139
- if ( !empty($caller) && is_object($caller) )
140
- $this->caller =& $caller;
141
- else
142
- $this->clear_caller();
143
- }
144
-
145
- /**
146
- * Clears reference to calling object of current field
147
- */
148
- function clear_caller() {
149
- unset($this->caller);
150
- }
151
-
152
- /**
153
- * Retrieves reference to caller object of current field
154
- * @return object Reference to caller object
155
- */
156
- function &get_caller() {
157
- $ret = null;
158
- if ( $this->has_caller() )
159
- $ret =& $this->caller;
160
- return $ret;
161
- }
162
-
163
- /**
164
- * Checks if field has a caller reference
165
- * @return bool TRUE if field is called by another field, FALSE otherwise
166
- */
167
- function has_caller() {
168
- return !empty($this->caller);
169
- }
170
-
171
- /**
172
- * Add/Set a property on the field definition
173
- * @param string $name Name of property
174
- * @param mixed $value Default value for property
175
- * @param string|array $group Group(s) property belongs to
176
- * @return boolean TRUE if property is successfully added to field type, FALSE otherwise
177
- */
178
- function set_property($name, $value = '', $group = null) {
179
- //Do not add if property name is not a string
180
- if ( !is_string($name) )
181
- return false;
182
- //Create property array
183
- $prop_arr = array();
184
- $prop_arr['value'] = $value;
185
- //Add to properties array
186
- $this->properties[$name] = $value;
187
- //Add property to specified groups
188
- if ( !empty($group) ) {
189
- $this->set_group_property($group, $name);
190
- }
191
- return true;
192
- }
193
-
194
- /**
195
- * Sets multiple properties on field type at once
196
- * @param array $properties Properties. Each element is an array containing the arguments to set a new property
197
- * @return boolean TRUE if successful, FALSE otherwise
198
- */
199
- function set_properties($properties) {
200
- if ( !is_array($properties) )
201
- return false;
202
- foreach ( $properties as $name => $val) {
203
- $this->set_property($name, $val);
204
- }
205
- }
206
-
207
- /**
208
- * Retreives property from field type
209
- * @param string $name Name of property to retrieve
210
- * @return mixed Specified Property if exists (Default: Empty string)
211
- */
212
- function get_property($name) {
213
- $val = $this->get_member_value('properties', $name);
214
- return $val;
215
- }
216
-
217
- /**
218
- * Adds Specified Property to a Group
219
- * @param string|array $group Group(s) to add property to
220
- * @param string $property Property to add to group
221
- */
222
- function set_group_property($group, $property) {
223
- if ( is_string($group) && isset($this->property_groups[$group][$property]) )
224
- return;
225
- if ( !is_array($group) ) {
226
- $group = array($group);
227
- }
228
-
229
- foreach ($group as $g) {
230
- $g = trim($g);
231
- //Initialize group if it doesn't already exist
232
- if ( !isset($this->property_groups[$g]) )
233
- $this->property_groups[$g] = array();
234
-
235
- //Add property to group
236
- $this->property_groups[$g][$property] = null;
237
- }
238
- }
239
-
240
- /**
241
- * Retrieve property group
242
- * @param string $group Group to retrieve
243
- * @return array Array of properties in specified group
244
- */
245
- function get_group($group) {
246
- return $this->get_member_value('property_groups', $group, array());
247
- }
248
-
249
- /**
250
- * Sets an element for the field type
251
- * @param string $name Name of element
252
- * @param CNR_Field_Type $type Reference of field type to use for element
253
- * @param array $properties Properties for element (passed as keyed associative array)
254
- * @param string $id_prop Name of property to set $name to (e.g. ID, etc.)
255
- */
256
- function set_element($name, $type, $properties = array(), $id_prop = 'id') {
257
- $name = trim(strval($name));
258
- if ( empty($name) )
259
- return false;
260
- //Create new field for element
261
- $el = new CNR_Field($name, $type);
262
- //Set container to current field instance
263
- $el->set_container($this);
264
- //Add properties to element
265
- $el->set_properties($properties);
266
- //Save element to current instance
267
- $this->elements[$name] =& $el;
268
- }
269
-
270
- /**
271
- * Add a layout to the field
272
- * @param string $name Name of layout
273
- * @param string $value Layout text
274
- */
275
- function set_layout($name, $value = '') {
276
- if ( !is_string($name) )
277
- return false;
278
- $name = trim($name);
279
- $this->layout[$name] = $value;
280
- return true;
281
- }
282
-
283
- /**
284
- * Retrieve specified layout
285
- * @param string $name Layout name
286
- * @param bool $parse_nested (optional) Whether nested layouts should be expanded in retreived layout or not (Default: TRUE)
287
- * @return string Specified layout text
288
- */
289
- function get_layout($name = 'form', $parse_nested = true) {
290
- //Retrieve specified layout (use $name value if no layout by that name exists)
291
- $layout = $this->get_member_value('layout', $name, $name);
292
-
293
- //Find all nested layouts in current layout
294
- if ( !empty($layout) && !!$parse_nested ) {
295
- $ph = $this->get_placeholder_defaults();
296
-
297
- while ($ph->match = $this->parse_layout($layout, $ph->pattern_layout)) {
298
- //Iterate through the different types of layout placeholders
299
- foreach ($ph->match as $tag => $instances) {
300
- //Iterate through instances of a specific type of layout placeholder
301
- foreach ($instances as $instance) {
302
- //Get nested layout
303
- $nested_layout = $this->get_member_value($instance);
304
-
305
- //Replace layout placeholder with retrieved item data
306
- if ( !empty($nested_layout) )
307
- $layout = str_replace($ph->start . $instance['match'] . $ph->end, $nested_layout, $layout);
308
- }
309
- }
310
- }
311
- }
312
-
313
- return $layout;
314
- }
315
-
316
- /**
317
- * Checks if specified layout exists
318
- * Finds layout if it exists in current object or any of its parents
319
- * @param string $layout Name of layout to check for
320
- * @return bool TRUE if layout exists, FALSE otherwise
321
- */
322
- function has_layout($layout) {
323
- $ret = false;
324
- if ( is_string($layout) && ($layout = trim($layout)) && !empty($layout) ) {
325
- $layout = $this->get_member_value('layout', $layout, false);
326
- if ( $layout !== false )
327
- $ret = true;
328
- }
329
-
330
- return $ret;
331
- }
332
-
333
- /**
334
- * Checks if layout content is valid
335
- * Layouts need to have placeholders to be valid
336
- * @param string $layout_content Layout content (markup)
337
- * @return bool TRUE if layout is valid, FALSE otherwise
338
- */
339
- function is_valid_layout($layout_content) {
340
- $ph = $this->get_placeholder_defaults();
341
- return preg_match($ph->pattern_general, $layout_content);
342
- }
343
-
344
- /**
345
- * Parse field layout with a regular expression
346
- * @param string $layout Layout data
347
- * @param string $search Regular expression pattern to search layout for
348
- * @return array Associative array containing all of the regular expression matches in the layout data
349
- * Array Structure:
350
- * root => placeholder tags
351
- * => Tag instances (array)
352
- * 'tag' => (string) tag name
353
- * 'match' => (string) placeholder match
354
- * 'attributes' => (array) attributes
355
- */
356
- function parse_layout($layout, $search) {
357
- $ph_xml = '';
358
- $parse_match = '';
359
- $ph_root_tag = 'ph_root_element';
360
- $ph_start_xml = '<';
361
- $ph_end_xml = ' />';
362
- $ph_wrap_start = '<' . $ph_root_tag . '>';
363
- $ph_wrap_end = '</' . $ph_root_tag . '>';
364
- $parse_result = false;
365
-
366
- //Find all nested layouts in layout
367
- $match_value = preg_match_all($search, $layout, $parse_match, PREG_PATTERN_ORDER);
368
-
369
- if ($match_value !== false && $match_value > 0) {
370
- $parse_result = array();
371
- //Get all matched elements
372
- $parse_match = $parse_match[1];
373
-
374
- //Build XML string from placeholders
375
- foreach ($parse_match as $ph) {
376
- $ph_xml .= $ph_start_xml . $ph . $ph_end_xml . ' ';
377
- }
378
- $ph_xml = $ph_wrap_start . $ph_xml . $ph_wrap_end;
379
- //Parse XML data
380
- $ph_prs = xml_parser_create();
381
- xml_parser_set_option($ph_prs, XML_OPTION_SKIP_WHITE, 1);
382
- xml_parser_set_option($ph_prs, XML_OPTION_CASE_FOLDING, 0);
383
- $ret = xml_parse_into_struct($ph_prs, $ph_xml, $parse_result['values'], $parse_result['index']);
384
- xml_parser_free($ph_prs);
385
-
386
- //Build structured array with all parsed data
387
-
388
- unset($parse_result['index'][$ph_root_tag]);
389
-
390
- //Build structured array
391
- $result = array();
392
- foreach ($parse_result['index'] as $tag => $instances) {
393
- $result[$tag] = array();
394
- //Instances
395
- foreach ($instances as $instance) {
396
- //Skip instance if it doesn't exist in parse results
397
- if (!isset($parse_result['values'][$instance]))
398
- continue;
399
-
400
- //Stop processing instance if a previously-saved instance with the same options already exists
401
- foreach ($result[$tag] as $tag_match) {
402
- if ($tag_match['match'] == $parse_match[$instance - 1])
403
- continue 2;
404
- }
405
-
406
- //Init instance data array
407
- $inst_data = array();
408
-
409
- //Add Tag to array
410
- $inst_data['tag'] = $parse_result['values'][$instance]['tag'];
411
-
412
- //Add instance data to array
413
- $inst_data['attributes'] = (isset($parse_result['values'][$instance]['attributes'])) ? $inst_data['attributes'] = $parse_result['values'][$instance]['attributes'] : '';
414
-
415
- //Add match to array
416
- $inst_data['match'] = $parse_match[$instance - 1];
417
-
418
- //Add to result array
419
- $result[$tag][] = $inst_data;
420
- }
421
- }
422
- $parse_result = $result;
423
- }
424
-
425
- return $parse_result;
426
- }
427
-
428
- /**
429
- * Retrieves default properties to use when evaluating layout placeholders
430
- * @return object Object with properties for evaluating layout placeholders
431
- */
432
- function get_placeholder_defaults() {
433
- $ph = new stdClass();
434
- $ph->start = '{';
435
- $ph->end = '}';
436
- $ph->reserved = array('ref' => 'ref_base');
437
- $ph->pattern_general = '/' . $ph->start . '([a-zA-Z0-9_].*?)' . $ph->end . '/i';
438
- $ph->pattern_layout = '/' . $ph->start . '([a-zA-Z0-9].*?\s+' . $ph->reserved['ref'] . '="layout.*?".*?)' . $ph->end . '/i';
439
- return $ph;
440
- }
441
-
442
- /**
443
- * Builds HTML for a field based on its properties
444
- * @param array $field Field properties (id, field, etc.)
445
- * @param array data Additional data for current field
446
- */
447
- function build_layout($layout = 'form', $data = null) {
448
- $out_default = '';
449
-
450
- /* Layout */
451
-
452
- //Get base layout
453
- $out = $this->get_layout($layout);
454
-
455
- //Only parse valid layouts
456
- if ( $this->is_valid_layout($out) ) {
457
- //Parse Layout
458
- $ph = $this->get_placeholder_defaults();
459
-
460
- //Search layout for placeholders
461
- while ( $ph->match = $this->parse_layout($out, $ph->pattern_general) ) {
462
- //Iterate through placeholders (tag, id, etc.)
463
- foreach ( $ph->match as $tag => $instances ) {
464
- //Iterate through instances of current placeholder
465
- foreach ( $instances as $instance ) {
466
- //Process value based on placeholder name
467
- $target_property = apply_filters('cnr_process_placeholder_' . $tag, '', $this, $instance, $layout, $data);
468
-
469
- //Process value using default processors (if necessary)
470
- if ( '' == $target_property ) {
471
- $target_property = apply_filters('cnr_process_placeholder', $target_property, $this, $instance, $layout, $data);
472
- }
473
-
474
- //Clear value if value not a string
475
- if ( !is_scalar($target_property) ) {
476
- $target_property = '';
477
- }
478
- //Replace layout placeholder with retrieved item data
479
- $out = str_replace($ph->start . $instance['match'] . $ph->end, $target_property, $out);
480
- }
481
- }
482
- }
483
- } else {
484
- $out = $out_default;
485
- }
486
-
487
- /* Return generated value */
488
-
489
- return $out;
490
- }
491
-
492
- /*-** Static Methods **-*/
493
-
494
- /**
495
- * Returns indacator to use field data (in layouts, property values, etc.)
496
- */
497
- function uses_data() {
498
- return self::USES_DATA;
499
- }
500
-
501
- /**
502
- * Register a function to handle a placeholder
503
- * Multiple handlers may be registered for a single placeholder
504
- * Basically a wrapper function to facilitate adding hooks for placeholder processing
505
- * @uses add_filter()
506
- * @param string $placeholder Name of placeholder to add handler for (Using 'all' will set the function as a handler for all placeholders
507
- * @param callback $handler Function to set as a handler
508
- * @param int $priority (optional) Priority of handler
509
- */
510
- static function register_placeholder_handler($placeholder, $handler, $priority = 10) {
511
- if ( 'all' == $placeholder )
512
- $placeholder = '';
513
- else
514
- $placeholder = '_' . $placeholder;
515
-
516
- add_filter('cnr_process_placeholder' . $placeholder, $handler, $priority, 5);
517
- }
518
-
519
- /**
520
- * Default placeholder processing
521
- * To be executed when current placeholder has not been handled by another handler
522
- * @param string $ph_output Value to be used in place of placeholder
523
- * @param CNR_Field $field Field containing placeholder
524
- * @param array $placeholder Current placeholder
525
- * @see CNR_Field::parse_layout for structure of $placeholder array
526
- * @param string $layout Layout to build
527
- * @param array $data Extended data for field
528
- * @return string Value to use in place of current placeholder
529
- */
530
- static function process_placeholder_default($ph_output, $field, $placeholder, $layout, $data) {
531
- //Validate parameters before processing
532
- if ( empty($ph_output) && is_a($field, 'CNR_Field_Type') && is_array($placeholder) ) {
533
- //Build path to replacement data
534
- $ph_output = $field->get_member_value($placeholder);
535
-
536
- //Check if value is group (properties, etc.)
537
- //All groups must have additional attributes (beyond reserved attributes) that define how items in group are used
538
- if (is_array($ph_output)
539
- && !empty($placeholder['attributes'])
540
- && is_array($placeholder['attributes'])
541
- && ($ph = $field->get_placeholder_defaults())
542
- && $attribs = array_diff(array_keys($placeholder['attributes']), array_values($ph->reserved))
543
- ) {
544
- /* Targeted property is an array, but the placeholder contains additional options on how property is to be used */
545
-
546
- //Find items matching criteria in $ph_output
547
- //Check for group criteria
548
- //TODO: Implement more robust/flexible criteria handling (2010-03-11: Currently only processes property groups)
549
- if ( 'properties' == $placeholder['tag'] && ($prop_group = $field->get_group($placeholder['attributes']['group'])) && !empty($prop_group) ) {
550
- /* Process group */
551
- $group_out = array();
552
- //Iterate through properties in group and build string
553
- foreach ( $prop_group as $prop_key => $prop_val ) {
554
- $group_out[] = $prop_key . '="' . $field->get_property($prop_key) . '"';
555
- }
556
- $ph_output = implode(' ', $group_out);
557
- }
558
- } elseif ( is_object($ph_output) && is_a($ph_output, $field->base_class) ) {
559
- /* Targeted property is actually a nested field */
560
- //Set caller to current field
561
- $ph_output->set_caller($field);
562
- //Build layout for nested element
563
- $ph_output = $ph_output->build_layout($layout);
564
- }
565
- }
566
-
567
- return $ph_output;
568
- }
569
-
570
- /**
571
- * Build Field ID attribute
572
- * @see CNR_Field_Type::process_placeholder_default for parameter descriptions
573
- * @return string Placeholder output
574
- */
575
- static function process_placeholder_id($ph_output, $field, $placeholder, $layout, $data) {
576
- //Get attributes
577
- $args = wp_parse_args($placeholder['attributes'], array('format' => 'attr_id'));
578
- return $field->get_id($args);
579
- }
580
-
581
- /**
582
- * Build Field name attribute
583
- * Name is formatted as an associative array for processing by PHP after submission
584
- * @see CNR_Field_Type::process_placeholder_default for parameter descriptions
585
- * @return string Placeholder output
586
- */
587
- static function process_placeholder_name($ph_output, $field, $placeholder, $layout, $data) {
588
- //Get attributes
589
- $args = wp_parse_args($placeholder['attributes'], array('format' => 'default'));
590
- return $field->get_id($args);
591
- }
592
-
593
- /**
594
- * Retrieve data for field
595
- * @see CNR_Field_Type::process_placeholder_default for parameter descriptions
596
- * @return string Placeholder output
597
- */
598
- static function process_placeholder_data($ph_output, $field, $placeholder, $layout) {
599
- $val = $field->get_data();
600
- if ( !is_null($val) ) {
601
- $ph_output = $val;
602
- $attr =& $placeholder['attributes'];
603
- //Get specific member in value (e.g. value from a specific field element)
604
- if ( isset($attr['element']) && is_array($ph_output) && ( $el = $attr['element'] ) && isset($ph_output[$el]) )
605
- $ph_output = $ph_output[$el];
606
- if ( isset($attr['format']) && 'display' == $attr['format'] )
607
- $ph_output = nl2br($ph_output);
608
- }
609
-
610
- //Return data
611
- return $ph_output;
612
- }
613
-
614
- /**
615
- * Loops over data to build field output
616
- * Options:
617
- * data - Dot-delimited path in field that contains data to loop through
618
- * layout - Name of layout to use for each data item in loop
619
- * layout_data - Name of layout to use for data item that matches previously-saved field data
620
- * @see CNR_Field_Type::process_placeholder_default for parameter descriptions
621
- * @return string Placeholder output
622
- */
623
- static function process_placeholder_loop($ph_output, $field, $placeholder, $layout, $data) {
624
- //Setup loop options
625
- $attr_defaults = array (
626
- 'layout' => '',
627
- 'layout_data' => null,
628
- 'data' => ''
629
- );
630
-
631
- $attr = wp_parse_args($placeholder['attributes'], $attr_defaults);
632
-
633
- if ( is_null($attr['layout_data']) ) {
634
- $attr['layout_data'] =& $attr['layout'];
635
- }
636
-
637
- //Get data for loop
638
- $path = explode('.', $attr['data']);
639
- $loop_data = $field->get_member_value($path);
640
- /*if ( isset($loop_data['value']) )
641
- $loop_data = $loop_data['value'];
642
- */
643
- $out = array();
644
-
645
- //Get field data
646
- $data = $field->get_data();
647
-
648
- //Iterate over data and build output
649
- if ( is_array($loop_data) && !empty($loop_data) ) {
650
- foreach ( $loop_data as $value => $label ) {
651
- //Load appropriate layout based on field value
652
- $layout = ( ($data === 0 && $value === $data) xor $data == $value ) ? $attr['layout_data'] : $attr['layout'];
653
- //Stop processing if no valid layout is returned
654
- if ( empty($layout) )
655
- continue;
656
- //Prep extended field data
657
- $data_ext = array('option_value' => $value, 'option_text' => $label);
658
- $out[] = $field->build_layout($layout, $data_ext);
659
- }
660
- }
661
-
662
- //Return output
663
- return implode($out);
664
- }
665
-
666
- /**
667
- * Returns specified value from extended data array for field
668
- * @see CNR_Field_Type::process_placeholder_default for parameter descriptions
669
- * @return string Placeholder output
670
- */
671
- static function process_placeholder_data_ext($ph_output, $field, $placeholder, $layout, $data) {
672
- if ( isset($placeholder['attributes']['id']) && ($key = $placeholder['attributes']['id']) && isset($data[$key]) ) {
673
- $ph_output = strval($data[$key]);
674
- }
675
-
676
- return $ph_output;
677
- }
678
-
679
- /**
680
- * WP Editor
681
- * @see CNR_Field_Type::process_placeholder_default for parameter descriptions
682
- * @return string Placeholder output
683
- */
684
- static function process_placeholder_rich_editor($ph_output, $field, $placeholder, $layout, $data) {
685
- $id = $field->get_id( array (
686
- 'format' => 'attr_id'
687
- ));
688
- $settings = array (
689
- 'textarea_name' => $field->get_id( array (
690
- 'format' => 'default'
691
- ))
692
- );
693
- ob_start();
694
- wp_editor($field->get_data(), $id, $settings);
695
- $out = ob_get_clean();
696
- return $out;
697
- }
698
  }
1
+ <?php
2
+ /**
3
+ * Content Type - Field Types
4
+ * Stores properties for a specific field
5
+ * @package Cornerstone
6
+ * @subpackage Content Types
7
+ * @author Archetyped
8
+ */
9
+ class CNR_Field_Type extends CNR_Content_Base {
10
+ /* Properties */
11
+
12
+ const USES_DATA = '{data}';
13
+
14
+ /**
15
+ * Base class name
16
+ * @var string
17
+ */
18
+ var $base_class = 'cnr_field_type';
19
+
20
+ /**
21
+ * @var array Array of Field types that make up current Field type
22
+ */
23
+ var $elements = array();
24
+
25
+ /**
26
+ * Structure: Property names stored as keys in group
27
+ * Root
28
+ * -> Group Name
29
+ * -> Property Name => Null
30
+ * Reason: Faster searching over large arrays
31
+ * @var array Groupings of Properties
32
+ */
33
+ var $property_groups = array();
34
+
35
+ /**
36
+ * @var array Field type layouts
37
+ */
38
+ var $layout = array();
39
+
40
+ /**
41
+ * @var CNR_Field_Type Parent field type (reference)
42
+ */
43
+ var $parent = null;
44
+
45
+ /**
46
+ * Object that field is in
47
+ * @var CNR_Field|CNR_Field_Type|CNR_Content_Type
48
+ */
49
+ var $container = null;
50
+
51
+ /**
52
+ * Object that called field
53
+ * Used to determine field hierarchy/nesting
54
+ * @var CNR_Field|CNR_Field_Type|CNR_Content_Type
55
+ */
56
+ var $caller = null;
57
+
58
+ /**
59
+ * Constructor
60
+ */
61
+ function __construct($id = '', $parent = null) {
62
+ parent::__construct($id);
63
+
64
+ $this->id = $id;
65
+ $this->set_parent($parent);
66
+ }
67
+
68
+ /* Getters/Setters */
69
+
70
+ /**
71
+ * Search for specified member value in field's container object (if exists)
72
+ * @param string $member Name of object member to search (e.g. properties, layout, etc.)
73
+ * @param string $name Value to retrieve from member
74
+ * @return mixed Member value if found (Default: empty string)
75
+ */
76
+ function get_container_value($member, $name = '', $default = '') {
77
+ $container =& $this->get_container();
78
+ return $this->get_object_value($container, $member, $name, $default, 'container');
79
+ }
80
+
81
+ /**
82
+ * Search for specified member value in field's container object (if exists)
83
+ * @param string $member Name of object member to search (e.g. properties, layout, etc.)
84
+ * @param string $name Value to retrieve from member
85
+ * @return mixed Member value if found (Default: empty string)
86
+ */
87
+ function get_caller_value($member, $name = '', $default = '') {
88
+ $caller =& $this->get_caller();
89
+ return $this->get_object_value($caller, $member, $name, $default, 'caller');
90
+ }
91
+
92
+ /**
93
+ * Sets reference to container object of current field
94
+ * Reference is cleared if no valid object is passed to method
95
+ * @param object $container
96
+ */
97
+ function set_container(&$container) {
98
+ if ( !empty($container) && is_object($container) ) {
99
+ //Set as param as container for current field
100
+ $this->container =& $container;
101
+ } else {
102
+ //Clear container member if argument is invalid
103
+ $this->clear_container();
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Clears reference to container object of current field
109
+ */
110
+ function clear_container() {
111
+ $this->container = null;
112
+ }
113
+
114
+ /**
115
+ * Retrieves reference to container object of current field
116
+ * @return object Reference to container object
117
+ */
118
+ function &get_container() {
119
+ $ret = null;
120
+ if ( $this->has_container() )
121
+ $ret =& $this->container;
122
+ return $ret;
123
+ }
124
+
125
+ /**
126
+ * Checks if field has a container reference
127
+ * @return bool TRUE if field is contained, FALSE otherwise
128
+ */
129
+ function has_container() {
130
+ return !empty($this->container);
131
+ }
132
+
133
+ /**
134
+ * Sets reference to calling object of current field
135
+ * Any existing reference is cleared if no valid object is passed to method
136
+ * @param object $caller Calling object
137
+ */
138
+ function set_caller(&$caller) {
139
+ if ( !empty($caller) && is_object($caller) )
140
+ $this->caller =& $caller;
141
+ else
142
+ $this->clear_caller();
143
+ }
144
+
145
+ /**
146
+ * Clears reference to calling object of current field
147
+ */
148
+ function clear_caller() {
149
+ unset($this->caller);
150
+ }
151
+
152
+ /**
153
+ * Retrieves reference to caller object of current field
154
+ * @return object Reference to caller object
155
+ */
156
+ function &get_caller() {
157
+ $ret = null;
158
+ if ( $this->has_caller() )
159
+ $ret =& $this->caller;
160
+ return $ret;
161
+ }
162
+
163
+ /**
164
+ * Checks if field has a caller reference
165
+ * @return bool TRUE if field is called by another field, FALSE otherwise
166
+ */
167
+ function has_caller() {
168
+ return !empty($this->caller);
169
+ }
170
+
171
+ /**
172
+ * Add/Set a property on the field definition
173
+ * @param string $name Name of property
174
+ * @param mixed $value Default value for property
175
+ * @param string|array $group Group(s) property belongs to
176
+ * @return boolean TRUE if property is successfully added to field type, FALSE otherwise
177
+ */
178
+ function set_property($name, $value = '', $group = null) {
179
+ //Do not add if property name is not a string
180
+ if ( !is_string($name) )
181
+ return false;
182
+ //Create property array
183
+ $prop_arr = array();
184
+ $prop_arr['value'] = $value;
185
+ //Add to properties array
186
+ $this->properties[$name] = $value;
187
+ //Add property to specified groups
188
+ if ( !empty($group) ) {
189
+ $this->set_group_property($group, $name);
190
+ }
191
+ return true;
192
+ }
193
+
194
+ /**
195
+ * Sets multiple properties on field type at once
196
+ * @param array $properties Properties. Each element is an array containing the arguments to set a new property
197
+ * @return boolean TRUE if successful, FALSE otherwise
198
+ */
199
+ function set_properties($properties) {
200
+ if ( !is_array($properties) )
201
+ return false;
202
+ foreach ( $properties as $name => $val) {
203
+ $this->set_property($name, $val);
204
+ }
205
+ }
206
+
207
+ /**
208
+ * Retreives property from field type
209
+ * @param string $name Name of property to retrieve
210
+ * @return mixed Specified Property if exists (Default: Empty string)
211
+ */
212
+ function get_property($name) {
213
+ $val = $this->get_member_value('properties', $name);
214
+ return $val;
215
+ }
216
+
217
+ /**
218
+ * Adds Specified Property to a Group
219
+ * @param string|array $group Group(s) to add property to
220
+ * @param string $property Property to add to group
221
+ */
222
+ function set_group_property($group, $property) {
223
+ if ( is_string($group) && isset($this->property_groups[$group][$property]) )
224
+ return;
225
+ if ( !is_array($group) ) {
226
+ $group = array($group);
227
+ }
228
+
229
+ foreach ($group as $g) {
230
+ $g = trim($g);
231
+ //Initialize group if it doesn't already exist
232
+ if ( !isset($this->property_groups[$g]) )
233
+ $this->property_groups[$g] = array();
234
+
235
+ //Add property to group
236
+ $this->property_groups[$g][$property] = null;
237
+ }
238
+ }
239
+
240
+ /**
241
+ * Retrieve property group
242
+ * @param string $group Group to retrieve
243
+ * @return array Array of properties in specified group
244
+ */
245
+ function get_group($group) {
246
+ return $this->get_member_value('property_groups', $group, array());
247
+ }
248
+
249
+ /**
250
+ * Sets an element for the field type
251
+ * @param string $name Name of element
252
+ * @param CNR_Field_Type $type Reference of field type to use for element
253
+ * @param array $properties Properties for element (passed as keyed associative array)
254
+ * @param string $id_prop Name of property to set $name to (e.g. ID, etc.)
255
+ */
256
+ function set_element($name, $type, $properties = array(), $id_prop = 'id') {
257
+ $name = trim(strval($name));
258
+ if ( empty($name) )
259
+ return false;
260
+ //Create new field for element
261
+ $el = new CNR_Field($name, $type);
262
+ //Set container to current field instance
263
+ $el->set_container($this);
264
+ //Add properties to element
265
+ $el->set_properties($properties);
266
+ //Save element to current instance
267
+ $this->elements[$name] =& $el;
268
+ }
269
+
270
+ /**
271
+ * Add a layout to the field
272
+ * @param string $name Name of layout
273
+ * @param string $value Layout text
274
+ */
275
+ function set_layout($name, $value = '') {
276
+ if ( !is_string($name) )
277
+ return false;
278
+ $name = trim($name);
279
+ $this->layout[$name] = $value;
280
+ return true;
281
+ }
282
+
283
+ /**
284
+ * Retrieve specified layout
285
+ * @param string $name Layout name
286
+ * @param bool $parse_nested (optional) Whether nested layouts should be expanded in retreived layout or not (Default: TRUE)
287
+ * @return string Specified layout text
288
+ */
289
+ function get_layout($name = 'form', $parse_nested = true) {
290
+ //Retrieve specified layout (use $name value if no layout by that name exists)
291
+ $layout = $this->get_member_value('layout', $name, $name);
292
+
293
+ //Find all nested layouts in current layout
294
+ if ( !empty($layout) && !!$parse_nested ) {
295
+ $ph = $this->get_placeholder_defaults();
296
+
297
+ while ($ph->match = $this->parse_layout($layout, $ph->pattern_layout)) {
298
+ //Iterate through the different types of layout placeholders
299
+ foreach ($ph->match as $tag => $instances) {
300
+ //Iterate through instances of a specific type of layout placeholder
301
+ foreach ($instances as $instance) {
302
+ //Get nested layout
303
+ $nested_layout = $this->get_member_value($instance);
304
+
305
+ //Replace layout placeholder with retrieved item data
306
+ if ( !empty($nested_layout) )
307
+ $layout = str_replace($ph->start . $instance['match'] . $ph->end, $nested_layout, $layout);
308
+ }
309
+ }
310
+ }
311
+ }
312
+
313
+ return $layout;
314
+ }
315
+
316
+ /**
317
+ * Checks if specified layout exists
318
+ * Finds layout if it exists in current object or any of its parents
319
+ * @param string $layout Name of layout to check for
320
+ * @return bool TRUE if layout exists, FALSE otherwise
321
+ */
322
+ function has_layout($layout) {
323
+ $ret = false;
324
+ if ( is_string($layout) && ($layout = trim($layout)) && !empty($layout) ) {
325
+ $layout = $this->get_member_value('layout', $layout, false);
326
+ if ( $layout !== false )
327
+ $ret = true;
328
+ }
329
+
330
+ return $ret;
331
+ }
332
+
333
+ /**
334
+ * Checks if layout content is valid
335
+ * Layouts need to have placeholders to be valid
336
+ * @param string $layout_content Layout content (markup)
337
+ * @return bool TRUE if layout is valid, FALSE otherwise
338
+ */
339
+ function is_valid_layout($layout_content) {
340
+ $ph = $this->get_placeholder_defaults();
341
+ return preg_match($ph->pattern_general, $layout_content);
342
+ }
343
+
344
+ /**
345
+ * Parse field layout with a regular expression
346
+ * @param string $layout Layout data
347
+ * @param string $search Regular expression pattern to search layout for
348
+ * @return array Associative array containing all of the regular expression matches in the layout data
349
+ * Array Structure:
350
+ * root => placeholder tags
351
+ * => Tag instances (array)
352
+ * 'tag' => (string) tag name
353
+ * 'match' => (string) placeholder match
354
+ * 'attributes' => (array) attributes
355
+ */
356
+ function parse_layout($layout, $search) {
357
+ $ph_xml = '';
358
+ $parse_match = '';
359
+ $ph_root_tag = 'ph_root_element';
360
+ $ph_start_xml = '<';
361
+ $ph_end_xml = ' />';
362
+ $ph_wrap_start = '<' . $ph_root_tag . '>';
363
+ $ph_wrap_end = '</' . $ph_root_tag . '>';
364
+ $parse_result = false;
365
+
366
+ //Find all nested layouts in layout
367
+ $match_value = preg_match_all($search, $layout, $parse_match, PREG_PATTERN_ORDER);
368
+
369
+ if ($match_value !== false && $match_value > 0) {
370
+ $parse_result = array();
371
+ //Get all matched elements
372
+ $parse_match = $parse_match[1];
373
+
374
+ //Build XML string from placeholders
375
+ foreach ($parse_match as $ph) {
376
+ $ph_xml .= $ph_start_xml . $ph . $ph_end_xml . ' ';
377
+ }
378
+ $ph_xml = $ph_wrap_start . $ph_xml . $ph_wrap_end;
379
+ //Parse XML data
380
+ $ph_prs = xml_parser_create();
381
+ xml_parser_set_option($ph_prs, XML_OPTION_SKIP_WHITE, 1);
382
+ xml_parser_set_option($ph_prs, XML_OPTION_CASE_FOLDING, 0);
383
+ $ret = xml_parse_into_struct($ph_prs, $ph_xml, $parse_result['values'], $parse_result['index']);
384
+ xml_parser_free($ph_prs);
385
+
386
+ //Build structured array with all parsed data
387
+
388
+ unset($parse_result['index'][$ph_root_tag]);
389
+
390
+ //Build structured array
391
+ $result = array();
392
+ foreach ($parse_result['index'] as $tag => $instances) {
393
+ $result[$tag] = array();
394
+ //Instances
395
+ foreach ($instances as $instance) {
396
+ //Skip instance if it doesn't exist in parse results
397
+ if (!isset($parse_result['values'][$instance]))
398
+ continue;
399
+
400
+ //Stop processing instance if a previously-saved instance with the same options already exists
401
+ foreach ($result[$tag] as $tag_match) {
402
+ if ($tag_match['match'] == $parse_match[$instance - 1])
403
+ continue 2;
404
+ }
405
+
406
+ //Init instance data array
407
+ $inst_data = array();
408
+
409
+ //Add Tag to array
410
+ $inst_data['tag'] = $parse_result['values'][$instance]['tag'];
411
+
412
+ //Add instance data to array
413
+ $inst_data['attributes'] = (isset($parse_result['values'][$instance]['attributes'])) ? $inst_data['attributes'] = $parse_result['values'][$instance]['attributes'] : '';
414
+
415
+ //Add match to array
416
+ $inst_data['match'] = $parse_match[$instance - 1];
417
+
418
+ //Add to result array
419
+ $result[$tag][] = $inst_data;
420
+ }
421
+ }
422
+ $parse_result = $result;
423
+ }
424
+
425
+ return $parse_result;
426
+ }
427
+
428
+ /**
429
+ * Retrieves default properties to use when evaluating layout placeholders
430
+ * @return object Object with properties for evaluating layout placeholders
431
+ */
432
+ function get_placeholder_defaults() {
433
+ $ph = new stdClass();
434
+ $ph->start = '{';
435
+ $ph->end = '}';
436
+ $ph->reserved = array('ref' => 'ref_base');
437
+ $ph->pattern_general = '/' . $ph->start . '([a-zA-Z0-9_].*?)' . $ph->end . '/i';
438
+ $ph->pattern_layout = '/' . $ph->start . '([a-zA-Z0-9].*?\s+' . $ph->reserved['ref'] . '="layout.*?".*?)' . $ph->end . '/i';
439
+ return $ph;
440
+ }
441
+
442
+ /**
443
+ * Builds HTML for a field based on its properties
444
+ * @param array $field Field properties (id, field, etc.)
445
+ * @param array data Additional data for current field
446
+ */
447
+ function build_layout($layout = 'form', $data = null) {
448
+ $out_default = '';
449
+
450
+ /* Layout */
451
+
452
+ //Get base layout
453
+ $out = $this->get_layout($layout);
454
+
455
+ //Only parse valid layouts
456
+ if ( $this->is_valid_layout($out) ) {
457
+ //Parse Layout
458
+ $ph = $this->get_placeholder_defaults();
459
+
460
+ //Search layout for placeholders
461
+ while ( $ph->match = $this->parse_layout($out, $ph->pattern_general) ) {
462
+ //Iterate through placeholders (tag, id, etc.)
463
+ foreach ( $ph->match as $tag => $instances ) {
464
+ //Iterate through instances of current placeholder
465
+ foreach ( $instances as $instance ) {
466
+ //Process value based on placeholder name
467
+ $target_property = apply_filters('cnr_process_placeholder_' . $tag, '', $this, $instance, $layout, $data);
468
+
469
+ //Process value using default processors (if necessary)
470
+ if ( '' == $target_property ) {
471
+ $target_property = apply_filters('cnr_process_placeholder', $target_property, $this, $instance, $layout, $data);
472
+ }
473
+
474
+ //Clear value if value not a string
475
+ if ( !is_scalar($target_property) ) {
476
+ $target_property = '';
477
+ }
478
+ //Replace layout placeholder with retrieved item data
479
+ $out = str_replace($ph->start . $instance['match'] . $ph->end, $target_property, $out);
480
+ }
481
+ }
482
+ }
483
+ } else {
484
+ $out = $out_default;
485
+ }
486
+
487
+ /* Return generated value */
488
+
489
+ return $out;
490
+ }
491
+
492
+ /*-** Static Methods **-*/
493
+
494
+ /**
495
+ * Returns indacator to use field data (in layouts, property values, etc.)
496
+ */
497
+ function uses_data() {
498
+ return self::USES_DATA;
499
+ }
500
+
501
+ /**
502
+ * Register a function to handle a placeholder
503
+ * Multiple handlers may be registered for a single placeholder
504
+ * Basically a wrapper function to facilitate adding hooks for placeholder processing
505
+ * @uses add_filter()
506
+ * @param string $placeholder Name of placeholder to add handler for (Using 'all' will set the function as a handler for all placeholders
507
+ * @param callback $handler Function to set as a handler
508
+ * @param int $priority (optional) Priority of handler
509
+ */
510
+ static function register_placeholder_handler($placeholder, $handler, $priority = 10) {
511
+ if ( 'all' == $placeholder )
512
+ $placeholder = '';
513
+ else
514
+ $placeholder = '_' . $placeholder;
515
+
516
+ add_filter('cnr_process_placeholder' . $placeholder, $handler, $priority, 5);
517
+ }
518
+
519
+ /**
520
+ * Default placeholder processing
521
+ * To be executed when current placeholder has not been handled by another handler
522
+ * @param string $ph_output Value to be used in place of placeholder
523
+ * @param CNR_Field $field Field containing placeholder
524
+ * @param array $placeholder Current placeholder
525
+ * @see CNR_Field::parse_layout for structure of $placeholder array
526
+ * @param string $layout Layout to build
527
+ * @param array $data Extended data for field
528
+ * @return string Value to use in place of current placeholder
529
+ */
530
+ static function process_placeholder_default($ph_output, $field, $placeholder, $layout, $data) {
531
+ //Validate parameters before processing
532
+ if ( empty($ph_output) && is_a($field, 'CNR_Field_Type') && is_array($placeholder) ) {
533
+ //Build path to replacement data
534
+ $ph_output = $field->get_member_value($placeholder);
535
+
536
+ //Check if value is group (properties, etc.)
537
+ //All groups must have additional attributes (beyond reserved attributes) that define how items in group are used
538
+ if (is_array($ph_output)
539
+ && !empty($placeholder['attributes'])
540
+ && is_array($placeholder['attributes'])
541
+ && ($ph = $field->get_placeholder_defaults())
542
+ && $attribs = array_diff(array_keys($placeholder['attributes']), array_values($ph->reserved))
543
+ ) {
544
+ /* Targeted property is an array, but the placeholder contains additional options on how property is to be used */
545
+
546
+ //Find items matching criteria in $ph_output
547
+ //Check for group criteria
548
+ //TODO: Implement more robust/flexible criteria handling (2010-03-11: Currently only processes property groups)
549
+ if ( 'properties' == $placeholder['tag'] && ($prop_group = $field->get_group($placeholder['attributes']['group'])) && !empty($prop_group) ) {
550
+ /* Process group */
551
+ $group_out = array();
552
+ //Iterate through properties in group and build string
553
+ foreach ( $prop_group as $prop_key => $prop_val ) {
554
+ $group_out[] = $prop_key . '="' . $field->get_property($prop_key) . '"';
555
+ }
556
+ $ph_output = implode(' ', $group_out);
557
+ }
558
+ } elseif ( is_object($ph_output) && is_a($ph_output, $field->base_class) ) {
559
+ /* Targeted property is actually a nested field */
560
+ //Set caller to current field
561
+ $ph_output->set_caller($field);
562
+ //Build layout for nested element
563
+ $ph_output = $ph_output->build_layout($layout);
564
+ }
565
+ }
566
+
567
+ return $ph_output;
568
+ }
569
+
570
+ /**
571
+ * Build Field ID attribute
572
+ * @see CNR_Field_Type::process_placeholder_default for parameter descriptions
573
+ * @return string Placeholder output
574
+ */
575
+ static function process_placeholder_id($ph_output, $field, $placeholder, $layout, $data) {
576
+ //Get attributes
577
+ $args = wp_parse_args($placeholder['attributes'], array('format' => 'attr_id'));
578
+ return $field->get_id($args);
579
+ }
580
+
581
+ /**
582
+ * Build Field name attribute
583
+ * Name is formatted as an associative array for processing by PHP after submission
584
+ * @see CNR_Field_Type::process_placeholder_default for parameter descriptions
585
+ * @return string Placeholder output
586
+ */
587
+ static function process_placeholder_name($ph_output, $field, $placeholder, $layout, $data) {
588
+ //Get attributes
589
+ $args = wp_parse_args($placeholder['attributes'], array('format' => 'default'));
590
+ return $field->get_id($args);
591
+ }
592
+
593
+ /**
594
+ * Retrieve data for field
595
+ * @see CNR_Field_Type::process_placeholder_default for parameter descriptions
596
+ * @return string Placeholder output
597
+ */
598
+ static function process_placeholder_data($ph_output, $field, $placeholder, $layout) {
599
+ $val = $field->get_data();
600
+ if ( !is_null($val) ) {
601
+ $ph_output = $val;
602
+ $attr =& $placeholder['attributes'];
603
+ //Get specific member in value (e.g. value from a specific field element)
604
+ if ( isset($attr['element']) && is_array($ph_output) && ( $el = $attr['element'] ) && isset($ph_output[$el]) )
605
+ $ph_output = $ph_output[$el];
606
+ if ( isset($attr['format']) && 'display' == $attr['format'] )
607
+ $ph_output = nl2br($ph_output);
608
+ }
609
+
610
+ //Return data
611
+ return $ph_output;
612
+ }
613
+
614
+ /**
615
+ * Loops over data to build field output
616
+ * Options:
617
+ * data - Dot-delimited path in field that contains data to loop through
618
+ * layout - Name of layout to use for each data item in loop
619
+ * layout_data - Name of layout to use for data item that matches previously-saved field data
620
+ * @see CNR_Field_Type::process_placeholder_default for parameter descriptions
621
+ * @return string Placeholder output
622
+ */
623
+ static function process_placeholder_loop($ph_output, $field, $placeholder, $layout, $data) {
624
+ //Setup loop options
625
+ $attr_defaults = array (
626
+ 'layout' => '',
627
+ 'layout_data' => null,
628
+ 'data' => ''
629
+ );
630
+
631
+ $attr = wp_parse_args($placeholder['attributes'], $attr_defaults);
632
+
633
+ if ( is_null($attr['layout_data']) ) {
634
+ $attr['layout_data'] =& $attr['layout'];
635
+ }
636
+
637
+ //Get data for loop
638
+ $path = explode('.', $attr['data']);
639
+ $loop_data = $field->get_member_value($path);
640
+ /*if ( isset($loop_data['value']) )
641
+ $loop_data = $loop_data['value'];
642
+ */
643
+ $out = array();
644
+
645
+ //Get field data
646
+ $data = $field->get_data();
647
+
648
+ //Iterate over data and build output
649
+ if ( is_array($loop_data) && !empty($loop_data) ) {
650
+ foreach ( $loop_data as $value => $label ) {
651
+ //Load appropriate layout based on field value
652
+ $layout = ( ($data === 0 && $value === $data) xor $data == $value ) ? $attr['layout_data'] : $attr['layout'];
653
+ //Stop processing if no valid layout is returned
654
+ if ( empty($layout) )
655
+ continue;
656
+ //Prep extended field data
657
+ $data_ext = array('option_value' => $value, 'option_text' => $label);
658
+ $out[] = $field->build_layout($layout, $data_ext);
659
+ }
660
+ }
661
+
662
+ //Return output
663
+ return implode($out);
664
+ }
665
+
666
+ /**
667
+ * Returns specified value from extended data array for field
668
+ * @see CNR_Field_Type::process_placeholder_default for parameter descriptions
669
+ * @return string Placeholder output
670
+ */
671
+ static function process_placeholder_data_ext($ph_output, $field, $placeholder, $layout, $data) {
672
+ if ( isset($placeholder['attributes']['id']) && ($key = $placeholder['attributes']['id']) && isset($data[$key]) ) {
673
+ $ph_output = strval($data[$key]);
674
+ }
675
+
676
+ return $ph_output;
677
+ }
678
+
679
+ /**
680
+ * WP Editor
681
+ * @see CNR_Field_Type::process_placeholder_default for parameter descriptions
682
+ * @return string Placeholder output
683
+ */
684
+ static function process_placeholder_rich_editor($ph_output, $field, $placeholder, $layout, $data) {
685
+ $id = $field->get_id( array (
686
+ 'format' => 'attr_id'
687
+ ));
688
+ $settings = array (
689
+ 'textarea_name' => $field->get_id( array (
690
+ 'format' => 'default'
691
+ ))
692
+ );
693
+ ob_start();
694
+ wp_editor($field->get_data(), $id, $settings);
695
+ $out = ob_get_clean();
696
+ return $out;
697
+ }
698
  }
includes/class.post.php CHANGED
@@ -1,248 +1,248 @@
1
- <?php
2
- /**
3
- * @package Cornerstone
4
- * @subpackage Posts
5
- * @author Archetyped
6
- *
7
- */
8
- class CNR_Post extends CNR_Base {
9
-
10
- /*-** Properties **-*/
11
-
12
- /**
13
- * Script files
14
- * @see CNR_Base::client_files
15
- * @var array
16
- */
17
- var $scripts = array (
18
- 'posts' => array (
19
- 'file' => 'js/lib.posts.js',
20
- 'deps' => '[core]',
21
- 'context' => 'admin'
22
- ),
23
- );
24
-
25
- /**
26
- * Default title separator
27
- * @var string
28
- */
29
- var $title_sep = '&lsaquo;';
30
-
31
- /*-** Initialization **-*/
32
-
33
- function register_hooks() {
34
- parent::register_hooks();
35
-
36
- //Template
37
- // add_filter('wp_title', $this->m('page_title'), 11, 3);
38
-
39
- //Admin
40
- add_action('admin_head', $this->m('admin_set_title'), 11);
41
- }
42
-
43
- /**
44
- * Gets entire parent tree of post as an array
45
- *
46
- * Array order is from top level to immediate post parent
47
- * @static
48
- * @param object $post Post to get path for
49
- * @param string $prop Property to retrieve from parents. If specified, array will contain only this property from parents
50
- * @param $depth Unused
51
- * @return array of Post Objects/Properties
52
- */
53
- static function get_parents($post, $prop = '', $depth = '') {
54
- $parents = get_post_ancestors($post = get_post($post, OBJECT, ''));
55
- if ( is_object($post) && !empty($parents) && ('id' != strtolower(trim($prop))) ) {
56
- //Retrieve post data for parents if full data or property other than post ID is required
57
- $args = array(
58
- 'include' => $parents,
59
- 'post_type' => 'any',
60
- );
61
- $ancestors = get_posts($args);
62
-
63
- //Sort array in ancestor order
64
- $temp_parents = array();
65
- foreach ($ancestors as $ancestor) {
66
- //Get index of ancestor
67
- $i = array_search($ancestor->ID, $parents);
68
- if ( false === $i )
69
- continue;
70
- //Insert post at index
71
- $temp_parents[$i] = $ancestor;
72
- }
73
-
74
- if ( !empty($temp_parents) )
75
- $parents = $temp_parents;
76
- }
77
- //Reverse Array (to put top level parent at beginning of array)
78
- $parents = array_reverse($parents);
79
- return $parents;
80
- }
81
-
82
- /**
83
- * Get the IDs of a collection of posts
84
- * @return array IDs of Posts passed to function
85
- * @param array $posts Array of Post objects
86
- */
87
- function get_ids($posts) {
88
- $callback = create_function('$post', 'return $post->ID;');
89
- $arr_ids = array_map($callback, $posts);
90
- return $arr_ids;
91
- }
92
-
93
- /*-** Children **-*/
94
-
95
- /**
96
- * Gets children posts of specified page and stores them for later use
97
- * This method hooks into 'the_posts' filter to retrieve child posts for any single page retrieved by WP
98
- * @param int|object $post ID or Object of Post to get children for
99
- * @return CNR_Post_Query $posts Posts array (required by 'the_posts' filter)
100
- *
101
- * @global WP_Query $wp_query
102
- */
103
- function &get_children($post = null) {
104
- //Global variables
105
- global $wp_query;
106
- $children = new CNR_Post_Query();
107
- if ( empty($post) && !empty($wp_query->posts) )
108
- $post = $wp_query->posts[0];
109
-
110
- if ( is_object($post) )
111
- $post = $post->ID;
112
- if ( is_numeric($post) )
113
- $post = (int) $post;
114
- else
115
- return $children;
116
-
117
- //Get children posts of page
118
- if ( $post ) {
119
- //Set arguments to retrieve children posts of current page
120
- $limit = ( is_feed() ) ? get_option('posts_per_rss') : get_option('posts_per_page');
121
- $offset = ( is_paged() ) ? ( (get_query_var('paged') - 1) * $limit ) : 0;
122
- $c_args = array(
123
- 'post_parent' => $post,
124
- 'numberposts' => $limit,
125
- 'offset' => $offset
126
- );
127
-
128
- //Create post query object
129
- $children->set_arg($c_args);
130
-
131
- //Get children posts
132
- $children->get();
133
- }
134
-
135
- return $children;
136
- }
137
-
138
- /*-** Post Metadata **-*/
139
-
140
- /**
141
- * Retrieves the post's section data
142
- * @param string $data (optional) Section data to return (Default: full section object)
143
- * Possible values:
144
- * NULL Full section post object
145
- * Column name Post column data (if exists)
146
- *
147
- * @return mixed post's section data (Default: ID value)
148
- */
149
- static function get_section($post = null, $data = null) {
150
- $p = get_post($post);
151
- $retval = 0;
152
- if ( is_object($p) && isset($p->post_parent) )
153
- $retval = intval($p->post_parent);
154
-
155
- //Get specified section data for posts with valid parents
156
- if ( $retval > 0 ) {
157
- if ( !empty($data) ) {
158
- $retval = get_post_field($data, $retval);
159
- } else {
160
- $retval = get_post($retval);
161
- }
162
- }
163
-
164
- return $retval;
165
- }
166
-
167
- /**
168
- * Prints the post's section data
169
- * @uses CNR_Post::get_section()
170
- * @param string $type (optional) Type of data to return (Default: ID)
171
- */
172
- static function the_section($post = null, $data = 'ID') {
173
- if ( empty($data) )
174
- $data = 'ID';
175
- echo CNR_Post::get_section($post, $data);
176
- }
177
-
178
- /*-** Admin **-*/
179
-
180
- function admin_set_title() {
181
- global $post;
182
-
183
- if ( !$post )
184
- return false;
185
-
186
- $obj = new stdClass();
187
- //Section title
188
- $sec = $this->get_section($post);
189
- if ( $sec )
190
- $obj->item_section = get_the_title($sec);
191
- //Separator
192
- $obj->title_sep = $this->page_title_get_sep();
193
- $this->util->extend_client_object('posts', $obj, true);
194
- }
195
-
196
- function page_title_get_sep($pad = true) {
197
- $sep = $this->title_sep;
198
- if ( $pad )
199
- $sep = ' ' . trim($sep) . ' ';
200
- return $sep;
201
- }
202
-
203
- /*-** Template **-*/
204
-
205
- /**
206
- * Builds page title for current request
207
- * Adds subtitle to title
208
- * Filter called by `wp_title` hook
209
- * @param $title
210
- * @param $sep
211
- * @param $seplocation
212
- * @return string Title text
213
- */
214
- function page_title_get($title, $sep = '', $seplocation = '') {
215
- global $post;
216
-
217
- $sep = $this->page_title_get_sep();
218
-
219
- if ( is_single() ) {
220
- //Append section name to post title
221
- $ptitle = get_the_title();
222
- $ptitle_pos = ( $ptitle ) ? strpos($title, $ptitle) : false;
223
- if ( $ptitle_pos !== false ) {
224
- //Get section
225
- if ( ( $sec = $this->get_section($post) ) ) {
226
- //Append section name to post title only once
227
- $title = substr_replace($ptitle, $ptitle . $sep . get_the_title($sec), $ptitle_pos, strlen($ptitle)) . substr($title, strlen($ptitle));
228
- }
229
- }
230
- }
231
-
232
- //Return new title
233
- return $title;
234
- }
235
-
236
- /**
237
- * Builds page title for current request
238
- * Filter called by `wp_title` hook
239
- * @param $title
240
- * @param $sep
241
- * @param $seplocation
242
- * @return string Title text
243
- * @uses CNR::page_title_get()
244
- */
245
- function page_title($title, $sep = '', $seplocation = '') {
246
- return $this->page_title_get($title, $sep, $seplocation);
247
- }
248
  }
1
+ <?php
2
+ /**
3
+ * @package Cornerstone
4
+ * @subpackage Posts
5
+ * @author Archetyped
6
+ *
7
+ */
8
+ class CNR_Post extends CNR_Base {
9
+
10
+ /*-** Properties **-*/
11
+
12
+ /**
13
+ * Script files
14
+ * @see CNR_Base::client_files
15
+ * @var array
16
+ */
17
+ var $scripts = array (
18
+ 'posts' => array (
19
+ 'file' => 'js/lib.posts.js',
20
+ 'deps' => '[core]',
21
+ 'context' => 'admin'
22
+ ),
23
+ );
24
+
25
+ /**
26
+ * Default title separator
27
+ * @var string
28
+ */
29
+ var $title_sep = '&lsaquo;';
30
+
31
+ /*-** Initialization **-*/
32
+
33
+ function register_hooks() {
34
+ parent::register_hooks();
35
+
36
+ //Template
37
+ // add_filter('wp_title', $this->m('page_title'), 11, 3);
38
+
39
+ //Admin
40
+ add_action('admin_head', $this->m('admin_set_title'), 11);
41
+ }
42
+
43
+ /**
44
+ * Gets entire parent tree of post as an array
45
+ *
46
+ * Array order is from top level to immediate post parent
47
+ * @static
48
+ * @param object $post Post to get path for
49
+ * @param string $prop Property to retrieve from parents. If specified, array will contain only this property from parents
50
+ * @param $depth Unused
51
+ * @return array of Post Objects/Properties
52
+ */
53
+ static function get_parents($post, $prop = '', $depth = '') {
54
+ $parents = get_post_ancestors($post = get_post($post, OBJECT, ''));
55
+ if ( is_object($post) && !empty($parents) && ('id' != strtolower(trim($prop))) ) {
56
+ //Retrieve post data for parents if full data or property other than post ID is required
57
+ $args = array(
58
+ 'include' => $parents,
59
+ 'post_type' => 'any',
60
+ );
61
+ $ancestors = get_posts($args);
62
+
63
+ //Sort array in ancestor order
64
+ $temp_parents = array();
65
+ foreach ($ancestors as $ancestor) {
66
+ //Get index of ancestor
67
+ $i = array_search($ancestor->ID, $parents);
68
+ if ( false === $i )
69
+ continue;
70
+ //Insert post at index
71
+ $temp_parents[$i] = $ancestor;
72
+ }
73
+
74
+ if ( !empty($temp_parents) )
75
+ $parents = $temp_parents;
76
+ }
77
+ //Reverse Array (to put top level parent at beginning of array)
78
+ $parents = array_reverse($parents);
79
+ return $parents;
80
+ }
81
+
82
+ /**
83
+ * Get the IDs of a collection of posts
84
+ * @return array IDs of Posts passed to function
85
+ * @param array $posts Array of Post objects
86
+ */
87
+ function get_ids($posts) {
88
+ $callback = create_function('$post', 'return $post->ID;');
89
+ $arr_ids = array_map($callback, $posts);
90
+ return $arr_ids;
91
+ }
92
+
93
+ /*-** Children **-*/
94
+
95
+ /**
96
+ * Gets children posts of specified page and stores them for later use
97
+ * This method hooks into 'the_posts' filter to retrieve child posts for any single page retrieved by WP
98
+ * @param int|object $post ID or Object of Post to get children for
99
+ * @return CNR_Post_Query $posts Posts array (required by 'the_posts' filter)
100
+ *
101
+ * @global WP_Query $wp_query
102
+ */
103
+ function &get_children($post = null) {
104
+ //Global variables
105
+ global $wp_query;
106
+ $children = new CNR_Post_Query();
107
+ if ( empty($post) && !empty($wp_query->posts) )
108
+ $post = $wp_query->posts[0];
109
+
110
+ if ( is_object($post) )
111
+ $post = $post->ID;
112
+ if ( is_numeric($post) )
113
+ $post = (int) $post;
114
+ else
115
+ return $children;
116
+
117
+ //Get children posts of page
118
+ if ( $post ) {
119
+ //Set arguments to retrieve children posts of current page
120
+ $limit = ( is_feed() ) ? get_option('posts_per_rss') : get_option('posts_per_page');
121
+ $offset = ( is_paged() ) ? ( (get_query_var('paged') - 1) * $limit ) : 0;
122
+ $c_args = array(
123
+ 'post_parent' => $post,
124
+ 'numberposts' => $limit,
125
+ 'offset' => $offset
126
+ );
127
+
128
+ //Create post query object
129
+ $children->set_arg($c_args);
130
+
131
+ //Get children posts
132
+ $children->get();
133
+ }
134
+
135
+ return $children;
136
+ }
137
+
138
+ /*-** Post Metadata **-*/
139
+
140
+ /**
141
+ * Retrieves the post's section data
142
+ * @param string $data (optional) Section data to return (Default: full section object)
143
+ * Possible values:
144
+ * NULL Full section post object
145
+ * Column name Post column data (if exists)
146
+ *
147
+ * @return mixed post's section data (Default: ID value)
148
+ */
149
+ static function get_section($post = null, $data = null) {
150
+ $p = get_post($post);
151
+ $retval = 0;
152
+ if ( is_object($p) && isset($p->post_parent) )
153
+ $retval = intval($p->post_parent);
154
+
155
+ //Get specified section data for posts with valid parents
156
+ if ( $retval > 0 ) {
157
+ if ( !empty($data) ) {
158
+ $retval = get_post_field($data, $retval);
159
+ } else {
160
+ $retval = get_post($retval);
161
+ }
162
+ }
163
+
164
+ return $retval;
165
+ }
166
+
167
+ /**
168
+ * Prints the post's section data
169
+ * @uses CNR_Post::get_section()
170
+ * @param string $type (optional) Type of data to return (Default: ID)
171
+ */
172
+ static function the_section($post = null, $data = 'ID') {
173
+ if ( empty($data) )
174
+ $data = 'ID';
175
+ echo CNR_Post::get_section($post, $data);
176
+ }
177
+
178
+ /*-** Admin **-*/
179
+
180
+ function admin_set_title() {
181
+ global $post;
182
+
183
+ if ( !$post )
184
+ return false;
185
+
186
+ $obj = new stdClass();
187
+ //Section title
188
+ $sec = $this->get_section($post);
189
+ if ( $sec )
190
+ $obj->item_section = get_the_title($sec);
191
+ //Separator
192
+ $obj->title_sep = $this->page_title_get_sep();
193
+ $this->util->extend_client_object('posts', $obj, true);
194
+ }
195
+
196
+ function page_title_get_sep($pad = true) {
197
+ $sep = $this->title_sep;
198
+ if ( $pad )
199
+ $sep = ' ' . trim($sep) . ' ';
200
+ return $sep;
201
+ }
202
+
203
+ /*-** Template **-*/
204
+
205
+ /**
206
+ * Builds page title for current request
207
+ * Adds subtitle to title
208
+ * Filter called by `wp_title` hook
209
+ * @param $title
210
+ * @param $sep
211
+ * @param $seplocation
212
+ * @return string Title text
213
+ */
214
+ function page_title_get($title, $sep = '', $seplocation = '') {
215
+ global $post;
216
+
217
+ $sep = $this->page_title_get_sep();
218
+
219
+ if ( is_single() ) {
220
+ //Append section name to post title
221
+ $ptitle = get_the_title();
222
+ $ptitle_pos = ( $ptitle ) ? strpos($title, $ptitle) : false;
223
+ if ( $ptitle_pos !== false ) {
224
+ //Get section
225
+ if ( ( $sec = $this->get_section($post) ) ) {
226
+ //Append section name to post title only once
227
+ $title = substr_replace($ptitle, $ptitle . $sep . get_the_title($sec), $ptitle_pos, strlen($ptitle)) . substr($title, strlen($ptitle));
228
+ }
229
+ }
230
+ }
231
+
232
+ //Return new title
233
+ return $title;
234
+ }
235
+
236
+ /**
237
+ * Builds page title for current request
238
+ * Filter called by `wp_title` hook
239
+ * @param $title
240
+ * @param $sep
241
+ * @param $seplocation
242
+ * @return string Title text
243
+ * @uses CNR::page_title_get()
244
+ */
245
+ function page_title($title, $sep = '', $seplocation = '') {
246
+ return $this->page_title_get($title, $sep, $seplocation);
247
+ }
248
  }
includes/class.post_query.php CHANGED
@@ -1,439 +1,439 @@
1
- <?php
2
- /**
3
- * @package Cornerstone
4
- * @subpackage Posts
5
- * @author Archetyped
6
- *
7
- * Represents a collection of posts in a query
8
- * Handles navigating through post collection, reporting status, etc.
9
- *
10
- */
11
- class CNR_Post_Query extends CNR_Base {
12
-
13
- /*-** Variables **-*/
14
-
15
- var $scripts = array (
16
- 'posts' => array (
17
- 'file' => 'js/lib.posts.js',
18
- 'deps' => '[core]',
19
- 'context' => 'admin'
20
- ),
21
- );
22
-
23
- /**
24
- * Holds posts
25
- * @var array
26
- */
27
- var $posts;
28
-
29
- /**
30
- * IDs of posts in $posts
31
- * @var array
32
- */
33
- var $post_ids;
34
-
35
- /**
36
- * whether or not object contains posts
37
- * @var bool
38
- */
39
- var $has;
40
-
41
- /**
42
- * Index of post in current iteration
43
- * @var int
44
- */
45
- var $current;
46
-
47
- /**
48
- * Total number of posts in object
49
- * @var int
50
- */
51
- var $count;
52
-
53
- /**
54
- * Total number of matching posts found in DB
55
- * All found posts may not have been returned in current query though (paging, etc.)
56
- * @var int
57
- */
58
- var $found = 0;
59
-
60
- /**
61
- * Query arguments
62
- * @var array
63
- */
64
- var $args;
65
-
66
- /**
67
- * Argument to be used during query to identify request
68
- * Prefix added during init
69
- * @see init()
70
- * @var string
71
- */
72
- var $arg_fetch = 'fetch';
73
-
74
- /**
75
- * TRUE if posts have been fetched, FALSE otherwise
76
- * @var bool
77
- */
78
- var $fetched;
79
-
80
- function __construct( $args = null ) {
81
- parent::__construct();
82
-
83
- parent::init();
84
- //Init properties
85
- $this->init();
86
-
87
- //Set arguments
88
- if ( !empty($args) && is_array($args) ) {
89
- $this->args = wp_parse_args($args, $this->args);
90
- }
91
- }
92
-
93
- /**
94
- * Initializes object properties with default values
95
- * @return void
96
- */
97
- function init() {
98
- $this->posts = array();
99
- $this->post_ids = array();
100
- $this->has = false;
101
- $this->current = -1;
102
- $this->count = 0;
103
- $this->found = 0;
104
- $this->arg_fetch = $this->add_prefix($this->arg_fetch);
105
- $this->args = array($this->arg_fetch => true, $this->get_prefix() => true);
106
- $this->fetched = false;
107
- }
108
-
109
- /**
110
- * Set argument value
111
- * @param string $arg Argument name
112
- * @param mixed $value Argument value
113
- */
114
- function set_arg($arg, $value = null) {
115
- if ( is_scalar($arg) ) { //Single argument (key/value) pair
116
- $this->args[$arg] = $value;
117
- } elseif ( is_array($arg) ) { //Multiple arguments
118
- $this->args = wp_parse_args($arg, $this->args);
119
- }
120
- }
121
-
122
- /**
123
- * Retrieve argument value
124
- * @param string $arg Argument name
125
- * @return mixed Argument value
126
- */
127
- function get_arg($arg) {
128
- return ( $this->arg_isset($arg) ) ? $this->args[$arg] : null;
129
- }
130
-
131
- /**
132
- * Checks if an argument is set in the object
133
- * @param string $arg Argument name
134
- * @return bool TRUE if argument is set, FALSE otherwise
135
- */
136
- function arg_isset($arg) {
137
- return ( isset($this->args[$arg]) );
138
- }
139
-
140
- /**
141
- * Gets posts matching parameters and stores them in object
142
- *
143
- * @param int $limit (optional) Maximum number of posts to retrieve (Default: -1 = All matching posts)
144
- * @param array $args (optional) Additional arguments to use in post query
145
- * @return array Retrieved posts
146
- */
147
- function get( $limit = null, $args = null ) {
148
- //Global variables
149
- global $wp_query;
150
-
151
- //Clear previously retrieved post data
152
- $this->unload();
153
-
154
- //Determine section
155
- $p_arg = 'post_parent';
156
- if ( ! $this->arg_isset($p_arg) ) {
157
- $parent = null;
158
- if ( is_page() ) {
159
- $parent = $wp_query->get_queried_object_id();
160
- }
161
- if ( !! $parent )
162
- $this->set_arg($p_arg, $parent);
163
- }
164
-
165
- //Check if parent is valid post ID
166
- if ((int)$parent < 1) {
167
- //Get featured posts from all sections if no valid parent is set
168
- $parent = null;
169
- }
170
-
171
- //Set query args
172
- if ( !empty($args) )
173
- $this->args = wp_parse_args($args, $this->args);
174
- //Set post limit
175
- if ( is_numeric($limit) )
176
- $limit = intval($limit);
177
- if ( ! $limit && !$this->arg_isset('numberposts') )
178
- $limit = ( is_feed() ) ? get_option('posts_per_rss') : get_option('posts_per_page');
179
- if ( $limit > 0 )
180
- $this->set_arg('numberposts', $limit);
181
-
182
- //Set offset (pagination)
183
- if ( !is_feed() ) {
184
- $c_page = $wp_query->get('paged');
185
- $offset = ( $c_page > 0 ) ? $c_page - 1 : 0;
186
- $offset = $limit * $offset;
187
- $this->set_arg('offset', $offset);
188
- }
189
-
190
- //Retrieve posts
191
- $filter = 'found_posts';
192
- $f_callback = $this->m('set_found');
193
- $action = 'parse_query';
194
- $a_callback = $this->m('set_found_flag');
195
-
196
- //Add filter to populate found property during query
197
- add_filter($filter, $f_callback);
198
- add_action($action, $a_callback);
199
- //Get posts
200
- $posts = get_posts($this->args);
201
- //Remove filter after query has completed
202
- remove_action($action, $a_callback);
203
- remove_filter($filter, $f_callback);
204
- //Save retrieved posts to array
205
- $this->load($posts);
206
- //Return retrieved posts so that array may be manipulated further if desired
207
- return $this->posts;
208
- }
209
-
210
- /**
211
- * Sets object properties with post data
212
- *
213
- * @param array $posts Array of post objects
214
- * @return void
215
- */
216
- function load( $posts = null ) {
217
- $this->fetched = true;
218
- if ( !empty($posts) ) {
219
- $this->posts = $posts;
220
- $this->has = true;
221
- $this->count = count($this->posts);
222
- }
223
- }
224
-
225
- /**
226
- * Resets object properties to allow for new data to be saved
227
- * @return void
228
- */
229
- function unload() {
230
- //Temporarily save properties that should persist
231
- $_args = $this->args;
232
-
233
- //Initialize object properties
234
- $this->init();
235
-
236
- //Restore persistent properties
237
- $this->args = $_args;
238
- }
239
-
240
- /**
241
- * Sets number of found posts in object's query
242
- * @link `found_posts` hook
243
- * @see WP_Query::get_posts()
244
- * @param int $num_found
245
- */
246
- function set_found($num_found) {
247
- $this->found = $num_found;
248
- }
249
-
250
- /**
251
- * Modifies query parameters to allow `found_posts` hook to be called
252
- * Unsets `no_found_rows` query parameter set in WP 3.1
253
- * @see WP_Query::parse_query()
254
- * @link `parse_query` action hook
255
- * @param WP_Query $q Query instance object
256
- */
257
- function set_found_flag(&$q) {
258
- if ( isset($q->query_vars[$this->arg_fetch]) ) {
259
- $q->query_vars['no_found_rows'] = false;
260
- }
261
- }
262
-
263
- /**
264
- * Makes sure query was run prior
265
- * @return void
266
- */
267
- function confirm_fetched() {
268
- if ( !$this->fetched )
269
- $this->get();
270
- }
271
-
272
- /**
273
- * Returns number of matching posts found in DB
274
- * May not necessarily match number of posts contained in object (due to post limits, pagination, etc.)
275
- * @return int Number of posts found
276
- */
277
- function found() {
278
- $this->confirm_fetched();
279
- return $this->found;
280
- }
281
-
282
- /**
283
- * Checks whether posts related to this object are available in the current context
284
- *
285
- * If no accessible posts are found, current post (section) is set as global post variable
286
- *
287
- * @see 'the_posts' filter
288
- * @see get_children()
289
- *
290
- * @param bool $fetch Whether posts should be fetched if they have not yet been retrieved
291
- * @return boolean TRUE if section contains children, FALSE otherwise
292
- * Note: Will also return FALSE if section contains children, but all children have been previously accessed
293
- *
294
- * @global WP_Query $wp_query
295
- * @global obj $post
296
- */
297
- function has( $fetch = true ) {
298
- global $wp_query, $post, $more;
299
-
300
- $this->confirm_fetched();
301
-
302
- //Check if any posts on current page were retrieved
303
- //If posts are found, make sure there are more posts
304
- if ( $this->count > 0 && ( $this->current < $this->count - 1 ) ) {
305
- return true;
306
- }
307
-
308
- //Reset current post position if all posts have been processed
309
- $this->rewind();
310
- $wp_query->in_the_loop = false;
311
- //If no posts were found (or the last post has been previously loaded),
312
- //load previous post back into global post variable
313
- $i = ( $wp_query->current_post >= 0 ) ? $wp_query->current_post : 0;
314
- if ( count($wp_query->posts) ) {
315
- $post = $wp_query->posts[ $i ];
316
- setup_postdata($post);
317
- }
318
-
319
- if ( is_single() || is_page() )
320
- $more = 1;
321
- return false;
322
- }
323
-
324
- /**
325
- * Loads next post into global $post variable for use in the loop
326
- * Allows use of WP template tags
327
- * @return void
328
- *
329
- * @global obj $post Post object
330
- */
331
- function next() {
332
- global $post, $more, $wp_query;
333
-
334
- if ( $this->has() ) {
335
- $wp_query->in_the_loop = true;
336
- //Increment post position
337
- $this->current++;
338
-
339
- //Load post into global post variable
340
- $post = $this->posts[ $this->current ];
341
-
342
- setup_postdata($post);
343
- $more = 0;
344
- }
345
- }
346
-
347
- /**
348
- * Resets position of current post
349
- * Allows for multiple loops over $posts array
350
- * @return void
351
- */
352
- function rewind() {
353
- $this->current = -1;
354
- }
355
-
356
- /**
357
- * Gets index of current post
358
- * @return int Index position of current post
359
- */
360
- function current() {
361
- return $this->current;
362
- }
363
-
364
- /**
365
- * Returns number of posts in object
366
- * @return int number of posts
367
- */
368
- function count() {
369
- $this->confirm_fetched();
370
- return $this->count;
371
- }
372
-
373
- /**
374
- * Gets the number of pages needed to list all found posts
375
- * @return int Total number of pages
376
- */
377
- function max_num_pages() {
378
- $this->confirm_fetched();
379
- $posts_per_page = $this->get_arg('numberposts');
380
- if ( ! $posts_per_page )
381
- $posts_per_page = get_option('posts_per_page');
382
- return ceil( $this->found / $posts_per_page );
383
- }
384
-
385
- /**
386
- * Checks if current post is the first post
387
- * @return bool TRUE if current post is the first post in array, FALSE otherwise
388
- */
389
- function is_first() {
390
- $this->confirm_fetched();
391
- return ( 0 == $this->current() );
392
- }
393
-
394
- /**
395
- * Checks if current featured post is the last item in the post array
396
- * @return bool TRUE if item is the last featured item, FALSE otherwise
397
- */
398
- function is_last() {
399
- $this->confirm_fetched();
400
- return ($this->current == $this->count - 1) ? true : false;
401
- }
402
-
403
- /**
404
- * @param int $post (optional) ID of post to check for existence in the object's posts array (uses global $post object if no value passed)
405
- * @return bool TRUE if post is in posts array
406
- */
407
- function contains( $post = null ) {
408
- $this->confirm_fetched();
409
- //Use argument value if it is an integer
410
- if ( is_numeric($post) && intval($post) > 0 ) {
411
- //Cast to object and set ID property (for later use)
412
- $post = (object) $post;
413
- $post->ID = $post->scalar;
414
- }
415
- //Otherwise check if argument is valid post
416
- elseif ( !$this->util->check_post($post) ) {
417
- return false;
418
- }
419
-
420
- //Check for existence of post ID in posts array
421
- return in_array($post->ID, $this->get_ids());
422
- }
423
-
424
- /**
425
- * Retrieve IDs of all retrieved posts
426
- */
427
- function get_ids() {
428
- $this->confirm_fetched();
429
-
430
- if ( $this->has && empty($this->post_ids) ) {
431
- //Build array of post ids in array
432
- foreach ($this->posts as $post) {
433
- $this->post_ids[] = $post->ID;
434
- }
435
- }
436
-
437
- return $this->post_ids;
438
- }
439
  }
1
+ <?php
2
+ /**
3
+ * @package Cornerstone
4
+ * @subpackage Posts
5
+ * @author Archetyped
6
+ *
7
+ * Represents a collection of posts in a query
8
+ * Handles navigating through post collection, reporting status, etc.
9
+ *
10
+ */
11
+ class CNR_Post_Query extends CNR_Base {
12
+
13
+ /*-** Variables **-*/
14
+
15
+ var $scripts = array (
16
+ 'posts' => array (
17
+ 'file' => 'js/lib.posts.js',
18
+ 'deps' => '[core]',
19
+ 'context' => 'admin'
20
+ ),
21
+ );
22
+
23
+ /**
24
+ * Holds posts
25
+ * @var array
26
+ */
27
+ var $posts;
28
+
29
+ /**
30
+ * IDs of posts in $posts
31
+ * @var array
32
+ */
33
+ var $post_ids;
34
+
35
+ /**
36
+ * whether or not object contains posts
37
+ * @var bool
38
+ */
39
+ var $has;
40
+
41
+ /**
42
+ * Index of post in current iteration
43
+ * @var int
44
+ */
45
+ var $current;
46
+
47
+ /**
48
+ * Total number of posts in object
49
+ * @var int
50
+ */
51
+ var $count;
52
+
53
+ /**
54
+ * Total number of matching posts found in DB
55
+ * All found posts may not have been returned in current query though (paging, etc.)
56
+ * @var int
57
+ */
58
+ var $found = 0;
59
+
60
+ /**
61
+ * Query arguments
62
+ * @var array
63
+ */
64
+ var $args;
65
+
66
+ /**
67
+ * Argument to be used during query to identify request
68
+ * Prefix added during init
69
+ * @see init()
70
+ * @var string
71
+ */
72
+ var $arg_fetch = 'fetch';
73
+
74
+ /**
75
+ * TRUE if posts have been fetched, FALSE otherwise
76
+ * @var bool
77
+ */
78
+ var $fetched;
79
+
80
+ function __construct( $args = null ) {
81
+ parent::__construct();
82
+
83
+ parent::init();
84
+ //Init properties
85
+ $this->init();
86
+
87
+ //Set arguments
88
+ if ( !empty($args) && is_array($args) ) {
89
+ $this->args = wp_parse_args($args, $this->args);
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Initializes object properties with default values
95
+ * @return void
96
+ */
97
+ function init() {
98
+ $this->posts = array();
99
+ $this->post_ids = array();
100
+ $this->has = false;
101
+ $this->current = -1;
102
+ $this->count = 0;
103
+ $this->found = 0;
104
+ $this->arg_fetch = $this->add_prefix($this->arg_fetch);
105
+ $this->args = array($this->arg_fetch => true, $this->get_prefix() => true);
106
+ $this->fetched = false;
107
+ }
108
+
109
+ /**
110
+ * Set argument value
111
+ * @param string $arg Argument name
112
+ * @param mixed $value Argument value
113
+ */
114
+ function set_arg($arg, $value = null) {
115
+ if ( is_scalar($arg) ) { //Single argument (key/value) pair
116
+ $this->args[$arg] = $value;
117
+ } elseif ( is_array($arg) ) { //Multiple arguments
118
+ $this->args = wp_parse_args($arg, $this->args);
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Retrieve argument value
124
+ * @param string $arg Argument name
125
+ * @return mixed Argument value
126
+ */
127
+ function get_arg($arg) {
128
+ return ( $this->arg_isset($arg) ) ? $this->args[$arg] : null;
129
+ }
130
+
131
+ /**
132
+ * Checks if an argument is set in the object
133
+ * @param string $arg Argument name
134
+ * @return bool TRUE if argument is set, FALSE otherwise
135
+ */
136
+ function arg_isset($arg) {
137
+ return ( isset($this->args[$arg]) );
138
+ }
139
+
140
+ /**
141
+ * Gets posts matching parameters and stores them in object
142
+ *
143
+ * @param int $limit (optional) Maximum number of posts to retrieve (Default: -1 = All matching posts)
144
+ * @param array $args (optional) Additional arguments to use in post query
145
+ * @return array Retrieved posts
146
+ */
147
+ function get( $limit = null, $args = null ) {
148
+ //Global variables
149
+ global $wp_query;
150
+
151
+ //Clear previously retrieved post data
152
+ $this->unload();
153
+
154
+ //Determine section
155
+ $p_arg = 'post_parent';
156
+ if ( ! $this->arg_isset($p_arg) ) {
157
+ $parent = null;
158
+ if ( is_page() ) {
159
+ $parent = $wp_query->get_queried_object_id();
160
+ }
161
+ if ( !! $parent )
162
+ $this->set_arg($p_arg, $parent);
163
+ }
164
+
165
+ //Check if parent is valid post ID
166
+ if ((int)$parent < 1) {
167
+ //Get featured posts from all sections if no valid parent is set
168
+ $parent = null;
169
+ }
170
+
171
+ //Set query args
172
+ if ( !empty($args) )
173
+ $this->args = wp_parse_args($args, $this->args);
174
+ //Set post limit
175
+ if ( is_numeric($limit) )
176
+ $limit = intval($limit);
177
+ if ( ! $limit && !$this->arg_isset('numberposts') )
178
+ $limit = ( is_feed() ) ? get_option('posts_per_rss') : get_option('posts_per_page');
179
+ if ( $limit > 0 )
180
+ $this->set_arg('numberposts', $limit);
181
+
182
+ //Set offset (pagination)
183
+ if ( !is_feed() ) {
184
+ $c_page = $wp_query->get('paged');
185
+ $offset = ( $c_page > 0 ) ? $c_page - 1 : 0;
186
+ $offset = $limit * $offset;
187
+ $this->set_arg('offset', $offset);
188
+ }
189
+
190
+ //Retrieve posts
191
+ $filter = 'found_posts';
192
+ $f_callback = $this->m('set_found');
193
+ $action = 'parse_query';
194
+ $a_callback = $this->m('set_found_flag');
195
+
196
+ //Add filter to populate found property during query
197
+ add_filter($filter, $f_callback);
198
+ add_action($action, $a_callback);
199
+ //Get posts
200
+ $posts = get_posts($this->args);
201
+ //Remove filter after query has completed
202
+ remove_action($action, $a_callback);
203
+ remove_filter($filter, $f_callback);
204
+ //Save retrieved posts to array
205
+ $this->load($posts);
206
+ //Return retrieved posts so that array may be manipulated further if desired
207
+ return $this->posts;
208
+ }
209
+
210
+ /**
211
+ * Sets object properties with post data
212
+ *
213
+ * @param array $posts Array of post objects
214
+ * @return void
215
+ */
216
+ function load( $posts = null ) {
217
+ $this->fetched = true;
218
+ if ( !empty($posts) ) {
219
+ $this->posts = $posts;
220
+ $this->has = true;
221
+ $this->count = count($this->posts);
222
+ }
223
+ }
224
+
225
+ /**
226
+ * Resets object properties to allow for new data to be saved
227
+ * @return void
228
+ */
229
+ function unload() {
230
+ //Temporarily save properties that should persist
231
+ $_args = $this->args;
232
+
233
+ //Initialize object properties
234
+ $this->init();
235
+
236
+ //Restore persistent properties
237
+ $this->args = $_args;
238
+ }
239
+
240
+ /**
241
+ * Sets number of found posts in object's query
242
+ * @link `found_posts` hook
243
+ * @see WP_Query::get_posts()
244
+ * @param int $num_found
245
+ */
246
+ function set_found($num_found) {
247
+ $this->found = $num_found;
248
+ }
249
+
250
+ /**
251
+ * Modifies query parameters to allow `found_posts` hook to be called
252
+ * Unsets `no_found_rows` query parameter set in WP 3.1
253
+ * @see WP_Query::parse_query()
254
+ * @link `parse_query` action hook
255
+ * @param WP_Query $q Query instance object
256
+ */
257
+ function set_found_flag(&$q) {
258
+ if ( isset($q->query_vars[$this->arg_fetch]) ) {
259
+ $q->query_vars['no_found_rows'] = false;
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Makes sure query was run prior
265
+ * @return void
266
+ */
267
+ function confirm_fetched() {
268
+ if ( !$this->fetched )
269
+ $this->get();
270
+ }
271
+
272
+ /**
273
+ * Returns number of matching posts found in DB
274
+ * May not necessarily match number of posts contained in object (due to post limits, pagination, etc.)
275
+ * @return int Number of posts found
276
+ */
277
+ function found() {
278
+ $this->confirm_fetched();
279
+ return $this->found;
280
+ }
281
+
282
+ /**
283
+ * Checks whether posts related to this object are available in the current context
284
+ *
285
+ * If no accessible posts are found, current post (section) is set as global post variable
286
+ *
287
+ * @see 'the_posts' filter
288
+ * @see get_children()
289
+ *
290
+ * @param bool $fetch Whether posts should be fetched if they have not yet been retrieved
291
+ * @return boolean TRUE if section contains children, FALSE otherwise
292
+ * Note: Will also return FALSE if section contains children, but all children have been previously accessed
293
+ *
294
+ * @global WP_Query $wp_query
295
+ * @global obj $post
296
+ */
297
+ function has( $fetch = true ) {
298
+ global $wp_query, $post, $more;
299
+
300
+ $this->confirm_fetched();
301
+
302
+ //Check if any posts on current page were retrieved
303
+ //If posts are found, make sure there are more posts
304
+ if ( $this->count > 0 && ( $this->current < $this->count - 1 ) ) {
305
+ return true;
306
+ }
307
+
308
+ //Reset current post position if all posts have been processed
309
+ $this->rewind();
310
+ $wp_query->in_the_loop = false;
311
+ //If no posts were found (or the last post has been previously loaded),
312
+ //load previous post back into global post variable
313
+ $i = ( $wp_query->current_post >= 0 ) ? $wp_query->current_post : 0;
314
+ if ( count($wp_query->posts) ) {
315
+ $post = $wp_query->posts[ $i ];
316
+ setup_postdata($post);
317
+ }
318
+
319
+ if ( is_single() || is_page() )
320
+ $more = 1;
321
+ return false;
322
+ }
323
+
324
+ /**
325
+ * Loads next post into global $post variable for use in the loop
326
+ * Allows use of WP template tags
327
+ * @return void
328
+ *
329
+ * @global obj $post Post object
330
+ */
331
+ function next() {
332
+ global $post, $more, $wp_query;
333
+
334
+ if ( $this->has() ) {
335
+ $wp_query->in_the_loop = true;
336
+ //Increment post position
337
+ $this->current++;
338
+
339
+ //Load post into global post variable
340
+ $post = $this->posts[ $this->current ];
341
+
342
+ setup_postdata($post);
343
+ $more = 0;
344
+ }
345
+ }
346
+
347
+ /**
348
+ * Resets position of current post
349
+ * Allows for multiple loops over $posts array
350
+ * @return void
351
+ */
352
+ function rewind() {
353
+ $this->current = -1;
354
+ }
355
+
356
+ /**
357
+ * Gets index of current post
358
+ * @return int Index position of current post
359
+ */
360
+ function current() {
361
+ return $this->current;
362
+ }
363
+
364
+ /**
365
+ * Returns number of posts in object
366
+ * @return int number of posts
367
+ */
368
+ function count() {
369
+ $this->confirm_fetched();
370
+ return $this->count;
371
+ }
372
+
373
+ /**
374
+ * Gets the number of pages needed to list all found posts
375
+ * @return int Total number of pages
376
+ */
377
+ function max_num_pages() {
378
+ $this->confirm_fetched();
379
+ $posts_per_page = $this->get_arg('numberposts');
380
+ if ( ! $posts_per_page )
381
+ $posts_per_page = get_option('posts_per_page');
382
+ return ceil( $this->found / $posts_per_page );
383
+ }
384
+
385
+ /**
386
+ * Checks if current post is the first post
387
+ * @return bool TRUE if current post is the first post in array, FALSE otherwise
388
+ */
389
+ function is_first() {
390
+ $this->confirm_fetched();
391
+ return ( 0 == $this->current() );
392
+ }
393
+
394
+ /**
395
+ * Checks if current featured post is the last item in the post array
396
+ * @return bool TRUE if item is the last featured item, FALSE otherwise
397
+ */
398
+ function is_last() {
399
+ $this->confirm_fetched();
400
+ return ($this->current == $this->count - 1) ? true : false;
401
+ }
402
+
403
+ /**
404
+ * @param int $post (optional) ID of post to check for existence in the object's posts array (uses global $post object if no value passed)
405
+ * @return bool TRUE if post is in posts array
406
+ */
407
+ function contains( $post = null ) {
408
+ $this->confirm_fetched();
409
+ //Use argument value if it is an integer
410
+ if ( is_numeric($post) && intval($post) > 0 ) {
411
+ //Cast to object and set ID property (for later use)
412
+ $post = (object) $post;
413
+ $post->ID = $post->scalar;
414
+ }
415
+ //Otherwise check if argument is valid post
416
+ elseif ( !$this->util->check_post($post) ) {
417
+ return false;
418
+ }
419
+
420
+ //Check for existence of post ID in posts array
421
+ return in_array($post->ID, $this->get_ids());
422
+ }
423
+
424
+ /**
425
+ * Retrieve IDs of all retrieved posts
426
+ */
427
+ function get_ids() {
428
+ $this->confirm_fetched();
429
+
430
+ if ( $this->has && empty($this->post_ids) ) {
431
+ //Build array of post ids in array
432
+ foreach ($this->posts as $post) {
433
+ $this->post_ids[] = $post->ID;
434
+ }
435
+ }
436
+
437
+ return $this->post_ids;
438
+ }
439
  }
js/lib.structure.admin.js CHANGED
@@ -1,7 +1,7 @@
1
  /**
2
  * Structure Administration
3
  * @package Cornerstone
4
- * @subpackage Structure
5
  */
6
 
7
  (function($) {
@@ -9,38 +9,112 @@
9
  if ( CNR && CNR.structure && CNR.structure.extend ) CNR.structure.extend('admin', {
10
  parent: CNR.structure,
11
  options: {},
12
-
13
  manage_option: function() {
14
  //Continue processing if permalink option has been defined
15
  if ( this.options && this.options.label && this.options.example && this.parent.options.permalink_structure ) {
 
 
16
  var o = this.options;
17
  var po = this.parent.options;
18
- //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 )
1
  /**
2
  * Structure Administration
3
  * @package Cornerstone
4
+ * @subpackage Structure
5
  */
6
 
7
  (function($) {
9
  if ( CNR && CNR.structure && CNR.structure.extend ) CNR.structure.extend('admin', {
10
  parent: CNR.structure,
11
  options: {},
12
+
13
  manage_option: function() {
14
  //Continue processing if permalink option has been defined
15
  if ( this.options && this.options.label && this.options.example && this.parent.options.permalink_structure ) {
16
+ const opts = this.options;
17
+ opts.parent = this.parent.options;
18
  var o = this.options;
19
  var po = this.parent.options;
20
+ // Options handling (WP 6.1+)
21
+ const cfg = {
22
+ selectors: {
23
+ match: '.structure-selection',
24
+ template: '.structure-selection > .row',
25
+ input: 'input[type="radio"]',
26
+ label: 'label',
27
+ example: 'p > code',
28
+ },
29
+ elements: {
30
+ template: null,
31
+ base: null,
32
+ input: null,
33
+ label: null,
34
+ example: null,
35
+ custom: document.getElementById( 'permalink_structure' ),
36
+ },
37
+ values: {
38
+ id_base: 'cnr-structured',
39
+ },
40
+ legacy: {
41
+ selectors: {
42
+ template: '.permalink-structure tr',
43
+ input: 'input[type="radio"]',
44
+ label: 'label',
45
+ example: 'td > code',
46
+ },
47
+ },
48
+ };
49
+ // Set up values
50
+ cfg.values.input_id = 'permalink-input-' + cfg.values.id_base;
51
+ cfg.values.example_id = 'permalink-' + cfg.values.id_base;
52
+ // Determine if legacy configuration should be used.
53
+ if ( !document.querySelector(cfg.selectors.match) ) {
54
+ let prop;
55
+ for ( prop in cfg.legacy ) {
56
+ cfg[ prop ] = cfg.legacy[ prop ];
57
+ }
58
+ }
59
+ // Reference elements object.
60
+ const els = cfg.elements;
61
+ // Retrieve single structure option to use as template for custom option.
62
+ els.template = document.querySelector( cfg.selectors.template );
63
+ // Stop if option not found.
64
+ if ( !els.template ) {
65
+ return false;
66
+ }
67
+ // Clone option element.
68
+ els.base = els.template.cloneNode( true );
69
+ // Input element.
70
+ els.input = els.base.querySelector( cfg.selectors.input );
71
+ if ( !els.input ) {
72
+ return false;
73
+ }
74
+ els.input.setAttribute( 'id', cfg.values.input_id );
75
+ els.input.setAttribute( 'value', opts.parent.permalink_structure );
76
+ if ( els.custom ) {
77
+ if ( els.custom.value === opts.parent.permalink_structure ) {
78
+ els.input.setAttribute( 'checked', 'checked' );
79
  } else {
80
+ els.input.removeAttribute( 'checked' );
81
+ }
82
+ // Event handler (option selected).
83
+ els.input.addEventListener('change', function() {
84
+ if ( els.custom.value === this.value ) {
85
+ return false;
86
+ }
87
+ els.custom.value = this.value;
88
+ });
89
+ }
90
+ // Label element.
91
+ els.label = els.base.querySelector( cfg.selectors.label );
92
+ if ( els.label ) {
93
+ // Attributes.
94
+ els.label.setAttribute( 'for', els.input.getAttribute( 'id' ) );
95
+ // Text.
96
+ // Fallback: Set text for label element directly.
97
+ let labelNode = els.label;
98
+ // Update text node within label element.
99
+ if ( els.label.childNodes.length > 1 ) {
100
+ // Get first text node.
101
+ let childNode = Array.from( els.label.childNodes ).find( cnode => cnode.nodeType === Node.TEXT_NODE );
102
+ if ( childNode ) {
103
+ labelNode = childNode;
104
+ }
105
  }
106
+ labelNode.textContent = labelNode.textContent.replace(/^(\s*)(\S.+?)(\s*)$/i, `\$1${opts.label}\$3`);
107
+ }
108
+ // Example permalink element.
109
+ els.example = els.base.querySelector( cfg.selectors.example );
110
+ if ( els.example ) {
111
+ els.example.setAttribute( 'id', cfg.values.example_id );
112
+ els.example.textContent = opts.example;
 
113
  }
114
+ // Insert option into list.
115
+ els.template.parentNode.insertBefore( els.base, els.template );
116
  }
117
+ }
118
  });
119
 
120
  if ( CNR.structure.admin.manage_option )
load.php CHANGED
@@ -1,65 +1,65 @@
1
- <?php
2
-
3
- /* Constants */
4
-
5
- if ( !defined('CNR_DEV') ) {
6
- define('CNR_DEV', ( isset( $_REQUEST['cnr_dev'] ) && !!$_REQUEST['cnr_dev'] ) );
7
- }
8
-
9
- /* Class Management */
10
-
11
- /**
12
- * Class loading handler
13
- * @param string $classname Class to load
14
- */
15
- function cnr_autoload($classname) {
16
- $prefix = 'cnr_';
17
- $cls = strtolower($classname);
18
- //Remove prefix
19
- if ( 0 !== strpos($cls, $prefix) ) {
20
- return false;
21
- }
22
- //Format class for filename
23
- $fn = 'class.' . substr($cls, strlen($prefix)) . '.php';
24
- //Build path
25
- $path = dirname(__FILE__) . '/' . "includes/" . $fn;
26
- //Load file
27
- if ( is_readable($path) ) {
28
- require $path;
29
- }
30
- }
31
-
32
- // Register autoloader
33
- spl_autoload_register('cnr_autoload');
34
-
35
- /* Load Assets */
36
- $path = dirname(__FILE__) . '/';
37
- require_once $path . 'controller.php';
38
- require_once $path . 'functions.php';
39
-
40
- /* Variables */
41
-
42
- //Global content type variables
43
- if ( !isset($GLOBALS['cnr_content_types']) )
44
- $GLOBALS['cnr_content_types'] = array();
45
- if ( !isset($GLOBALS['cnr_field_types']) )
46
- $GLOBALS['cnr_field_types'] = array();
47
-
48
- /* Init */
49
- $GLOBALS['cnr_content_utilities'] = new CNR_Content_Utilities();
50
- $GLOBALS['cnr_content_utilities']->init();
51
-
52
- /* Hooks */
53
-
54
- // Register Default placeholder handlers
55
- cnr_register_placeholder_handler('all', array('CNR_Field_Type', 'process_placeholder_default'), 11);
56
- cnr_register_placeholder_handler('field_id', array('CNR_Field_Type', 'process_placeholder_id'));
57
- cnr_register_placeholder_handler('field_name', array('CNR_Field_Type', 'process_placeholder_name'));
58
- cnr_register_placeholder_handler('data', array('CNR_Field_Type', 'process_placeholder_data'));
59
- cnr_register_placeholder_handler('loop', array('CNR_Field_Type', 'process_placeholder_loop'));
60
- cnr_register_placeholder_handler('data_ext', array('CNR_Field_Type', 'process_placeholder_data_ext'));
61
- cnr_register_placeholder_handler('rich_editor', array('CNR_Field_Type', 'process_placeholder_rich_editor'));
62
-
63
- /* Start */
64
-
65
  $GLOBALS['cnr'] = new Cornerstone();
1
+ <?php
2
+
3
+ /* Constants */
4
+
5
+ if ( !defined('CNR_DEV') ) {
6
+ define('CNR_DEV', ( isset( $_REQUEST['cnr_dev'] ) && !!$_REQUEST['cnr_dev'] ) );
7
+ }
8
+
9
+ /* Class Management */
10
+
11
+ /**
12
+ * Class loading handler
13
+ * @param string $classname Class to load
14
+ */
15
+ function cnr_autoload($classname) {
16
+ $prefix = 'cnr_';
17
+ $cls = strtolower($classname);
18
+ //Remove prefix
19
+ if ( 0 !== strpos($cls, $prefix) ) {
20
+ return false;
21
+ }
22
+ //Format class for filename
23
+ $fn = 'class.' . substr($cls, strlen($prefix)) . '.php';
24
+ //Build path
25
+ $path = dirname(__FILE__) . '/' . "includes/" . $fn;
26
+ //Load file
27
+ if ( is_readable($path) ) {
28
+ require $path;
29
+ }
30
+ }
31
+
32
+ // Register autoloader
33
+ spl_autoload_register('cnr_autoload');
34
+
35
+ /* Load Assets */
36
+ $path = dirname(__FILE__) . '/';
37
+ require_once $path . 'controller.php';
38
+ require_once $path . 'functions.php';
39
+
40
+ /* Variables */
41
+
42
+ //Global content type variables
43
+ if ( !isset($GLOBALS['cnr_content_types']) )
44
+ $GLOBALS['cnr_content_types'] = array();
45
+ if ( !isset($GLOBALS['cnr_field_types']) )
46
+ $GLOBALS['cnr_field_types'] = array();
47
+
48
+ /* Init */
49
+ $GLOBALS['cnr_content_utilities'] = new CNR_Content_Utilities();
50
+ $GLOBALS['cnr_content_utilities']->init();
51
+
52
+ /* Hooks */
53
+
54
+ // Register Default placeholder handlers
55
+ cnr_register_placeholder_handler('all', array('CNR_Field_Type', 'process_placeholder_default'), 11);
56
+ cnr_register_placeholder_handler('field_id', array('CNR_Field_Type', 'process_placeholder_id'));
57
+ cnr_register_placeholder_handler('field_name', array('CNR_Field_Type', 'process_placeholder_name'));
58
+ cnr_register_placeholder_handler('data', array('CNR_Field_Type', 'process_placeholder_data'));
59
+ cnr_register_placeholder_handler('loop', array('CNR_Field_Type', 'process_placeholder_loop'));
60
+ cnr_register_placeholder_handler('data_ext', array('CNR_Field_Type', 'process_placeholder_data_ext'));
61
+ cnr_register_placeholder_handler('rich_editor', array('CNR_Field_Type', 'process_placeholder_rich_editor'));
62
+
63
+ /* Start */
64
+
65
  $GLOBALS['cnr'] = new Cornerstone();
main.php CHANGED
@@ -4,17 +4,17 @@
4
  *
5
  * @package Cornerstone
6
  * @author Archetyped <support@archetyped.com>
7
- * @copyright 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.8
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;
4
  *
5
  * @package Cornerstone
6
  * @author Archetyped <support@archetyped.com>
7
+ * @copyright 2022 Archetyped
8
  *
9
  * Plugin Name: Cornerstone
10
  * Plugin URI: http://archetyped.com/tools/cornerstone/
11
  * Description: Enhanced content management for WordPress
12
+ * Version: 0.8.0
13
  * Requires at least: 5.3
14
  * Text Domain: cornerstone
15
  * Author: Archetyped
16
  * Author URI: http://archetyped.com
17
+ * Support URI: https://github.com/archetyped/cornerstone/wiki/Support-&-Feedback
18
  */
19
 
20
  $cnr = null;
package.json CHANGED
@@ -1,20 +1,20 @@
1
- {
2
- "name": "cornerstone",
3
- "version": "0.7.8",
4
- "title": "Cornerstone",
5
- "description": "Enhanced content management for WordPress",
6
- "author": "Archetyped <support@archetyped.com>",
7
- "license": "GPLv2",
8
- "private": true,
9
- "devDependencies": {
10
- "grunt": "^0.4.5",
11
- "grunt-contrib-jshint": "^0.11.2",
12
- "grunt-contrib-uglify": "^0.9.1",
13
- "grunt-contrib-watch": "^0.6.1",
14
- "grunt-phplint": "0.0.5",
15
- "grunt-sass": "^1.0.0",
16
- "jshint-stylish": "^2.0.1",
17
- "load-grunt-tasks": "^3.2.0",
18
- "time-grunt": "^1.2.1"
19
- }
20
- }
1
+ {
2
+ "name": "cornerstone",
3
+ "version": "0.8.0",
4
+ "title": "Cornerstone",
5
+ "description": "Enhanced content management for WordPress",
6
+ "author": "Archetyped <support@archetyped.com>",
7
+ "license": "GPLv2",
8
+ "private": true,
9
+ "devDependencies": {
10
+ "grunt": "^0.4.5",
11
+ "grunt-contrib-jshint": "^0.11.2",
12
+ "grunt-contrib-uglify": "^0.9.1",
13
+ "grunt-contrib-watch": "^0.6.1",
14
+ "grunt-phplint": "0.0.5",
15
+ "grunt-sass": "^1.0.0",
16
+ "jshint-stylish": "^2.0.1",
17
+ "load-grunt-tasks": "^3.2.0",
18
+ "time-grunt": "^1.2.1"
19
+ }
20
+ }
readme.txt CHANGED
@@ -1,137 +1,141 @@
1
- === Cornerstone ===
2
- Contributors: Archetyped
3
- Donate: https://gum.co/cnr-donate
4
- Tags: cornerstone, cms, content, management, system, structure, organization, sections
5
- Plugin Link: http://archetyped.com/tools/cornerstone/
6
- Requires at least: 5.3
7
- Tested up to: 5.4
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.8 =
64
-
65
- * Fix: Utilities: `count()` used on non-Countable value. (props @nikelaos)
66
-
67
- = 0.7.7 =
68
-
69
- * Fix: Spelling.
70
-
71
- = 0.7.6 =
72
-
73
- * Add: Block editor support for Custom Post Types (CPTs).
74
- * Update: WordPress 5.3 Compatibility confirmed.
75
- * Update: WordPress minimum version requirement (5.3).
76
- * Fix: Plugin initialization.
77
- * Fix: Display/Save custom field data in post/page editor.
78
- * Fix: Display/Save section in Quick Editor.
79
- * Optimize: Code cleanup.
80
-
81
- = 0.7.5 =
82
-
83
- * Update: Use plugin-specific text domain for localized strings
84
-
85
- = 0.7.4 =
86
-
87
- * Add: Text Domain header for translations
88
-
89
- = 0.7.3 =
90
-
91
- * Fix: Registration of Media field types
92
- * Update: Confirm compatibility with WordPress v4.3.1
93
-
94
- = 0.7.2 =
95
-
96
- * Update: WordPress 4.2.3 support
97
- * Optimize: Autoload classes
98
- * Optimize: Remove legacy PHP code
99
-
100
- = 0.7.1 =
101
-
102
- * Update: WordPress 3.8 support
103
- * Update: Readme content (description, features, links)
104
-
105
- = 0.7 =
106
-
107
- * Update: WordPress 3.6 support
108
- * Update: Remove deprecated jQuery code
109
- * Update: Remove legacy shortcode code
110
- * Optimize: Custom content types only added to main and CNR-initiated queries (Props Andy Owen)
111
- * Optimize: Remove legacy files/code
112
- * Optimize: Permalink structure reset when plugin activated/deactivated
113
- * Optimize: Post Editor: Update permalink preview when section changed
114
- * Optimize: Improved client-side file loading
115
- * Optimize: Encapsulate client-side code
116
- * Fix: Custom content types not displayed in sections (Unflappable Andy)
117
-
118
- = 0.6 =
119
-
120
- * Add: Content type support
121
- * Add: Media support
122
- * Add: Display tagline in home page title
123
- * Optimize: Internal performance
124
- * Optimize: Code cleanup
125
- * Fix: Section not always saved
126
- * Fix: Section posts fetched prior to checking posts' properties
127
- * Fix: Pagination compatibility for WordPress 3.1+
128
- * Fix: Various bug fixes
129
-
130
- = 0.5.1b =
131
-
132
- * Structure
133
- * Fix: Section column duplication bug
134
-
135
- = 0.5b =
136
-
137
- * Public beta
 
 
 
 
1
+ === Cornerstone ===
2
+ Contributors: Archetyped
3
+ Donate: https://gum.co/cnr-donate
4
+ Tags: cornerstone, cms, content, management, system, structure, organization, sections
5
+ Plugin Link: http://archetyped.com/tools/cornerstone/
6
+ Requires at least: 5.3
7
+ Tested up to: 6.1
8
+ Stable tag: trunk
9
+
10
+ Enhanced content management for WordPress
11
+
12
+ == Description ==
13
+ Cornerstone makes WordPress practical for **any type of site** by enhancing its content management capabilities. Too long have we had to resort to hacks like using categories in menus to build a pseudo site structure (you know what I'm talking about).
14
+
15
+ Cornerstone enhances your WordPress site in several ways. One of the most useful features is one that allows WordPress to be used for sites that go beyond just blogging-- **Posts in Sections**. Create a section, add posts to it, they show up when visitors navigate to that section. Simple as that. It really is, but because you're awesome [here's a tutorial on how to do it](http://archetyped.com/know/how-to-organize-posts-in-sections-in-wordpress-with-cornerstone/).
16
+
17
+ ### Thanks for the Support!
18
+
19
+ The support from the users that love Cornerstone is huge. You can support Cornerstone's future development and help to make it even better by donating or even just by sending me a nice message :)
20
+
21
+ [Donate to Cornerstone](https://gum.co/cnr-donate)
22
+
23
+ ### Features
24
+
25
+ * ''NEW: Content Types'' - Add custom fields to default (posts, pages, etc.) and custom (events, properties, etc.) post types
26
+ * Posts in Sections (see above for more info. Why are you reading from the bottom up?)
27
+ * Structured permalinks - post permalinks are based on the section they are in (e.g. ``/section-name/post-name/``)
28
+ * RSS for Sections - Let users subscribe to and receive updates for specific sections on the site.
29
+
30
+ ### Next Up
31
+
32
+ * Template functionality - enhanced page titles, featured content, etc.
33
+ * And more, which is where your feedback comes in.
34
+
35
+ [Plugin home page](http://archetyped.com/tools/cornerstone/)
36
+
37
+ == Installation ==
38
+
39
+ Install and Activate the plugin from the WordPress.org Plugins Repository
40
+
41
+ #### Tutorials
42
+
43
+ * [How to Organize Posts in Sections with Cornerstone](http://archetyped.com/know/how-to-organize-posts-in-sections-in-wordpress-with-cornerstone/)
44
+ * [Cornerstone: Creating Custom Content Types in WordPress](http://archetyped.com/know/cornerstone-creating-custom-content-types-in-wordpress/)
45
+ * [Cornerstone: Using Custom Field Data in WordPress](http://archetyped.com/know/cornerstone-using-custom-field-data-in-wordpress/)
46
+
47
+ == Upgrade Notice ==
48
+
49
+ No upgrade notices
50
+
51
+ == Frequently Asked Questions ==
52
+
53
+ Post your questions/comments at [Cornerstone's official issue tracker](https://github.com/archetyped/cornerstone/wiki/Support-&-Feedback).
54
+
55
+ == Screenshots ==
56
+
57
+ 1. Set a post's section on the post edit form
58
+ 1. Manage posts by section on post management page
59
+ 1. Quickly modify a post's section
60
+
61
+ == Changelog ==
62
+
63
+ = 0.8.0 =
64
+
65
+ * Update: Custom permalink structure option integration for WordPress 6.1+.
66
+
67
+ = 0.7.8 =
68
+
69
+ * Fix: Utilities: `count()` used on non-Countable value. (props @nikelaos)
70
+
71
+ = 0.7.7 =
72
+
73
+ * Fix: Spelling.
74
+
75
+ = 0.7.6 =
76
+
77
+ * Add: Block editor support for Custom Post Types (CPTs).
78
+ * Update: WordPress 5.3 Compatibility confirmed.
79
+ * Update: WordPress minimum version requirement (5.3).
80
+ * Fix: Plugin initialization.
81
+ * Fix: Display/Save custom field data in post/page editor.
82
+ * Fix: Display/Save section in Quick Editor.
83
+ * Optimize: Code cleanup.
84
+
85
+ = 0.7.5 =
86
+
87
+ * Update: Use plugin-specific text domain for localized strings
88
+
89
+ = 0.7.4 =
90
+
91
+ * Add: Text Domain header for translations
92
+
93
+ = 0.7.3 =
94
+
95
+ * Fix: Registration of Media field types
96
+ * Update: Confirm compatibility with WordPress v4.3.1
97
+
98
+ = 0.7.2 =
99
+
100
+ * Update: WordPress 4.2.3 support
101
+ * Optimize: Autoload classes
102
+ * Optimize: Remove legacy PHP code
103
+
104
+ = 0.7.1 =
105
+
106
+ * Update: WordPress 3.8 support
107
+ * Update: Readme content (description, features, links)
108
+
109
+ = 0.7 =
110
+
111
+ * Update: WordPress 3.6 support
112
+ * Update: Remove deprecated jQuery code
113
+ * Update: Remove legacy shortcode code
114
+ * Optimize: Custom content types only added to main and CNR-initiated queries (Props Andy Owen)
115
+ * Optimize: Remove legacy files/code
116
+ * Optimize: Permalink structure reset when plugin activated/deactivated
117
+ * Optimize: Post Editor: Update permalink preview when section changed
118
+ * Optimize: Improved client-side file loading
119
+ * Optimize: Encapsulate client-side code
120
+ * Fix: Custom content types not displayed in sections (Unflappable Andy)
121
+
122
+ = 0.6 =
123
+
124
+ * Add: Content type support
125
+ * Add: Media support
126
+ * Add: Display tagline in home page title
127
+ * Optimize: Internal performance
128
+ * Optimize: Code cleanup
129
+ * Fix: Section not always saved
130
+ * Fix: Section posts fetched prior to checking posts' properties
131
+ * Fix: Pagination compatibility for WordPress 3.1+
132
+ * Fix: Various bug fixes
133
+
134
+ = 0.5.1b =
135
+
136
+ * Structure
137
+ * Fix: Section column duplication bug
138
+
139
+ = 0.5b =
140
+
141
+ * Public beta