Version Description
- Fixes bug in formatting of YouTube videos
- Fixes bug in formatting of Facebook embeds
- Adds support for subtitles - see
instant_articles_subtitle
filter
Download this release
Release Info
Developer | wpcomvip |
Plugin | Instant Articles for WP |
Version | 0.2 |
Comparing to | |
See all releases |
Version 0.2
- LICENSE +340 -0
- class-instant-articles-dom-transform-filter-runner.php +51 -0
- class-instant-articles-post.php +717 -0
- compat.php +30 -0
- compat/class-instant-articles-co-authors-plus.php +50 -0
- compat/class-instant-articles-google-analytics-for-wordpress.php +50 -0
- compat/class-instant-articles-jetpack.php +84 -0
- compat/class-instant-articles-yoast-seo.php +69 -0
- dom-transform-filters/class-instant-articles-dom-transform-filter-address.php +77 -0
- dom-transform-filters/class-instant-articles-dom-transform-filter-blockquote.php +121 -0
- dom-transform-filters/class-instant-articles-dom-transform-filter-emptyelements.php +207 -0
- dom-transform-filters/class-instant-articles-dom-transform-filter-heading.php +78 -0
- dom-transform-filters/class-instant-articles-dom-transform-filter-image.php +151 -0
- dom-transform-filters/class-instant-articles-dom-transform-filter-ordered-list.php +77 -0
- dom-transform-filters/class-instant-articles-dom-transform-filter-table.php +83 -0
- dom-transform-filters/class-instant-articles-dom-transform-filter-unordered-list.php +77 -0
- dom-transform-filters/class-instant-articles-dom-transform-filter-video.php +80 -0
- dom-transform-filters/class-instant-articles-dom-transform-filter.php +111 -0
- embeds.php +131 -0
- facebook-instant-articles.php +187 -0
- feed-template.php +46 -0
- languages/instant-articles.pot +41 -0
- readme.txt +61 -0
- shortcodes.php +254 -0
- template.php +124 -0
LICENSE
ADDED
@@ -0,0 +1,340 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
GNU GENERAL PUBLIC LICENSE
|
2 |
+
Version 2, June 1991
|
3 |
+
|
4 |
+
Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
|
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 |
+
{description}
|
294 |
+
Copyright (C) {year} {fullname}
|
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.
|
340 |
+
|
class-instant-articles-dom-transform-filter-runner.php
ADDED
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Class for handling registrating and execution of the DOM transformation filters
|
5 |
+
*
|
6 |
+
* @since 0.1
|
7 |
+
*/
|
8 |
+
class Instant_Articles_DOM_Transform_Filter_Runner {
|
9 |
+
|
10 |
+
/** @var array Our array for keeping the classnames of the DOM transformation filters */
|
11 |
+
protected static $_stack = array();
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Register a DOM transformation filter class
|
15 |
+
*
|
16 |
+
* @since 0.1
|
17 |
+
* @param string $className The name of a class that extends Instant_Articles_DOM_Transform_Filter
|
18 |
+
* @param int $priority Optional. Used to specify the order in which the filters are executed.
|
19 |
+
* Default 10. Lower numbers correspond with earlier execution, and filters
|
20 |
+
* with the same priority are executed in the order in which they were added.
|
21 |
+
*/
|
22 |
+
static function register( $className, $priority = 10 ) {
|
23 |
+
if ( ! is_array( self::$_stack[ $priority ] ) ) {
|
24 |
+
self::$_stack[ $priority ] = array();
|
25 |
+
}
|
26 |
+
if ( ! in_array( $className, self::$_stack[ $priority ], true ) && is_subclass_of( $className, 'Instant_Articles_DOM_Transform_Filter' ) ) {
|
27 |
+
self::$_stack[ $priority ][] = $className;
|
28 |
+
}
|
29 |
+
}
|
30 |
+
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Run all the registered DOM tranformation filters
|
34 |
+
*
|
35 |
+
* @since 0.1
|
36 |
+
* @param DOMDocument $DOMDocument The DOMDocument we are working on
|
37 |
+
* @param int $post_id The current post ID
|
38 |
+
* @return DOMDocument The modified DOMDocument
|
39 |
+
*/
|
40 |
+
static function run( $DOMDocument, $post_id ) {
|
41 |
+
ksort( self::$_stack );
|
42 |
+
foreach ( self::$_stack as $priority => $filters ) {
|
43 |
+
foreach ( $filters as $className ) {
|
44 |
+
$obj = new $className( $DOMDocument, $post_id );
|
45 |
+
$obj->run();
|
46 |
+
}
|
47 |
+
}
|
48 |
+
return $DOMDocument;
|
49 |
+
}
|
50 |
+
|
51 |
+
}
|
class-instant-articles-post.php
ADDED
@@ -0,0 +1,717 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Class responsible for constructing our content and preparing it for rendering
|
5 |
+
*
|
6 |
+
* @since 0.1
|
7 |
+
*/
|
8 |
+
class Instant_Articles_Post {
|
9 |
+
|
10 |
+
/** @var int ID of the post */
|
11 |
+
protected $_ID = 0;
|
12 |
+
|
13 |
+
/** @var string Cached version of the Instant Article body */
|
14 |
+
protected $_content = null;
|
15 |
+
|
16 |
+
/** @var string The post’s optional subtitle */
|
17 |
+
protected $_subtitle = null;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Setup data and build the content
|
21 |
+
*
|
22 |
+
* @since 0.1
|
23 |
+
* @param int $post_id ID of the post
|
24 |
+
*/
|
25 |
+
function __construct( $post_id ) {
|
26 |
+
$this->_ID = $post_id;
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Get the ID for this post
|
31 |
+
*
|
32 |
+
* @since 0.1
|
33 |
+
* @return int The post ID
|
34 |
+
*/
|
35 |
+
public function get_the_ID() {
|
36 |
+
return $this->_ID;
|
37 |
+
}
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Get the title for this post
|
41 |
+
*
|
42 |
+
* @since 0.1
|
43 |
+
* @return string The title
|
44 |
+
*/
|
45 |
+
public function get_the_title() {
|
46 |
+
$title = get_the_title( $this->get_the_ID() );
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Filter the post title for use in instant articles
|
50 |
+
*
|
51 |
+
* @since 0.1
|
52 |
+
*
|
53 |
+
* @param string $title The current post title.
|
54 |
+
* @param Instant_Article_Post $instant_article_post The instant article post
|
55 |
+
*/
|
56 |
+
$title = apply_filters( 'instant_articles_title', $title, $this );
|
57 |
+
|
58 |
+
return $title;
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Get the title for this post
|
63 |
+
*
|
64 |
+
* @since 0.1
|
65 |
+
* @return string The title
|
66 |
+
*/
|
67 |
+
public function get_the_title_rss() {
|
68 |
+
$title = $this->get_the_title();
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Apply the default WP Filters for the post title for use in a feed. This ensures proper escaping.
|
72 |
+
*
|
73 |
+
* @since 0.1
|
74 |
+
*
|
75 |
+
* @param string $title The current post title.
|
76 |
+
*/
|
77 |
+
$title = apply_filters( 'the_title_rss', $title );
|
78 |
+
|
79 |
+
return $title;
|
80 |
+
}
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Check if this post has a subtitle
|
84 |
+
*
|
85 |
+
* @since 0.2
|
86 |
+
* @return bool Whether the post has a subtitle or not
|
87 |
+
*/
|
88 |
+
public function has_subtitle() {
|
89 |
+
|
90 |
+
$has_subtitle = false;
|
91 |
+
|
92 |
+
$subtitle = $this->get_the_subtitle();
|
93 |
+
|
94 |
+
if ( strlen( $subtitle ) ) {
|
95 |
+
$has_subtitle = true;
|
96 |
+
}
|
97 |
+
|
98 |
+
return $subtitle;
|
99 |
+
}
|
100 |
+
|
101 |
+
/**
|
102 |
+
* Get the subtitle for this post
|
103 |
+
*
|
104 |
+
* @since 0.2
|
105 |
+
* @return string The subtitle
|
106 |
+
*/
|
107 |
+
public function get_the_subtitle() {
|
108 |
+
|
109 |
+
// If we have already been through this function, we’ll have the result stored here
|
110 |
+
if ( ! is_null( $this->_subtitle ) ) {
|
111 |
+
return $this->_subtitle;
|
112 |
+
}
|
113 |
+
|
114 |
+
$subtitle = '';
|
115 |
+
|
116 |
+
/**
|
117 |
+
* Filter the subtitle for use in instant articles
|
118 |
+
*
|
119 |
+
* @since 0.2
|
120 |
+
*
|
121 |
+
* @param string $subtitle The current subtitle for the post.
|
122 |
+
* @param Instant_Article_Post $instant_article_post The instant article post
|
123 |
+
*/
|
124 |
+
$subtitle = apply_filters( 'instant_articles_subtitle', $subtitle, $this );
|
125 |
+
|
126 |
+
$this->_subtitle = $subtitle;
|
127 |
+
|
128 |
+
return $subtitle;
|
129 |
+
}
|
130 |
+
|
131 |
+
/**
|
132 |
+
* Get the canonical URL for this post
|
133 |
+
*
|
134 |
+
* A little warning here: It is extremely important that this is the same canonical URL as is used on the web site.
|
135 |
+
* This is the identificator Facebook use to connect the "read" web article with the instant article.
|
136 |
+
* Do not add any querystring params or URL fragments it. Not any. Not even for tracking.
|
137 |
+
*
|
138 |
+
* @since 0.1
|
139 |
+
* @return string The canonical URL
|
140 |
+
*/
|
141 |
+
public function get_canonical_url() {
|
142 |
+
$url = get_permalink( $this->get_the_ID() );
|
143 |
+
|
144 |
+
return $url;
|
145 |
+
}
|
146 |
+
|
147 |
+
/**
|
148 |
+
* Get the excerpt for this post
|
149 |
+
*
|
150 |
+
* @since 0.1
|
151 |
+
* @return string The excerpt
|
152 |
+
*/
|
153 |
+
public function get_the_excerpt() {
|
154 |
+
|
155 |
+
$post = get_post( $this->get_the_ID() );
|
156 |
+
|
157 |
+
// This should ideally not happen, but it may do so if someone tampers with the query.
|
158 |
+
// Returning the same protected post excerpt as "usual" may help them identify what’s going on.
|
159 |
+
if ( post_password_required( $this->get_the_ID() ) ) {
|
160 |
+
return __( 'There is no excerpt because this is a protected post.' );
|
161 |
+
}
|
162 |
+
|
163 |
+
// Make sure no “read more” link is added
|
164 |
+
add_filter( 'excerpt_more', '__return_empty_string', 999 );
|
165 |
+
|
166 |
+
/**
|
167 |
+
* Apply the default WP Filters for the post excerpt
|
168 |
+
*
|
169 |
+
* @since 0.1
|
170 |
+
*
|
171 |
+
* @param string $post_excerpt The post excerpt.
|
172 |
+
*/
|
173 |
+
$excerpt = apply_filters( 'get_the_excerpt', $post->post_excerpt );
|
174 |
+
|
175 |
+
/**
|
176 |
+
* Filter the post excerpt for instant articles
|
177 |
+
*
|
178 |
+
* @since 0.1
|
179 |
+
*
|
180 |
+
* @param string $excerpt The current post excerpt.
|
181 |
+
* @param Instant_Article_Post $instant_article_post The instant article post
|
182 |
+
*/
|
183 |
+
$excerpt = apply_filters( 'instant_articles_excerpt', $excerpt, $this );
|
184 |
+
|
185 |
+
return $excerpt;
|
186 |
+
}
|
187 |
+
|
188 |
+
/**
|
189 |
+
* Get the excerpt for this post
|
190 |
+
*
|
191 |
+
* @since 0.1
|
192 |
+
* @return string The excerpt
|
193 |
+
*/
|
194 |
+
public function get_the_excerpt_rss() {
|
195 |
+
|
196 |
+
$excerpt = $this->get_the_excerpt();
|
197 |
+
|
198 |
+
/**
|
199 |
+
* Apply the default WP Filters for the post excerpt for use in a feed. This ensures proper escaping.
|
200 |
+
*
|
201 |
+
* @since 0.1
|
202 |
+
*
|
203 |
+
* @param string $excerpt The current post excerpt.
|
204 |
+
*/
|
205 |
+
$excerpt = apply_filters( 'the_excerpt_rss', $excerpt );
|
206 |
+
|
207 |
+
return $excerpt;
|
208 |
+
}
|
209 |
+
|
210 |
+
/**
|
211 |
+
* Get the article body for this post
|
212 |
+
*
|
213 |
+
* @since 0.1
|
214 |
+
* @return string The content
|
215 |
+
*/
|
216 |
+
public function get_the_content() {
|
217 |
+
|
218 |
+
if ( is_null( $this->_content ) ) {
|
219 |
+
$this->_content = $this->_get_the_content();
|
220 |
+
}
|
221 |
+
|
222 |
+
return $this->_content;
|
223 |
+
|
224 |
+
}
|
225 |
+
|
226 |
+
/**
|
227 |
+
* Put together the article body for this post
|
228 |
+
*
|
229 |
+
* @since 0.1
|
230 |
+
* @return string The content
|
231 |
+
*/
|
232 |
+
protected function _get_the_content() {
|
233 |
+
|
234 |
+
// Try to get the content from a transient, but only if the cached version have the same modtime
|
235 |
+
$cache_mod_time = get_transient( 'instantarticles_mod_' . $this->get_the_ID() );
|
236 |
+
if ( $cache_mod_time == get_post_modified_time( 'Y-m-d H:i:s', true, $this->get_the_ID() ) ) {
|
237 |
+
$content = get_transient( 'instantarticles_content_' . $this->get_the_ID() );
|
238 |
+
if ( $content !== false && strlen( $content ) ) {
|
239 |
+
return $content;
|
240 |
+
}
|
241 |
+
}
|
242 |
+
|
243 |
+
global $post, $more;
|
244 |
+
|
245 |
+
// force $more
|
246 |
+
$orig_more = $more;
|
247 |
+
$more = 1;
|
248 |
+
|
249 |
+
// If we’re not it the loop or otherwise properly setup
|
250 |
+
$reset_postdata = false;
|
251 |
+
if ( $this->get_the_ID() !== $post->ID ) {
|
252 |
+
$post = get_post( $this->get_the_ID() );
|
253 |
+
setup_postdata( $post );
|
254 |
+
$reset_postdata = true;
|
255 |
+
}
|
256 |
+
|
257 |
+
// Now get the content
|
258 |
+
$content = get_the_content();
|
259 |
+
|
260 |
+
/**
|
261 |
+
* Apply the default filter 'the_content' for the post content
|
262 |
+
*
|
263 |
+
* @since 0.1
|
264 |
+
* @param string $content The current post content.
|
265 |
+
*/
|
266 |
+
$content = apply_filters( 'the_content', $content );
|
267 |
+
|
268 |
+
// Maybe cleanup some globals after us?
|
269 |
+
$more = $orig_more;
|
270 |
+
if ( $reset_postdata ) {
|
271 |
+
wp_reset_postdata();
|
272 |
+
}
|
273 |
+
|
274 |
+
// Some people choose to disable wpautop. Due to the Instant Articles spec, we really want it in!
|
275 |
+
$content = wpautop( $content );
|
276 |
+
|
277 |
+
/**
|
278 |
+
* Filter the post content for Instant Articles
|
279 |
+
*
|
280 |
+
* @since 0.1
|
281 |
+
* @param string $content The post content.
|
282 |
+
*/
|
283 |
+
$content = apply_filters( 'instant_articles_content', $content );
|
284 |
+
|
285 |
+
|
286 |
+
if ( class_exists( 'DOMDocument' ) && has_action( 'instant_articles_register_dom_transformation_filters' ) ) {
|
287 |
+
|
288 |
+
/* If we have filters that wants to work on the DOM, we generate one instance of DOMDocument
|
289 |
+
they can all work on, instead of having to handle the conversion themselves. */
|
290 |
+
|
291 |
+
$libxml_previous_state = libxml_use_internal_errors( true );
|
292 |
+
$DOMDocument = new DOMDocument( '1.0', get_option( 'blog_charset' ) );
|
293 |
+
|
294 |
+
// DOMDocument isn’t handling encodings too well, so let’s help it a little
|
295 |
+
if ( function_exists( 'mb_convert_encoding' ) ) {
|
296 |
+
$content = mb_convert_encoding( $content, 'HTML-ENTITIES', get_option( 'blog_charset' ) );
|
297 |
+
}
|
298 |
+
|
299 |
+
$result = $DOMDocument->loadHTML( '<!doctype html><html><body>' . $content . '</body></html>' );
|
300 |
+
libxml_clear_errors();
|
301 |
+
libxml_use_internal_errors( $libxml_previous_state );
|
302 |
+
|
303 |
+
if ( $result ) {
|
304 |
+
|
305 |
+
// Register the DOM transformation filters if not done yet
|
306 |
+
if ( ! did_action( 'instant_articles_register_dom_transformation_filters' ) ) {
|
307 |
+
do_action( 'instant_articles_register_dom_transformation_filters' );
|
308 |
+
}
|
309 |
+
Instant_Articles_DOM_Transform_Filter_Runner::run( $DOMDocument, $this->get_the_ID() );
|
310 |
+
|
311 |
+
|
312 |
+
$body = $DOMDocument->getElementsByTagName( 'body' )->item( 0 );
|
313 |
+
|
314 |
+
$filtered_content = '';
|
315 |
+
foreach ( $body->childNodes as $node ) {
|
316 |
+
if ( method_exists( $DOMDocument, 'saveHTML' ) ) { // Requires PHP 5.3.6
|
317 |
+
$filtered_content .= $DOMDocument->saveHTML( $node );
|
318 |
+
} else {
|
319 |
+
$temp_content = $DOMDocument->saveXML( $node );
|
320 |
+
$iframe_pattern = "#<iframe([^>]+)/>#is"; // self-closing iframe element
|
321 |
+
$temp_content = preg_replace( $iframe_pattern, "<iframe$1></iframe>", $temp_content );
|
322 |
+
$filtered_content .= $temp_content;
|
323 |
+
}
|
324 |
+
}
|
325 |
+
|
326 |
+
$content = $filtered_content;
|
327 |
+
unset( $filtered_content );
|
328 |
+
|
329 |
+
}
|
330 |
+
|
331 |
+
}
|
332 |
+
|
333 |
+
// Cache the content
|
334 |
+
set_transient( 'instantarticles_mod_' . $this->get_the_ID(), get_post_modified_time( 'Y-m-d H:i:s', true, $this->get_the_ID() ), WEEK_IN_SECONDS );
|
335 |
+
set_transient( 'instantarticles_content_' . $this->get_the_ID(), $content, WEEK_IN_SECONDS );
|
336 |
+
|
337 |
+
return $content;
|
338 |
+
}
|
339 |
+
|
340 |
+
/**
|
341 |
+
* Get the published date for this post
|
342 |
+
*
|
343 |
+
* @since 0.1
|
344 |
+
* @return string The published date
|
345 |
+
*/
|
346 |
+
public function get_the_pubdate() {
|
347 |
+
|
348 |
+
$date = get_post_time( get_option( 'date_format' ), true, $this->get_the_ID() );
|
349 |
+
|
350 |
+
/**
|
351 |
+
* Filter the post date for instant articles
|
352 |
+
*
|
353 |
+
* @since 0.1
|
354 |
+
*
|
355 |
+
* @param string $date The current post date.
|
356 |
+
* @param Instant_Article_Post $instant_article_post The instant article post
|
357 |
+
*/
|
358 |
+
$date = apply_filters( 'instant_articles_date', $date, $this );
|
359 |
+
|
360 |
+
return $date;
|
361 |
+
|
362 |
+
}
|
363 |
+
|
364 |
+
/**
|
365 |
+
* Get the modified date for this post
|
366 |
+
*
|
367 |
+
* @since 0.1
|
368 |
+
* @return string The modified date
|
369 |
+
*/
|
370 |
+
public function get_the_moddate() {
|
371 |
+
|
372 |
+
$modified_date = get_post_modified_time( get_option('date_format'), true, $this->get_the_ID() );
|
373 |
+
|
374 |
+
/**
|
375 |
+
* Filter the post modified date for instant articles
|
376 |
+
*
|
377 |
+
* @since 0.1
|
378 |
+
*
|
379 |
+
* @param string $modified_date The current post modified date.
|
380 |
+
* @param Instant_Article_Post $instant_article_post The instant article post
|
381 |
+
*/
|
382 |
+
$modified_date = apply_filters( 'instant_articles_modified_date', $modified_date, $this );
|
383 |
+
|
384 |
+
return $modified_date;
|
385 |
+
|
386 |
+
}
|
387 |
+
|
388 |
+
/**
|
389 |
+
* Get the published date for this post (ISO 8601)
|
390 |
+
*
|
391 |
+
* @since 0.1
|
392 |
+
* @return string The published date formatted suitable for use in the RSS feed and the html time elements (ISO 8601)
|
393 |
+
*/
|
394 |
+
public function get_the_pubdate_iso() {
|
395 |
+
|
396 |
+
$published_date = mysql2date( 'c', get_post_time( 'Y-m-d H:i:s', true, $this->get_the_ID() ), false );
|
397 |
+
|
398 |
+
/**
|
399 |
+
* Filter the post published date (ISO 8601)
|
400 |
+
*
|
401 |
+
* @since 0.1
|
402 |
+
*
|
403 |
+
* @param string $published_date The current post published date.
|
404 |
+
* @param Instant_Article_Post $instant_article_post The instant article post
|
405 |
+
*/
|
406 |
+
$published_date = apply_filters( 'instant_articles_published_date_iso', $published_date, $this );
|
407 |
+
|
408 |
+
return $published_date;
|
409 |
+
|
410 |
+
}
|
411 |
+
|
412 |
+
/**
|
413 |
+
* Get the modified date for this post (ISO 8601)
|
414 |
+
*
|
415 |
+
* @since 0.1
|
416 |
+
* @return string The modified date formatted suitable for use in the RSS feed and the html time elements (ISO 8601)
|
417 |
+
*/
|
418 |
+
public function get_the_moddate_iso() {
|
419 |
+
|
420 |
+
$modified_date = mysql2date( 'c', get_post_modified_time( 'Y-m-d H:i:s', true, $this->get_the_ID() ), false );
|
421 |
+
|
422 |
+
/**
|
423 |
+
* Filter the post modified date (ISO 8601)
|
424 |
+
*
|
425 |
+
* @since 0.1
|
426 |
+
*
|
427 |
+
* @param string $modified_date The current post modified date.
|
428 |
+
* @param Instant_Article_Post $instant_article_post The instant article post
|
429 |
+
*/
|
430 |
+
$modified_date = apply_filters( 'instant_articles_modified_date_iso', $modified_date, $this );
|
431 |
+
|
432 |
+
return $modified_date;
|
433 |
+
|
434 |
+
}
|
435 |
+
/**
|
436 |
+
* Get the author(s)
|
437 |
+
*
|
438 |
+
* @since 0.1
|
439 |
+
* @return array $authors The post author(s)
|
440 |
+
*/
|
441 |
+
public function get_the_authors() {
|
442 |
+
|
443 |
+
$authors = array();
|
444 |
+
|
445 |
+
$post = get_post( $this->get_the_ID() );
|
446 |
+
|
447 |
+
$WP_User = get_userdata( $post->post_author );
|
448 |
+
|
449 |
+
if ( is_a( $WP_User, 'WP_User' ) ) {
|
450 |
+
$author = new stdClass;
|
451 |
+
$author->ID = $WP_User->ID;
|
452 |
+
$author->display_name = $WP_User->data->display_name;
|
453 |
+
$author->first_name = $WP_User->first_name;
|
454 |
+
$author->last_name = $WP_User->last_name;
|
455 |
+
$author->user_login = $WP_User->data->user_login;
|
456 |
+
$author->user_nicename = $WP_User->data->user_nicename;
|
457 |
+
$author->user_email = $WP_User->data->user_email;
|
458 |
+
$author->user_url = $WP_User->data->user_url;
|
459 |
+
$author->bio = $WP_User->description;
|
460 |
+
|
461 |
+
$authors[] = $author;
|
462 |
+
}
|
463 |
+
|
464 |
+
/**
|
465 |
+
* Filter the post author(s)
|
466 |
+
*
|
467 |
+
* @since 0.1
|
468 |
+
*
|
469 |
+
* @param array $authors The current post author(s).
|
470 |
+
* @param int $post_id The instant article post
|
471 |
+
*/
|
472 |
+
$authors = apply_filters( 'instant_articles_authors', $authors, $this->get_the_ID() );
|
473 |
+
|
474 |
+
return $authors;
|
475 |
+
}
|
476 |
+
|
477 |
+
/**
|
478 |
+
* Get featured image for cover
|
479 |
+
*
|
480 |
+
* @since 0.1
|
481 |
+
* @return array {
|
482 |
+
* Array containing image source and caption.
|
483 |
+
*
|
484 |
+
* @type string $src Image URL
|
485 |
+
* @type string $caption Image caption
|
486 |
+
* }
|
487 |
+
*/
|
488 |
+
public function get_the_featured_image() {
|
489 |
+
|
490 |
+
$image_data = array(
|
491 |
+
'src' => '',
|
492 |
+
'caption' => '',
|
493 |
+
);
|
494 |
+
if ( has_post_thumbnail( $this->get_the_ID() ) ) {
|
495 |
+
|
496 |
+
$image_array = wp_get_attachment_image_src( get_post_thumbnail_id( $this->get_the_ID() ), 'full' );
|
497 |
+
$attachment_id = get_post_thumbnail_id( $this->get_the_ID() );
|
498 |
+
|
499 |
+
if ( is_array( $image_array ) ) {
|
500 |
+
$image_data['src'] = $image_array[0];
|
501 |
+
$attachment_post = get_post( $attachment_id );
|
502 |
+
if ( is_a( $attachment_post, 'WP_Post' ) ) {
|
503 |
+
$image_data['caption'] = $attachment_post->post_excerpt;
|
504 |
+
}
|
505 |
+
}
|
506 |
+
}
|
507 |
+
|
508 |
+
/**
|
509 |
+
* Filter the featured image
|
510 |
+
*
|
511 |
+
* @since 0.1
|
512 |
+
* @param array $image_data {
|
513 |
+
* Array containg image source and caption.
|
514 |
+
*
|
515 |
+
* @type string $src Image URL
|
516 |
+
* @type string $caption Image caption
|
517 |
+
* }
|
518 |
+
* @param int $post_id The post ID
|
519 |
+
*/
|
520 |
+
$image_data = apply_filters( 'instant_articles_featured_image', $image_data, $this->get_the_ID() );
|
521 |
+
return $image_data;
|
522 |
+
}
|
523 |
+
|
524 |
+
/**
|
525 |
+
* Get the cover media
|
526 |
+
*
|
527 |
+
* @since 0.1
|
528 |
+
* @return array
|
529 |
+
*/
|
530 |
+
public function get_cover_media() {
|
531 |
+
|
532 |
+
$cover_media = new stdClass;
|
533 |
+
$cover_media->type = 'none';
|
534 |
+
|
535 |
+
// If someone else is handling this, let them. Otherwise fall back to us trying to use the featured image.
|
536 |
+
if ( has_filter( 'instant_articles_cover_media' ) ) {
|
537 |
+
/**
|
538 |
+
* Filter the cover media
|
539 |
+
*
|
540 |
+
* @since 0.1
|
541 |
+
* @param stdClass $cover_media The cover media object
|
542 |
+
* @param int $post_id The current post ID
|
543 |
+
*/
|
544 |
+
$cover_media = apply_filters( 'instant_articles_cover_media', $cover_media, $this->get_the_ID() );
|
545 |
+
} else {
|
546 |
+
$featured_image_data = $this->get_the_featured_image();
|
547 |
+
if ( isset( $featured_image_data['src'] ) && strlen( $featured_image_data['src'] ) ) {
|
548 |
+
$cover_media->type = 'image';
|
549 |
+
$cover_media->src = $featured_image_data['src'];
|
550 |
+
$cover_media->caption = $featured_image_data['caption'];
|
551 |
+
}
|
552 |
+
}
|
553 |
+
|
554 |
+
return $cover_media;
|
555 |
+
}
|
556 |
+
|
557 |
+
/**
|
558 |
+
* Get kicker text
|
559 |
+
*
|
560 |
+
* @since 0.1
|
561 |
+
* @return string
|
562 |
+
*/
|
563 |
+
public function get_the_kicker() {
|
564 |
+
|
565 |
+
$category = '';
|
566 |
+
|
567 |
+
if ( has_category() ) {
|
568 |
+
$categories = get_the_category();
|
569 |
+
|
570 |
+
if ( is_array( $categories ) && isset( $categories[0]->name ) && __( 'Uncategorized' ) !== $categories[0]->name ) {
|
571 |
+
$category = $categories[0]->name;
|
572 |
+
}
|
573 |
+
}
|
574 |
+
|
575 |
+
/**
|
576 |
+
* Filter the kicker text
|
577 |
+
*
|
578 |
+
* @since 0.1
|
579 |
+
*
|
580 |
+
* @param string $category The first category returned from get_the_category().
|
581 |
+
* @param int $post_id The post ID
|
582 |
+
*/
|
583 |
+
$category_kicker = apply_filters('instant_articles_cover_kicker', $category, $this->get_the_ID() );
|
584 |
+
|
585 |
+
return $category_kicker ? $category_kicker : '';
|
586 |
+
}
|
587 |
+
|
588 |
+
/**
|
589 |
+
* Get newsfeed cover type, image or video
|
590 |
+
*
|
591 |
+
* @since 0.1
|
592 |
+
* @return string
|
593 |
+
*/
|
594 |
+
public function get_newsfeed_cover() {
|
595 |
+
|
596 |
+
$type = 'image';
|
597 |
+
|
598 |
+
/**
|
599 |
+
* Filter the cover type property
|
600 |
+
*
|
601 |
+
* @since 0.1
|
602 |
+
*
|
603 |
+
* @param string $type Set to 'video' for video cover. Featured image (image) is default.
|
604 |
+
* @param int $post_id The post ID
|
605 |
+
*/
|
606 |
+
$type = apply_filters( 'instant_articles_cover_type', $type, $this->get_the_ID() );
|
607 |
+
|
608 |
+
return $type;
|
609 |
+
}
|
610 |
+
|
611 |
+
/**
|
612 |
+
* Get credits for footer.
|
613 |
+
*
|
614 |
+
* @since 0.1
|
615 |
+
* @return string
|
616 |
+
*/
|
617 |
+
public function get_the_footer_credits() {
|
618 |
+
|
619 |
+
/**
|
620 |
+
* Filter credits
|
621 |
+
*
|
622 |
+
* @since 0.1
|
623 |
+
*
|
624 |
+
* @param string No credits set by default.
|
625 |
+
* @param int The post ID
|
626 |
+
*/
|
627 |
+
$footer_credits = apply_filters( 'instant_articles_footer_credits', '', $this->get_the_ID() );
|
628 |
+
return $footer_credits;
|
629 |
+
}
|
630 |
+
|
631 |
+
|
632 |
+
/**
|
633 |
+
* Get copyright for footer
|
634 |
+
*
|
635 |
+
* @since 0.1
|
636 |
+
* @return string
|
637 |
+
*/
|
638 |
+
public function get_the_footer_copyright() {
|
639 |
+
|
640 |
+
/**
|
641 |
+
* Filter copyright
|
642 |
+
*
|
643 |
+
* @since 0.1
|
644 |
+
*
|
645 |
+
* @param string No copyright set by default.
|
646 |
+
* @param int The post ID
|
647 |
+
*/
|
648 |
+
$footer_copyright = apply_filters( 'instant_articles_footer_copyright', '', $this->get_the_ID() );
|
649 |
+
return $footer_copyright;
|
650 |
+
}
|
651 |
+
|
652 |
+
/**
|
653 |
+
* Render post
|
654 |
+
*
|
655 |
+
* @since 0.1
|
656 |
+
*/
|
657 |
+
function render() {
|
658 |
+
|
659 |
+
/**
|
660 |
+
* Fires before the instant article is rendered
|
661 |
+
*
|
662 |
+
* @since 0.1
|
663 |
+
* @param Instant_Article_Post $instant_article_post The instant article post
|
664 |
+
*/
|
665 |
+
do_action( 'instant_articles_before_render_post', $this );
|
666 |
+
|
667 |
+
$default_template = dirname( __FILE__ ) . '/template.php';
|
668 |
+
|
669 |
+
/**
|
670 |
+
* Filter the path to the template to use to render the instant article
|
671 |
+
*
|
672 |
+
* @since 0.1
|
673 |
+
* @param string $template Path to the current (default) template.
|
674 |
+
* @param Instant_Article_Post $instant_article_post The instant article post
|
675 |
+
*/
|
676 |
+
$template = apply_filters( 'instant_articles_render_post_template', $default_template, $this );
|
677 |
+
|
678 |
+
// Make sure the template exists. Devs do the darndest things.
|
679 |
+
// Note on validate_file(): Return value of 0 means nothing is wrong, greater than 0 means something was wrong.
|
680 |
+
if ( ! file_exists( $template ) || validate_file( $template ) ) {
|
681 |
+
$template = $default_template;
|
682 |
+
}
|
683 |
+
include $template;
|
684 |
+
|
685 |
+
/**
|
686 |
+
* Fires after the instant article is rendered
|
687 |
+
*
|
688 |
+
* @since 0.1
|
689 |
+
* @param Instant_Article_Post $instant_article_post The instant article post
|
690 |
+
*/
|
691 |
+
do_action( 'instant_articles_after_render_post', $this );
|
692 |
+
}
|
693 |
+
|
694 |
+
/**
|
695 |
+
* Article <head> style
|
696 |
+
*
|
697 |
+
* @since 0.1
|
698 |
+
* @return string The article style
|
699 |
+
*/
|
700 |
+
function get_article_style() {
|
701 |
+
|
702 |
+
/**
|
703 |
+
* Filter the article style to use
|
704 |
+
*
|
705 |
+
* @since 0.1
|
706 |
+
* @param string $template Path to the current (default) template.
|
707 |
+
* @param Instant_Article_Post $instant_article_post The instant article post
|
708 |
+
*/
|
709 |
+
$article_style = apply_filters( 'instant_articles_style', 'default', $this );
|
710 |
+
|
711 |
+
return $article_style;
|
712 |
+
}
|
713 |
+
|
714 |
+
}
|
715 |
+
|
716 |
+
|
717 |
+
|
compat.php
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// Load compat layer for Co-Authors Plus
|
4 |
+
if ( function_exists( 'get_coauthors' ) && ! defined( 'CAP_IA_COMPAT' ) ) {
|
5 |
+
include( dirname( __FILE__ ) . '/compat/class-instant-articles-co-authors-plus.php' );
|
6 |
+
$cap = new Instant_Articles_Co_Authors_Plus;
|
7 |
+
$cap->init();
|
8 |
+
}
|
9 |
+
|
10 |
+
// Load compat layer for Yoast SEO
|
11 |
+
if ( defined( 'WPSEO_VERSION' ) && ! defined( 'WPSEO_IA_COMPAT' ) ) {
|
12 |
+
include( dirname( __FILE__ ) . '/compat/class-instant-articles-yoast-seo.php' );
|
13 |
+
$yseo = new Instant_Articles_Yoast_SEO;
|
14 |
+
$yseo->init();
|
15 |
+
}
|
16 |
+
|
17 |
+
// Load support for Google Analytics for WordPress (Google Analytics by Yoast)
|
18 |
+
if ( defined( 'GAWP_VERSION' ) && ! defined( 'GAWP_IA_COMPAT' ) ) {
|
19 |
+
include( dirname( __FILE__ ) . '/compat/class-instant-articles-google-analytics-for-wordpress.php' );
|
20 |
+
$gawp = new Instant_Articles_Google_Analytics_For_WordPress;
|
21 |
+
$gawp->init();
|
22 |
+
}
|
23 |
+
|
24 |
+
// Load support for Jetpack
|
25 |
+
if ( defined( 'JETPACK__VERSION' ) ) {
|
26 |
+
include( dirname( __FILE__ ) . '/compat/class-instant-articles-jetpack.php' );
|
27 |
+
$jp = new Instant_Articles_Jetpack;
|
28 |
+
$jp->init();
|
29 |
+
}
|
30 |
+
|
compat/class-instant-articles-co-authors-plus.php
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Compatibility layer for Co-Authors Plus
|
5 |
+
*
|
6 |
+
* @since 0.1
|
7 |
+
*
|
8 |
+
*/
|
9 |
+
class Instant_Articles_Co_Authors_Plus {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Init the compat layer
|
13 |
+
*
|
14 |
+
*/
|
15 |
+
function init() {
|
16 |
+
add_filter( 'instant_articles_authors', array( $this, 'authors' ), 10, 2 );
|
17 |
+
}
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Filter the authors
|
21 |
+
*
|
22 |
+
* @param array $authors The current authors
|
23 |
+
* @param int $post_id The current post ID
|
24 |
+
*/
|
25 |
+
function authors( $authors, $post_id ) {
|
26 |
+
if ( function_exists( 'get_coauthors' ) ) {
|
27 |
+
$coauthors = get_coauthors( $post_id );
|
28 |
+
|
29 |
+
$authors = array();
|
30 |
+
foreach( $coauthors as $coauthor ) {
|
31 |
+
|
32 |
+
$author = new stdClass;
|
33 |
+
$author->ID = $coauthor->ID;
|
34 |
+
$author->display_name = is_a( $coauthor, 'WP_User' ) ? $coauthor->data->display_name : $coauthor->display_name;
|
35 |
+
$author->first_name = $coauthor->first_name;
|
36 |
+
$author->last_name = $coauthor->last_name;
|
37 |
+
$author->user_login = is_a( $coauthor, 'WP_User' ) ? $coauthor->data->user_login : $coauthor->user_login;
|
38 |
+
$author->user_nicename = is_a( $coauthor, 'WP_User' ) ? $coauthor->data->user_nicename : $coauthor->user_nicename;
|
39 |
+
$author->user_email = is_a( $coauthor, 'WP_User' ) ? $coauthor->data->user_email : $coauthor->user_email;
|
40 |
+
$author->user_url = is_a( $coauthor, 'WP_User' ) ? $coauthor->data->user_url : $coauthor->website;
|
41 |
+
$author->bio = $coauthor->description;
|
42 |
+
|
43 |
+
$authors[] = $author;
|
44 |
+
}
|
45 |
+
}
|
46 |
+
|
47 |
+
return $authors;
|
48 |
+
}
|
49 |
+
|
50 |
+
}
|
compat/class-instant-articles-google-analytics-for-wordpress.php
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Support class for Google Analytics for WordPress (Google Analytics by Yoast)
|
5 |
+
*
|
6 |
+
* @since 0.1
|
7 |
+
*
|
8 |
+
*/
|
9 |
+
class Instant_Articles_Google_Analytics_For_WordPress {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Init the compat layer
|
13 |
+
*
|
14 |
+
*/
|
15 |
+
function init() {
|
16 |
+
add_filter( 'instant_articles_content', array( $this, 'add_ga_code' ), 10, 1 );
|
17 |
+
}
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Add the GA tracking code to the end of the content
|
21 |
+
*
|
22 |
+
* @since 0.1
|
23 |
+
* @param string $content The post content.
|
24 |
+
*/
|
25 |
+
function add_ga_code( $content ) {
|
26 |
+
|
27 |
+
$options = Yoast_GA_Options::instance()->options;
|
28 |
+
|
29 |
+
if ( isset( $options['enable_universal'] ) && 1 == $options['enable_universal'] ) {
|
30 |
+
$tracker = new Yoast_GA_Universal;
|
31 |
+
} else {
|
32 |
+
$tracker = new Yoast_GA_JS;
|
33 |
+
}
|
34 |
+
|
35 |
+
ob_start();
|
36 |
+
$tracker->tracking();
|
37 |
+
$ga_code = ob_get_clean();
|
38 |
+
|
39 |
+
if ( strlen( $ga_code ) ) {
|
40 |
+
$ga = '<figure class="op-tracker"><iframe>';
|
41 |
+
$ga .= $ga_code;
|
42 |
+
$ga .= '</iframe></figure>';
|
43 |
+
|
44 |
+
$content .= $ga;
|
45 |
+
}
|
46 |
+
|
47 |
+
return $content;
|
48 |
+
}
|
49 |
+
|
50 |
+
}
|
compat/class-instant-articles-jetpack.php
ADDED
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Support class for Jetpack
|
5 |
+
*
|
6 |
+
* @since 0.2
|
7 |
+
*
|
8 |
+
*/
|
9 |
+
class Instant_Articles_Jetpack {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Init the compat layer
|
13 |
+
*
|
14 |
+
*/
|
15 |
+
function init() {
|
16 |
+
$this->_fix_youtube_embed();
|
17 |
+
$this->_fix_facebook_embed();
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Remove the YouTube embed handling in Jetpack
|
22 |
+
*
|
23 |
+
*/
|
24 |
+
private function _fix_youtube_embed() {
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Do not "fix" bare URLs on their own line of the form
|
28 |
+
* http://www.youtube.com/v/9FhMMmqzbD8?fs=1&hl=en_US
|
29 |
+
* as we have oEmbed to handle those
|
30 |
+
* Registered in jetpack/modules/shortcodes/youtube.php
|
31 |
+
*/
|
32 |
+
wp_embed_unregister_handler( 'wpcom_youtube_embed_crazy_url' );
|
33 |
+
}
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Fix the Facebook embed handling
|
37 |
+
*
|
38 |
+
*/
|
39 |
+
private function _fix_facebook_embed() {
|
40 |
+
|
41 |
+
// All of these are registered in jetpack/modules/shortcodes/facebook.php
|
42 |
+
|
43 |
+
if ( defined( 'JETPACK_FACEBOOK_EMBED_REGEX' ) ) {
|
44 |
+
wp_embed_unregister_handler( 'facebook' );
|
45 |
+
wp_embed_register_handler( 'facebook', JETPACK_FACEBOOK_EMBED_REGEX, array( __CLASS__, 'facebook_embed_handler' ) );
|
46 |
+
}
|
47 |
+
if ( defined( 'JETPACK_FACEBOOK_ALTERNATE_EMBED_REGEX' ) ) {
|
48 |
+
wp_embed_unregister_handler( 'facebook-alternate' );
|
49 |
+
wp_embed_register_handler( 'facebook-alternate', JETPACK_FACEBOOK_ALTERNATE_EMBED_REGEX, array( __CLASS__, 'facebook_embed_handler' ) );
|
50 |
+
}
|
51 |
+
if ( defined( 'JETPACK_FACEBOOK_PHOTO_EMBED_REGEX' ) ) {
|
52 |
+
wp_embed_unregister_handler( 'facebook-photo' );
|
53 |
+
wp_embed_register_handler( 'facebook-photo', JETPACK_FACEBOOK_PHOTO_EMBED_REGEX, array( __CLASS__, 'facebook_embed_handler' ) );
|
54 |
+
}
|
55 |
+
if ( defined( 'JETPACK_FACEBOOK_PHOTO_ALTERNATE_EMBED_REGEX' ) ) {
|
56 |
+
wp_embed_unregister_handler( 'facebook-alternate-photo' );
|
57 |
+
wp_embed_register_handler( 'facebook-alternate-photo', JETPACK_FACEBOOK_PHOTO_ALTERNATE_EMBED_REGEX, array( __CLASS__, 'facebook_embed_handler' ) );
|
58 |
+
}
|
59 |
+
if ( defined( 'JETPACK_FACEBOOK_VIDEO_EMBED_REGEX' ) ) {
|
60 |
+
wp_embed_unregister_handler( 'facebook-video' );
|
61 |
+
wp_embed_register_handler( 'facebook-video', JETPACK_FACEBOOK_VIDEO_EMBED_REGEX, array( __CLASS__, 'facebook_embed_handler' ) );
|
62 |
+
}
|
63 |
+
if ( defined( 'JETPACK_FACEBOOK_VIDEO_ALTERNATE_EMBED_REGEX' ) ) {
|
64 |
+
wp_embed_unregister_handler( 'facebook-alternate-video' );
|
65 |
+
wp_embed_register_handler( 'facebook-alternate-video', JETPACK_FACEBOOK_VIDEO_ALTERNATE_EMBED_REGEX, array( __CLASS__, 'facebook_embed_handler' ) );
|
66 |
+
}
|
67 |
+
}
|
68 |
+
|
69 |
+
public static function facebook_embed_handler( $matches, $attr, $url ) {
|
70 |
+
|
71 |
+
$locale = get_locale();
|
72 |
+
|
73 |
+
// Source: https://www.facebook.com/translations/FacebookLocales.xml
|
74 |
+
$fbLocales = array( 'af_ZA', 'ak_GH', 'am_ET', 'ar_AR', 'as_IN', 'ay_BO', 'az_AZ', 'be_BY', 'bg_BG', 'bn_IN', 'br_FR', 'bs_BA', 'ca_ES', 'cb_IQ', 'ck_US', 'co_FR', 'cs_CZ', 'cx_PH', 'cy_GB', 'da_DK', 'de_DE', 'el_GR', 'en_GB', 'en_IN', 'en_PI', 'en_UD', 'en_US', 'eo_EO', 'es_CL', 'es_CO', 'es_ES', 'es_LA', 'es_MX', 'es_VE', 'et_EE', 'eu_ES', 'fa_IR', 'fb_LT', 'ff_NG', 'fi_FI', 'fo_FO', 'fr_CA', 'fr_FR', 'fy_NL', 'ga_IE', 'gl_ES', 'gn_PY', 'gu_IN', 'gx_GR', 'ha_NG', 'he_IL', 'hi_IN', 'hr_HR', 'ht_HT', 'hu_HU', 'hy_AM', 'id_ID', 'ig_NG', 'is_IS', 'it_IT', 'ja_JP', 'ja_KS', 'jv_ID', 'ka_GE', 'kk_KZ', 'km_KH', 'kn_IN', 'ko_KR', 'ku_TR', 'ky_KG', 'la_VA', 'lg_UG', 'li_NL', 'ln_CD', 'lo_LA', 'lt_LT', 'lv_LV', 'mg_MG', 'mi_NZ', 'mk_MK', 'ml_IN', 'mn_MN', 'mr_IN', 'ms_MY', 'mt_MT', 'my_MM', 'nb_NO', 'nd_ZW', 'ne_NP', 'nl_BE', 'nl_NL', 'nn_NO', 'ny_MW', 'or_IN', 'pa_IN', 'pl_PL', 'ps_AF', 'pt_BR', 'pt_PT', 'qc_GT', 'qu_PE', 'rm_CH', 'ro_RO', 'ru_RU', 'rw_RW', 'sa_IN', 'sc_IT', 'se_NO', 'si_LK', 'sk_SK', 'sl_SI', 'sn_ZW', 'so_SO', 'sq_AL', 'sr_RS', 'sv_SE', 'sw_KE', 'sy_SY', 'sz_PL', 'ta_IN', 'te_IN', 'tg_TJ', 'th_TH', 'tk_TM', 'tl_PH', 'tl_ST', 'tr_TR', 'tt_RU', 'tz_MA', 'uk_UA', 'ur_PK', 'uz_UZ', 'vi_VN', 'wo_SN', 'xh_ZA', 'yi_DE', 'yo_NG', 'zh_CN', 'zh_HK', 'zh_TW', 'zu_ZA', 'zz_TR', );
|
75 |
+
|
76 |
+
// If our locale isn’t supported by Facebook, we’ll fallback to en_US
|
77 |
+
if ( ! in_array( $locale, $fbLocales) ) {
|
78 |
+
$locale = 'en_US';
|
79 |
+
}
|
80 |
+
|
81 |
+
return '<figure class="op-social"><iframe><script src="//connect.facebook.net/' . $locale . '/sdk.js#xfbml=1&version=v2.2" async></script><div class="fb-post" data-href="' . esc_url( $url ) . '"></div></iframe></figure>';
|
82 |
+
}
|
83 |
+
|
84 |
+
}
|
compat/class-instant-articles-yoast-seo.php
ADDED
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Compatibility layer for Yoast SEO
|
5 |
+
*
|
6 |
+
* @since 0.1
|
7 |
+
*
|
8 |
+
*/
|
9 |
+
class Instant_Articles_Yoast_SEO {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Init the compat layer
|
13 |
+
*
|
14 |
+
*/
|
15 |
+
function init() {
|
16 |
+
add_filter( 'instant_articles_featured_image', array( $this, 'override_featured_image' ), 10, 2 );
|
17 |
+
add_filter( 'instant_articles_authors', array( $this, 'user_url' ), 11 , 2 ); // Hook in after other author modifications (like the Co-Authors Plus plugin)
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Override the featured image with the one set for Facebook
|
22 |
+
*
|
23 |
+
* @since 0.1
|
24 |
+
* @param array $image data The current image data
|
25 |
+
* @param int $post_id The instant article post
|
26 |
+
* @return array The filtered image data
|
27 |
+
*/
|
28 |
+
function override_featured_image( $image_data, $post_id ) {
|
29 |
+
|
30 |
+
$image_url = get_post_meta( $post_id, '_yoast_wpseo_opengraph-image', true );
|
31 |
+
|
32 |
+
if ( strlen( $image_url ) ) {
|
33 |
+
$image_data[ 'src' ] = $image_url;
|
34 |
+
|
35 |
+
$desc = get_post_meta( $post_id, '_yoast_wpseo_opengraph-description', true );
|
36 |
+
|
37 |
+
if ( strlen( $desc ) ) {
|
38 |
+
$image_data[ 'caption' ] = $desc;
|
39 |
+
}
|
40 |
+
}
|
41 |
+
|
42 |
+
return $image_data;
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Use the facebook URL as user_url if no other is set
|
47 |
+
*
|
48 |
+
* @since 0.1
|
49 |
+
* @param array $authors The current post author(s).
|
50 |
+
* @param int $post_id The instant article post
|
51 |
+
* @return array The filtered authors
|
52 |
+
*/
|
53 |
+
function user_url( $authors, $post_id ) {
|
54 |
+
|
55 |
+
foreach ( $authors as $author ) {
|
56 |
+
if ( ! strlen( $author->user_url ) ) {
|
57 |
+
$facebook_profile_url = get_user_meta( $author->ID, 'facebook', true );
|
58 |
+
if ( strlen( $facebook_profile_url ) ) {
|
59 |
+
$author->user_url = $facebook_profile_url;
|
60 |
+
$author->user_url_rel = 'facebook';
|
61 |
+
}
|
62 |
+
}
|
63 |
+
}
|
64 |
+
|
65 |
+
return $authors;
|
66 |
+
|
67 |
+
}
|
68 |
+
|
69 |
+
}
|
dom-transform-filters/class-instant-articles-dom-transform-filter-address.php
ADDED
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Instant Articles DOM Transformation Filter for Address elements
|
5 |
+
*
|
6 |
+
* @since 0.1
|
7 |
+
*/
|
8 |
+
class Instant_Articles_DOM_Transform_Filter_Address extends Instant_Articles_DOM_Transform_Filter {
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Run the transformation
|
12 |
+
*
|
13 |
+
* Implements the abstract method from Instant_Articles_DOM_Transform_Filter
|
14 |
+
*
|
15 |
+
* @since 0.1
|
16 |
+
* @return DOMDocument
|
17 |
+
*/
|
18 |
+
public function run() {
|
19 |
+
|
20 |
+
$DOMNodeList = $this->_DOMDocument->getElementsByTagName( 'address' );
|
21 |
+
|
22 |
+
// Transform all nodes found
|
23 |
+
$this->_transform_elements( $DOMNodeList );
|
24 |
+
|
25 |
+
return $this->_DOMDocument;
|
26 |
+
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Build a DOMDocumentFragment for the element
|
31 |
+
*
|
32 |
+
* @since 0.1
|
33 |
+
* @return DOMDocumentFragment The fragment ready to be inserted into the DOM
|
34 |
+
*/
|
35 |
+
protected function _build_fragment( $properties ) {
|
36 |
+
|
37 |
+
$DOMDocumentFragment = $this->_DOMDocument->createDocumentFragment();
|
38 |
+
|
39 |
+
$element = $this->_DOMDocument->createElement( 'p' );
|
40 |
+
while ( $properties->childNodes->length ) {
|
41 |
+
$element->appendChild( $properties->childNodes->item( 0 ) );
|
42 |
+
}
|
43 |
+
|
44 |
+
$DOMDocumentFragment->appendChild( $element );
|
45 |
+
|
46 |
+
return $DOMDocumentFragment;
|
47 |
+
}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Find the element properties
|
51 |
+
*
|
52 |
+
* Implements the abstract method from Instant_Articles_DOM_Transform_Filter
|
53 |
+
*
|
54 |
+
* @since 0.1
|
55 |
+
* @param $DOMNode $DOMNode The original domnode
|
56 |
+
* @return stdClass The element properties used for building the new fragment
|
57 |
+
*/
|
58 |
+
protected function get_properties( $DOMNode ) {
|
59 |
+
|
60 |
+
$properties = new stdClass;
|
61 |
+
|
62 |
+
$properties->childNodes = $DOMNode->childNodes;
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Filter the video element properties
|
66 |
+
*
|
67 |
+
* @since 0.1
|
68 |
+
* @param object $properties The element properties
|
69 |
+
* @param int $post_id The post ID of the current post
|
70 |
+
*/
|
71 |
+
$properties = apply_filters( 'instant_articles_address_properties', $properties, $this->_post_id );
|
72 |
+
|
73 |
+
return $properties;
|
74 |
+
|
75 |
+
}
|
76 |
+
|
77 |
+
}
|
dom-transform-filters/class-instant-articles-dom-transform-filter-blockquote.php
ADDED
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Instant Articles DOM Transformation Filter for Blockquotes
|
5 |
+
*
|
6 |
+
* @since 0.1
|
7 |
+
*/
|
8 |
+
class Instant_Articles_DOM_Transform_Filter_Blockquote extends Instant_Articles_DOM_Transform_Filter {
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Run the transformation
|
12 |
+
*
|
13 |
+
* Implements the abstract method from Instant_Articles_DOM_Transform_Filter
|
14 |
+
*
|
15 |
+
* @since 0.1
|
16 |
+
* @return DOMDocument
|
17 |
+
*/
|
18 |
+
public function run() {
|
19 |
+
|
20 |
+
$DOMNodeList = $this->_DOMDocument->getElementsByTagName( 'blockquote' );
|
21 |
+
|
22 |
+
// Transform all nodes found
|
23 |
+
$this->_transform_elements( $DOMNodeList );
|
24 |
+
|
25 |
+
return $this->_DOMDocument;
|
26 |
+
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Build a DOMDocumentFragment for the element
|
31 |
+
*
|
32 |
+
* @since 0.1
|
33 |
+
* @return DOMDocumentFragment The fragment ready to be inserted into the DOM
|
34 |
+
*/
|
35 |
+
protected function _build_fragment( $properties ) {
|
36 |
+
|
37 |
+
$DOMDocumentFragment = $this->_DOMDocument->createDocumentFragment();
|
38 |
+
|
39 |
+
$quoteContainerType = 'blockquote';
|
40 |
+
if ( strlen( $properties->cite ) ) {
|
41 |
+
$quoteContainerType = 'aside';
|
42 |
+
}
|
43 |
+
|
44 |
+
$quoteContainer = $this->_DOMDocument->createElement( $quoteContainerType );
|
45 |
+
$quoteContainer->appendChild( $this->_DOMDocument->createTextNode( $properties->quote ) );
|
46 |
+
|
47 |
+
if ( is_a( $properties->childNodes, 'DOMNodeList' ) ) {
|
48 |
+
foreach( $properties->childNodes as $pNode ) {
|
49 |
+
$newNode = $this->_DOMDocument->createElement( 'p' );
|
50 |
+
$newNode->appendChild( $this->_DOMDocument->createTextNode( trim( $pNode->nodeValue ) ) );
|
51 |
+
$quoteContainer->appendChild( $newNode );
|
52 |
+
}
|
53 |
+
}
|
54 |
+
|
55 |
+
if ( strlen( $properties->cite ) ) {
|
56 |
+
$citeNode = $this->_DOMDocument->createElement( 'cite' );
|
57 |
+
$citeNode->appendChild( $this->_DOMDocument->createTextNode( $properties->cite ) );
|
58 |
+
$quoteContainer->appendChild( $citeNode );
|
59 |
+
}
|
60 |
+
|
61 |
+
$DOMDocumentFragment->appendChild( $quoteContainer );
|
62 |
+
|
63 |
+
return $DOMDocumentFragment;
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Find the element properties
|
68 |
+
*
|
69 |
+
* Implements the abstract method from Instant_Articles_DOM_Transform_Filter
|
70 |
+
*
|
71 |
+
* @since 0.1
|
72 |
+
* @param $DOMNode $DOMNode The original domnode
|
73 |
+
* @return stdClass The element properties used for building the new fragment
|
74 |
+
*/
|
75 |
+
protected function get_properties( $DOMNode ) {
|
76 |
+
|
77 |
+
$properties = new stdClass;
|
78 |
+
|
79 |
+
$properties->quote = $DOMNode->nodeValue;
|
80 |
+
$properties->cite = '';
|
81 |
+
$properties->childNodes = null;
|
82 |
+
|
83 |
+
$cite = $DOMNode->getAttribute( 'cite' );
|
84 |
+
if ( strlen( $cite ) ) {
|
85 |
+
$properties->cite = $cite;
|
86 |
+
}
|
87 |
+
|
88 |
+
$citeNodeList = $DOMNode->getElementsByTagName( 'cite' );
|
89 |
+
if ( $citeNodeList->length ) {
|
90 |
+
$citeNode = $citeNodeList->item( 0 );
|
91 |
+
if ( strlen( $citeNode->nodeValue ) ) {
|
92 |
+
$properties->cite = $citeNode->nodeValue;
|
93 |
+
}
|
94 |
+
while ( $citeNodeList->length ) {
|
95 |
+
$citeNode = $citeNodeList->item( 0 );
|
96 |
+
$citeNode->parentNode->removeChild( $citeNode );
|
97 |
+
}
|
98 |
+
}
|
99 |
+
|
100 |
+
$pNodeList = $DOMNode->getElementsByTagName( 'p' );
|
101 |
+
if ( 1 === $pNodeList->length ) {
|
102 |
+
$properties->quote = $pNodeList->item( 0 )->nodeValue;
|
103 |
+
} elseif ( $pNodeList->length ) {
|
104 |
+
$properties->quote = '';
|
105 |
+
$properties->childNodes = $pNodeList;
|
106 |
+
}
|
107 |
+
|
108 |
+
/**
|
109 |
+
* Filter the blockquote element properties
|
110 |
+
*
|
111 |
+
* @since 0.1
|
112 |
+
* @param object $properties The element properties
|
113 |
+
* @param int $post_id The post ID of the current post
|
114 |
+
*/
|
115 |
+
$properties = apply_filters( 'instant_articles_blockquote_properties', $properties, $this->_post_id );
|
116 |
+
|
117 |
+
return $properties;
|
118 |
+
|
119 |
+
}
|
120 |
+
|
121 |
+
}
|
dom-transform-filters/class-instant-articles-dom-transform-filter-emptyelements.php
ADDED
@@ -0,0 +1,207 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Instant Articles DOM Transformation Filter for empty elements
|
5 |
+
*
|
6 |
+
* @since 0.1
|
7 |
+
*/
|
8 |
+
class Instant_Articles_DOM_Transform_Filter_Emptyelements extends Instant_Articles_DOM_Transform_Filter {
|
9 |
+
|
10 |
+
// Elements excluded from checking: img, iframe, html, head, body, base, br, hr, link, meta, meter, script, audio, video, map, canvas, col, data, embed, input, keygen, menuitem, object, output, param, progress, source, textarea, track
|
11 |
+
protected $_checkTagNames = array(
|
12 |
+
'a',
|
13 |
+
'abbr',
|
14 |
+
'address',
|
15 |
+
'article',
|
16 |
+
'aside',
|
17 |
+
'b',
|
18 |
+
'bdi',
|
19 |
+
'bdo',
|
20 |
+
'blockquote',
|
21 |
+
'button',
|
22 |
+
'caption',
|
23 |
+
'cite',
|
24 |
+
'code',
|
25 |
+
'colgroup',
|
26 |
+
'datalist',
|
27 |
+
'dd',
|
28 |
+
'del',
|
29 |
+
'details',
|
30 |
+
'dfn',
|
31 |
+
'dialog',
|
32 |
+
'div',
|
33 |
+
'dl',
|
34 |
+
'dt',
|
35 |
+
'em',
|
36 |
+
'fieldset',
|
37 |
+
'figcaption',
|
38 |
+
'figure',
|
39 |
+
'footer',
|
40 |
+
'form',
|
41 |
+
'h1',
|
42 |
+
'h2',
|
43 |
+
'h3',
|
44 |
+
'h4',
|
45 |
+
'h5',
|
46 |
+
'h6',
|
47 |
+
'header',
|
48 |
+
'hgroup',
|
49 |
+
'i',
|
50 |
+
'ins',
|
51 |
+
'kbd',
|
52 |
+
'label',
|
53 |
+
'legend',
|
54 |
+
'li ',
|
55 |
+
'main',
|
56 |
+
'map',
|
57 |
+
'mark',
|
58 |
+
'menu',
|
59 |
+
'nav',
|
60 |
+
'noscript',
|
61 |
+
'ol',
|
62 |
+
'optgroup',
|
63 |
+
'option',
|
64 |
+
'p',
|
65 |
+
'pre',
|
66 |
+
'q',
|
67 |
+
'rb',
|
68 |
+
'rp',
|
69 |
+
'rt',
|
70 |
+
'rtc',
|
71 |
+
'ruby',
|
72 |
+
's',
|
73 |
+
'samp',
|
74 |
+
'section',
|
75 |
+
'select',
|
76 |
+
'small',
|
77 |
+
'span',
|
78 |
+
'strong',
|
79 |
+
'style',
|
80 |
+
'sub',
|
81 |
+
'summary',
|
82 |
+
'sup',
|
83 |
+
'table',
|
84 |
+
'tbody',
|
85 |
+
'td',
|
86 |
+
'template',
|
87 |
+
'tfoot',
|
88 |
+
'th',
|
89 |
+
'thead',
|
90 |
+
'time',
|
91 |
+
'title',
|
92 |
+
'tr',
|
93 |
+
'u',
|
94 |
+
'ul',
|
95 |
+
'var',
|
96 |
+
'wbr',
|
97 |
+
);
|
98 |
+
|
99 |
+
/**
|
100 |
+
* Run the transformation
|
101 |
+
*
|
102 |
+
* Implements the abstract method from Instant_Articles_DOM_Transform_Filter
|
103 |
+
*
|
104 |
+
* @since 0.1
|
105 |
+
* @return DOMDocument
|
106 |
+
*/
|
107 |
+
public function run() {
|
108 |
+
|
109 |
+
|
110 |
+
$xpathQuery = '//' . implode( ' | //', $this->_checkTagNames );;
|
111 |
+
|
112 |
+
$xpath = new DOMXpath( $this->_DOMDocument );
|
113 |
+
$DOMNodeList = $xpath->query( $xpathQuery );
|
114 |
+
|
115 |
+
// Filter out empty elements
|
116 |
+
$this->_filter_empty_elements( $DOMNodeList );
|
117 |
+
|
118 |
+
return $this->_DOMDocument;
|
119 |
+
|
120 |
+
}
|
121 |
+
|
122 |
+
/**
|
123 |
+
* Remove all empty elements
|
124 |
+
*
|
125 |
+
* @param DOMNodeList $DOMNodeList The list of all elements we are checking
|
126 |
+
* @return DOMNodeList The modified list of all remaining elements
|
127 |
+
*/
|
128 |
+
protected function _filter_empty_elements( DOMNodeList $DOMNodeList ) {
|
129 |
+
|
130 |
+
$NodeListIndex = 0;
|
131 |
+
|
132 |
+
// We might reduce $DOMNodeList->length during the loop
|
133 |
+
for ( $NodeListIndex = 0; $NodeListIndex < $DOMNodeList->length; ++$NodeListIndex ) {
|
134 |
+
|
135 |
+
$DOMNode = $DOMNodeList->item( $NodeListIndex );
|
136 |
+
|
137 |
+
if ( ! isset( $DOMNode->nodeName ) || ! in_array( $DOMNode->nodeName, $this->_checkTagNames, true ) ) {
|
138 |
+
continue;
|
139 |
+
}
|
140 |
+
|
141 |
+
// Climb up to make sure we’re not in an Instant Article element (which should have proper handling elsewhere)
|
142 |
+
$parentNode = $DOMNode;
|
143 |
+
while ( isset( $parentNode->nodeName ) && $parentNode->nodeName != 'body' ) {
|
144 |
+
if ( 'figure' == $parentNode->nodeName && false !== strpos( $parentNode->getAttribute( 'class' ), 'op-' ) ) {
|
145 |
+
// We found an element that’s likely to be an Instant Article element
|
146 |
+
continue 2;
|
147 |
+
}
|
148 |
+
$parentNode = $parentNode->parentNode;
|
149 |
+
}
|
150 |
+
|
151 |
+
|
152 |
+
// Check all childnodes first
|
153 |
+
if ( is_a( $DOMNode, 'DOMElement' ) && isset( $DOMNode->childNodes ) && is_a( $DOMNode->childNodes, 'DOMNodeList' ) ) {
|
154 |
+
$this->_filter_empty_elements( $DOMNode->childNodes );
|
155 |
+
}
|
156 |
+
|
157 |
+
|
158 |
+
if ( isset( $DOMNode->nodeValue ) && '' == trim( $DOMNode->nodeValue ) ) {
|
159 |
+
|
160 |
+
if ( ! isset( $DOMNode->childNodes ) || is_null( $DOMNode->childNodes ) || ( is_a( $DOMNode->childNodes, 'DOMNodeList' ) && ! $DOMNode->childNodes->length ) ) {
|
161 |
+
|
162 |
+
// If the element is an empty node, remove it. But we must have a parentNode to remove a node
|
163 |
+
if ( is_a( $DOMNode->parentNode, 'DOMElement' ) ) {
|
164 |
+
$DOMNode->parentNode->removeChild( $DOMNode );
|
165 |
+
}
|
166 |
+
}
|
167 |
+
|
168 |
+
}
|
169 |
+
|
170 |
+
}
|
171 |
+
|
172 |
+
|
173 |
+
return $DOMNodeList;
|
174 |
+
}
|
175 |
+
|
176 |
+
/**
|
177 |
+
* Find the element properties
|
178 |
+
*
|
179 |
+
* Implements the abstract method from Instant_Articles_DOM_Transform_Filter
|
180 |
+
*
|
181 |
+
* @since 0.1
|
182 |
+
* @param $DOMNode $DOMNode The original domnode
|
183 |
+
* @return stdClass The element properties used for building the new fragment
|
184 |
+
*/
|
185 |
+
protected function get_properties( $DOMNode ) {
|
186 |
+
|
187 |
+
$properties = new stdClass;
|
188 |
+
return $properties;
|
189 |
+
|
190 |
+
}
|
191 |
+
|
192 |
+
/**
|
193 |
+
* Build a DOMDocumentFragment for the image element
|
194 |
+
*
|
195 |
+
* Implements the abstract method from Instant_Articles_DOM_Transform_Filter
|
196 |
+
*
|
197 |
+
* @since 0.1
|
198 |
+
* @return DOMDocumentFragment|false The fragment ready to be inserted into the DOM. False if no replacement should happen.
|
199 |
+
*/
|
200 |
+
protected function _build_fragment( $properties) {
|
201 |
+
return false;
|
202 |
+
}
|
203 |
+
|
204 |
+
|
205 |
+
}
|
206 |
+
|
207 |
+
|
dom-transform-filters/class-instant-articles-dom-transform-filter-heading.php
ADDED
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Instant Articles DOM Transformation Filter for headings
|
5 |
+
*
|
6 |
+
* @since 0.1
|
7 |
+
*/
|
8 |
+
class Instant_Articles_DOM_Transform_Filter_Heading extends Instant_Articles_DOM_Transform_Filter {
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Run the transformation
|
12 |
+
*
|
13 |
+
* Implements the abstract method from Instant_Articles_DOM_Transform_Filter
|
14 |
+
*
|
15 |
+
* @since 0.1
|
16 |
+
* @return DOMDocument
|
17 |
+
*/
|
18 |
+
public function run() {
|
19 |
+
|
20 |
+
$xpath = new DOMXpath( $this->_DOMDocument );
|
21 |
+
$DOMNodeList = $xpath->query( "//h3 | //h4 | //h5 | //h6" );
|
22 |
+
|
23 |
+
// Transform all nodes found
|
24 |
+
$this->_transform_elements( $DOMNodeList );
|
25 |
+
|
26 |
+
return $this->_DOMDocument;
|
27 |
+
|
28 |
+
}
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Build a DOMDocumentFragment for the element
|
32 |
+
*
|
33 |
+
* @since 0.1
|
34 |
+
* @return DOMDocumentFragment The fragment ready to be inserted into the DOM
|
35 |
+
*/
|
36 |
+
protected function _build_fragment( $properties ) {
|
37 |
+
|
38 |
+
$DOMDocumentFragment = $this->_DOMDocument->createDocumentFragment();
|
39 |
+
$h = $this->_DOMDocument->createElement( 'h2' );
|
40 |
+
|
41 |
+
for ( $i = 0; $i < $properties->childNodes->length; ++$i ) {
|
42 |
+
$h->appendChild( $properties->childNodes->item( $i ) );
|
43 |
+
}
|
44 |
+
|
45 |
+
$DOMDocumentFragment->appendChild( $h );
|
46 |
+
|
47 |
+
return $DOMDocumentFragment;
|
48 |
+
}
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Find the element properties
|
52 |
+
*
|
53 |
+
* Implements the abstract method from Instant_Articles_DOM_Transform_Filter
|
54 |
+
*
|
55 |
+
* @since 0.1
|
56 |
+
* @param $DOMNode $DOMNode The original domnode
|
57 |
+
* @return stdClass The element properties used for building the new fragment
|
58 |
+
*/
|
59 |
+
protected function get_properties( $DOMNode ) {
|
60 |
+
|
61 |
+
$properties = new stdClass;
|
62 |
+
|
63 |
+
$properties->childNodes = $DOMNode->childNodes;
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Filter the ol element properties
|
67 |
+
*
|
68 |
+
* @since 0.1
|
69 |
+
* @param object $properties The element properties
|
70 |
+
* @param int $post_id The post ID of the current post
|
71 |
+
*/
|
72 |
+
$properties = apply_filters( 'instant_articles_heading_properties', $properties, $this->_post_id );
|
73 |
+
|
74 |
+
return $properties;
|
75 |
+
|
76 |
+
}
|
77 |
+
|
78 |
+
}
|
dom-transform-filters/class-instant-articles-dom-transform-filter-image.php
ADDED
@@ -0,0 +1,151 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Instant Articles DOM Transformation Filter for Images
|
5 |
+
*
|
6 |
+
* @since 0.1
|
7 |
+
*/
|
8 |
+
class Instant_Articles_DOM_Transform_Filter_Image extends Instant_Articles_DOM_Transform_Filter {
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Run the transformation
|
12 |
+
*
|
13 |
+
* Implements the abstract method from Instant_Articles_DOM_Transform_Filter
|
14 |
+
*
|
15 |
+
* @since 0.1
|
16 |
+
* @return DOMDocument
|
17 |
+
*/
|
18 |
+
public function run() {
|
19 |
+
|
20 |
+
$DOMNodeList = $this->_DOMDocument->getElementsByTagName( 'img' );
|
21 |
+
|
22 |
+
// Filter out multiple uses of single images
|
23 |
+
$this->_filter_multiple_uses( $DOMNodeList );
|
24 |
+
|
25 |
+
// Transform all images left
|
26 |
+
$this->_transform_elements( $DOMNodeList );
|
27 |
+
|
28 |
+
return $this->_DOMDocument;
|
29 |
+
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* The Instant Articles spec says we can only use each image once.
|
34 |
+
*
|
35 |
+
* @param DOMNodeList $DOMNodeList the list of images in the document
|
36 |
+
* @return DOMNodeList The modified list of images in the document
|
37 |
+
*/
|
38 |
+
protected function _filter_multiple_uses( DOMNodeList $DOMNodeList ) {
|
39 |
+
|
40 |
+
$used_images = array();
|
41 |
+
|
42 |
+
$NodeListIndex = 0;
|
43 |
+
|
44 |
+
// We’ll increase $NodeListIndex and/or reduce $DOMNodeList->length
|
45 |
+
while ( $NodeListIndex < $DOMNodeList->length ) {
|
46 |
+
|
47 |
+
$DOMNode = $DOMNodeList->item( $NodeListIndex );
|
48 |
+
|
49 |
+
$src = $DOMNode->getAttribute( 'src' );
|
50 |
+
|
51 |
+
// See how far up the tree we can go
|
52 |
+
$removeDOMNode = $DOMNode;
|
53 |
+
while ( 'body' != $removeDOMNode->parentNode->nodeName && 1 === $removeDOMNode->parentNode->childNodes->length ) {
|
54 |
+
$removeDOMNode = $removeDOMNode->parentNode;
|
55 |
+
}
|
56 |
+
|
57 |
+
// If the image is used already, remove it
|
58 |
+
if ( in_array( $src, $used_images, true ) ) {
|
59 |
+
// Please note that when we remove the node, $DOMNodeList->length is n-1. Our $NodeListIndex will thus point to the next item in the list.
|
60 |
+
$removeDOMNode->parentNode->removeChild( $removeDOMNode );
|
61 |
+
}
|
62 |
+
|
63 |
+
// Add the src to the stack so we can check for multiple uses later
|
64 |
+
$used_images[] = $src;
|
65 |
+
|
66 |
+
++$NodeListIndex;
|
67 |
+
|
68 |
+
}
|
69 |
+
|
70 |
+
|
71 |
+
return $DOMNodeList;
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Build a DOMDocumentFragment for the image element
|
76 |
+
*
|
77 |
+
* @since 0.1
|
78 |
+
* @return DOMDocumentFragment The fragment ready to be inserted into the DOM
|
79 |
+
*/
|
80 |
+
protected function _build_fragment( $properties) {
|
81 |
+
|
82 |
+
$DOMDocumentFragment = $this->_DOMDocument->createDocumentFragment();
|
83 |
+
$figure = $this->_DOMDocument->createElement( 'figure' );
|
84 |
+
$img = $this->_DOMDocument->createElement( 'img' );
|
85 |
+
$img->setAttribute( 'src', $properties->img->url );
|
86 |
+
|
87 |
+
$figure->appendChild( $img );
|
88 |
+
$DOMDocumentFragment->appendChild( $figure );
|
89 |
+
|
90 |
+
return $DOMDocumentFragment;
|
91 |
+
}
|
92 |
+
|
93 |
+
/**
|
94 |
+
* Find the element properties
|
95 |
+
*
|
96 |
+
* Implements the abstract method from Instant_Articles_DOM_Transform_Filter
|
97 |
+
*
|
98 |
+
* @since 0.1
|
99 |
+
* @param $DOMNode $DOMNode The original domnode
|
100 |
+
* @return stdClass The element properties used for building the new fragment
|
101 |
+
*/
|
102 |
+
protected function get_properties( $DOMNode ) {
|
103 |
+
|
104 |
+
$src = $DOMNode->getAttribute( 'src' );
|
105 |
+
if ( function_exists( 'wpcom_vip_attachment_url_to_postid' ) ) {
|
106 |
+
$attachment_id = wpcom_vip_attachment_url_to_postid( $src );
|
107 |
+
} else {
|
108 |
+
$attachment_id = attachment_url_to_postid( $src );
|
109 |
+
}
|
110 |
+
|
111 |
+
$properties = new stdClass;
|
112 |
+
$properties->img = new stdClass;
|
113 |
+
|
114 |
+
/* Try to use WP internals to get an image of the recommended size. Fallback to use the URL from the original img src in the post. */
|
115 |
+
|
116 |
+
|
117 |
+
if ( $attachment_id ) {
|
118 |
+
// The recommended resolution is 2048x2048 pixels.
|
119 |
+
$img_props = wp_get_attachment_image_src( $attachment_id, array( 2048, 2048 ) ); // Returns an array (url, width, height), or false, if no image is available.
|
120 |
+
} else {
|
121 |
+
$imagesize = getimagesize( $src );
|
122 |
+
if ( is_array( $imagesize ) ) {
|
123 |
+
$img_props = array( $src, $imagesize[0], $imagesize[1] );
|
124 |
+
}
|
125 |
+
}
|
126 |
+
if ( is_array( $img_props ) ) {
|
127 |
+
list( $properties->img->url, $properties->img->width, $properties->img->height ) = $img_props;
|
128 |
+
} else {
|
129 |
+
$properties->img->url = $src;
|
130 |
+
$properties->img->width = '';
|
131 |
+
$properties->img->height = '';
|
132 |
+
}
|
133 |
+
|
134 |
+
/**
|
135 |
+
* Filter the image properties
|
136 |
+
*
|
137 |
+
* @since 0.1
|
138 |
+
* @param object $img_props The element properties
|
139 |
+
* @param int $post_id The post ID of the current post
|
140 |
+
* @param int $attachment_id The attachment ID (post ID) to the image (if reverse lookup from url to postid worked)
|
141 |
+
*/
|
142 |
+
$properties = apply_filters( 'instant_articles_image_properties', $properties, $this->_post_id, $attachment_id );
|
143 |
+
|
144 |
+
return $properties;
|
145 |
+
|
146 |
+
}
|
147 |
+
|
148 |
+
|
149 |
+
}
|
150 |
+
|
151 |
+
|
dom-transform-filters/class-instant-articles-dom-transform-filter-ordered-list.php
ADDED
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Instant Articles DOM Transformation Filter for ordered List
|
5 |
+
*
|
6 |
+
* @since 0.1
|
7 |
+
*/
|
8 |
+
class Instant_Articles_DOM_Transform_Filter_Ordered_List extends Instant_Articles_DOM_Transform_Filter {
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Run the transformation
|
12 |
+
*
|
13 |
+
* Implements the abstract method from Instant_Articles_DOM_Transform_Filter
|
14 |
+
*
|
15 |
+
* @since 0.1
|
16 |
+
* @return DOMDocument
|
17 |
+
*/
|
18 |
+
public function run() {
|
19 |
+
|
20 |
+
$DOMNodeList = $this->_DOMDocument->getElementsByTagName( 'ol' );
|
21 |
+
|
22 |
+
// Transform all nodes found
|
23 |
+
$this->_transform_elements( $DOMNodeList );
|
24 |
+
|
25 |
+
return $this->_DOMDocument;
|
26 |
+
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Build a DOMDocumentFragment for the element
|
31 |
+
*
|
32 |
+
* @since 0.1
|
33 |
+
* @return DOMDocumentFragment The fragment ready to be inserted into the DOM
|
34 |
+
*/
|
35 |
+
protected function _build_fragment( $properties ) {
|
36 |
+
|
37 |
+
$DOMDocumentFragment = $this->_DOMDocument->createDocumentFragment();
|
38 |
+
$ol = $this->_DOMDocument->createElement( 'ol' );
|
39 |
+
|
40 |
+
for ( $i = 0; $i < $properties->childNodes->length; ++$i ) {
|
41 |
+
$ol->appendChild( $properties->childNodes->item( $i ) );
|
42 |
+
}
|
43 |
+
|
44 |
+
$DOMDocumentFragment->appendChild( $ol );
|
45 |
+
|
46 |
+
return $DOMDocumentFragment;
|
47 |
+
}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Find the element properties
|
51 |
+
*
|
52 |
+
* Implements the abstract method from Instant_Articles_DOM_Transform_Filter
|
53 |
+
*
|
54 |
+
* @since 0.1
|
55 |
+
* @param $DOMNode $DOMNode The original domnode
|
56 |
+
* @return stdClass The element properties used for building the new fragment
|
57 |
+
*/
|
58 |
+
protected function get_properties( $DOMNode ) {
|
59 |
+
|
60 |
+
$properties = new stdClass;
|
61 |
+
|
62 |
+
$properties->childNodes = $DOMNode->childNodes;
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Filter the ol element properties
|
66 |
+
*
|
67 |
+
* @since 0.1
|
68 |
+
* @param object $properties The element properties
|
69 |
+
* @param int $post_id The post ID of the current post
|
70 |
+
*/
|
71 |
+
$properties = apply_filters( 'instant_articles_ol_properties', $properties, $this->_post_id );
|
72 |
+
|
73 |
+
return $properties;
|
74 |
+
|
75 |
+
}
|
76 |
+
|
77 |
+
}
|
dom-transform-filters/class-instant-articles-dom-transform-filter-table.php
ADDED
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Instant Articles DOM Transformation Filter for Tables
|
5 |
+
*
|
6 |
+
* @since 0.1
|
7 |
+
*/
|
8 |
+
class Instant_Articles_DOM_Transform_Filter_Table extends Instant_Articles_DOM_Transform_Filter {
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Run the transformation
|
12 |
+
*
|
13 |
+
* Implements the abstract method from Instant_Articles_DOM_Transform_Filter
|
14 |
+
*
|
15 |
+
* @since 0.1
|
16 |
+
* @return DOMDocument
|
17 |
+
*/
|
18 |
+
public function run() {
|
19 |
+
|
20 |
+
$DOMNodeList = $this->_DOMDocument->getElementsByTagName( 'table' );
|
21 |
+
|
22 |
+
// Transform all nodes found
|
23 |
+
$this->_transform_elements( $DOMNodeList );
|
24 |
+
|
25 |
+
return $this->_DOMDocument;
|
26 |
+
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Build a DOMDocumentFragment for the element
|
31 |
+
*
|
32 |
+
* @since 0.1
|
33 |
+
* @return DOMDocumentFragment|false The fragment ready to be inserted into the DOM. False if no replacement should happen.
|
34 |
+
*/
|
35 |
+
protected function _build_fragment( $properties ) {
|
36 |
+
|
37 |
+
if ( ! is_a( $properties->table, 'DOMElement' ) ) {
|
38 |
+
return false;
|
39 |
+
}
|
40 |
+
|
41 |
+
$DOMDocumentFragment = $this->_DOMDocument->createDocumentFragment();
|
42 |
+
|
43 |
+
$figure = $this->_DOMDocument->createElement( 'figure' );
|
44 |
+
$figure->setAttribute( 'class', 'op-interactive' );
|
45 |
+
$DOMDocumentFragment->appendChild( $figure );
|
46 |
+
|
47 |
+
$iframe = $this->_DOMDocument->createElement( 'iframe' );
|
48 |
+
$figure->appendChild( $iframe );
|
49 |
+
|
50 |
+
$iframe->appendChild( $properties->table );
|
51 |
+
|
52 |
+
return $DOMDocumentFragment;
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Find the element properties
|
57 |
+
*
|
58 |
+
* Implements the abstract method from Instant_Articles_DOM_Transform_Filter
|
59 |
+
*
|
60 |
+
* @since 0.1
|
61 |
+
* @param $DOMNode $DOMNode The original domnode
|
62 |
+
* @return stdClass The element properties used for building the new fragment
|
63 |
+
*/
|
64 |
+
protected function get_properties( $DOMNode ) {
|
65 |
+
|
66 |
+
$properties = new stdClass;
|
67 |
+
|
68 |
+
$properties->table = clone $DOMNode;
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Filter the blockquote element properties
|
72 |
+
*
|
73 |
+
* @since 0.1
|
74 |
+
* @param object $properties The element properties
|
75 |
+
* @param int $post_id The post ID of the current post
|
76 |
+
*/
|
77 |
+
$properties = apply_filters( 'instant_articles_table_properties', $properties, $this->_post_id );
|
78 |
+
|
79 |
+
return $properties;
|
80 |
+
|
81 |
+
}
|
82 |
+
|
83 |
+
}
|
dom-transform-filters/class-instant-articles-dom-transform-filter-unordered-list.php
ADDED
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Instant Articles DOM Transformation Filter for Unordered List
|
5 |
+
*
|
6 |
+
* @since 0.1
|
7 |
+
*/
|
8 |
+
class Instant_Articles_DOM_Transform_Filter_Unordered_List extends Instant_Articles_DOM_Transform_Filter {
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Run the transformation
|
12 |
+
*
|
13 |
+
* Implements the abstract method from Instant_Articles_DOM_Transform_Filter
|
14 |
+
*
|
15 |
+
* @since 0.1
|
16 |
+
* @return DOMDocument
|
17 |
+
*/
|
18 |
+
public function run() {
|
19 |
+
|
20 |
+
$DOMNodeList = $this->_DOMDocument->getElementsByTagName( 'ul' );
|
21 |
+
|
22 |
+
// Transform all nodes found
|
23 |
+
$this->_transform_elements( $DOMNodeList );
|
24 |
+
|
25 |
+
return $this->_DOMDocument;
|
26 |
+
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Build a DOMDocumentFragment for the element
|
31 |
+
*
|
32 |
+
* @since 0.1
|
33 |
+
* @return DOMDocumentFragment The fragment ready to be inserted into the DOM
|
34 |
+
*/
|
35 |
+
protected function _build_fragment( $properties ) {
|
36 |
+
|
37 |
+
$DOMDocumentFragment = $this->_DOMDocument->createDocumentFragment();
|
38 |
+
$ul = $this->_DOMDocument->createElement( 'ul' );
|
39 |
+
|
40 |
+
for ( $i = 0; $i < $properties->childNodes->length; ++$i ) {
|
41 |
+
$ul->appendChild( $properties->childNodes->item( $i ) );
|
42 |
+
}
|
43 |
+
|
44 |
+
$DOMDocumentFragment->appendChild( $ul );
|
45 |
+
|
46 |
+
return $DOMDocumentFragment;
|
47 |
+
}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Find the element properties
|
51 |
+
*
|
52 |
+
* Implements the abstract method from Instant_Articles_DOM_Transform_Filter
|
53 |
+
*
|
54 |
+
* @since 0.1
|
55 |
+
* @param $DOMNode $DOMNode The original domnode
|
56 |
+
* @return stdClass The element properties used for building the new fragment
|
57 |
+
*/
|
58 |
+
protected function get_properties( $DOMNode ) {
|
59 |
+
|
60 |
+
$properties = new stdClass;
|
61 |
+
|
62 |
+
$properties->childNodes = $DOMNode->childNodes;
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Filter the ul element properties
|
66 |
+
*
|
67 |
+
* @since 0.1
|
68 |
+
* @param object $properties The element properties
|
69 |
+
* @param int $post_id The post ID of the current post
|
70 |
+
*/
|
71 |
+
$properties = apply_filters( 'instant_articles_ul_properties', $properties, $this->_post_id );
|
72 |
+
|
73 |
+
return $properties;
|
74 |
+
|
75 |
+
}
|
76 |
+
|
77 |
+
}
|
dom-transform-filters/class-instant-articles-dom-transform-filter-video.php
ADDED
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Instant Articles DOM Transformation Filter for Videos
|
5 |
+
*
|
6 |
+
* @since 0.1
|
7 |
+
*/
|
8 |
+
class Instant_Articles_DOM_Transform_Filter_Video extends Instant_Articles_DOM_Transform_Filter {
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Run the transformation
|
12 |
+
*
|
13 |
+
* Implements the abstract method from Instant_Articles_DOM_Transform_Filter
|
14 |
+
*
|
15 |
+
* @since 0.1
|
16 |
+
* @return DOMDocument The modified DOMDocument
|
17 |
+
*/
|
18 |
+
public function run() {
|
19 |
+
|
20 |
+
$DOMNodeList = $this->_DOMDocument->getElementsByTagName( 'video' );
|
21 |
+
|
22 |
+
// Transform all nodes found
|
23 |
+
$this->_transform_elements( $DOMNodeList );
|
24 |
+
|
25 |
+
return $this->_DOMDocument;
|
26 |
+
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Build a DOMDocumentFragment for the element
|
31 |
+
*
|
32 |
+
* @since 0.1
|
33 |
+
* @return DOMDocumentFragment The fragment ready to be inserted into the DOM
|
34 |
+
*/
|
35 |
+
protected function _build_fragment( $properties ) {
|
36 |
+
|
37 |
+
$DOMDocumentFragment = $this->_DOMDocument->createDocumentFragment();
|
38 |
+
|
39 |
+
$wrapper = $this->_DOMDocument->createElement( 'figure' );
|
40 |
+
$video = $this->_DOMDocument->createElement( 'video' );
|
41 |
+
$wrapper->appendChild( $video );
|
42 |
+
|
43 |
+
foreach ( $properties->childNodes as $childNode ) {
|
44 |
+
$video->appendChild( $childNode );
|
45 |
+
}
|
46 |
+
|
47 |
+
$DOMDocumentFragment->appendChild( $wrapper );
|
48 |
+
|
49 |
+
return $DOMDocumentFragment;
|
50 |
+
}
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Find the element properties
|
54 |
+
*
|
55 |
+
* Implements the abstract method from Instant_Articles_DOM_Transform_Filter
|
56 |
+
*
|
57 |
+
* @since 0.1
|
58 |
+
* @param $DOMNode $DOMNode The original domnode
|
59 |
+
* @return stdClass The element properties used for building the new fragment
|
60 |
+
*/
|
61 |
+
protected function get_properties( $DOMNode ) {
|
62 |
+
|
63 |
+
$properties = new stdClass;
|
64 |
+
|
65 |
+
$properties->childNodes = $DOMNode->childNodes;
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Filter the video element properties
|
69 |
+
*
|
70 |
+
* @since 0.1
|
71 |
+
* @param object $properties The element properties
|
72 |
+
* @param int $post_id The post ID of the current post
|
73 |
+
*/
|
74 |
+
$properties = apply_filters( 'instant_articles_video_properties', $properties, $this->_post_id );
|
75 |
+
|
76 |
+
return $properties;
|
77 |
+
|
78 |
+
}
|
79 |
+
|
80 |
+
}
|
dom-transform-filters/class-instant-articles-dom-transform-filter.php
ADDED
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Instant Articles DOM Transformation Filter
|
5 |
+
*
|
6 |
+
* @since 0.1
|
7 |
+
*/
|
8 |
+
abstract class Instant_Articles_DOM_Transform_Filter {
|
9 |
+
|
10 |
+
|
11 |
+
protected $_DOMDocument;
|
12 |
+
protected $_post_id;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Constructor
|
16 |
+
*
|
17 |
+
* @since 0.1
|
18 |
+
* @param DOMDocument $DOMDocument The DOMDocument object we will be working on.
|
19 |
+
* @param int $post_id The WP post ID to the current post
|
20 |
+
*/
|
21 |
+
function __construct( $DOMDocument, $post_id ) {
|
22 |
+
$this->_DOMDocument = $DOMDocument;
|
23 |
+
$this->_post_id = $post_id;
|
24 |
+
}
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Run the transformation
|
28 |
+
*
|
29 |
+
* @since 0.1
|
30 |
+
* @return DOMDocument THe transformed DOM document.
|
31 |
+
*/
|
32 |
+
abstract public function run();
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Dispatch each element in the nodelist to the transformer
|
36 |
+
*
|
37 |
+
* Note that we work directly on the DOMNodeList itself. Objects are passed by ref.
|
38 |
+
*
|
39 |
+
* @since 0.1
|
40 |
+
* @param DOMNodeList $DOMNodeList List of images
|
41 |
+
* @return DOMNodeList The DOMNodeList. If you want to chain.
|
42 |
+
*/
|
43 |
+
protected function _transform_elements( DOMNodeList $DOMNodeList ) {
|
44 |
+
|
45 |
+
// A foreach won’t work as we are changing the elements
|
46 |
+
for ( $i = 0; $i < $DOMNodeList->length; ++$i ) {
|
47 |
+
$origLength = $DOMNodeList->length;
|
48 |
+
|
49 |
+
$this->_transform_element( $DOMNodeList->item( $i ) );
|
50 |
+
|
51 |
+
if ( $origLength !== $DOMNodeList->length ) {
|
52 |
+
// The element is replaced by an element of another type and is no longer in the nodelist
|
53 |
+
--$i;
|
54 |
+
}
|
55 |
+
}
|
56 |
+
|
57 |
+
return $DOMNodeList;
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Transform an element
|
62 |
+
*
|
63 |
+
* Note that we work directly on the DOMNode itself. Objects are passed by ref.
|
64 |
+
*
|
65 |
+
* @since 0.1
|
66 |
+
* @param DOMNode $DOMNode The original DOM node
|
67 |
+
* @return DOMNode The tranformed DOMNode. If you want to chain.
|
68 |
+
*/
|
69 |
+
protected function _transform_element( DOMNode $DOMNode ) {
|
70 |
+
|
71 |
+
// See how far up the tree we can go.
|
72 |
+
$replaceNode = $DOMNode;
|
73 |
+
while ( 'body' != $replaceNode->parentNode->nodeName && 1 === $replaceNode->parentNode->childNodes->length ) {
|
74 |
+
$replaceNode = $replaceNode->parentNode;
|
75 |
+
}
|
76 |
+
// If we can’t go all the way to the top, we bail.
|
77 |
+
if ( 'body' != $replaceNode->parentNode->nodeName ) {
|
78 |
+
return $DOMNode;
|
79 |
+
}
|
80 |
+
|
81 |
+
$properties = $this->get_properties( $DOMNode );
|
82 |
+
$DOMDocumentFragment = $this->_build_fragment( $properties );
|
83 |
+
|
84 |
+
if ( is_a( $DOMDocumentFragment, 'DOMDocumentFragment' ) ) {
|
85 |
+
$replaceNode->parentNode->replaceChild( $DOMDocumentFragment, $replaceNode );
|
86 |
+
}
|
87 |
+
|
88 |
+
return $DOMNode;
|
89 |
+
}
|
90 |
+
|
91 |
+
/**
|
92 |
+
* Find the element properties
|
93 |
+
*
|
94 |
+
* @since 0.1
|
95 |
+
* @param $DOMNode $DOMNode The original domnode
|
96 |
+
* @return Object The properties we need to build the new fragment
|
97 |
+
*/
|
98 |
+
abstract protected function get_properties( $DOMNode );
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Build the new fragment
|
102 |
+
*
|
103 |
+
* @since 0.1
|
104 |
+
* @param Object $properties The properties we need for this element
|
105 |
+
* @return DOMDocumentFragment|false The fragment ready to be inserted into the DOM. False if no replacement should happen.
|
106 |
+
*/
|
107 |
+
abstract protected function _build_fragment( $DOMNode );
|
108 |
+
|
109 |
+
}
|
110 |
+
|
111 |
+
|
embeds.php
ADDED
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Oembed/embed handling
|
5 |
+
*
|
6 |
+
* The embed result is most likely cached, so we have to handle it late. If it is not cached and we handle it early, it will be cached with an unwanted result
|
7 |
+
* for regular posts. So we resort to parsing the embed result in the embed_oembed_html and embed_handler_html filters.
|
8 |
+
*/
|
9 |
+
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Remove all extra oembed html filters added by themes and plugins.
|
13 |
+
*
|
14 |
+
*/
|
15 |
+
remove_all_filters( 'embed_oembed_html' );
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Filter the oembed results to see if we should do some extra handling
|
19 |
+
*
|
20 |
+
* @since 0.1
|
21 |
+
* @param string $html The original HTML returned from the external oembed provider
|
22 |
+
* @param string $url The URL found in the content
|
23 |
+
* @param mixed $attr An array with extra attributes
|
24 |
+
* @param int $post_ID The post ID
|
25 |
+
* @return string The potentially filtered HTML
|
26 |
+
*/
|
27 |
+
function instant_articles_embed_oembed_html( $html, $url, $attr, $post_ID ) {
|
28 |
+
|
29 |
+
if ( ! class_exists( 'WP_oEmbed' ) ) {
|
30 |
+
include_once( ABSPATH . WPINC . '/class-oembed.php' );
|
31 |
+
}
|
32 |
+
|
33 |
+
// Instead of checking all possible URL variants, use the provider list from WP_oEmbed
|
34 |
+
$WP_oEmbed = new WP_oEmbed();
|
35 |
+
$providerURL = $WP_oEmbed->get_provider( $url );
|
36 |
+
|
37 |
+
$provider_name = false;
|
38 |
+
if ( false !== strpos( $providerURL, 'instagram.com' ) ) {
|
39 |
+
$provider_name = 'instagram';
|
40 |
+
} elseif( false !== strpos( $providerURL, 'twitter.com' ) ) {
|
41 |
+
$provider_name = 'twitter';
|
42 |
+
} elseif( false !== strpos( $providerURL, 'youtube.com' ) ) {
|
43 |
+
$provider_name = 'youtube';
|
44 |
+
} elseif( false !== strpos( $providerURL, 'vine.co' ) ) {
|
45 |
+
$provider_name = 'vine';
|
46 |
+
}
|
47 |
+
|
48 |
+
$provider_name = apply_filters( 'instant_articles_social_embed_type', $provider_name, $url );
|
49 |
+
|
50 |
+
if ( $provider_name ) {
|
51 |
+
$html = instant_articles_embed_get_html( $provider_name, $html, $url, $attr, $post_ID );
|
52 |
+
}
|
53 |
+
|
54 |
+
return $html;
|
55 |
+
|
56 |
+
}
|
57 |
+
add_filter( 'embed_oembed_html', 'instant_articles_embed_oembed_html', 10, 4 );
|
58 |
+
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Filter the embed results for embeds
|
62 |
+
*
|
63 |
+
* @since 0.1
|
64 |
+
* @param string $provider_name The name of the embed provider. E.g. “instagram” or “youtube”
|
65 |
+
* @param string $html The original HTML returned from the external oembed/embed provider
|
66 |
+
* @param string $url The URL found in the content
|
67 |
+
* @param mixed $attr An array with extra attributes
|
68 |
+
* @param int $post_ID The post ID
|
69 |
+
* @return string The filtered HTML
|
70 |
+
*/
|
71 |
+
function instant_articles_embed_get_html( $provider_name, $html, $url, $attr, $post_ID ) {
|
72 |
+
|
73 |
+
/*
|
74 |
+
Example output from instagram:
|
75 |
+
<blockquote class="instagram-media" data-instgrm-version="6" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);"><div style="padding:8px;">
|
76 |
+
<div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50.0% 0; text-align:center; width:100%;">
|
77 |
+
<div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAAGFBMVEUiIiI9PT0eHh4gIB4hIBkcHBwcHBwcHBydr+JQAAAACHRSTlMABA4YHyQsM5jtaMwAAADfSURBVDjL7ZVBEgMhCAQBAf//42xcNbpAqakcM0ftUmFAAIBE81IqBJdS3lS6zs3bIpB9WED3YYXFPmHRfT8sgyrCP1x8uEUxLMzNWElFOYCV6mHWWwMzdPEKHlhLw7NWJqkHc4uIZphavDzA2JPzUDsBZziNae2S6owH8xPmX8G7zzgKEOPUoYHvGz1TBCxMkd3kwNVbU0gKHkx+iZILf77IofhrY1nYFnB/lQPb79drWOyJVa/DAvg9B/rLB4cC+Nqgdz/TvBbBnr6GBReqn/nRmDgaQEej7WhonozjF+Y2I/fZou/qAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"/>
|
78 |
+
</div>
|
79 |
+
<p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;"><a href="https://www.instagram.com/p/6l9z5RTbAN/" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;" target="_blank">A photo posted by Bjørn Johansen (@bjornjohansen)</a> on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2015-08-20T05:19:53+00:00">Aug 19, 2015 at 10:19pm PDT</time></p>
|
80 |
+
</div>
|
81 |
+
</blockquote>
|
82 |
+
<p><script async="" defer="defer" src="//platform.instagram.com/en_US/embeds.js"/></p>
|
83 |
+
*/
|
84 |
+
|
85 |
+
/*
|
86 |
+
Example output from twitter:
|
87 |
+
<blockquote class="twitter-tweet" width="550"><p lang="en" dir="ltr">Will my Drupal site upgrade itself automatically to version 8, or do I click a button somewhere?</p>
|
88 |
+
<p>— Bjørn Johansen (@bjornjohansen) <a href="https://twitter.com/bjornjohansen/status/667263124794417152">November 19, 2015</a></p>
|
89 |
+
</blockquote>
|
90 |
+
<p><script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"/></p>
|
91 |
+
*/
|
92 |
+
|
93 |
+
/*
|
94 |
+
Example output from youtube:
|
95 |
+
<iframe width="660" height="371" src="https://www.youtube.com/embed/SQjSJ0T7PiE?feature=oembed" frameborder="0" allowfullscreen></iframe>
|
96 |
+
*/
|
97 |
+
|
98 |
+
/*
|
99 |
+
Example output from vine:
|
100 |
+
<iframe class="vine-embed" src="https://vine.co/v/e9U7gav5e5h/embed/simple" width="660" height="660" frameborder="0"/><script async="" src="//platform.vine.co/static/scripts/embed.js"/>
|
101 |
+
*/
|
102 |
+
|
103 |
+
/**
|
104 |
+
* Filter the HTML that will go into the Instant Article Social Embed markup
|
105 |
+
*
|
106 |
+
* @since 0.1
|
107 |
+
* @param string $html The HTML
|
108 |
+
* @param string $url The URL found in the content
|
109 |
+
* @param mixed $attr An array with extra attributes
|
110 |
+
* @param int $post_ID The post ID
|
111 |
+
*/
|
112 |
+
$html = apply_filters( "instant_articles_social_embed_{$provider_name}", $html, $url, $attr, $post_ID);
|
113 |
+
|
114 |
+
$html = sprintf( '<figure class="op-social"><iframe>%s</iframe></figure>', $html );
|
115 |
+
|
116 |
+
/**
|
117 |
+
* Filter the Instant Article Social Embed markup
|
118 |
+
*
|
119 |
+
* @since 0.1
|
120 |
+
* @param string $html The Social Embed markup
|
121 |
+
* @param string $url The URL found in the content
|
122 |
+
* @param mixed $attr An array with extra attributes
|
123 |
+
* @param int $post_ID The post ID
|
124 |
+
*/
|
125 |
+
$html = apply_filters( 'instant_articles_social_embed', $html, $url, $attr, $post_ID );
|
126 |
+
|
127 |
+
return $html;
|
128 |
+
}
|
129 |
+
|
130 |
+
|
131 |
+
|
facebook-instant-articles.php
ADDED
@@ -0,0 +1,187 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Plugin Name: Instant Articles for WP
|
4 |
+
* Description: Add support for Instant Articles for Facebook to your WordPress site.
|
5 |
+
* Author: Automattic, Dekode
|
6 |
+
* Author URI: https://vip.wordpress.com/plugins/instant-articles/
|
7 |
+
* Version: 0.2
|
8 |
+
* Text Domain: instant-articles
|
9 |
+
* License: GPLv2
|
10 |
+
* License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
11 |
+
*/
|
12 |
+
|
13 |
+
defined( 'ABSPATH' ) || die('Shame on you');
|
14 |
+
|
15 |
+
|
16 |
+
// Let users define their own feed slug
|
17 |
+
if ( ! defined( 'INSTANT_ARTICLES_SLUG' ) ) {
|
18 |
+
define( 'INSTANT_ARTICLES_SLUG', 'instant-articles' );
|
19 |
+
}
|
20 |
+
|
21 |
+
require_once( dirname( __FILE__ ) . '/dom-transform-filters/class-instant-articles-dom-transform-filter.php' );
|
22 |
+
require_once( dirname( __FILE__ ) . '/class-instant-articles-dom-transform-filter-runner.php' );
|
23 |
+
require_once( dirname( __FILE__ ) . '/class-instant-articles-post.php' );
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Plugin activation hook to add our rewrite rules
|
27 |
+
*
|
28 |
+
* @since 0.1
|
29 |
+
*/
|
30 |
+
function instant_articles_activate(){
|
31 |
+
instant_articles_init();
|
32 |
+
flush_rewrite_rules();
|
33 |
+
}
|
34 |
+
register_activation_hook( __FILE__, 'instant_articles_activate' );
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Plugin activation hook to remove our rewrite rules
|
38 |
+
*
|
39 |
+
* @since 0.1
|
40 |
+
*/
|
41 |
+
function instant_articles_deactivate(){
|
42 |
+
flush_rewrite_rules();
|
43 |
+
}
|
44 |
+
register_deactivation_hook( __FILE__, 'instant_articles_deactivate' );
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Load plugin textdomain
|
48 |
+
*
|
49 |
+
* @since 0.1
|
50 |
+
*/
|
51 |
+
function instant_articles_load_textdomain() {
|
52 |
+
load_plugin_textdomain( 'instant-articles', false, plugin_dir_path( __FILE__ ) . '/languages' );
|
53 |
+
}
|
54 |
+
add_action( 'plugins_loaded', 'instant_articles_load_textdomain' );
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Register our special feed
|
58 |
+
*
|
59 |
+
* @since 0.1
|
60 |
+
*/
|
61 |
+
function instant_articles_init() {
|
62 |
+
add_feed( INSTANT_ARTICLES_SLUG, 'instant_articles_feed' );
|
63 |
+
}
|
64 |
+
add_action( 'init', 'instant_articles_init' );
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Feed display callback
|
68 |
+
*
|
69 |
+
* @since 0.1
|
70 |
+
*/
|
71 |
+
function instant_articles_feed() {
|
72 |
+
|
73 |
+
// Load compat layers
|
74 |
+
include( dirname( __FILE__ ) . '/compat.php' );
|
75 |
+
|
76 |
+
// Load shortcode handlers
|
77 |
+
include( dirname( __FILE__ ) . '/shortcodes.php' );
|
78 |
+
|
79 |
+
// Load embedded content handlers
|
80 |
+
include( dirname( __FILE__ ) . '/embeds.php' );
|
81 |
+
|
82 |
+
// Load the feed template
|
83 |
+
include( dirname( __FILE__ ) . '/feed-template.php' );
|
84 |
+
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Register included DOM transformation filters
|
89 |
+
*
|
90 |
+
* @since 0.1
|
91 |
+
*/
|
92 |
+
function instant_articles_register_transformation_filters() {
|
93 |
+
|
94 |
+
include( dirname( __FILE__ ) . '/dom-transform-filters/class-instant-articles-dom-transform-filter-video.php' );
|
95 |
+
Instant_Articles_DOM_Transform_Filter_Runner::register( 'Instant_Articles_DOM_Transform_Filter_Video' );
|
96 |
+
|
97 |
+
include( dirname( __FILE__ ) . '/dom-transform-filters/class-instant-articles-dom-transform-filter-image.php' );
|
98 |
+
Instant_Articles_DOM_Transform_Filter_Runner::register( 'Instant_Articles_DOM_Transform_Filter_Image' );
|
99 |
+
|
100 |
+
include( dirname( __FILE__ ) . '/dom-transform-filters/class-instant-articles-dom-transform-filter-blockquote.php' );
|
101 |
+
Instant_Articles_DOM_Transform_Filter_Runner::register( 'Instant_Articles_DOM_Transform_Filter_Blockquote' );
|
102 |
+
|
103 |
+
include( dirname( __FILE__ ) . '/dom-transform-filters/class-instant-articles-dom-transform-filter-unordered-list.php' );
|
104 |
+
Instant_Articles_DOM_Transform_Filter_Runner::register( 'Instant_Articles_DOM_Transform_Filter_Unordered_List' );
|
105 |
+
|
106 |
+
include( dirname( __FILE__ ) . '/dom-transform-filters/class-instant-articles-dom-transform-filter-ordered-list.php' );
|
107 |
+
Instant_Articles_DOM_Transform_Filter_Runner::register( 'Instant_Articles_DOM_Transform_Filter_Ordered_List' );
|
108 |
+
|
109 |
+
include( dirname( __FILE__ ) . '/dom-transform-filters/class-instant-articles-dom-transform-filter-table.php' );
|
110 |
+
Instant_Articles_DOM_Transform_Filter_Runner::register( 'Instant_Articles_DOM_Transform_Filter_Table' );
|
111 |
+
|
112 |
+
include( dirname( __FILE__ ) . '/dom-transform-filters/class-instant-articles-dom-transform-filter-address.php' );
|
113 |
+
Instant_Articles_DOM_Transform_Filter_Runner::register( 'Instant_Articles_DOM_Transform_Filter_Address' );
|
114 |
+
|
115 |
+
//Instant articles only support h1 and h2. Convert h3-h6 to h2.
|
116 |
+
include( dirname( __FILE__ ) . '/dom-transform-filters/class-instant-articles-dom-transform-filter-heading.php' );
|
117 |
+
Instant_Articles_DOM_Transform_Filter_Runner::register( 'Instant_Articles_DOM_Transform_Filter_Heading' );
|
118 |
+
|
119 |
+
// Remove empty elements
|
120 |
+
include( dirname( __FILE__ ) . '/dom-transform-filters/class-instant-articles-dom-transform-filter-emptyelements.php' );
|
121 |
+
Instant_Articles_DOM_Transform_Filter_Runner::register( 'Instant_Articles_DOM_Transform_Filter_Emptyelements', 90 );
|
122 |
+
}
|
123 |
+
add_action( 'instant_articles_register_dom_transformation_filters', 'instant_articles_register_transformation_filters' );
|
124 |
+
|
125 |
+
|
126 |
+
/**
|
127 |
+
* Modify the main query for our feed.
|
128 |
+
*
|
129 |
+
* We want the posts in the modified order, to provide Facebook with content updates even for older posts.
|
130 |
+
* Facebook will only import 100 posts at the time.
|
131 |
+
* Facebook will only update posts modified within the last 24 hours
|
132 |
+
*
|
133 |
+
* @param WP_Query $query The WP_Query object. Passed by reference.
|
134 |
+
*/
|
135 |
+
function instant_articles_query( $query ) {
|
136 |
+
|
137 |
+
if ( $query->is_main_query() && $query->is_feed( INSTANT_ARTICLES_SLUG ) ) {
|
138 |
+
|
139 |
+
$query->set( 'orderby', 'modified' );
|
140 |
+
$query->set( 'posts_per_page', 100 );
|
141 |
+
$query->set( 'posts_per_rss', 100 );
|
142 |
+
|
143 |
+
/**
|
144 |
+
* If the constant INSTANT_ARTICLES_LIMIT_POSTS is set to true, we will limit the feed
|
145 |
+
* to only include posts which are modified within the last 24 hours.
|
146 |
+
* Facebook will initially need 100 posts to pass the review, but will only update
|
147 |
+
* already imported articles if they are modified within the last 24 hours.
|
148 |
+
*/
|
149 |
+
if ( defined( 'INSTANT_ARTICLES_LIMIT_POSTS' ) && INSTANT_ARTICLES_LIMIT_POSTS ) {
|
150 |
+
$query->set( 'date_query', array(
|
151 |
+
array(
|
152 |
+
'column' => 'post_modified',
|
153 |
+
'after' => '1 day ago',
|
154 |
+
),
|
155 |
+
) );
|
156 |
+
}
|
157 |
+
}
|
158 |
+
|
159 |
+
}
|
160 |
+
add_action( 'pre_get_posts', 'instant_articles_query', 10, 1 );
|
161 |
+
|
162 |
+
|
163 |
+
/**
|
164 |
+
* Filter the SQL query to not include posts with empty content -- FB will complain
|
165 |
+
*
|
166 |
+
* @since 0.1
|
167 |
+
* @param string $where The original where part of the SQL statement
|
168 |
+
* @param WP_Query $query The WP_Query instance
|
169 |
+
* @return string The modified where part of the SQL statement
|
170 |
+
*/
|
171 |
+
function instant_articles_query_where( $where, $query ) {
|
172 |
+
|
173 |
+
// Don’t modify the SQL query with a potentially expensive WHERE clause if we’re OK with fewer posts than 100 and are OK with filtering in the loop
|
174 |
+
if ( defined( 'INSTANT_ARTICLES_LIMIT_POSTS' ) && INSTANT_ARTICLES_LIMIT_POSTS ) {
|
175 |
+
return $where;
|
176 |
+
}
|
177 |
+
|
178 |
+
if ( $query->is_main_query() && $query->is_feed( INSTANT_ARTICLES_SLUG ) ) {
|
179 |
+
global $wpdb;
|
180 |
+
$where .= " AND {$wpdb->posts}.post_content NOT LIKE ''";
|
181 |
+
}
|
182 |
+
return $where;
|
183 |
+
|
184 |
+
}
|
185 |
+
add_filter( 'posts_where' , 'instant_articles_query_where', 10, 2 );
|
186 |
+
|
187 |
+
|
feed-template.php
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
header( 'Content-Type: ' . feed_content_type( 'rss2' ) . '; charset=' . get_option( 'blog_charset' ), true );
|
3 |
+
echo '<?xml version="1.0" encoding="' . esc_attr( get_option( 'blog_charset' ) ) . '"?' . '>';
|
4 |
+
|
5 |
+
$last_modified = null;
|
6 |
+
?>
|
7 |
+
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
|
8 |
+
<channel>
|
9 |
+
<title><?php bloginfo_rss( 'name' ); ?> - <?php esc_html_e( 'Instant Articles', 'instant-articles' ); ?></title>
|
10 |
+
<link><?php bloginfo_rss('url') ?></link>
|
11 |
+
<description><?php bloginfo_rss( 'description' ) ?></description>
|
12 |
+
<?php while ( have_posts() ) : the_post(); ?>
|
13 |
+
<?php
|
14 |
+
$instant_article_post = new Instant_Articles_Post( get_the_ID() );
|
15 |
+
|
16 |
+
// If we’re OK with a limited post set: Do not include posts with empty content -- FB will complain.
|
17 |
+
if ( defined( 'INSTANT_ARTICLES_LIMIT_POSTS' ) && INSTANT_ARTICLES_LIMIT_POSTS && ! strlen( trim( $instant_article_post->get_the_content() ) ) ) {
|
18 |
+
continue;
|
19 |
+
}
|
20 |
+
|
21 |
+
// Posts are sorted by modification time, so our first accepted post should be the one last modified
|
22 |
+
if ( is_null( $last_modified ) ) {
|
23 |
+
$last_modified = $instant_article_post->get_the_moddate_iso();
|
24 |
+
}
|
25 |
+
?>
|
26 |
+
<item>
|
27 |
+
<title><?php echo esc_html( $instant_article_post->get_the_title_rss() ); ?></title>
|
28 |
+
<link><?php echo esc_url( $instant_article_post->get_canonical_url() ); ?></link>
|
29 |
+
<content:encoded><![CDATA[<?php $instant_article_post->render(); ?>]]></content:encoded>
|
30 |
+
<guid isPermaLink="false"><?php esc_html( the_guid() ); ?></guid>
|
31 |
+
<description><![CDATA[<?php echo esc_html( $instant_article_post->get_the_excerpt_rss() ); ?>]]></description>
|
32 |
+
<pubDate><?php echo esc_html( $instant_article_post->get_the_pubdate_iso() ); ?></pubDate>
|
33 |
+
<modDate><?php echo esc_html( $instant_article_post->get_the_moddate_iso() ); ?></modDate>
|
34 |
+
<?php $authors = $instant_article_post->get_the_authors(); ?>
|
35 |
+
<?php if ( is_array( $authors ) && count( $authors ) ) : ?>
|
36 |
+
<?php foreach ( $authors as $author ) : ?>
|
37 |
+
<author><?php echo esc_html( $author->display_name ); ?></author>
|
38 |
+
<?php endforeach; ?>
|
39 |
+
<?php endif; ?>
|
40 |
+
</item>
|
41 |
+
<?php endwhile; ?>
|
42 |
+
<?php if ( ! is_null( $last_modified ) ) : ?>
|
43 |
+
<lastBuildDate><?php echo esc_html( $last_modified ); ?></lastBuildDate>
|
44 |
+
<?php endif; ?>
|
45 |
+
</channel>
|
46 |
+
</rss>
|
languages/instant-articles.pot
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#, fuzzy
|
2 |
+
msgid ""
|
3 |
+
msgstr ""
|
4 |
+
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
|
5 |
+
"Project-Id-Version: Instant Articles for WP\n"
|
6 |
+
"POT-Creation-Date: 2015-11-27 11:17+0100\n"
|
7 |
+
"PO-Revision-Date: 2015-11-12 20:54+0100\n"
|
8 |
+
"Last-Translator: Bjørn Johansen <bjorn@dekode.no>\n"
|
9 |
+
"Language-Team: Bjørn Johansen <bjorn@dekode.no>\n"
|
10 |
+
"MIME-Version: 1.0\n"
|
11 |
+
"Content-Type: text/plain; charset=UTF-8\n"
|
12 |
+
"Content-Transfer-Encoding: 8bit\n"
|
13 |
+
"X-Generator: Poedit 1.8.6\n"
|
14 |
+
"X-Poedit-Basepath: ..\n"
|
15 |
+
"X-Poedit-WPHeader: instant-articles.php\n"
|
16 |
+
"X-Poedit-SourceCharset: UTF-8\n"
|
17 |
+
"X-Poedit-KeywordsList: __;_e;_n:1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;esc_attr__;"
|
18 |
+
"esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c;"
|
19 |
+
"_n_noop:1,2;_nx_noop:3c,1,2;__ngettext_noop:1,2\n"
|
20 |
+
"X-Poedit-SearchPath-0: .\n"
|
21 |
+
"X-Poedit-SearchPathExcluded-0: *.js\n"
|
22 |
+
|
23 |
+
#: feed-template.php:7
|
24 |
+
msgid "Instant Articles"
|
25 |
+
msgstr ""
|
26 |
+
|
27 |
+
#. Plugin Name of the plugin/theme
|
28 |
+
msgid "Instant Articles for WP"
|
29 |
+
msgstr ""
|
30 |
+
|
31 |
+
#. Description of the plugin/theme
|
32 |
+
msgid "Add support for Instant Articles for Facebook to your WordPress site."
|
33 |
+
msgstr ""
|
34 |
+
|
35 |
+
#. Author of the plugin/theme
|
36 |
+
msgid "Dekode"
|
37 |
+
msgstr ""
|
38 |
+
|
39 |
+
#. Author URI of the plugin/theme
|
40 |
+
msgid "https://dekode.no"
|
41 |
+
msgstr ""
|
readme.txt
ADDED
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
=== Facebook Instant Articles for WP ===
|
2 |
+
Contributors: automattic, bjornjohansen, dekode, trrine, olethomas, philipjohn
|
3 |
+
Tags: instant articles, facebook, mobile
|
4 |
+
Requires at least: 4.3
|
5 |
+
Tested up to: 4.5
|
6 |
+
Stable tag: 0.2
|
7 |
+
License: GPLv2 or later
|
8 |
+
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
9 |
+
|
10 |
+
Enable Instant Articles for Facebook on your WordPress site.
|
11 |
+
|
12 |
+
== Description ==
|
13 |
+
|
14 |
+
This plugin adds support for Instant Articles for Facebook, which is a new way for publishers to distribute fast, interactive stories on Facebook. Instant Articles are preloaded in the Facebook mobile app so they load instantly.
|
15 |
+
|
16 |
+
With the plugin active, a special RSS feed will be available at the URL `/feed/instant-articles`.
|
17 |
+
|
18 |
+
Developers: please note that this plugin is still in early stages and the underlying APIs (like filters, classes, etc.) may change.
|
19 |
+
|
20 |
+
= Feed submission to Facebook =
|
21 |
+
|
22 |
+
Facebook has a review process where they verify that all Instant Articles are properly formatted, have content consistency with their mobile web counterparts, and adhere to their community standards and content policies. You will not be able to publish Instant Articles in Facebook until your feed has been approved.
|
23 |
+
|
24 |
+
It's important to note that if you use meta fields to add extra text, images or videos to your Posts, Facebook will expect you to add these to your Instant Articles output too. This plugin includes hooks to help you do that.
|
25 |
+
|
26 |
+
[See Facebook's documentation for full details of the submission process.](https://developers.facebook.com/docs/instant-articles)
|
27 |
+
|
28 |
+
Facebook requires a minimum number of articles in your feed before they will review it. Once your feed has been approved, you can set the constant `INSTANT_ARTICLES_LIMIT_POSTS` to `true` to limit the feed to only show posts that have been modified within the last 24 hours. (Facebook will ignore any articles which were last modified more than 24 hours ago.)
|
29 |
+
|
30 |
+
Facebook will fetch your feed every 3 minutes.
|
31 |
+
|
32 |
+
== Installation ==
|
33 |
+
|
34 |
+
1. Upload the folder to the `/wp-content/plugins/` directory
|
35 |
+
1. Activate the plugin through the 'Plugins' menu in WordPress
|
36 |
+
|
37 |
+
== Frequently Asked Questions ==
|
38 |
+
|
39 |
+
= How do I customize the output for my site? =
|
40 |
+
|
41 |
+
There are a number of filters available in the plugin for modifying the output. Note that these are not finalized and may change.
|
42 |
+
|
43 |
+
= How do I change the feed slug/URL? =
|
44 |
+
|
45 |
+
To change the feed slug, set the constant INSTANT_ARTICLES_SLUG to whatever you like. If you do, remember to flush the rewrite rules afterwards.
|
46 |
+
By default it is set to `instant-articles` which usually will give you a feed URL set to `/feed/instant-articles`
|
47 |
+
|
48 |
+
= How do I flush the rewrite rules after changing the feed slug? =
|
49 |
+
|
50 |
+
Usually simply visiting the permalinks settings page in the WordPress dashboard will do the trick (/wp-admin/options-permalink.php)
|
51 |
+
|
52 |
+
== Changelog ==
|
53 |
+
|
54 |
+
= 0.2 =
|
55 |
+
|
56 |
+
* Fixes bug in formatting of YouTube videos
|
57 |
+
* Fixes bug in formatting of Facebook embeds
|
58 |
+
* Adds support for subtitles - see `instant_articles_subtitle` filter
|
59 |
+
|
60 |
+
= 0.1 =
|
61 |
+
* Initial version
|
shortcodes.php
ADDED
@@ -0,0 +1,254 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
add_shortcode( 'gallery', 'instant_articles_shortcode_handler_gallery' );
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Gallery Shortcode. Based on the built in gallery shortcode
|
7 |
+
* @param array $attr Array of attributes passed to shortcode.
|
8 |
+
* @return string The generated content.
|
9 |
+
*/
|
10 |
+
function instant_articles_shortcode_handler_gallery( $attr ) {
|
11 |
+
|
12 |
+
$post = get_post();
|
13 |
+
|
14 |
+
if ( ! empty( $attr['ids'] ) ) {
|
15 |
+
// 'ids' is explicitly ordered, unless you specify otherwise.
|
16 |
+
if ( empty( $attr['orderby'] ) ) {
|
17 |
+
$attr['orderby'] = 'post__in';
|
18 |
+
}
|
19 |
+
$attr['include'] = $attr['ids'];
|
20 |
+
}
|
21 |
+
|
22 |
+
$atts = shortcode_atts( array(
|
23 |
+
'id' => $post ? $post->ID : 0,
|
24 |
+
'order' => 'ASC',
|
25 |
+
'orderby' => 'menu_order ID',
|
26 |
+
'include' => '',
|
27 |
+
'exclude' => '',
|
28 |
+
), $attr, 'gallery' );
|
29 |
+
|
30 |
+
$id = intval( $atts['id'] );
|
31 |
+
|
32 |
+
if ( ! empty( $atts['include'] ) ) {
|
33 |
+
$include = explode( ',', $atts['include'] );
|
34 |
+
|
35 |
+
$attachments = new WP_Query( array(
|
36 |
+
'post__in' => $include,
|
37 |
+
'post_status' => 'inherit',
|
38 |
+
'post_type' => 'attachment',
|
39 |
+
'post_mime_type' => 'image',
|
40 |
+
'order' => $atts['order'],
|
41 |
+
'orderby' => $atts['orderby']
|
42 |
+
) );
|
43 |
+
} elseif ( ! empty( $atts['exclude'] ) ) {
|
44 |
+
$exclude = explode( ',', $atts['exclude'] );
|
45 |
+
|
46 |
+
$attachments = new WP_Query( array(
|
47 |
+
'post_parent' => $id,
|
48 |
+
'post__not_in' => $exclude,
|
49 |
+
'post_status' => 'inherit',
|
50 |
+
'post_type' => 'attachment',
|
51 |
+
'post_mime_type' => 'image',
|
52 |
+
'order' => $atts['order'],
|
53 |
+
'orderby' => $atts['orderby']
|
54 |
+
) );
|
55 |
+
} else {
|
56 |
+
$attachments = new WP_Query( array(
|
57 |
+
'post_parent' => $id,
|
58 |
+
'post_status' => 'inherit',
|
59 |
+
'post_type' => 'attachment',
|
60 |
+
'post_mime_type' => 'image',
|
61 |
+
'order' => $atts['order'],
|
62 |
+
'orderby' => $atts['orderby']
|
63 |
+
) );
|
64 |
+
}
|
65 |
+
|
66 |
+
$output = '';
|
67 |
+
|
68 |
+
if ( $attachments->have_posts() ) {
|
69 |
+
$output = '<figure class="op-slideshow">';
|
70 |
+
|
71 |
+
while ( $attachments->have_posts() ) {
|
72 |
+
$attachments->the_post();
|
73 |
+
|
74 |
+
$image_src = wp_get_attachment_image_src( get_the_ID(), 'large' );
|
75 |
+
|
76 |
+
if ( $image_src ) {
|
77 |
+
$output .= '<figure>';
|
78 |
+
$output .= '<img src="' . esc_url( $image_src[0] ) . '" alt="' . esc_attr( get_the_title() ) . '">';
|
79 |
+
|
80 |
+
$caption = wp_strip_all_tags( $attachments->post->post_excerpt, true );
|
81 |
+
if ( $caption ) {
|
82 |
+
$output .= '<figcaption>' . esc_html( $caption ) . '</figcaption>';
|
83 |
+
}
|
84 |
+
$output .= '</figure>';
|
85 |
+
}
|
86 |
+
}
|
87 |
+
|
88 |
+
$output .= '</figure>';
|
89 |
+
}
|
90 |
+
|
91 |
+
wp_reset_postdata();
|
92 |
+
|
93 |
+
return $output;
|
94 |
+
}
|
95 |
+
|
96 |
+
|
97 |
+
add_shortcode( 'caption', 'instant_articles_shortcode_handler_caption' );
|
98 |
+
add_shortcode( 'wp_caption', 'instant_articles_shortcode_handler_caption' );
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Caption/WP-Caption Shortcode. Based on the built in caption shortcode
|
102 |
+
* @param array $attr Array of attributes passed to shortcode.
|
103 |
+
* @param string $content The content passed to the shortcode.
|
104 |
+
* @return string $output The generated content.
|
105 |
+
*/
|
106 |
+
function instant_articles_shortcode_handler_caption( $attr, $content = null ) {
|
107 |
+
// New-style shortcode with the caption inside the shortcode with the link and image tags.
|
108 |
+
if ( ! isset( $attr['caption'] ) ) {
|
109 |
+
if ( preg_match( '#((?:<a [^>]+>\s*)?<img [^>]+>(?:\s*</a>)?)(.*)#is', $content, $matches ) ) {
|
110 |
+
$content = $matches[1];
|
111 |
+
$attr['caption'] = trim( $matches[2] );
|
112 |
+
}
|
113 |
+
}
|
114 |
+
|
115 |
+
$atts = shortcode_atts( array(
|
116 |
+
'figcaptionclass' => '',
|
117 |
+
'caption' => '',
|
118 |
+
'cite' => '',
|
119 |
+
'subtitle' => '',
|
120 |
+
), $attr, 'caption' );
|
121 |
+
|
122 |
+
if ( ! strlen( trim( $atts['caption'] ) ) ) {
|
123 |
+
return '';
|
124 |
+
}
|
125 |
+
|
126 |
+
$output = '';
|
127 |
+
|
128 |
+
$content = do_shortcode( $content );
|
129 |
+
|
130 |
+
$doc = new DOMDocument();
|
131 |
+
$doc->loadHTML( '<html><body>' . $content . '</body></html>' );
|
132 |
+
$imgs = $doc->getElementsByTagName( 'img' );
|
133 |
+
|
134 |
+
if ( $imgs->length > 0 ) {
|
135 |
+
$img_src = $imgs->item(0)->getAttribute( 'src' );
|
136 |
+
if ( $img_src ) {
|
137 |
+
|
138 |
+
$alt = $imgs->item(0)->getAttribute( 'alt' );
|
139 |
+
|
140 |
+
$classes = array();
|
141 |
+
$classes = trim( $atts['figcaptionclass'] );
|
142 |
+
$class_attr = ( strlen( $classes ) ) ? ' class="' . esc_attr( $classes ) . '"' : '';
|
143 |
+
|
144 |
+
$caption = wp_strip_all_tags( $atts['caption'], true );
|
145 |
+
|
146 |
+
$subtitle = ( strlen( $atts['subtitle'] ) ) ? '<h2>' . esc_html( $atts['subtitle'] ) . '</h2>' : '';
|
147 |
+
$cite = ( strlen( $atts['cite'] ) ) ? '<cite>' . esc_html( $atts['cite'] ) . '</cite>' : '';
|
148 |
+
|
149 |
+
$output = '<figure><img src="' . esc_url( $img_src ) . '" alt="' . esc_attr( $alt ) . '"><figcaption' . $class_attr . '><h1>' . esc_html( $caption ) . '</h1>' . $subtitle . $cite . '</figcaption></figure>';
|
150 |
+
}
|
151 |
+
}
|
152 |
+
|
153 |
+
return $output;
|
154 |
+
}
|
155 |
+
|
156 |
+
|
157 |
+
add_shortcode( 'audio', 'instant_articles_shortcode_handler_audio' );
|
158 |
+
|
159 |
+
/**
|
160 |
+
* Audio Shortcode
|
161 |
+
* @param array $atts Array of attributes passed to shortcode.
|
162 |
+
* @return string The generated content.
|
163 |
+
*/
|
164 |
+
function instant_articles_shortcode_handler_audio( $atts ) {
|
165 |
+
|
166 |
+
if ( $atts['mp3'] ) {
|
167 |
+
$audio_src = $atts['mp3'];
|
168 |
+
} else if ( $atts['src'] ) {
|
169 |
+
$audio_src = $atts['src'];
|
170 |
+
} else if ( $atts['ogg'] ) {
|
171 |
+
$audio_src = $atts['ogg'];
|
172 |
+
} else if ( $atts['wav'] ) {
|
173 |
+
$audio_src = $atts['wav'];
|
174 |
+
} else {
|
175 |
+
$audio_src = null;
|
176 |
+
}
|
177 |
+
|
178 |
+
if ( $audio_src ) :
|
179 |
+
$file_url = array_reverse( explode( '/', $audio_src ) ) ;
|
180 |
+
|
181 |
+
return '<figure><audio title="' . esc_html( $file_url[0] ) . '"><source src="' . esc_url( $audio_src ) .'"></audio></figure>';
|
182 |
+
|
183 |
+
endif;
|
184 |
+
|
185 |
+
}
|
186 |
+
|
187 |
+
add_shortcode( 'video', 'instant_articles_shortcode_handler_video' );
|
188 |
+
|
189 |
+
/**
|
190 |
+
* Video Shortcode
|
191 |
+
* @param array $atts Array of attributes passed to shortcode.
|
192 |
+
* @return string The generated content.
|
193 |
+
*/
|
194 |
+
function instant_articles_shortcode_handler_video( $atts ) {
|
195 |
+
|
196 |
+
if ( $atts['mp4'] ) {
|
197 |
+
$video_src = $atts['mp4'];
|
198 |
+
$type = 'mp4';
|
199 |
+
} else if ( $atts['src'] ) {
|
200 |
+
$video_src = $atts['src'];
|
201 |
+
$file_src_array = array_reverse( explode( '.', $video_src ) ) ;
|
202 |
+
$type = $file_src_array[0];
|
203 |
+
} else if ( $atts['ogv'] ) {
|
204 |
+
$video_src = $atts['ogv'];
|
205 |
+
$type = 'ogv';
|
206 |
+
} else if ( $atts['webm'] ) {
|
207 |
+
$video_src = $atts['webm'];
|
208 |
+
$type = 'webm';
|
209 |
+
} else {
|
210 |
+
$video_src = null;
|
211 |
+
}
|
212 |
+
|
213 |
+
if ( $video_src ) :
|
214 |
+
|
215 |
+
return '<figure><video><source src="' . esc_url( $video_src ) . '" type="video/' . esc_html( $type ) . '" /></video></figure>';
|
216 |
+
|
217 |
+
endif;
|
218 |
+
}
|
219 |
+
|
220 |
+
add_shortcode( 'playlist', 'instant_articles_shortcode_handler_playlist' );
|
221 |
+
/**
|
222 |
+
* Playlist Shortcode
|
223 |
+
* @param array $atts Array of attributes passed to shortcode.
|
224 |
+
* @return string The generated content.
|
225 |
+
*/
|
226 |
+
function instant_articles_shortcode_handler_playlist( $atts ) {
|
227 |
+
|
228 |
+
if ( ! is_array( $atts ) || ! array_key_exists( 'ids', $atts ) ) {
|
229 |
+
return '';
|
230 |
+
}
|
231 |
+
|
232 |
+
$ids = explode( ',', $atts['ids'] );
|
233 |
+
|
234 |
+
$output = '<figure>';
|
235 |
+
|
236 |
+
if ( isset( $atts['type'] ) && 'video' === $atts['type'] ) :
|
237 |
+
foreach ($ids as $id) {
|
238 |
+
$extension = wp_check_filetype( wp_get_attachment_url( $id ) );
|
239 |
+
if ( is_array( $extension ) && array_key_exists( 'ext', $extension ) && false !== $extension['ext'] ) {
|
240 |
+
$output .= '<video><source src="' . wp_get_attachment_url( $id ) . '" type="video/' . $extension['ext'] .'" /></video>';
|
241 |
+
}
|
242 |
+
}
|
243 |
+
else :
|
244 |
+
foreach ($ids as $id) {
|
245 |
+
$output .= '<audio title="' . basename( get_attached_file( $id ) ) . '"><source src="' . wp_get_attachment_url( $id ) . '"></audio>';
|
246 |
+
}
|
247 |
+
endif;
|
248 |
+
|
249 |
+
$output .= '</figure>';
|
250 |
+
|
251 |
+
return $output;
|
252 |
+
}
|
253 |
+
|
254 |
+
|
template.php
ADDED
@@ -0,0 +1,124 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
<html lang="<?php echo esc_attr( get_bloginfo( 'language' ) ); ?>" prefix="op: http://media.facebook.com/op#">
|
3 |
+
<head>
|
4 |
+
<meta charset="<?php echo esc_attr( get_option( 'blog_charset' ) ); ?>">
|
5 |
+
<link rel="canonical" href="<?php echo esc_url( $this->get_canonical_url() ); ?>">
|
6 |
+
<meta property="op:markup_version" content="v1.0">
|
7 |
+
<meta property="fb:article_style" content="<?php echo esc_attr( $this->get_article_style() ); ?>">
|
8 |
+
|
9 |
+
<?php
|
10 |
+
/**
|
11 |
+
* Fires in the head element of each article
|
12 |
+
*
|
13 |
+
* @since 0.1
|
14 |
+
*
|
15 |
+
* @param Instant_Articles_Post $ia_post The current article object
|
16 |
+
*/
|
17 |
+
do_action( 'instant_articles_article_head', $this );
|
18 |
+
?>
|
19 |
+
</head>
|
20 |
+
|
21 |
+
<body>
|
22 |
+
<article>
|
23 |
+
<header>
|
24 |
+
<!-- The cover -->
|
25 |
+
<?php $cover_media = $this->get_cover_media(); ?>
|
26 |
+
<?php if ( 'image' === $cover_media->type ) : ?>
|
27 |
+
<figure>
|
28 |
+
<img src="<?php echo esc_url( $cover_media->src ); ?>" />
|
29 |
+
<?php if ( strlen( $cover_media->caption ) ) : ?>
|
30 |
+
<figcaption><?php echo esc_html( $cover_media->caption ); ?></figcaption>
|
31 |
+
<?php endif; ?>
|
32 |
+
</figure>
|
33 |
+
<?php elseif ( 'video' === $cover_media->type ) : ?>
|
34 |
+
<figure>
|
35 |
+
<video>
|
36 |
+
<source src="<?php echo esc_url( $cover_media->src ); ?>" type="<?php echo esc_attr( $cover_media->mime_type ); ?>" />
|
37 |
+
</video>
|
38 |
+
</figure>
|
39 |
+
<?php elseif ( 'slideshow' === $cover_media->type ) : ?>
|
40 |
+
<figure class="op-slideshow">
|
41 |
+
<?php foreach ( $cover_media->items as $item ) : ?>
|
42 |
+
<figure>
|
43 |
+
<img src="<?php echo esc_url( $item->src ); ?>" />
|
44 |
+
</figure>
|
45 |
+
<?php endforeach; ?>
|
46 |
+
</figure>
|
47 |
+
<?php endif; ?>
|
48 |
+
|
49 |
+
<h1><?php echo esc_html( $this->get_the_title() ); ?></h1>
|
50 |
+
|
51 |
+
<?php if ( $this->has_subtitle() ) : ?>
|
52 |
+
<h2><?php echo esc_html( $this->get_the_subtitle() ); ?></h2>
|
53 |
+
<?php endif; ?>
|
54 |
+
|
55 |
+
<!-- The date and time when your article was originally published -->
|
56 |
+
<time class="op-published" datetime="<?php echo esc_attr( $this->get_the_pubdate_iso() ); ?>"><?php echo esc_html( $this->get_the_pubdate() ); ?></time>
|
57 |
+
|
58 |
+
<!-- The date and time when your article was last updated -->
|
59 |
+
<time class="op-modified" datetime="<?php echo esc_attr( $this->get_the_moddate_iso() ); ?>"><?php echo esc_html( $this->get_the_moddate() ); ?></time>
|
60 |
+
|
61 |
+
<!-- The authors of your article -->
|
62 |
+
<?php $authors = $this->get_the_authors(); ?>
|
63 |
+
<?php if ( is_array( $authors ) && count( $authors ) ) : ?>
|
64 |
+
<?php foreach ( $authors as $author ) : ?>
|
65 |
+
<address>
|
66 |
+
<?php
|
67 |
+
$attributes = '';
|
68 |
+
if ( strlen( $author->user_url ) ) {
|
69 |
+
$attributes = ' href="' . esc_url( $author->user_url ) . '"';
|
70 |
+
|
71 |
+
if ( isset( $author->role_contribution ) && strlen( $author->role_contribution ) ) {
|
72 |
+
$attributes .= ' title="' . esc_attr( $author->role_contribution ) . '"';
|
73 |
+
}
|
74 |
+
|
75 |
+
if ( isset( $author->user_url_rel ) && strlen( $author->user_url_rel ) ) {
|
76 |
+
$attributes .= ' rel="' . esc_attr( $author->user_url_rel ) . '"';
|
77 |
+
}
|
78 |
+
}
|
79 |
+
?>
|
80 |
+
<a<?php echo $attributes; ?>>
|
81 |
+
<?php echo esc_html( $author->display_name ); ?>
|
82 |
+
</a>
|
83 |
+
<?php if ( strlen( $author->bio ) ) : ?>
|
84 |
+
<?php echo esc_html( $author->bio ); ?>
|
85 |
+
<?php endif; ?>
|
86 |
+
</address>
|
87 |
+
<?php endforeach; ?>
|
88 |
+
<?php endif; ?>
|
89 |
+
|
90 |
+
<?php if ( $kicker_text = $this->get_the_kicker() ) : ?>
|
91 |
+
<!-- A kicker for your article -->
|
92 |
+
<h3 class="op-kicker"><?php echo esc_html( $kicker_text); ?></h3>
|
93 |
+
<?php endif; ?>
|
94 |
+
|
95 |
+
<?php
|
96 |
+
/**
|
97 |
+
* Fires in the header element of each article
|
98 |
+
*
|
99 |
+
* @since 0.1
|
100 |
+
*
|
101 |
+
* @param Instant_Articles_Post $ia_post The current article object
|
102 |
+
*/
|
103 |
+
do_action( 'instant_articles_article_header', $this );
|
104 |
+
?>
|
105 |
+
|
106 |
+
</header>
|
107 |
+
|
108 |
+
<!-- Article body goes here -->
|
109 |
+
<?php echo $this->get_the_content(); ?>
|
110 |
+
|
111 |
+
<footer>
|
112 |
+
<?php if ( $footer_credits = $this->get_the_footer_credits( ) ) : ?>
|
113 |
+
<!-- Credits for your article -->
|
114 |
+
<aside><?php echo esc_html( $footer_credits ); ?></aside>
|
115 |
+
<?php endif; ?>
|
116 |
+
|
117 |
+
<?php if ( $footer_copyright = $this->get_the_footer_copyright( ) ) : ?>
|
118 |
+
<!-- Copyright details for your article -->
|
119 |
+
<small><?php echo esc_html ( $footer_copyright ); ?></small>
|
120 |
+
<?php endif; ?>
|
121 |
+
</footer>
|
122 |
+
</article>
|
123 |
+
</body>
|
124 |
+
</html>
|